Skip to content

fix(registries): use buildPURL for proper percent-encoding in urls().purl()#38

Merged
oritwoen merged 1 commit intomainfrom
fix/registry-purl-encoding
Mar 13, 2026
Merged

fix(registries): use buildPURL for proper percent-encoding in urls().purl()#38
oritwoen merged 1 commit intomainfrom
fix/registry-purl-encoding

Conversation

@oritwoen
Copy link
Owner

Summary

  • urls().purl() in every registry manually built PURL strings without percent-encoding
  • For npm scoped packages like @babel/core, the output pkg:npm/@babel/core can't round-trip through parsePURL because the @ gets mistaken for a version separator
  • Switched all six registries to use the shared buildPURL function which encodes per the PURL spec

Test plan

  • Added test for npm scoped package PURL output (@babel/core -> pkg:npm/%40babel/core)
  • Full test suite passes (322 tests)

…purl()

All registries manually constructed PURL strings without percent-encoding.
For npm scoped packages like @babel/core, this produced pkg:npm/@babel/core
which can't round-trip through parsePURL - the @ gets mistaken for a
version separator and parsing fails.

Switched every registry to use the shared buildPURL function which
handles encoding correctly per the PURL spec.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 507f261c-7142-4a5e-a383-7ed5caf9ad4b

📥 Commits

Reviewing files that changed from the base of the PR and between 476754a and 3ecd497.

📒 Files selected for processing (7)
  • src/registries/alpm.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/rubygems.ts
  • test/unit/registries.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Keep TypeScript imports with .ts extension style in source files
Do not parse PURLs outside src/core/purl.ts; call createFromPURL or parsePURL instead
Do not duplicate retry/backoff constants outside src/core/client.ts; centralize all retry logic
Do not hardcode cache TTL in random modules; use DEFAULT_TTL from src/cache/lockfile.ts
Respect Node.js runtime floor of >=22.6.0 and modern syntax assumptions

Use .ts import suffixes consistently

Files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
src/registries/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/registries/**/*.ts: Do not bypass Client for direct fetch logic in registries; use the centralized HTTP client
Do not hardcode ecosystem registries using switch logic; use plugin-like registration factories instead

Register ecosystem adapters through factory and side-effect import hub when adding to src/registries/

Files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
src/**/!(client).ts

📄 CodeRabbit inference engine (src/AGENTS.md)

Do not implement fetch/retry behavior outside src/core/client.ts

Files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
src/registries/*.ts

📄 CodeRabbit inference engine (src/registries/AGENTS.md)

src/registries/*.ts: Each registry adapter in src/registries/ must expose ecosystem, fetchPackage, fetchVersions, fetchDependencies, fetchMaintainers, and urls exports
Convert source-specific fields into core Package/Version/Dependency/Maintainer shapes before returning from adapter methods
Map remote API failures to core error classes in registry adapters
Do not call fetch directly in registry adapters; use Client instead
Do not return raw upstream payloads through public methods in registry adapters
Keep adapter-to-adapter imports forbidden; each adapter internals must be self-contained
Keep per-registry API quirks isolated to that adapter file before normalizing to shared types

Files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Vitest globals with test/unit and test/e2e split for testing

test/**/*.test.ts: Test files should use *.test.ts naming convention
Vitest globals (describe, it, expect, vi) are enabled and should be used without imports in test files

Files:

  • test/unit/registries.test.ts
test/unit/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

Unit tests should rely on mocks/spies rather than external dependencies

Files:

  • test/unit/registries.test.ts
test/unit/{registry,registries}.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

Registry factory and plugin tests should be located in test/unit/registry.test.ts and test/unit/registries.test.ts for registration and per-ecosystem behavior testing

