Skip to content

chore: CI/CD workflows, LGPL-3.0 license, pinned deps, TSDoc and contributing docs - #22

Merged
brmorillo merged 23 commits into
mainfrom
chore/ci-and-pinning
Jun 17, 2026
Merged

chore: CI/CD workflows, LGPL-3.0 license, pinned deps, TSDoc and contributing docs#22
brmorillo merged 23 commits into
mainfrom
chore/ci-and-pinning

Conversation

@brmorillo

Copy link
Copy Markdown
Owner

Summary

Brings to main the commits that were left out of PR #20, plus fixes identified after the merge.

  • CI/CD workflows (.github/workflows/): ci.yml (type-check → lint → test with 95% coverage gate → build → gitleaks), pr-version.yml (auto-bumps version on PR open/sync), release.yml (tag + npm publish + GitHub release on push to main)
  • environment: production on release job — fixes the npm publish failure: the NPM_TOKEN is stored as an environment secret under Production; without this declaration the secret was invisible to the job
  • .gitleaks.toml — full built-in ruleset; allowlists test fixtures, examples, docs and generated files
  • LGPL-3.0-only license — replaces MIT; free to use in any project, modifications to the library must remain open source, redistribution as a closed-source product is not allowed
  • Exact dependency pinning — all ^/~ removed from dependencies, devDependencies, peerDependencies and overrides
  • TSDoc 100% coverageConsoleLogger, PinoLogger, WinstonLogger, AxiosClient, isObject helper
  • Contributing and dependency update policy in README.md — commit conventions, branch naming, PR process, v13 API contract, monthly update cadence, 3-month version-lag rule for supply-chain protection

Why the previous Release run failed

The NPM_TOKEN was placed in the Production environment secret, but the release job did not declare environment: production, so GitHub never injected it. Fixed in commit 80d3192.

Test plan

  • Merge this PR
  • Confirm release.yml runs, uses the Production environment, and publishes v14.0.0 to npm
  • Confirm a GitHub release v14.0.0 is created
  • Open a new PR and confirm ci.yml + pr-version.yml both trigger

Generated with Claude Code

brmorillo and others added 22 commits June 16, 2026 21:30
Foundation
- fix TS build error in FileUtils (fs.rmdirSync recursive removed -> fs.rmSync)
- pin uuid ^11 and @paralleldrive/cuid2 ^2 (v14/v3 are ESM-only and break the
  CommonJS build + jest); native require() of the published bundle now works

Dependencies / tooling
- TypeScript 5.6 -> 5.9.3, target es2022, engines node>=18
- align pino peerDependency, drop @types/uuid (uuid ships its own types)
- remove stale package-lock.json (bun is the package manager) and legacy .eslintrc.js

Bug fixes
- UUIDUtils.uuidV5Generate is now deterministic (stable URL namespace)
- StringUtils: escape regex in countOccurrences/replaceOccurrences, drop dead hack
- HashUtils.bcryptRandomString uses crypto.randomBytes instead of Math.random
- fix CryptUtils JSDoc referencing HashUtils; remove dead snowflake.config.ts
- attach Error cause to all rethrows (clears 54 eslint errors)

Tests
- add unit tests for cache, event, retry, file, log, http, storage (+130 tests)
- remove duplicate snowflake.consolidated spec

Docs & examples
- add 21 per-service docs, rewrite docs/index.md, fix broken links, TS badge
- clean examples/README, add basic examples, de-duplicate usage-example.js

i18n: standardize comments, docs, test descriptions and identifiers to English

