Skip to content

feat(purl): add canonicalizePurl to eol-shared (PR 1/3 — purl-identity-canonicalization)#28

Merged
facundo-herodevs merged 1 commit into
mainfrom
feat/purl-identity-canonicalization
Jun 24, 2026
Merged

feat(purl): add canonicalizePurl to eol-shared (PR 1/3 — purl-identity-canonicalization)#28
facundo-herodevs merged 1 commit into
mainfrom
feat/purl-identity-canonicalization

Conversation

@facundo-herodevs

@facundo-herodevs facundo-herodevs commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds canonicalizePurl to @herodevs/eol-shared — a single canonical-identity function so the same logical package yields one docId / routing key / DB key across eol-engine (write) and eol-api (read).
  • It parses + serializes through packageurl-js, applying only go→golang / rubygems→gem type aliases and lowercasing namespace/name for case-insensitive ecosystems (nuget, composer, cargo, npm). golang/maven/gem names are case-preserved; pypi/pub are left to the library.
  • Exposes an optional onUncanonicalized(purl, error) hook so consumers can observe serialize-failure passthroughs (metric/log) without breaking the never-throw contract.

Context — PR 1 of 3 (cross-repo)

This is the first of three ordered PRs for the purl-identity-canonicalization change. eol-shared must merge and be tagged v0.1.21 before the consumers can pin it:

  1. eol-shared (this PR) → tag v0.1.21
  2. eol-engine — add the dep + canonicalize at write chokepoints
  3. eol-api — add the dep + canonicalize at read chokepoints

Tagging v0.1.21 is a separate step after this merges; it is intentionally not part of this PR.

Changes

File Change
src/eol/utils.ts New canonicalizePurl(purl, onUncanonicalized?) (library-based parse → alias → case-fold → serialize), with a guarded observability hook
src/eol/utils.test.ts Authoritative unit suite: per-ecosystem case matrix, alias cases, version byte-preservation, encoding, idempotency, qualifier/subpath edges, unparseable passthrough, and hook firing rules
src/index.ts Export canonicalizePurl
package.json Widen the test glob to src/**/*.test.* so nested specs are run

Behavior notes

  • Never throws. Unparseable input → returned unchanged (silent). Parsed-but-not-serializable input (a known packageurl-js@2 golang-validator limitation on bare-major versions like @v1) → returned unchanged and the optional onUncanonicalized hook fires. A hook that itself throws is swallowed.
  • Qualifiers/subpath are canonically re-encoded by the serializer (deterministic, idempotent).
  • Centralizing on a single pinned packageurl-js major makes write and read produce identical output regardless of each consumer's own version.

Test plan

  • npm ci (installs locked deps) — passes
  • npm run type-check — clean
  • npm run build — exit 0
  • npm run lint — clean
  • npm run format:check — clean
  • npm run test — 174/174 pass (Node 22 & 24)
  • Verified canonical output and the golang edge against packageurl-js@2.0.1
  • Validated fixed-point behavior against ~15M prod docs (0 structural re-keying in scope)

…ization

Add canonicalizePurl to @herodevs/eol-shared: parses with packageurl-js, applies go->golang / rubygems->gem type aliases and lowercases namespace/name for case-insensitive ecosystems (nuget, composer, cargo, npm), then re-serializes. Exposes an optional onUncanonicalized hook so callers can observe serialize-failure passthroughs without breaking the never-throw contract. Includes the authoritative unit test suite and widens the test glob to run nested specs.
@facundo-herodevs facundo-herodevs force-pushed the feat/purl-identity-canonicalization branch from c05b202 to 744ff92 Compare June 24, 2026 21:17
@facundo-herodevs facundo-herodevs enabled auto-merge (squash) June 24, 2026 21:17
@facundo-herodevs facundo-herodevs merged commit cfa3cda into main Jun 24, 2026
7 checks passed
@facundo-herodevs facundo-herodevs deleted the feat/purl-identity-canonicalization branch June 24, 2026 22:27
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.

3 participants