Files:

  • test/unit/registries.test.ts
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:29.354Z
Learning: Applies to src/commands/**/*.ts : PURL input resolution and optional `pkg:` prefix normalization should be implemented in `src/commands/shared.ts`
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:38.679Z
Learning: No duplicate PURL parsing utilities outside purl.ts
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/purl.test.ts : PURL contract tests should be located in `test/unit/purl.test.ts` for parse/build validation
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:29.354Z
Learning: Applies to src/commands/**/*.ts : Do not reimplement PURL parsing in individual command files; use the shared implementation from `src/commands/shared.ts`
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:03.586Z
Learning: Applies to src/registries/**/*.ts : Do not hardcode ecosystem registries using switch logic; use plugin-like registration factories instead
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Each registry adapter in `src/registries/` must expose `ecosystem`, `fetchPackage`, `fetchVersions`, `fetchDependencies`, `fetchMaintainers`, and `urls` exports
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:38.679Z
Learning: PURL parse and build behavior is the single source of truth for validation and normalization
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:12.605Z
Learning: Applies to src/{commands,helpers}/**/*.ts : Route all parsing logic through `createFromPURL` instead of duplicating in commands or helpers
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/{registry,registries}.test.ts : Registry factory and plugin tests should be located in `test/unit/registry.test.ts` and `test/unit/registries.test.ts` for registration and per-ecosystem behavior testing
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Keep per-registry API quirks isolated to that adapter file before normalizing to shared types
📚 Learning: 2026-03-10T07:36:29.354Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:29.354Z
Learning: Applies to src/commands/**/*.ts : PURL input resolution and optional `pkg:` prefix normalization should be implemented in `src/commands/shared.ts`

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • test/unit/registries.test.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:38.679Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:38.679Z
Learning: No duplicate PURL parsing utilities outside purl.ts

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • test/unit/registries.test.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:12.605Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:12.605Z
Learning: Applies to src/helpers.ts : Wrap `createFromPURL` when adding convenience API in `src/helpers.ts` and preserve normalization path

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:03.586Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:03.586Z
Learning: Applies to src/**/*.ts : Do not parse PURLs outside `src/core/purl.ts`; call `createFromPURL` or `parsePURL` instead

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • test/unit/registries.test.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:29.354Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:29.354Z
Learning: Applies to src/commands/**/*.ts : Do not reimplement PURL parsing in individual command files; use the shared implementation from `src/commands/shared.ts`

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:12.605Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:12.605Z
Learning: Applies to src/{commands,helpers}/**/*.ts : Route all parsing logic through `createFromPURL` instead of duplicating in commands or helpers

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:54.862Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/purl.test.ts : PURL contract tests should be located in `test/unit/purl.test.ts` for parse/build validation

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • test/unit/registries.test.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/cargo.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:03.586Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:03.586Z
Learning: Applies to src/registries/**/*.ts : Do not hardcode ecosystem registries using switch logic; use plugin-like registration factories instead

Applied to files:

  • src/registries/alpm.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:46.164Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Keep per-registry API quirks isolated to that adapter file before normalizing to shared types

Applied to files:

  • src/registries/alpm.ts
  • test/unit/registries.test.ts
📚 Learning: 2026-03-10T07:36:12.605Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:12.605Z
Learning: Changes to shared models in `src/core/types.ts` impact all registries and helpers

Applied to files:

  • src/registries/alpm.ts
  • src/registries/rubygems.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
📚 Learning: 2026-03-10T07:36:38.679Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:38.679Z
Learning: PURL parse and build behavior is the single source of truth for validation and normalization

Applied to files:

  • src/registries/alpm.ts
  • src/registries/packagist.ts
  • src/registries/pypi.ts
📚 Learning: 2026-03-10T07:36:54.862Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/{registry,registries}.test.ts : Registry factory and plugin tests should be located in `test/unit/registry.test.ts` and `test/unit/registries.test.ts` for registration and per-ecosystem behavior testing

Applied to files:

  • test/unit/registries.test.ts
📚 Learning: 2026-03-10T07:36:54.862Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/{lockfile,cached-registry}.test.ts : Cache behavior tests should be located in `test/unit/lockfile.test.ts` and `test/unit/cached-registry.test.ts` for freshness, TTL, integrity, and wrapper behavior validation

Applied to files:

  • test/unit/registries.test.ts
📚 Learning: 2026-03-10T07:36:54.862Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:54.862Z
Learning: Applies to test/unit/{license,repository}.test.ts : Normalization tests should be located in `test/unit/license.test.ts` and `test/unit/repository.test.ts` for canonical output normalization

Applied to files:

  • test/unit/registries.test.ts
📚 Learning: 2026-03-10T07:36:46.164Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Each registry adapter in `src/registries/` must expose `ecosystem`, `fetchPackage`, `fetchVersions`, `fetchDependencies`, `fetchMaintainers`, and `urls` exports

Applied to files:

  • test/unit/registries.test.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:46.164Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Use `npm.ts` as a reference implementation pattern when building new registry adapters

Applied to files:

  • test/unit/registries.test.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:46.164Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Convert source-specific fields into core `Package`/`Version`/`Dependency`/`Maintainer` shapes before returning from adapter methods

Applied to files:

  • src/registries/packagist.ts
📚 Learning: 2026-03-10T07:36:38.679Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:38.679Z
Learning: Applies to src/core/**/*.ts : Throw typed errors (InvalidPURLError, NotFoundError, RateLimitError) instead of plain Error in core flows

Applied to files:

  • src/registries/packagist.ts
  • src/registries/pypi.ts
  • src/registries/npm.ts
📚 Learning: 2026-03-10T07:36:46.164Z
Learnt from: CR
Repo: oritwoen/regxa PR: 0
File: src/registries/AGENTS.md:0-0
Timestamp: 2026-03-10T07:36:46.164Z
Learning: Applies to src/registries/*.ts : Map remote API failures to core error classes in registry adapters

Applied to files:

  • src/registries/npm.ts
🧬 Code graph analysis (7)
src/registries/alpm.ts (3)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/index.ts (1)
  • buildPURL (6-6)
src/core/index.ts (1)
  • buildPURL (4-4)
src/registries/rubygems.ts (3)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/index.ts (1)
  • buildPURL (6-6)
src/core/index.ts (1)
  • buildPURL (4-4)
test/unit/registries.test.ts (1)
src/registries/npm.ts (1)
  • urls (314-338)
src/registries/packagist.ts (1)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/registries/pypi.ts (3)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/index.ts (1)
  • buildPURL (6-6)
src/core/index.ts (1)
  • buildPURL (4-4)
src/registries/cargo.ts (3)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/index.ts (1)
  • buildPURL (6-6)
src/core/index.ts (1)
  • buildPURL (4-4)
src/registries/npm.ts (3)
src/core/purl.ts (1)
  • buildPURL (127-159)
src/index.ts (1)
  • buildPURL (6-6)
src/core/index.ts (1)
  • buildPURL (4-4)
🔇 Additional comments (7)
src/registries/cargo.ts (1)

15-15: PURL generation is correctly centralized for Cargo.

This removes manual string assembly and delegates encoding to buildPURL, which is the right fix for reserved-character safety.

Also applies to: 275-277

src/registries/pypi.ts (1)

15-15: PyPI PURL path now follows the shared builder correctly.

Keeping normalizeName() before buildPURL() preserves PyPI canonical naming while fixing percent-encoding behavior.

Also applies to: 219-221

src/registries/rubygems.ts (1)

15-15: RubyGems PURL generation change is correct.

buildPURL removes manual delimiter handling and gives consistent encoding for gem names and versions.

Also applies to: 224-226

test/unit/registries.test.ts (1)

122-128: This test covers the scoped npm PURL regression path well.

It checks scoped and unscoped names with and without versions, so the encoding fix is guarded against rollback.

src/registries/alpm.ts (1)

14-14: ALPM PURL construction now uses the right shared path.

Passing namespace and pkgName into buildPURL keeps adapter logic intact and fixes encoding in one place.

Also applies to: 174-177

src/registries/packagist.ts (1)

15-15: Packagist PURL generation update is solid.

Using parseName() + buildPURL() keeps vendor/package semantics and removes manual concatenation edge cases.

Also applies to: 251-254

src/registries/npm.ts (1)

15-15: This npm scoped-name PURL fix is correct.

Splitting scoped names into namespace + bare package and passing both to buildPURL fixes the @ delimiter ambiguity that broke round-trips.

Also applies to: 332-336


📝 Walkthrough

Walkthrough

Refactors six registry implementations (alpm, cargo, npm, packagist, pypi, rubygems) to replace manual PURL string construction with calls to a centralized buildPURL utility function. Each registry now delegates purl generation to this shared utility while extracting appropriate namespace and name fields. A test verifies npm scoped package handling.

Changes

Cohort / File(s) Summary
Registry PURL builders
src/registries/alpm.ts, src/registries/cargo.ts, src/registries/pypi.ts, src/registries/rubygems.ts
Replaced inline PURL string construction with buildPURL() calls. Each registry imports buildPURL and passes type, name, and version parameters. Behavior preserved, implementation path changed.
Registry PURL builders (namespace-aware)
src/registries/npm.ts, src/registries/packagist.ts
Replaced inline PURL construction with buildPURL() calls that extract namespace. npm extracts namespace via this.extractNamespace() for scoped packages; packagist parses via this.parseName() to derive vendor and package separately.
Test coverage
test/unit/registries.test.ts
Added npm scoped package test verifying purl output for @babel/core (encoded as %40babel/core) and unscoped lodash packages.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • aeitwoen

Poem

🏗️ PURL strings once scattered and loose,
Now gathered in buildPURL's embrace,
Six registries march in uniform grace,
One function to rule the construction—
No more manual string reduction! 📦✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the main change: switching registries to use buildPURL for proper percent-encoding in PURL generation.
Description check ✅ Passed Description clearly explains the problem (missing percent-encoding breaks npm scoped packages), the solution (use shared buildPURL), and includes test coverage verification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/registry-purl-encoding
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/registry-purl-encoding
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot requested a review from aeitwoen March 13, 2026 18:41
@codeant-ai
Copy link

codeant-ai bot commented Mar 13, 2026

Sequence Diagram

This PR changes registry URL helpers to generate PURLs through a shared builder instead of manual string concatenation. The new flow percent-encodes package parts (notably npm scopes) so generated PURLs can be parsed reliably in round-trip scenarios.

sequenceDiagram
    participant Caller
    participant Registry
    participant PURLBuilder
    participant PURLParser

    Caller->>Registry: Request purl from urls helper
    Registry->>Registry: Derive namespace and package name
    Registry->>PURLBuilder: Build purl with type name namespace version
    PURLBuilder-->>Registry: Return percent encoded purl
    Registry-->>Caller: Return valid purl string
    Caller->>PURLParser: Parse generated purl
    PURLParser-->>Caller: Return parsed package fields
Loading

Generated by CodeAnt AI

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 7 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Modifies the logic for generating package identifiers (PURLs) across multiple registry modules and introduces new string parsing logic for scoped packages in npm/packagist.

Architecture diagram
sequenceDiagram
    participant Client
    participant Registry as Registry (npm, cargo, etc.)
    participant PURL as buildPURL (core/purl)

    Note over Client, PURL: PURL Generation Flow (Refactored)

    Client->>Registry: urls().purl(name, version)
    
    alt Registry: npm
        Registry->>Registry: Extract namespace (e.g., "@babel")
        Registry->>Registry: Extract bare name (e.g., "core")
    else Registry: packagist / alpm
        Registry->>Registry: Parse vendor/namespace and package name
    else Registry: pypi
        Registry->>Registry: Normalize name
    end

    Registry->>PURL: CHANGED: buildPURL({ type, namespace, name, version })
    
    Note over PURL: NEW: Applies percent-encoding<br/>(e.g., "@" becomes "%40")

    PURL-->>Registry: Encoded PURL string
    Registry-->>Client: "pkg:type/namespace/name@version"

    Note over Client, Registry: Example (npm scoped package)
    Client->>Registry: purl("@babel/core", "7.0.0")
    Registry->>PURL: buildPURL({ type: "npm", namespace: "@babel", name: "core", ... })
    PURL-->>Client: "pkg:npm/%40babel/core@7.0.0"
Loading

@oritwoen oritwoen merged commit 1d9b27f into main Mar 13, 2026
3 checks passed
@oritwoen oritwoen deleted the fix/registry-purl-encoding branch March 13, 2026 18:48
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