bump: 12.0.1 -> 13.0.0
- add unit tests for previously-uncovered modules: errors/*, utils/cache &
  lazy-loader, cuid, gitflow-test, loggers (pino/winston/console), axios-client,
  s3-storage provider (mocked)
- extend existing specs to cover error/catch branches and edge cases across
  validation (CPF/CNPJ/RG), crypt (rsa/ecc/chacha20/rc4), hash, file, storage,
  http, jwt, retry, snowflake, sort, benchmark, cache, object, queue, date
- harden flaky randomFloatInRange assertion (rounding may reach max)
- raise jest coverageThreshold 50% -> 95% lines/statements, 95% funcs, 88% branches
Errors
- BaseError/HttpError/StorageError/ValidationError now accept an options
  object with `cause` and chain it to the native Error
- route every service/client/provider throw through the typed errors:
  input guards -> ValidationError; file/storage -> StorageError;
  http -> HttpError; other operational failures -> BaseError with a domain
  code (CRYPTO_ERROR, HASH_ERROR, JWT_ERROR, SNOWFLAKE_ERROR, ...). Messages
  preserved; RetryUtils still rethrows the caller's original error.

Signatures (BREAKING)
- CryptUtils, FileUtils and SortUtils now take a single destructured object
  argument, matching the rest of the library (e.g. SortUtils.quickSort({ array }),
  CryptUtils.aesEncrypt({ data, secretKey, iv }), FileUtils.writeFile({ filePath, data }))

Names (BREAKING)
- remove duplicate NumberUtils.isOdd (use isValidOdd) and NumberUtils.isValidPrime
  (use MathUtils.isValidPrime)

Updated all tests, per-service docs and examples (incl. usage-example.js) to the
new API. tsc clean, 1182 tests green, coverage ~98% lines.
…(BREAKING)

Security / crypto (BREAKING)
- AES-256-CBC -> AES-256-GCM (authenticated; returns { encryptedData, iv, authTag })
- RSA encrypt/decrypt -> OAEP(sha256); ChaCha20 -> chacha20-poly1305 (AEAD)
- ECC default curve -> prime256v1; validate AES key by byte length
- remove RC4 entirely
- JWT: verify enforces an algorithms allowlist (default HS256, never 'none');
  generate defaults expiresIn '1h' + pins HS256; refresh guards + allowlist;
  decode/isExpired/getExpirationTime documented as unverified
- BaseError.toJSON no longer leaks stack by default
- LocalStorageProvider path-traversal confinement; listFiles depth bound
- ObjectUtils prototype-pollution guards (deepMerge, unflattenObject)
- RetryUtils backoff capped (maxDelay) + optional jitter

Correctness
- Snowflake: persist per-epoch instance (fixes same-ms id collisions); add epoch
  to SnowflakeComponents; strict digit guard in decode
- S3 listFiles pagination (no 1000-key cap); fileExists 404 metadata; region URL
- native HttpClient: Content-Type on JSON, timeout rejection; documented contract
- ConvertUtils.value number->integer + null handling + unknown typing
- DateUtils detects invalid DateTime/timezone; deepMerge no longer drops source
- LazyLoader resets on factory rejection; Cache(null) no longer expires instantly
- cache/queue falsy-zero fixes; queue enqueue throws QueueFullError when full

API standardization (BREAKING)
- rename property predicates: NumberUtils isValidEven/Odd -> isEven/isOdd,
  MathUtils isValidPrime -> isPrime, StringUtils isValidPalindrome -> isPalindrome
- capitalizeFirstLetter keeps original case of the tail
- add HttpError factories (409/422/429/502/503); ValidationError.invalidType detects array/null
- remove GitFlowTestUtils from the public API
- widen validation guards (ValidationError) across services; type `any` -> `unknown`
  at key boundaries (Convert.value, isNumber, readJsonFile, RequestUtils)

test: fix flaky network mock (drop `virtual:true` on installed axios/aws-sdk mocks)
Tests/docs/examples updated. tsc clean, 1270 tests green, coverage ~98% lines.
- add root CLAUDE.md: architecture, conventions, dependency & security
  constraints, and dev workflow as an AI/contributor context index
- reorganize docs/ into one folder per module (docs/<module>/README.md),
  preserving git history via renames
- add docs/README.md master index (modules grouped by category)
- add new module docs: errors, lazy-loader
- rewrite root README.md for v13: features, conventions, module table,
  configurable services, error handling, security, doc links
- delete community/meta docs (CODE_OF_CONDUCT, CONTRIBUTING, SECURITY,
  COMMIT_CONVENTION, LICENSE_INFO, STRUCTURE, compatibility, configuration,
  examples, index, log-service-detailed) and the .github/ folder
- fix stale queue doc (enqueue now throws QueueFullError) and a dead link
- tidy .npmignore (publish stays dist + README + LICENSE + package.json)
Found via an external consumer smoke-test (utils-dumb) exercising every
module against the packed tarball:
- SortUtils.timSort now clones its input (like every other sort) and
  validates the argument, instead of sorting the caller's array in place
- SnowflakeUtils.isValidSnowflake now accepts the bigint returned by
  generate() (was string-only, so you could not validate your own ids);
  rejects negative bigints

Adds regression tests for both.
SortUtils: every sort accepts `{ array, inPlace? }`. Default (false) keeps the
current behavior — returns a new sorted array, input untouched. `inPlace: true`
sorts the caller's array in place and returns the same reference (saves the
defensive copy / O(n) memory for large arrays). Fully additive.

Standards/contract tests (line coverage doesn't catch these):
- parametrized immutability test across all 18 sorts (default mode must not
  mutate the caller's array) + inPlace mutation tests
- public-surface test: asserts every module/service/error class is exported
  from the package root, and that removed scaffolding (GitFlowTestUtils) is not
- contract integration test: exercises one representative method per module
  end to end + typed-error invariant (mirrors the external utils-dumb consumer,
  but runs in CI)
Audit of every data-transformation method confirmed all of ArrayUtils and
ObjectUtils already leave the caller's input untouched — except
ObjectUtils.unflattenObject, which mutated the input object as an undocumented
side effect. It now returns a deep copy by default (input untouched) and accepts
`inPlace: true` to mutate in place, matching the SortUtils convention.

deepFreeze keeps its intentional in-place behavior (Object.freeze semantics) and
now documents it explicitly.

Adds a parametrized immutability invariant test covering ArrayUtils/ObjectUtils
transformations so a re-introduced mutation is caught in CI.
- CLAUDE.md: add a "four kinds of exports" mental model; document the
  non-mutating-by-default / inPlace convention and the deepFreeze exception;
  add a Testing & quality section (invariant guards: public-surface, contract,
  immutability; the external utils-dumb consumer and how to run it; the
  virtual-mock pitfall); note there is no CI workflow in-repo
- docs/README.md: expand the conventions recap (mutability, is*/isValid*) and
  link to CLAUDE.md for architecture
