@pnpm/resolving.npm-resolver 1101.0.2 → 1101.1.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/lib/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import type { PackageMeta } from '@pnpm/resolving.registry.types';
4
4
  import type { DirectoryResolution, PkgResolutionId, PreferredVersions, ResolveResult, TarballResolution, WantedDependency, WorkspacePackages } from '@pnpm/resolving.resolver-base';
5
5
  import type { DependencyManifest, PackageVersionPolicy, PinnedVersion, Registries, TrustPolicy } from '@pnpm/types';
6
6
  import { fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, RegistryResponseError } from './fetch.js';
7
- import { parseBareSpecifier, type RegistryPackageSpec } from './parseBareSpecifier.js';
7
+ import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, type RegistryPackageSpec } from './parseBareSpecifier.js';
8
8
  import { type PackageMetaCache, pickPackage, type PickPackageOptions } from './pickPackage.js';
9
9
  import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js';
10
10
  import { workspacePrefToNpm } from './workspacePrefToNpm.js';
@@ -20,7 +20,8 @@ export declare class NoMatchingVersionError extends PnpmError {
20
20
  readonly immatureVersion?: string;
21
21
  constructor(opts: NoMatchingVersionErrorOptions);
22
22
  }
23
- export { fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, type PackageMeta, type PackageMetaCache, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, type RegistryPackageSpec, RegistryResponseError, workspacePrefToNpm, };
23
+ export declare function formatTimeAgo(date: Date): string | null;
24
+ export { BUILTIN_NAMED_REGISTRIES, fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, type PackageMeta, type PackageMetaCache, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, type RegistryPackageSpec, RegistryResponseError, workspacePrefToNpm, };
24
25
  export { whichVersionIsPinned } from './whichVersionIsPinned.js';
25
26
  export interface ResolverFactoryOptions {
26
27
  cacheDir: string;
@@ -32,6 +33,7 @@ export interface ResolverFactoryOptions {
32
33
  retry?: RetryTimeoutOptions;
33
34
  timeout?: number;
34
35
  registries: Registries;
36
+ namedRegistries?: Record<string, string>;
35
37
  saveWorkspaceProtocol?: boolean | 'rolling';
36
38
  preserveAbsolutePaths?: boolean;
37
39
  strictPublishedByCheck?: boolean;
@@ -54,6 +56,14 @@ export interface JsrResolveResult extends ResolveResult {
54
56
  resolution: TarballResolution;
55
57
  resolvedVia: 'jsr-registry';
56
58
  }
59
+ export interface NamedRegistryResolveResult extends ResolveResult {
60
+ alias: string;
61
+ /** The named-registry alias that was matched, e.g. `gh` or a user-defined name. */
62
+ registryName: string;
63
+ manifest: DependencyManifest;
64
+ resolution: TarballResolution;
65
+ resolvedVia: 'named-registry';
66
+ }
57
67
  export interface WorkspaceResolveResult extends ResolveResult {
58
68
  manifest: DependencyManifest;
59
69
  resolution: DirectoryResolution;
@@ -61,16 +71,19 @@ export interface WorkspaceResolveResult extends ResolveResult {
61
71
  }
62
72
  export type NpmResolver = (wantedDependency: WantedDependency & {
63
73
  optional?: boolean;
64
- }, opts: ResolveFromNpmOptions) => Promise<NpmResolveResult | JsrResolveResult | WorkspaceResolveResult | null>;
74
+ }, opts: ResolveFromNpmOptions) => Promise<NpmResolveResult | JsrResolveResult | NamedRegistryResolveResult | WorkspaceResolveResult | null>;
65
75
  export declare function createNpmResolver(fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, opts: ResolverFactoryOptions): {
66
76
  resolveFromNpm: NpmResolver;
67
77
  resolveFromJsr: NpmResolver;
78
+ resolveFromNamedRegistry: NpmResolver;
68
79
  clearCache: () => void;
69
80
  };
70
81
  export interface ResolveFromNpmContext {
71
82
  pickPackage: (spec: RegistryPackageSpec, opts: PickPackageOptions) => ReturnType<typeof pickPackage>;
72
83
  getAuthHeaderValueByURI: (registry: string) => string | undefined;
73
84
  registries: Registries;
85
+ namedRegistries: Record<string, string>;
86
+ namedRegistryNames: ReadonlySet<string>;
74
87
  saveWorkspaceProtocol?: boolean | 'rolling';
75
88
  peekManifestFromStore?: (opts: {
76
89
  id: PkgResolutionId;
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@ import ssri from 'ssri';
13
13
  import versionSelectorType from 'version-selector-type';
14
14
  import { fetchMetadataFromFromRegistry, RegistryResponseError } from './fetch.js';
15
15
  import { normalizeRegistryUrl } from './normalizeRegistryUrl.js';
16
- import { parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
16
+ import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, parseNamedRegistrySpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
17
17
  import { pickPackage, } from './pickPackage.js';
18
18
  import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js';
19
19
  import { failIfTrustDowngraded } from './trustChecks.js';
@@ -29,7 +29,7 @@ export class NoMatchingVersionError extends PnpmError {
29
29
  let errorMessage;
30
30
  if (opts.publishedBy && opts.immatureVersion && opts.packageMeta.time) {
31
31
  const time = new Date(opts.packageMeta.time[opts.immatureVersion]);
32
- const releaseAgeText = formatTimeAgo(time);
32
+ const releaseAgeText = formatTimeAgo(time) ?? 'just now';
33
33
  const pkgName = opts.wantedDependency.alias ?? opts.packageMeta.name;
34
34
  errorMessage = `Version ${opts.immatureVersion} (released ${releaseAgeText}) of ${pkgName} does not meet the minimumReleaseAge constraint`;
35
35
  }
@@ -41,25 +41,36 @@ export class NoMatchingVersionError extends PnpmError {
41
41
  this.immatureVersion = opts.immatureVersion;
42
42
  }
43
43
  }
44
- function formatTimeAgo(date) {
45
- const now = Date.now();
46
- const diffMs = now - date.getTime();
47
- // Handle clock skew (future dates) and very recent releases (< 1 minute)
48
- if (diffMs < 60 * 1000) {
49
- return 'just now';
50
- }
51
- const diffMinutes = Math.floor(diffMs / (60 * 1000));
52
- const diffHours = Math.floor(diffMs / (60 * 60 * 1000));
53
- const diffDays = Math.floor(diffMs / (24 * 60 * 60 * 1000));
54
- if (diffHours >= 48) {
55
- return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
44
+ export function formatTimeAgo(date) {
45
+ const ts = date.getTime();
46
+ if (isNaN(ts)) {
47
+ return null;
56
48
  }
57
- if (diffMinutes >= 90) {
58
- return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
49
+ const now = Date.now();
50
+ const diffMs = now - ts;
51
+ // Handle clock skew (future dates)
52
+ if (diffMs < 0) {
53
+ return null;
59
54
  }
60
- return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`;
55
+ const diffSec = Math.floor(diffMs / 1000);
56
+ const diffMin = Math.floor(diffSec / 60);
57
+ const diffHour = Math.floor(diffMin / 60);
58
+ const diffDay = Math.floor(diffHour / 24);
59
+ const diffMonth = Math.floor(diffDay / 30);
60
+ const diffYear = Math.floor(diffDay / 365);
61
+ if (diffYear > 0)
62
+ return `${diffYear} year${diffYear === 1 ? '' : 's'} ago`;
63
+ if (diffMonth > 0)
64
+ return `${diffMonth} month${diffMonth === 1 ? '' : 's'} ago`;
65
+ if (diffDay > 0)
66
+ return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`;
67
+ if (diffHour > 0)
68
+ return `${diffHour} hour${diffHour === 1 ? '' : 's'} ago`;
69
+ if (diffMin > 0)
70
+ return `${diffMin} minute${diffMin === 1 ? '' : 's'} ago`;
71
+ return 'a few seconds ago';
61
72
  }
62
- export { fetchMetadataFromFromRegistry, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, RegistryResponseError, workspacePrefToNpm, };
73
+ export { BUILTIN_NAMED_REGISTRIES, fetchMetadataFromFromRegistry, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, RegistryResponseError, workspacePrefToNpm, };
63
74
  export { whichVersionIsPinned } from './whichVersionIsPinned.js';
64
75
  export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
65
76
  if (typeof opts.cacheDir !== 'string') {
@@ -103,6 +114,8 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
103
114
  return request;
104
115
  };
105
116
  }
117
+ const namedRegistries = mergeNamedRegistries(opts.namedRegistries);
118
+ const namedRegistryNames = new Set(Object.keys(namedRegistries));
106
119
  const ctx = {
107
120
  getAuthHeaderValueByURI: getAuthHeader,
108
121
  pickPackage: pickPackage.bind(null, {
@@ -117,12 +130,15 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
117
130
  ignoreMissingTimeField: opts.ignoreMissingTimeField,
118
131
  }),
119
132
  registries: opts.registries,
133
+ namedRegistries,
134
+ namedRegistryNames,
120
135
  saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
121
136
  peekManifestFromStore,
122
137
  };
123
138
  return {
124
139
  resolveFromNpm: resolveNpm.bind(null, ctx),
125
140
  resolveFromJsr: resolveJsr.bind(null, ctx),
141
+ resolveFromNamedRegistry: resolveFromNamedRegistry.bind(null, ctx),
126
142
  clearCache: () => {
127
143
  if ('clear' in metaCache && typeof metaCache.clear === 'function') {
128
144
  metaCache.clear();
@@ -326,52 +342,117 @@ async function resolveNpm(ctx, wantedDependency, opts) {
326
342
  async function resolveJsr(ctx, wantedDependency, opts) {
327
343
  if (!wantedDependency.bareSpecifier)
328
344
  return null;
329
- const defaultTag = opts.defaultTag ?? 'latest';
330
- const registry = ctx.registries['@jsr']; // '@jsr' is always defined
331
- const spec = parseJsrSpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, wantedDependency.alias, defaultTag);
345
+ const spec = parseJsrSpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, wantedDependency.alias, opts.defaultTag ?? 'latest');
346
+ if (spec == null)
347
+ return null;
348
+ const picked = await pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, ctx.registries['@jsr']); // '@jsr' is always defined
349
+ return {
350
+ ...picked,
351
+ normalizedBareSpecifier: opts.calcSpecifier
352
+ ? calcPrefixedSpecifier('jsr:', spec.jsrPkgName, wantedDependency, picked.manifest.version, opts.pinnedVersion)
353
+ : undefined,
354
+ resolvedVia: 'jsr-registry',
355
+ alias: spec.jsrPkgName,
356
+ };
357
+ }
358
+ // Merges user-supplied named-registry aliases (from config) on top of pnpm's
359
+ // built-in defaults (e.g. `gh` → GitHub Packages). User entries take precedence
360
+ // so GHES users can point `gh` at their enterprise host. URLs are validated
361
+ // here so typos like `npm.work.example.com` (no scheme) surface at startup
362
+ // rather than as a confusing 404 during resolution. The named-registry
363
+ // resolver runs last in the resolution chain, so an alias that collides with
364
+ // another specifier scheme (e.g. `git`, `github`, `jsr`) is silently shadowed
365
+ // by that scheme's dedicated resolver — no cross-resolver knowledge needed.
366
+ function mergeNamedRegistries(userDefined) {
367
+ const merged = { ...BUILTIN_NAMED_REGISTRIES };
368
+ if (!userDefined)
369
+ return merged;
370
+ for (const [alias, url] of Object.entries(userDefined)) {
371
+ if (typeof url !== 'string' || !isValidHttpUrl(url)) {
372
+ throw new PnpmError('INVALID_NAMED_REGISTRY_URL', `The named registry alias '${alias}' is mapped to '${String(url)}', which is not a valid http(s) URL.`, { hint: 'Provide a URL that starts with http:// or https://, e.g. https://npm.pkg.example.com/' });
373
+ }
374
+ merged[alias] = url;
375
+ }
376
+ return merged;
377
+ }
378
+ function isValidHttpUrl(url) {
379
+ try {
380
+ const parsed = new URL(url);
381
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
382
+ }
383
+ catch {
384
+ return false;
385
+ }
386
+ }
387
+ // Resolves a `<alias>:` specifier from one of the configured named registries.
388
+ // The `gh:` alias ships as a built-in default pointing at the GitHub Packages
389
+ // npm registry; additional aliases come from pnpm-workspace.yaml's
390
+ // `namedRegistries` field. Auth tokens are looked up by the resolved registry
391
+ // URL, so a `//npm.pkg.github.com/:_authToken=...` entry in `.npmrc` is
392
+ // picked up automatically for `gh:` specifiers (and analogously for any user-
393
+ // configured alias).
394
+ async function resolveFromNamedRegistry(ctx, wantedDependency, opts) {
395
+ if (!wantedDependency.bareSpecifier)
396
+ return null;
397
+ const spec = parseNamedRegistrySpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, ctx.namedRegistryNames, wantedDependency.alias, opts.defaultTag ?? 'latest');
332
398
  if (spec == null)
333
399
  return null;
400
+ const registry = ctx.namedRegistries[spec.registryName];
401
+ if (!registry)
402
+ return null; // defensive: should never trigger because parse checks the alias set
403
+ const picked = await pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, registry);
404
+ return {
405
+ ...picked,
406
+ normalizedBareSpecifier: opts.calcSpecifier
407
+ ? calcPrefixedSpecifier(`${spec.registryName}:`, spec.name, wantedDependency, picked.manifest.version, opts.pinnedVersion)
408
+ : undefined,
409
+ resolvedVia: 'named-registry',
410
+ registryName: spec.registryName,
411
+ // Exposes the scoped package name so callers that omit an explicit alias
412
+ // (e.g. `pnpm add gh:@acme/foo`) record the dependency under `@acme/foo`.
413
+ alias: spec.name,
414
+ };
415
+ }
416
+ // Shared inner shell for resolvers that pull from a single registry URL with
417
+ // an already-parsed RegistryPackageSpec (jsr, named-registry). Returns the
418
+ // fields common to their result envelopes; each caller adds its own
419
+ // resolvedVia, alias, and normalizedBareSpecifier.
420
+ async function pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, registry) {
334
421
  const authHeaderValue = ctx.getAuthHeaderValueByURI(registry);
335
422
  const { meta, pickedPackage } = await ctx.pickPackage(spec, {
336
423
  pickLowestVersion: opts.pickLowestVersion,
337
424
  publishedBy: opts.publishedBy,
425
+ publishedByExclude: opts.publishedByExclude,
338
426
  authHeaderValue,
339
427
  dryRun: opts.dryRun === true,
340
428
  preferredVersionSelectors: opts.preferredVersions?.[spec.name],
341
429
  registry,
342
430
  includeLatestTag: opts.update === 'latest',
431
+ optional: wantedDependency.optional,
343
432
  });
344
433
  if (pickedPackage == null) {
345
434
  throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry });
346
435
  }
347
- const id = `${pickedPackage.name}@${pickedPackage.version}`;
348
- const resolution = {
349
- integrity: getIntegrity(pickedPackage.dist),
350
- tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
351
- };
352
436
  return {
353
- id,
437
+ id: `${pickedPackage.name}@${pickedPackage.version}`,
354
438
  latest: meta['dist-tags'].latest,
355
439
  manifest: pickedPackage,
356
- normalizedBareSpecifier: opts.calcSpecifier
357
- ? calcJsrSpecifier({
358
- wantedDependency,
359
- spec,
360
- version: pickedPackage.version,
361
- defaultPinnedVersion: opts.pinnedVersion,
362
- })
363
- : undefined,
364
- resolution,
365
- resolvedVia: 'jsr-registry',
440
+ resolution: {
441
+ integrity: getIntegrity(pickedPackage.dist),
442
+ tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
443
+ },
366
444
  publishedAt: meta.time?.[pickedPackage.version],
367
- alias: spec.jsrPkgName,
368
445
  };
369
446
  }
370
- function calcJsrSpecifier({ wantedDependency, spec, version, defaultPinnedVersion, }) {
447
+ // Builds a `<prefix><pkgName>@<range>` specifier (or a bare `<prefix><range>`
448
+ // when the dependency alias matches the package name). Shared between the
449
+ // jsr and named-registry resolvers since they only differ in `prefix` and
450
+ // which spec field holds the package name.
451
+ function calcPrefixedSpecifier(prefix, pkgName, wantedDependency, version, defaultPinnedVersion) {
371
452
  const range = calcRange(version, wantedDependency, defaultPinnedVersion);
372
- if (!wantedDependency.alias || spec.jsrPkgName === wantedDependency.alias)
373
- return `jsr:${range}`;
374
- return `jsr:${spec.jsrPkgName}@${range}`;
453
+ if (!wantedDependency.alias || pkgName === wantedDependency.alias)
454
+ return `${prefix}${range}`;
455
+ return `${prefix}${pkgName}@${range}`;
375
456
  }
376
457
  function calcSpecifier({ wantedDependency, spec, version, defaultPinnedVersion, }) {
377
458
  if (wantedDependency.prevSpecifier === wantedDependency.bareSpecifier && wantedDependency.prevSpecifier && versionSelectorType(wantedDependency.prevSpecifier)?.type === 'tag') {
@@ -9,3 +9,8 @@ export interface JsrRegistryPackageSpec extends RegistryPackageSpec {
9
9
  jsrPkgName: string;
10
10
  }
11
11
  export declare function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier: string, alias: string | undefined, defaultTag: string): JsrRegistryPackageSpec | null;
12
+ export declare const BUILTIN_NAMED_REGISTRIES: Readonly<Record<string, string>>;
13
+ export interface NamedRegistryPackageSpec extends RegistryPackageSpec {
14
+ registryName: string;
15
+ }
16
+ export declare function parseNamedRegistrySpecifierToRegistryPackageSpec(rawSpecifier: string, knownRegistryNames: ReadonlySet<string>, packageAlias: string | undefined, defaultTag: string): NamedRegistryPackageSpec | null;
@@ -1,18 +1,29 @@
1
+ import { PnpmError } from '@pnpm/error';
1
2
  import { parseJsrSpecifier } from '@pnpm/resolving.jsr-specifier-parser';
2
- import parseNpmTarballUrl from 'parse-npm-tarball-url';
3
+ import { parseNpmTarballUrl } from 'parse-npm-tarball-url';
4
+ import semver from 'semver';
3
5
  import getVersionSelectorType from 'version-selector-type';
4
6
  export function parseBareSpecifier(bareSpecifier, alias, defaultTag, registry) {
5
7
  let name = alias;
6
8
  if (bareSpecifier.startsWith('npm:')) {
7
9
  bareSpecifier = bareSpecifier.slice(4);
8
- const index = bareSpecifier.lastIndexOf('@');
9
- if (index < 1) {
10
- name = bareSpecifier;
11
- bareSpecifier = defaultTag;
10
+ // `npm:<version_selector>` fall back to the outer dependency alias as
11
+ // the package name, mirroring the named-registry shape (e.g. `gh:^1.0.0`).
12
+ // Restricted to semver ranges/versions so unscoped package names like
13
+ // `npm:is-positive` keep their npm package-aliasing meaning.
14
+ if (alias && semver.validRange(bareSpecifier) != null) {
15
+ name = alias;
12
16
  }
13
17
  else {
14
- name = bareSpecifier.slice(0, index);
15
- bareSpecifier = bareSpecifier.slice(index + 1);
18
+ const index = bareSpecifier.lastIndexOf('@');
19
+ if (index < 1) {
20
+ name = bareSpecifier;
21
+ bareSpecifier = defaultTag;
22
+ }
23
+ else {
24
+ name = bareSpecifier.slice(0, index);
25
+ bareSpecifier = bareSpecifier.slice(index + 1);
26
+ }
16
27
  }
17
28
  }
18
29
  if (name) {
@@ -26,7 +37,7 @@ export function parseBareSpecifier(bareSpecifier, alias, defaultTag, registry) {
26
37
  }
27
38
  }
28
39
  if (bareSpecifier.startsWith(registry)) {
29
- const pkg = parseNpmTarballUrl.default(bareSpecifier);
40
+ const pkg = parseNpmTarballUrl(bareSpecifier);
30
41
  if (pkg != null) {
31
42
  return {
32
43
  fetchSpec: pkg.version,
@@ -52,4 +63,75 @@ export function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier, alias, defa
52
63
  jsrPkgName: spec.jsrPkgName,
53
64
  };
54
65
  }
66
+ export const BUILTIN_NAMED_REGISTRIES = Object.freeze({
67
+ gh: 'https://npm.pkg.github.com/',
68
+ });
69
+ // Parses a named-registry specifier of the shape `<alias>:<body>` into a
70
+ // RegistryPackageSpec. Returns `null` when the specifier does not use one of
71
+ // the configured aliases, so the caller can fall through to other resolvers.
72
+ // Supported shapes:
73
+ // - `<alias>:[@<owner>/]<name>[@<version_selector>]`
74
+ // - `<alias>:<version_selector>` paired with a package alias
75
+ export function parseNamedRegistrySpecifierToRegistryPackageSpec(rawSpecifier, knownRegistryNames, packageAlias, defaultTag) {
76
+ const colon = rawSpecifier.indexOf(':');
77
+ if (colon <= 0)
78
+ return null;
79
+ const registryName = rawSpecifier.substring(0, colon);
80
+ if (!knownRegistryNames.has(registryName))
81
+ return null;
82
+ const body = rawSpecifier.substring(colon + 1);
83
+ let pkgName;
84
+ let versionSelector;
85
+ if (semver.validRange(body) != null) {
86
+ // `<alias>:<version_selector>` — fall back to the dependency alias as
87
+ // the package name. Unresolvable without one.
88
+ if (!packageAlias)
89
+ return null;
90
+ pkgName = packageAlias;
91
+ versionSelector = body;
92
+ }
93
+ else if (body[0] === '@') {
94
+ // `<alias>:@<owner>/<name>[@<version_selector>]` — scoped package.
95
+ const index = body.lastIndexOf('@');
96
+ if (index === 0) {
97
+ pkgName = body;
98
+ }
99
+ else {
100
+ pkgName = body.substring(0, index);
101
+ versionSelector = body.substring(index + '@'.length);
102
+ }
103
+ if (pkgName.indexOf('/') === -1 || pkgName.endsWith('/')) {
104
+ throw new PnpmError('INVALID_NAMED_REGISTRY_PACKAGE_NAME', `The package name '${pkgName}' in named registry '${registryName}:' is invalid`);
105
+ }
106
+ }
107
+ else if (packageAlias?.startsWith('@')) {
108
+ // `<alias>:<tag>` paired with a scoped alias — body is a version
109
+ // selector (tag/dist-tag). Mirrors GitHub Packages, where the package
110
+ // is always scoped and a bare body is a tag.
111
+ pkgName = packageAlias;
112
+ versionSelector = body;
113
+ }
114
+ else {
115
+ // `<alias>:<name>[@<version_selector>]` — unscoped package in body.
116
+ const index = body.lastIndexOf('@');
117
+ if (index < 1) {
118
+ pkgName = body;
119
+ }
120
+ else {
121
+ pkgName = body.substring(0, index);
122
+ versionSelector = body.substring(index + '@'.length);
123
+ }
124
+ if (!pkgName)
125
+ return null;
126
+ }
127
+ const selector = getVersionSelectorType(versionSelector ?? defaultTag);
128
+ if (selector == null)
129
+ return null;
130
+ return {
131
+ fetchSpec: selector.normalized,
132
+ name: pkgName,
133
+ type: selector.type,
134
+ registryName,
135
+ };
136
+ }
55
137
  //# sourceMappingURL=parseBareSpecifier.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/resolving.npm-resolver",
3
- "version": "1101.0.2",
3
+ "version": "1101.1.0",
4
4
  "description": "Resolver for npm-hosted packages",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,38 +28,38 @@
28
28
  "dependencies": {
29
29
  "@zkochan/retry": "^0.2.0",
30
30
  "encode-registry": "^3.0.1",
31
- "lru-cache": "^11.3.6",
31
+ "lru-cache": "^11.2.7",
32
32
  "normalize-path": "^3.0.0",
33
- "p-limit": "^7.3.0",
33
+ "p-limit": "^7.1.0",
34
34
  "p-memoize": "8.0.0",
35
- "parse-npm-tarball-url": "^4.0.0",
35
+ "parse-npm-tarball-url": "^5.0.0",
36
36
  "path-temp": "^3.0.0",
37
37
  "ramda": "npm:@pnpm/ramda@0.28.1",
38
38
  "rename-overwrite": "^7.0.1",
39
- "semver": "^7.7.4",
39
+ "semver": "^7.7.2",
40
40
  "semver-utils": "^1.1.4",
41
41
  "ssri": "13.0.1",
42
42
  "version-selector-type": "^3.0.0",
43
- "@pnpm/config.pick-registry-for-package": "1100.0.2",
44
- "@pnpm/constants": "1100.0.0",
45
- "@pnpm/core-loggers": "1100.0.1",
43
+ "@pnpm/config.pick-registry-for-package": "1100.0.3",
46
44
  "@pnpm/crypto.hash": "1100.0.1",
47
45
  "@pnpm/error": "1100.0.0",
48
46
  "@pnpm/fetching.types": "1100.0.1",
49
- "@pnpm/fs.graceful-fs": "1100.1.0",
50
47
  "@pnpm/resolving.jsr-specifier-parser": "1100.0.0",
51
- "@pnpm/resolving.registry.pkg-metadata-filter": "1100.0.2",
52
- "@pnpm/resolving.registry.types": "1100.0.2",
53
- "@pnpm/resolving.resolver-base": "1100.1.2",
54
- "@pnpm/store.cafs": "1100.1.2",
55
- "@pnpm/store.index": "1100.0.0",
56
- "@pnpm/types": "1101.0.0",
48
+ "@pnpm/fs.graceful-fs": "1100.1.0",
49
+ "@pnpm/resolving.registry.pkg-metadata-filter": "1100.0.3",
50
+ "@pnpm/store.cafs": "1100.1.3",
51
+ "@pnpm/store.index": "1100.1.0",
52
+ "@pnpm/resolving.registry.types": "1100.0.3",
53
+ "@pnpm/types": "1101.1.0",
54
+ "@pnpm/resolving.resolver-base": "1100.1.3",
55
+ "@pnpm/constants": "1100.0.0",
57
56
  "@pnpm/workspace.range-resolver": "1100.0.1",
58
- "@pnpm/workspace.spec-parser": "1100.0.0"
57
+ "@pnpm/workspace.spec-parser": "1100.0.0",
58
+ "@pnpm/core-loggers": "1100.0.2"
59
59
  },
60
60
  "peerDependencies": {
61
- "@pnpm/logger": "^1001.0.1",
62
- "@pnpm/worker": "^1100.1.2"
61
+ "@pnpm/logger": ">=1001.0.0 <1002.0.0",
62
+ "@pnpm/worker": "^1100.1.4"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@jest/globals": "30.3.0",
@@ -69,12 +69,12 @@
69
69
  "@types/ssri": "^7.1.5",
70
70
  "load-json-file": "^7.0.1",
71
71
  "tempy": "3.0.0",
72
- "@pnpm/config.version-policy": "1100.0.2",
72
+ "@pnpm/config.version-policy": "1100.0.3",
73
73
  "@pnpm/logger": "1100.0.0",
74
- "@pnpm/network.fetch": "1100.0.2",
75
- "@pnpm/resolving.npm-resolver": "1101.0.2",
74
+ "@pnpm/resolving.npm-resolver": "1101.1.0",
75
+ "@pnpm/network.fetch": "1100.0.3",
76
76
  "@pnpm/test-fixtures": "1100.0.0",
77
- "@pnpm/testing.mock-agent": "1100.0.2"
77
+ "@pnpm/testing.mock-agent": "1100.0.3"
78
78
  },
79
79
  "engines": {
80
80
  "node": ">=22.13"