@agntn/registries 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/dist/_chunks/cached-registry.d.mts +35 -0
- package/dist/_chunks/cached-registry.d.mts.map +1 -0
- package/dist/_chunks/errors.d.mts +90 -0
- package/dist/_chunks/errors.d.mts.map +1 -0
- package/dist/_chunks/helpers.mjs +95 -0
- package/dist/_chunks/helpers.mjs.map +1 -0
- package/dist/_chunks/index.d.mts +142 -0
- package/dist/_chunks/index.d.mts.map +1 -0
- package/dist/_chunks/index2.d.mts +50 -0
- package/dist/_chunks/index2.d.mts.map +1 -0
- package/dist/_chunks/index3.d.mts +18 -0
- package/dist/_chunks/index3.d.mts.map +1 -0
- package/dist/_chunks/lockfile.d.mts +50 -0
- package/dist/_chunks/lockfile.d.mts.map +1 -0
- package/dist/_chunks/paths.d.mts +11 -0
- package/dist/_chunks/paths.d.mts.map +1 -0
- package/dist/_chunks/purl.mjs +192 -0
- package/dist/_chunks/purl.mjs.map +1 -0
- package/dist/_chunks/registries.mjs +1197 -0
- package/dist/_chunks/registries.mjs.map +1 -0
- package/dist/_chunks/registry.d.mts +39 -0
- package/dist/_chunks/registry.d.mts.map +1 -0
- package/dist/_chunks/registry.mjs +201 -0
- package/dist/_chunks/registry.mjs.map +1 -0
- package/dist/_chunks/storage.d.mts +22 -0
- package/dist/_chunks/storage.d.mts.map +1 -0
- package/dist/_chunks/types.d.mts +73 -0
- package/dist/_chunks/types.d.mts.map +1 -0
- package/dist/ai.d.mts +35 -0
- package/dist/ai.d.mts.map +1 -0
- package/dist/ai.mjs +50 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/cache/cached-registry.d.mts +4 -0
- package/dist/cache/cached-registry.mjs +92 -0
- package/dist/cache/cached-registry.mjs.map +1 -0
- package/dist/cache/index.d.mts +8 -0
- package/dist/cache/index.mjs +12 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/cache/lockfile.d.mts +2 -0
- package/dist/cache/lockfile.mjs +68 -0
- package/dist/cache/lockfile.mjs.map +1 -0
- package/dist/cache/paths.d.mts +2 -0
- package/dist/cache/paths.mjs +26 -0
- package/dist/cache/paths.mjs.map +1 -0
- package/dist/cache/storage.d.mts +2 -0
- package/dist/cache/storage.mjs +40 -0
- package/dist/cache/storage.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +19 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/commands/cache.d.mts +7 -0
- package/dist/commands/cache.d.mts.map +1 -0
- package/dist/commands/cache.mjs +80 -0
- package/dist/commands/cache.mjs.map +1 -0
- package/dist/commands/deps.d.mts +23 -0
- package/dist/commands/deps.d.mts.map +1 -0
- package/dist/commands/deps.mjs +72 -0
- package/dist/commands/deps.mjs.map +1 -0
- package/dist/commands/info.d.mts +26 -0
- package/dist/commands/info.d.mts.map +1 -0
- package/dist/commands/info.mjs +54 -0
- package/dist/commands/info.mjs.map +1 -0
- package/dist/commands/maintainers.d.mts +23 -0
- package/dist/commands/maintainers.d.mts.map +1 -0
- package/dist/commands/maintainers.mjs +46 -0
- package/dist/commands/maintainers.mjs.map +1 -0
- package/dist/commands/shared.d.mts +27 -0
- package/dist/commands/shared.d.mts.map +1 -0
- package/dist/commands/shared.mjs +69 -0
- package/dist/commands/shared.mjs.map +1 -0
- package/dist/commands/versions.d.mts +31 -0
- package/dist/commands/versions.d.mts.map +1 -0
- package/dist/commands/versions.mjs +58 -0
- package/dist/commands/versions.mjs.map +1 -0
- package/dist/core/index.d.mts +4 -0
- package/dist/core/index.mjs +3 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.mjs +10 -0
- package/dist/registries/index.d.mts +4 -0
- package/dist/registries/index.mjs +4 -0
- package/dist/types.d.mts +3 -0
- package/dist/types.mjs +1 -0
- package/package.json +102 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { c as URLBuilder, i as Package, l as Version, n as Dependency, r as Maintainer } from "./types.mjs";
|
|
2
|
+
import { o as Client, t as Registry } from "./registry.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/registries/npm.d.ts
|
|
5
|
+
/** npm registry client. */
|
|
6
|
+
declare class NpmRegistry extends Registry {
|
|
7
|
+
constructor(baseURL: string, client: Client);
|
|
8
|
+
readonly baseURL: string;
|
|
9
|
+
readonly client: Client;
|
|
10
|
+
ecosystem(): string;
|
|
11
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
12
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
13
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
14
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
15
|
+
urls(): URLBuilder;
|
|
16
|
+
/** Build a stable dedup key. Prefers email (unique per account), falls back to name. */
|
|
17
|
+
private maintainerKey;
|
|
18
|
+
/** Encode package name for URL (handle scoped packages). */
|
|
19
|
+
private encodeName;
|
|
20
|
+
/** Extract namespace from scoped package name. */
|
|
21
|
+
private extractNamespace;
|
|
22
|
+
/** Extract and normalize license. */
|
|
23
|
+
private extractLicense;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/registries/cargo.d.ts
|
|
27
|
+
/** Crates.io registry client. */
|
|
28
|
+
declare class CargoRegistry extends Registry {
|
|
29
|
+
constructor(baseURL: string, client: Client);
|
|
30
|
+
readonly baseURL: string;
|
|
31
|
+
readonly client: Client;
|
|
32
|
+
ecosystem(): string;
|
|
33
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
34
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
35
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
36
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
37
|
+
urls(): URLBuilder;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/registries/pypi.d.ts
|
|
41
|
+
/** PyPI registry client. */
|
|
42
|
+
declare class PyPIRegistry extends Registry {
|
|
43
|
+
constructor(baseURL: string, client: Client);
|
|
44
|
+
readonly baseURL: string;
|
|
45
|
+
readonly client: Client;
|
|
46
|
+
private readonly downloadUrls;
|
|
47
|
+
ecosystem(): string;
|
|
48
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
49
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
50
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
51
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
52
|
+
urls(): URLBuilder;
|
|
53
|
+
/** Match a filename to a version from the known versions list. */
|
|
54
|
+
private matchFileVersion;
|
|
55
|
+
/** Normalize package name per PEP 503. */
|
|
56
|
+
private normalizeName;
|
|
57
|
+
/** Extract repository URL from project_urls. */
|
|
58
|
+
private extractRepository;
|
|
59
|
+
/** Find a project URL by key, case-insensitive. */
|
|
60
|
+
private findProjectUrl;
|
|
61
|
+
/** Parse comma-separated keywords. */
|
|
62
|
+
private parseKeywords;
|
|
63
|
+
/** Parse PEP 508 dependency string into a normalized Dependency. */
|
|
64
|
+
private parsePEP508;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/registries/rubygems.d.ts
|
|
68
|
+
/** RubyGems registry client. */
|
|
69
|
+
declare class RubyGemsRegistry extends Registry {
|
|
70
|
+
constructor(baseURL: string, client: Client);
|
|
71
|
+
readonly baseURL: string;
|
|
72
|
+
readonly client: Client;
|
|
73
|
+
ecosystem(): string;
|
|
74
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
75
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
76
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
77
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
78
|
+
urls(): URLBuilder;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/registries/packagist.d.ts
|
|
82
|
+
/** Packagist registry client. */
|
|
83
|
+
declare class PackagistRegistry extends Registry {
|
|
84
|
+
constructor(baseURL: string, client: Client);
|
|
85
|
+
readonly baseURL: string;
|
|
86
|
+
readonly client: Client;
|
|
87
|
+
ecosystem(): string;
|
|
88
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
89
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
90
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
91
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
92
|
+
urls(): URLBuilder;
|
|
93
|
+
/** Parse "vendor/package" format. */
|
|
94
|
+
private parseName;
|
|
95
|
+
/** Find the highest non-dev version. */
|
|
96
|
+
private findLatestVersion;
|
|
97
|
+
/** Extract and normalize licenses. */
|
|
98
|
+
private extractLicenses;
|
|
99
|
+
/** Skip PHP and extension dependencies. */
|
|
100
|
+
private shouldSkipDependency;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/registries/alpm.d.ts
|
|
104
|
+
/** ALPM registry client — routes to Arch Linux official repos or AUR based on namespace. */
|
|
105
|
+
declare class AlpmRegistry extends Registry {
|
|
106
|
+
constructor(baseURL: string, client: Client);
|
|
107
|
+
readonly baseURL: string;
|
|
108
|
+
readonly client: Client;
|
|
109
|
+
ecosystem(): string;
|
|
110
|
+
fetchPackage(name: string, signal?: AbortSignal): Promise<Package>;
|
|
111
|
+
fetchVersions(name: string, signal?: AbortSignal): Promise<Version[]>;
|
|
112
|
+
fetchDependencies(name: string, version: string, signal?: AbortSignal): Promise<Dependency[]>;
|
|
113
|
+
fetchMaintainers(name: string, signal?: AbortSignal): Promise<Maintainer[]>;
|
|
114
|
+
urls(): URLBuilder;
|
|
115
|
+
private fetchOfficialPackage;
|
|
116
|
+
private fetchOfficialVersions;
|
|
117
|
+
private fetchOfficialDependencies;
|
|
118
|
+
private fetchOfficialMaintainers;
|
|
119
|
+
/** Search official repos by exact package name. Prefers x86_64 results. */
|
|
120
|
+
private searchOfficial;
|
|
121
|
+
private fetchAurPackage;
|
|
122
|
+
private fetchAurVersions;
|
|
123
|
+
private fetchAurDependencies;
|
|
124
|
+
private fetchAurMaintainers;
|
|
125
|
+
/** Fetch package info from AUR RPC v5. */
|
|
126
|
+
private fetchAurInfo;
|
|
127
|
+
/** Parse "namespace/name" into components. Defaults to "arch" namespace. */
|
|
128
|
+
private parseName;
|
|
129
|
+
private assertSupportedNamespace;
|
|
130
|
+
private assertVersionMatches;
|
|
131
|
+
/** Format ALPM version: epoch:pkgver-pkgrel (epoch omitted when 0). */
|
|
132
|
+
private formatVersion;
|
|
133
|
+
/** Build normalized dependency list from ALPM dependency arrays. */
|
|
134
|
+
private buildDependencies;
|
|
135
|
+
/** Parse a dependency string like "glibc>=2.33" or "bash". */
|
|
136
|
+
private parseDep;
|
|
137
|
+
/** Parse optional dependency like "perl-locale-gettext: translation support". */
|
|
138
|
+
private parseOptDep;
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { CargoRegistry as a, PyPIRegistry as i, PackagistRegistry as n, NpmRegistry as o, RubyGemsRegistry as r, AlpmRegistry as t };
|
|
142
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/registries/npm.ts","../../src/registries/cargo.ts","../../src/registries/pypi.ts","../../src/registries/rubygems.ts","../../src/registries/packagist.ts","../../src/registries/alpm.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { c as URLBuilder, i as Package, l as Version, n as Dependency, r as Maintainer } from "./types.mjs";
|
|
2
|
+
import { o as Client } from "./registry.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/helpers.d.ts
|
|
5
|
+
/** Fetch normalized package metadata from a PURL. */
|
|
6
|
+
declare function fetchPackageFromPURL(purl: string, signal?: AbortSignal, client?: Client): Promise<Package>;
|
|
7
|
+
/** Fetch all versions from a PURL. */
|
|
8
|
+
declare function fetchVersionsFromPURL(purl: string, signal?: AbortSignal, client?: Client): Promise<Version[]>;
|
|
9
|
+
/** Fetch dependencies for a specific version from a PURL. */
|
|
10
|
+
declare function fetchDependenciesFromPURL(purl: string, signal?: AbortSignal, client?: Client): Promise<Dependency[]>;
|
|
11
|
+
/** Fetch maintainers from a PURL. */
|
|
12
|
+
declare function fetchMaintainersFromPURL(purl: string, signal?: AbortSignal, client?: Client): Promise<Maintainer[]>;
|
|
13
|
+
/** Bulk fetch packages from multiple PURLs, with concurrency limit. */
|
|
14
|
+
declare function bulkFetchPackages(purls: string[], options?: {
|
|
15
|
+
concurrency?: number;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
client?: Client;
|
|
18
|
+
}): Promise<Map<string, Package>>;
|
|
19
|
+
/**
|
|
20
|
+
* Select the best matching version from a list.
|
|
21
|
+
*
|
|
22
|
+
* Resolution order:
|
|
23
|
+
* 1. Exact match for `requested` (non-yanked/deprecated/retracted)
|
|
24
|
+
* 2. Exact match for `latest` (non-yanked/deprecated/retracted)
|
|
25
|
+
* 3. Newest available version with no negative status (by publishedAt)
|
|
26
|
+
*
|
|
27
|
+
* Returns `null` when no usable version exists.
|
|
28
|
+
*/
|
|
29
|
+
declare function selectVersion(versions: Version[], options?: {
|
|
30
|
+
requested?: string;
|
|
31
|
+
latest?: string;
|
|
32
|
+
}): Version | null;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the best documentation URL for a package.
|
|
35
|
+
*
|
|
36
|
+
* Fallback chain:
|
|
37
|
+
* 1. `package.documentation` (explicit docs URL from registry)
|
|
38
|
+
* 2. `package.homepage` (project homepage)
|
|
39
|
+
* 3. `urls.documentation()` (ecosystem default, e.g. docs.rs, rubydoc.info)
|
|
40
|
+
*/
|
|
41
|
+
declare function resolveDocsUrl(pkg: Package, urls: URLBuilder, version?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the best README URL for a package.
|
|
44
|
+
*
|
|
45
|
+
* Returns the ecosystem-specific URL where the package README can be fetched.
|
|
46
|
+
*/
|
|
47
|
+
declare function resolveReadmeUrl(pkg: Package, urls: URLBuilder, version?: string): string;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { fetchVersionsFromPURL as a, selectVersion as c, fetchPackageFromPURL as i, fetchDependenciesFromPURL as n, resolveDocsUrl as o, fetchMaintainersFromPURL as r, resolveReadmeUrl as s, bulkFetchPackages as t };
|
|
50
|
+
//# sourceMappingURL=index2.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index2.d.mts","names":[],"sources":["../../src/helpers.ts"],"mappings":";;;;;iBAMsB,oBAAA,CACpB,IAAA,UACA,MAAA,GAAS,WAAA,EACT,MAAA,GAAS,MAAA,GACR,OAAA,CAAQ,OAAA;AAJX;AAAA,iBAUsB,qBAAA,CACpB,IAAA,UACA,MAAA,GAAS,WAAA,EACT,MAAA,GAAS,MAAA,GACR,OAAA,CAAQ,OAAA;;iBAMW,yBAAA,CACpB,IAAA,UACA,MAAA,GAAS,WAAA,EACT,MAAA,GAAS,MAAA,GACR,OAAA,CAAQ,UAAA;;iBASW,wBAAA,CACpB,IAAA,UACA,MAAA,GAAS,WAAA,EACT,MAAA,GAAS,MAAA,GACR,OAAA,CAAQ,UAAA;;iBAQW,iBAAA,CACpB,KAAA,YACA,OAAA;EAAY,WAAA;EAAsB,MAAA,GAAS,WAAA;EAAa,MAAA,GAAS,MAAA;AAAA,IAChE,OAAA,CAAQ,GAAA,SAAY,OAAA;;;;;;;AAtCvB;;;;iBA0EgB,aAAA,CACd,QAAA,EAAU,OAAA,IACV,OAAA;EACE,SAAA;EACA,MAAA;AAAA,IAED,OAAA;;;;;;;;;iBAiCa,cAAA,CAAe,GAAA,EAAK,OAAA,EAAS,IAAA,EAAM,UAAA,EAAY,OAAA;;AAvG/D;;;;iBAgHgB,gBAAA,CAAiB,GAAA,EAAK,OAAA,EAAS,IAAA,EAAM,UAAA,EAAY,OAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { o as Client, t as Registry } from "./registry.mjs";
|
|
2
|
+
import { Storage } from "unstorage";
|
|
3
|
+
|
|
4
|
+
//#region src/cache/index.d.ts
|
|
5
|
+
/** Options for creating a cached registry. */
|
|
6
|
+
interface CreateCachedOptions {
|
|
7
|
+
/** Custom base URL for the registry API. */
|
|
8
|
+
baseURL?: string;
|
|
9
|
+
/** Custom HTTP client. */
|
|
10
|
+
client?: Client;
|
|
11
|
+
/** Custom unstorage instance. Overrides the globally configured storage. */
|
|
12
|
+
storage?: Storage;
|
|
13
|
+
}
|
|
14
|
+
/** Create a registry instance with caching + lockfile tracking. */
|
|
15
|
+
declare function createCached(ecosystem: string, options?: CreateCachedOptions): Registry;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { createCached as n, CreateCachedOptions as t };
|
|
18
|
+
//# sourceMappingURL=index3.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index3.d.mts","names":[],"sources":["../../src/cache/index.ts"],"mappings":";;;;;UA+BiB,mBAAA;EAIN;EAFT,OAAA;EAIU;EAFV,MAAA,GAAS,MAAA;EAEQ;EAAjB,OAAA,GAAU,OAAA;AAAA;;iBAII,YAAA,CAAa,SAAA,UAAmB,OAAA,GAAU,mBAAA,GAAsB,QAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Storage } from "unstorage";
|
|
2
|
+
|
|
3
|
+
//#region src/cache/lockfile.d.ts
|
|
4
|
+
/** Default TTL per data type (seconds). */
|
|
5
|
+
declare const DEFAULT_TTL: {
|
|
6
|
+
readonly package: 3600;
|
|
7
|
+
readonly versions: 1800;
|
|
8
|
+
readonly dependencies: 86400;
|
|
9
|
+
readonly maintainers: 86400;
|
|
10
|
+
};
|
|
11
|
+
type EntryType = keyof typeof DEFAULT_TTL;
|
|
12
|
+
/** A single cached entry tracked in the lockfile. */
|
|
13
|
+
interface LockfileEntry {
|
|
14
|
+
/** PURL-style key: 'npm:lodash', 'cargo:serde' */
|
|
15
|
+
key: string;
|
|
16
|
+
/** What kind of data: package, versions, dependencies, maintainers */
|
|
17
|
+
type: EntryType;
|
|
18
|
+
/** Unix timestamp (ms) when fetched */
|
|
19
|
+
fetchedAt: number;
|
|
20
|
+
/** TTL in seconds */
|
|
21
|
+
ttl: number;
|
|
22
|
+
/** Latest version at time of caching (for package entries) */
|
|
23
|
+
latestVersion?: string;
|
|
24
|
+
/** Content hash (sha256 of JSON-serialized value) for integrity */
|
|
25
|
+
integrity: string;
|
|
26
|
+
}
|
|
27
|
+
/** The full lockfile structure. */
|
|
28
|
+
interface Lockfile {
|
|
29
|
+
version: number;
|
|
30
|
+
entries: Record<string, LockfileEntry>;
|
|
31
|
+
}
|
|
32
|
+
/** Read the lockfile from storage. Returns empty lockfile if not found or invalid. */
|
|
33
|
+
declare function readLockfile(storage?: Storage): Promise<Lockfile>;
|
|
34
|
+
/** Write the lockfile to storage. */
|
|
35
|
+
declare function writeLockfile(lockfile: Lockfile, storage?: Storage): Promise<void>;
|
|
36
|
+
/** Build a cache key from ecosystem + name + type + optional version. */
|
|
37
|
+
declare function cacheKey(ecosystem: string, name: string, type: EntryType, version?: string): string;
|
|
38
|
+
/** Check if a lockfile entry is still fresh. */
|
|
39
|
+
declare function isFresh(entry: LockfileEntry): boolean;
|
|
40
|
+
/** Get a lockfile entry if it exists and is fresh. Returns null otherwise. */
|
|
41
|
+
declare function getFreshEntry(lockfile: Lockfile, key: string): LockfileEntry | null;
|
|
42
|
+
/** Upsert an entry in the lockfile. Does NOT persist — call writeLockfile() after. */
|
|
43
|
+
declare function setEntry(lockfile: Lockfile, entry: LockfileEntry): void;
|
|
44
|
+
/** Remove an entry from the lockfile. */
|
|
45
|
+
declare function removeEntry(lockfile: Lockfile, key: string): void;
|
|
46
|
+
/** Remove all stale entries from the lockfile. Returns count of removed entries. */
|
|
47
|
+
declare function pruneStale(lockfile: Lockfile): number;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { cacheKey as a, pruneStale as c, setEntry as d, writeLockfile as f, LockfileEntry as i, readLockfile as l, EntryType as n, getFreshEntry as o, Lockfile as r, isFresh as s, DEFAULT_TTL as t, removeEntry as u };
|
|
50
|
+
//# sourceMappingURL=lockfile.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.d.mts","names":[],"sources":["../../src/cache/lockfile.ts"],"mappings":";;;;cAOa,WAAA;EAAA;;;;;KAOD,SAAA,gBAAyB,WAAA;;UAGpB,aAAA;;EAEf,GAAA;EALU;EAOV,IAAA,EAAM,SAAA;;EAEN,SAAA;EAT8C;EAW9C,GAAA;EAR4B;EAU5B,aAAA;EANe;EAQf,SAAA;AAAA;;UAIe,QAAA;EACf,OAAA;EACA,OAAA,EAAS,MAAA,SAAe,aAAA;AAAA;;iBAQJ,YAAA,CAAa,OAAA,GAAU,OAAA,GAAU,OAAA,CAAQ,QAAA;AAV/D;AAAA,iBAsBsB,aAAA,CAAc,QAAA,EAAU,QAAA,EAAU,OAAA,GAAU,OAAA,GAAU,OAAA;;iBAM5D,QAAA,CACd,SAAA,UACA,IAAA,UACA,IAAA,EAAM,SAAA,EACN,OAAA;;iBAOc,OAAA,CAAQ,KAAA,EAAO,aAAA;;iBAMf,aAAA,CAAc,QAAA,EAAU,QAAA,EAAU,GAAA,WAAc,aAAA;;iBAQhD,QAAA,CAAS,QAAA,EAAU,QAAA,EAAU,KAAA,EAAO,aAAA;AA3CpD;AAAA,iBAgDgB,WAAA,CAAY,QAAA,EAAU,QAAA,EAAU,GAAA;;iBAKhC,UAAA,CAAW,QAAA,EAAU,QAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/cache/paths.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the cache directory following platform conventions:
|
|
4
|
+
* - Linux: $XDG_CACHE_HOME/registries or ~/.cache/registries
|
|
5
|
+
* - macOS: ~/Library/Caches/registries
|
|
6
|
+
* - Windows: %LOCALAPPDATA%/registries/cache
|
|
7
|
+
*/
|
|
8
|
+
declare function getCacheDir(): string;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getCacheDir as t };
|
|
11
|
+
//# sourceMappingURL=paths.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.mts","names":[],"sources":["../../src/cache/paths.ts"],"mappings":";;AAWA;;;;;iBAAgB,WAAA,CAAA"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { l as InvalidPURLError, n as create } from "./registry.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a license string to SPDX expression.
|
|
4
|
+
*
|
|
5
|
+
* Handles common variations: "MIT License" → "MIT", "Apache 2.0" → "Apache-2.0".
|
|
6
|
+
* Passes through already-valid SPDX identifiers unchanged.
|
|
7
|
+
*/
|
|
8
|
+
function normalizeLicense(raw) {
|
|
9
|
+
if (!raw) return "";
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed) return "";
|
|
12
|
+
const lower = trimmed.toLowerCase();
|
|
13
|
+
const known = LICENSE_MAP.get(lower);
|
|
14
|
+
if (known) return known;
|
|
15
|
+
return trimmed;
|
|
16
|
+
}
|
|
17
|
+
const LICENSE_MAP = new Map([
|
|
18
|
+
["mit license", "MIT"],
|
|
19
|
+
["mit", "MIT"],
|
|
20
|
+
["apache license 2.0", "Apache-2.0"],
|
|
21
|
+
["apache 2.0", "Apache-2.0"],
|
|
22
|
+
["apache-2.0", "Apache-2.0"],
|
|
23
|
+
["apache license, version 2.0", "Apache-2.0"],
|
|
24
|
+
["apache2", "Apache-2.0"],
|
|
25
|
+
["bsd-2-clause", "BSD-2-Clause"],
|
|
26
|
+
["bsd-3-clause", "BSD-3-Clause"],
|
|
27
|
+
["bsd 2-clause", "BSD-2-Clause"],
|
|
28
|
+
["bsd 3-clause", "BSD-3-Clause"],
|
|
29
|
+
["simplified bsd license", "BSD-2-Clause"],
|
|
30
|
+
["new bsd license", "BSD-3-Clause"],
|
|
31
|
+
["isc license", "ISC"],
|
|
32
|
+
["isc", "ISC"],
|
|
33
|
+
["gpl-2.0", "GPL-2.0-only"],
|
|
34
|
+
["gpl-3.0", "GPL-3.0-only"],
|
|
35
|
+
["gpl-2.0-only", "GPL-2.0-only"],
|
|
36
|
+
["gpl-3.0-only", "GPL-3.0-only"],
|
|
37
|
+
["lgpl-2.1", "LGPL-2.1-only"],
|
|
38
|
+
["lgpl-3.0", "LGPL-3.0-only"],
|
|
39
|
+
["mpl-2.0", "MPL-2.0"],
|
|
40
|
+
["mozilla public license 2.0", "MPL-2.0"],
|
|
41
|
+
["unlicense", "Unlicense"],
|
|
42
|
+
["the unlicense", "Unlicense"],
|
|
43
|
+
["cc0-1.0", "CC0-1.0"],
|
|
44
|
+
["cc0", "CC0-1.0"],
|
|
45
|
+
["public domain", "Unlicense"],
|
|
46
|
+
["zlib", "Zlib"],
|
|
47
|
+
["wtfpl", "WTFPL"],
|
|
48
|
+
["artistic-2.0", "Artistic-2.0"],
|
|
49
|
+
["boost software license 1.0", "BSL-1.0"],
|
|
50
|
+
["bsl-1.0", "BSL-1.0"]
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* Combine multiple license strings into a single SPDX expression.
|
|
54
|
+
*
|
|
55
|
+
* Default operator is OR (disjunctive, user picks one).
|
|
56
|
+
* Pass "AND" for ecosystems where multiple licenses mean all apply (e.g. Arch Linux).
|
|
57
|
+
*/
|
|
58
|
+
function combineLicenses(licenses, operator = "OR") {
|
|
59
|
+
const normalized = licenses.map((l) => normalizeLicense(l)).filter(Boolean);
|
|
60
|
+
if (normalized.length === 0) return "";
|
|
61
|
+
if (normalized.length === 1) return normalized[0];
|
|
62
|
+
return normalized.join(` ${operator} `);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Normalize a repository URL to a clean HTTPS git URL.
|
|
66
|
+
*
|
|
67
|
+
* Handles various formats:
|
|
68
|
+
* - "git+https://github.com/foo/bar.git" → "https://github.com/foo/bar"
|
|
69
|
+
* - "git://github.com/foo/bar" → "https://github.com/foo/bar"
|
|
70
|
+
* - "ssh://git@github.com/foo/bar.git" → "https://github.com/foo/bar"
|
|
71
|
+
* - "github:foo/bar" → "https://github.com/foo/bar"
|
|
72
|
+
* - { url: "..." } → extracted and normalized
|
|
73
|
+
*/
|
|
74
|
+
function normalizeRepositoryURL(raw) {
|
|
75
|
+
if (!raw) return "";
|
|
76
|
+
let url;
|
|
77
|
+
if (typeof raw === "string") url = raw;
|
|
78
|
+
else if (typeof raw === "object" && raw !== null) {
|
|
79
|
+
const obj = raw;
|
|
80
|
+
if (typeof obj["url"] === "string") url = obj["url"];
|
|
81
|
+
else return "";
|
|
82
|
+
} else return "";
|
|
83
|
+
url = url.trim();
|
|
84
|
+
if (!url) return "";
|
|
85
|
+
if (url.startsWith("github:")) url = `https://github.com/${url.slice(7)}`;
|
|
86
|
+
if (url.startsWith("gitlab:")) url = `https://gitlab.com/${url.slice(7)}`;
|
|
87
|
+
if (url.startsWith("bitbucket:")) url = `https://bitbucket.org/${url.slice(10)}`;
|
|
88
|
+
if (url.startsWith("git+")) url = url.slice(4);
|
|
89
|
+
if (url.startsWith("git://")) url = "https://" + url.slice(6);
|
|
90
|
+
if (url.startsWith("ssh://git@")) url = "https://" + url.slice(10);
|
|
91
|
+
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
92
|
+
if (sshMatch) url = `https://${sshMatch[1]}/${sshMatch[2]}`;
|
|
93
|
+
if (url.endsWith("/")) url = url.slice(0, -1);
|
|
94
|
+
if (url.endsWith(".git")) url = url.slice(0, -4);
|
|
95
|
+
return url;
|
|
96
|
+
}
|
|
97
|
+
/** Decode a percent-encoded PURL component, throwing InvalidPURLError on malformed sequences. */
|
|
98
|
+
function decodePURLComponent(purlStr, value) {
|
|
99
|
+
try {
|
|
100
|
+
return decodeURIComponent(value);
|
|
101
|
+
} catch {
|
|
102
|
+
throw new InvalidPURLError(purlStr, "malformed percent-encoding");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse a PURL string into its components.
|
|
107
|
+
*
|
|
108
|
+
* Format: `pkg:<type>/<namespace>/<name>@<version>?<qualifiers>#<subpath>`
|
|
109
|
+
*
|
|
110
|
+
* @see https://github.com/package-url/purl-spec (ECMA-427)
|
|
111
|
+
*/
|
|
112
|
+
function parsePURL(purlStr) {
|
|
113
|
+
if (!purlStr.startsWith("pkg:")) throw new InvalidPURLError(purlStr, "must start with \"pkg:\"");
|
|
114
|
+
let remainder = purlStr.slice(4);
|
|
115
|
+
let subpath = "";
|
|
116
|
+
const hashIdx = remainder.indexOf("#");
|
|
117
|
+
if (hashIdx !== -1) {
|
|
118
|
+
subpath = remainder.slice(hashIdx + 1).split("/").map((s) => decodePURLComponent(purlStr, s)).join("/");
|
|
119
|
+
remainder = remainder.slice(0, hashIdx);
|
|
120
|
+
}
|
|
121
|
+
const qualifiers = {};
|
|
122
|
+
const queryIdx = remainder.indexOf("?");
|
|
123
|
+
if (queryIdx !== -1) {
|
|
124
|
+
const queryStr = remainder.slice(queryIdx + 1);
|
|
125
|
+
remainder = remainder.slice(0, queryIdx);
|
|
126
|
+
for (const pair of queryStr.split("&")) {
|
|
127
|
+
const eqIdx = pair.indexOf("=");
|
|
128
|
+
if (eqIdx !== -1) qualifiers[decodePURLComponent(purlStr, pair.slice(0, eqIdx))] = decodePURLComponent(purlStr, pair.slice(eqIdx + 1));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
let version = "";
|
|
132
|
+
const lastSlashBeforeVersion = remainder.lastIndexOf("/");
|
|
133
|
+
const lastAtIdx = remainder.lastIndexOf("@");
|
|
134
|
+
if (lastAtIdx !== -1 && lastAtIdx > lastSlashBeforeVersion) {
|
|
135
|
+
version = decodePURLComponent(purlStr, remainder.slice(lastAtIdx + 1));
|
|
136
|
+
remainder = remainder.slice(0, lastAtIdx);
|
|
137
|
+
}
|
|
138
|
+
const slashIdx = remainder.indexOf("/");
|
|
139
|
+
if (slashIdx === -1) throw new InvalidPURLError(purlStr, "missing type/name separator");
|
|
140
|
+
const type = remainder.slice(0, slashIdx).toLowerCase();
|
|
141
|
+
if (!type) throw new InvalidPURLError(purlStr, "empty type");
|
|
142
|
+
const rest = remainder.slice(slashIdx + 1);
|
|
143
|
+
if (!rest) throw new InvalidPURLError(purlStr, "empty name");
|
|
144
|
+
const lastSlashIdx = rest.lastIndexOf("/");
|
|
145
|
+
let namespace = "";
|
|
146
|
+
let name;
|
|
147
|
+
if (lastSlashIdx !== -1) {
|
|
148
|
+
namespace = rest.slice(0, lastSlashIdx).split("/").map((s) => decodePURLComponent(purlStr, s)).join("/");
|
|
149
|
+
name = decodePURLComponent(purlStr, rest.slice(lastSlashIdx + 1));
|
|
150
|
+
} else name = decodePURLComponent(purlStr, rest);
|
|
151
|
+
if (type === "pypi") name = name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
152
|
+
if (type === "alpm") name = name.toLowerCase();
|
|
153
|
+
return {
|
|
154
|
+
type,
|
|
155
|
+
namespace,
|
|
156
|
+
name,
|
|
157
|
+
version,
|
|
158
|
+
qualifiers,
|
|
159
|
+
subpath
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** Build the full name from namespace + name (e.g., "@scope/pkg" for npm). */
|
|
163
|
+
function fullName(parsed) {
|
|
164
|
+
if (parsed.namespace) return `${parsed.namespace}/${parsed.name}`;
|
|
165
|
+
return parsed.name;
|
|
166
|
+
}
|
|
167
|
+
/** Create a registry instance from a PURL, returning [registry, name, version]. */
|
|
168
|
+
function createFromPURL(purlStr, client) {
|
|
169
|
+
const parsed = parsePURL(purlStr);
|
|
170
|
+
const baseURL = parsed.qualifiers["repository_url"] ?? "";
|
|
171
|
+
return [
|
|
172
|
+
create(parsed.type, baseURL || void 0, client),
|
|
173
|
+
fullName(parsed),
|
|
174
|
+
parsed.version
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
/** Build a PURL string from components. Inverse of `parsePURL`. */
|
|
178
|
+
function buildPURL(parts) {
|
|
179
|
+
let purl = `pkg:${parts.type}/`;
|
|
180
|
+
if (parts.namespace) purl += `${parts.namespace.split("/").map((s) => encodeURIComponent(s)).join("/")}/`;
|
|
181
|
+
purl += encodeURIComponent(parts.name);
|
|
182
|
+
if (parts.version) purl += `@${encodeURIComponent(parts.version)}`;
|
|
183
|
+
if (parts.qualifiers && Object.keys(parts.qualifiers).length > 0) {
|
|
184
|
+
const qs = Object.entries(parts.qualifiers).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
185
|
+
purl += `?${qs}`;
|
|
186
|
+
}
|
|
187
|
+
if (parts.subpath) purl += `#${parts.subpath.split("/").map((s) => encodeURIComponent(s)).join("/")}`;
|
|
188
|
+
return purl;
|
|
189
|
+
}
|
|
190
|
+
export { normalizeRepositoryURL as a, parsePURL as i, createFromPURL as n, combineLicenses as o, fullName as r, normalizeLicense as s, buildPURL as t };
|
|
191
|
+
|
|
192
|
+
//# sourceMappingURL=purl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purl.mjs","names":[],"sources":["../../src/core/license.ts","../../src/core/repository.ts","../../src/core/purl.ts"],"sourcesContent":["/**\n * Normalize a license string to SPDX expression.\n *\n * Handles common variations: \"MIT License\" → \"MIT\", \"Apache 2.0\" → \"Apache-2.0\".\n * Passes through already-valid SPDX identifiers unchanged.\n */\nexport function normalizeLicense(raw: string | null | undefined): string {\n if (!raw) return \"\";\n\n const trimmed = raw.trim();\n if (!trimmed) return \"\";\n\n // Common mappings\n const lower = trimmed.toLowerCase();\n const known = LICENSE_MAP.get(lower);\n if (known) return known;\n\n // Already looks like SPDX — return as-is\n return trimmed;\n}\n\nconst LICENSE_MAP = new Map<string, string>([\n [\"mit license\", \"MIT\"],\n [\"mit\", \"MIT\"],\n [\"apache license 2.0\", \"Apache-2.0\"],\n [\"apache 2.0\", \"Apache-2.0\"],\n [\"apache-2.0\", \"Apache-2.0\"],\n [\"apache license, version 2.0\", \"Apache-2.0\"],\n [\"apache2\", \"Apache-2.0\"],\n [\"bsd-2-clause\", \"BSD-2-Clause\"],\n [\"bsd-3-clause\", \"BSD-3-Clause\"],\n [\"bsd 2-clause\", \"BSD-2-Clause\"],\n [\"bsd 3-clause\", \"BSD-3-Clause\"],\n [\"simplified bsd license\", \"BSD-2-Clause\"],\n [\"new bsd license\", \"BSD-3-Clause\"],\n [\"isc license\", \"ISC\"],\n [\"isc\", \"ISC\"],\n [\"gpl-2.0\", \"GPL-2.0-only\"],\n [\"gpl-3.0\", \"GPL-3.0-only\"],\n [\"gpl-2.0-only\", \"GPL-2.0-only\"],\n [\"gpl-3.0-only\", \"GPL-3.0-only\"],\n [\"lgpl-2.1\", \"LGPL-2.1-only\"],\n [\"lgpl-3.0\", \"LGPL-3.0-only\"],\n [\"mpl-2.0\", \"MPL-2.0\"],\n [\"mozilla public license 2.0\", \"MPL-2.0\"],\n [\"unlicense\", \"Unlicense\"],\n [\"the unlicense\", \"Unlicense\"],\n [\"cc0-1.0\", \"CC0-1.0\"],\n [\"cc0\", \"CC0-1.0\"],\n [\"public domain\", \"Unlicense\"],\n [\"zlib\", \"Zlib\"],\n [\"wtfpl\", \"WTFPL\"],\n [\"artistic-2.0\", \"Artistic-2.0\"],\n [\"boost software license 1.0\", \"BSL-1.0\"],\n [\"bsl-1.0\", \"BSL-1.0\"],\n]);\n\n/**\n * Combine multiple license strings into a single SPDX expression.\n *\n * Default operator is OR (disjunctive, user picks one).\n * Pass \"AND\" for ecosystems where multiple licenses mean all apply (e.g. Arch Linux).\n */\nexport function combineLicenses(\n licenses: (string | null | undefined)[],\n operator: \"OR\" | \"AND\" = \"OR\",\n): string {\n const normalized = licenses.map((l) => normalizeLicense(l)).filter(Boolean);\n\n if (normalized.length === 0) return \"\";\n if (normalized.length === 1) return normalized[0]!;\n return normalized.join(` ${operator} `);\n}\n","/**\n * Normalize a repository URL to a clean HTTPS git URL.\n *\n * Handles various formats:\n * - \"git+https://github.com/foo/bar.git\" → \"https://github.com/foo/bar\"\n * - \"git://github.com/foo/bar\" → \"https://github.com/foo/bar\"\n * - \"ssh://git@github.com/foo/bar.git\" → \"https://github.com/foo/bar\"\n * - \"github:foo/bar\" → \"https://github.com/foo/bar\"\n * - { url: \"...\" } → extracted and normalized\n */\nexport function normalizeRepositoryURL(raw: unknown): string {\n if (!raw) return \"\";\n\n let url: string;\n\n if (typeof raw === \"string\") {\n url = raw;\n } else if (typeof raw === \"object\" && raw !== null) {\n const obj = raw as Record<string, unknown>;\n if (typeof obj[\"url\"] === \"string\") {\n url = obj[\"url\"];\n } else {\n return \"\";\n }\n } else {\n return \"\";\n }\n\n url = url.trim();\n if (!url) return \"\";\n\n // GitHub shorthand: \"github:user/repo\"\n if (url.startsWith(\"github:\")) {\n url = `https://github.com/${url.slice(7)}`;\n }\n\n // GitLab shorthand\n if (url.startsWith(\"gitlab:\")) {\n url = `https://gitlab.com/${url.slice(7)}`;\n }\n\n // Bitbucket shorthand\n if (url.startsWith(\"bitbucket:\")) {\n url = `https://bitbucket.org/${url.slice(10)}`;\n }\n\n // Strip \"git+\" prefix\n if (url.startsWith(\"git+\")) {\n url = url.slice(4);\n }\n\n // Convert git:// to https://\n if (url.startsWith(\"git://\")) {\n url = \"https://\" + url.slice(6);\n }\n\n // Convert ssh://git@host/... to https://host/...\n if (url.startsWith(\"ssh://git@\")) {\n url = \"https://\" + url.slice(10);\n }\n\n // Convert git@host:user/repo to https://host/user/repo\n const sshMatch = url.match(/^git@([^:]+):(.+)$/);\n if (sshMatch) {\n url = `https://${sshMatch[1]}/${sshMatch[2]}`;\n }\n\n // Strip trailing slash first so \".git/\" doesn't survive\n if (url.endsWith(\"/\")) {\n url = url.slice(0, -1);\n }\n\n // Strip .git suffix\n if (url.endsWith(\".git\")) {\n url = url.slice(0, -4);\n }\n\n return url;\n}\n","import type { ParsedPURL } from \"./types.ts\";\nimport type { Client } from \"./client.ts\";\nimport { create, type Registry } from \"./registry.ts\";\nimport { InvalidPURLError } from \"./errors.ts\";\n\n/** Decode a percent-encoded PURL component, throwing InvalidPURLError on malformed sequences. */\nfunction decodePURLComponent(purlStr: string, value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n throw new InvalidPURLError(purlStr, \"malformed percent-encoding\");\n }\n}\n\n/**\n * Parse a PURL string into its components.\n *\n * Format: `pkg:<type>/<namespace>/<name>@<version>?<qualifiers>#<subpath>`\n *\n * @see https://github.com/package-url/purl-spec (ECMA-427)\n */\nexport function parsePURL(purlStr: string): ParsedPURL {\n if (!purlStr.startsWith(\"pkg:\")) {\n throw new InvalidPURLError(purlStr, 'must start with \"pkg:\"');\n }\n\n let remainder = purlStr.slice(4); // strip 'pkg:'\n\n // Extract subpath\n let subpath = \"\";\n const hashIdx = remainder.indexOf(\"#\");\n if (hashIdx !== -1) {\n subpath = remainder\n .slice(hashIdx + 1)\n .split(\"/\")\n .map((s) => decodePURLComponent(purlStr, s))\n .join(\"/\");\n remainder = remainder.slice(0, hashIdx);\n }\n\n // Extract qualifiers\n const qualifiers: Record<string, string> = {};\n const queryIdx = remainder.indexOf(\"?\");\n if (queryIdx !== -1) {\n const queryStr = remainder.slice(queryIdx + 1);\n remainder = remainder.slice(0, queryIdx);\n for (const pair of queryStr.split(\"&\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx !== -1) {\n qualifiers[decodePURLComponent(purlStr, pair.slice(0, eqIdx))] = decodePURLComponent(\n purlStr,\n pair.slice(eqIdx + 1),\n );\n }\n }\n }\n\n // Extract version — the version separator is the last '@' that appears\n // after the last '/', so scoped names like 'npm/@vue/core' are not mistaken\n // for having a version.\n let version = \"\";\n const lastSlashBeforeVersion = remainder.lastIndexOf(\"/\");\n const lastAtIdx = remainder.lastIndexOf(\"@\");\n if (lastAtIdx !== -1 && lastAtIdx > lastSlashBeforeVersion) {\n version = decodePURLComponent(purlStr, remainder.slice(lastAtIdx + 1));\n remainder = remainder.slice(0, lastAtIdx);\n }\n\n // Extract type\n const slashIdx = remainder.indexOf(\"/\");\n if (slashIdx === -1) {\n throw new InvalidPURLError(purlStr, \"missing type/name separator\");\n }\n\n const type = remainder.slice(0, slashIdx).toLowerCase();\n if (!type) {\n throw new InvalidPURLError(purlStr, \"empty type\");\n }\n\n const rest = remainder.slice(slashIdx + 1);\n if (!rest) {\n throw new InvalidPURLError(purlStr, \"empty name\");\n }\n\n // Extract namespace and name\n const lastSlashIdx = rest.lastIndexOf(\"/\");\n let namespace = \"\";\n let name: string;\n\n if (lastSlashIdx !== -1) {\n namespace = rest\n .slice(0, lastSlashIdx)\n .split(\"/\")\n .map((s) => decodePURLComponent(purlStr, s))\n .join(\"/\");\n name = decodePURLComponent(purlStr, rest.slice(lastSlashIdx + 1));\n } else {\n name = decodePURLComponent(purlStr, rest);\n }\n\n // Ecosystem-specific normalization per PURL spec\n if (type === \"pypi\") {\n name = name.toLowerCase().replace(/[-_.]+/g, \"-\");\n }\n\n if (type === \"alpm\") {\n name = name.toLowerCase();\n }\n\n return { type, namespace, name, version, qualifiers, subpath };\n}\n\n/** Build the full name from namespace + name (e.g., \"@scope/pkg\" for npm). */\nexport function fullName(parsed: ParsedPURL): string {\n if (parsed.namespace) {\n return `${parsed.namespace}/${parsed.name}`;\n }\n return parsed.name;\n}\n\n/** Create a registry instance from a PURL, returning [registry, name, version]. */\nexport function createFromPURL(purlStr: string, client?: Client): [Registry, string, string] {\n const parsed = parsePURL(purlStr);\n const baseURL = parsed.qualifiers[\"repository_url\"] ?? \"\";\n const reg = create(parsed.type, baseURL || undefined, client);\n return [reg, fullName(parsed), parsed.version];\n}\n\n/** Build a PURL string from components. Inverse of `parsePURL`. */\nexport function buildPURL(parts: {\n type: string;\n name: string;\n version?: string;\n namespace?: string;\n qualifiers?: Record<string, string>;\n subpath?: string;\n}): string {\n let purl = `pkg:${parts.type}/`;\n if (parts.namespace) {\n purl += `${parts.namespace\n .split(\"/\")\n .map((s) => encodeURIComponent(s))\n .join(\"/\")}/`;\n }\n purl += encodeURIComponent(parts.name);\n if (parts.version) {\n purl += `@${encodeURIComponent(parts.version)}`;\n }\n if (parts.qualifiers && Object.keys(parts.qualifiers).length > 0) {\n const qs = Object.entries(parts.qualifiers)\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n purl += `?${qs}`;\n }\n if (parts.subpath) {\n purl += `#${parts.subpath\n .split(\"/\")\n .map((s) => encodeURIComponent(s))\n .join(\"/\")}`;\n }\n return purl;\n}\n"],"mappings":"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"}
|