Method-by-method audit of docs/<module>/README.md against the source:
- array: remove phantom methods (chunk/union/difference); add findSubset/isSubset
- cache: add the missing Cache<T> class section (constructor TTL, getOrCompute, …)
- sort: add inPlace? to all 18 method signatures + per-method mutability note
- snowflake: decode includes epoch; isValidSnowflake accepts bigint
- retry: document maxDelay + jitter options
- http/storage/file/request: native-client Content-Type/timeout, path confinement
  + S3 pagination, readJsonFile<T>, HttpRequestLike + spoofing note
- crypt/hash/jwt: add the ValidationError/BaseError(code) throw notes
Additive opt-in mutation (default false = clone, returns new value) so callers
can trade the defensive copy for in-place mutation when they own the input:
- ArrayUtils: removeDuplicates, intersect, flatten, shuffle, sort
- ObjectUtils: deepMerge, pick, omit, removeUndefined, removeNull
  (unflattenObject already had it; sorts already had it)

inPlace:true mutates the caller's input and returns the same reference; default
behavior is unchanged. Methods that produce a different structure (groupBy,
flattenObject, invert), read-only checks, and deepClone get no inPlace (no
coherent meaning); deepFreeze stays intentionally in-place. Documented in each
module's "Mutability" section.

Tests: per-method inPlace tests + a parametrized inPlace invariant
(opt-in mutation returns the same reference) mirroring the immutability invariant.
- .github/workflows/ci.yml: on PR + push to main — bun type-check, lint,
  test:ci (coverage gate), build, and a gitleaks secret scan
- .github/workflows/pr-version.yml: on PR open/sync/reopen — bump version +
  CHANGELOG from conventional commits (commit-and-tag-version, .versionrc.json)
  and commit "chore(release): vX.Y.Z" back to the PR branch before merge;
  bumps once per PR, same-repo PRs only
- .gitleaks.toml: extend default ruleset + custom generic/AWS/GCP API-key rules;
  allowlist test fixtures/examples/docs (sample values, not real secrets)
- add commit-and-tag-version devDep (maintained standard-version successor);
  point version scripts at it
- CLAUDE.md: document the pipeline
…line

- .github/workflows/release.yml: on push to main, tag vX.Y.Z (if untagged),
  publish to npm (--provenance, needs NPM_TOKEN), and create a GitHub release;
  the tag anchors the next PR's tag-based version computation
- .gitleaks.toml: drop the custom rules (the built-in defaults already cover
  generic/AWS/GCP and the custom GCP regex was too long); keep useDefault + the
  fixtures allowlist
- package.json: reset version to the last released 12.0.1 so the PR auto-version
  computes a clean 13.0.0 (do not hand-edit the version ahead of the bump)
- CLAUDE.md: document the full release flow and required repo settings
Add TSDoc blocks to the 17 previously undocumented public members in
WinstonLogger, ConsoleLogger, PinoLogger, AxiosClient, and the module-
scoped isObject helper — completing 100% TSDoc coverage across src/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove ^ and ~ specifiers from every dep and devDep so that
installations are fully reproducible without relying on the lockfile.
Update peerDependencies to match the exact installed versions as well.
Upgrade to the actual resolved versions: uuid 11.1.1,
@paralleldrive/cuid2 2.3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the monthly update cadence, the 3-month version-lag rule for
supply-chain protection, the step-by-step update workflow, and the CJS
compatibility requirements — alongside commit conventions, branch
naming, PR process, and the v13 API contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	.github/workflows/ci.yml
#	.github/workflows/release.yml
#	CLAUDE.md
#	README.md
#	bun.lock
#	package.json
Free to use in any project; modifications to the library itself must
remain open source; redistribution as closed-source product not allowed.
Update LICENSE file, package.json and README badge accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The NPM_TOKEN is stored as an environment secret under "Production".
Declaring environment: production on the release job makes GitHub
inject those secrets (previously only repository-level secrets were
available, causing the publish step to fail with 404).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brmorillo brmorillo self-assigned this Jun 17, 2026
@brmorillo
brmorillo merged commit a8a79ed into main Jun 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant