@pnpm/resolving.tarball-url 1100.0.0 → 1101.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # @pnpm/resolving.tarball-url
2
+
3
+ ## 1101.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - The `registries` setting now declares a registry once, keyed by its URL, with everything about that registry in the entry: how it lays out tarball URLs, the scopes routed to it, and the bare-specifier prefix it answers to.
8
+
9
+ ```yaml
10
+ registries:
11
+ https://artifactory.example.com/artifactory/api/npm/npm-virtual/:
12
+ serverType: artifactory
13
+ scopes: ['@acme', '@acme-internal']
14
+ prefix: work
15
+ ```
16
+
17
+ - **`serverType`** tells pnpm how the registry lays out its tarball URLs, which decides whether a URL can be omitted from `pnpm-lock.yaml`:
18
+ - **undeclared** (the default) — strict. Only the exact canonical URL is treated as reconstructible.
19
+ - **`npm`** — the registry behaves like `registry.npmjs.org`, which also serves a scoped package from its percent-encoded path. Declare this for a faithful mirror or caching proxy of the public registry so its tarball URLs can be omitted too.
20
+ - **`artifactory`** — JFrog Artifactory repeats the scope in a scoped package's tarball filename (`@acme/widget/-/@acme/widget-1.0.0.tgz`) where the npm registry strips it (`@acme/widget/-/widget-1.0.0.tgz`). Declaring it lets pnpm rebuild that URL, so it is omitted from `pnpm-lock.yaml` instead of being written out for every scoped package [pnpm/get-npm-tarball-url#16](https://github.com/pnpm/get-npm-tarball-url/issues/16).
21
+ - **`scopes`** lists the `@`-prefixed scopes that resolve from this registry. A bare `'@'` is the scope-less default registry, the one the `registry` setting names.
22
+ - **`prefix`** is the alias a dependency addresses this registry by, as in `"foo": "work:^1.0.0"`.
23
+
24
+ The layout is never inferred from the registry URL, so nothing changes unless you declare it; `registry.npmjs.org` continues to behave as `npm` without being declared. Because the lockfile depends on `serverType`, it is read from `pnpm-workspace.yaml` only — a `serverType` in the global `config.yaml` is ignored, so one developer's machine cannot shape a lockfile their collaborators read back with a different layout. Credentials are rejected in this setting, in a key as well as in a field, and still belong in `.npmrc`. An entry that routes nothing to itself and matches no configured registry is reported as a warning rather than silently ignored.
25
+
26
+ ### Migrating
27
+
28
+ The older `registries` shape, a map of `<scope>: <url>` strings, still works and needs no change:
29
+
30
+ ```yaml
31
+ registries:
32
+ '@acme': https://npm.acme.example/
33
+ ```
34
+
35
+ `namedRegistries` is deprecated in favor of the `prefix` field, and is still read for prefixes `registries` does not declare.
36
+
37
+ `toLockfileResolution` and `isCanonicalRegistryTarballUrl` now take their registry and layout as an options object rather than positional arguments, so `@pnpm/lockfile.utils` and `@pnpm/resolving.tarball-url` get a major bump.
38
+
39
+ ### Patch Changes
40
+
41
+ - Fixed `404` errors when installing from a registry that serves scoped packages only from a percent-encoded path, such as GitHub Enterprise Server. Outside `registry.npmjs.org`, a tarball URL that encodes the scope separator as `%2f` or `%2F` is no longer mistaken for one that pnpm can rebuild from the package name, version, and registry, so it is kept in `pnpm-lock.yaml` and requested verbatim on the next install [#13534](https://github.com/pnpm/pnpm/issues/13534).
42
+
43
+ - Updated dependencies:
44
+ - @pnpm/types@1102.0.0
45
+
46
+ ## 1100.0.1
47
+
48
+ ### Patch Changes
49
+
50
+ - Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug [#13164](https://github.com/pnpm/pnpm/issues/13164).
package/lib/index.d.ts CHANGED
@@ -1,12 +1,24 @@
1
+ import type { RegistryServerType } from '@pnpm/types';
2
+ export interface TarballUrlOptions {
3
+ registry?: string;
4
+ /**
5
+ * Undeclared by default, which is the strict reading: only the exact
6
+ * canonical URL is reconstructible. See {@link RegistryServerType}.
7
+ */
8
+ serverType?: RegistryServerType;
9
+ }
1
10
  /**
2
11
  * Build the canonical tarball URL of an npm package — i.e. the URL pnpm derives
3
12
  * from a package's name, version, and registry. Vendored from the
4
13
  * `get-npm-tarball-url` package so the logic and its inverse
5
14
  * ({@link isCanonicalRegistryTarballUrl}) live together in the monorepo.
15
+ *
16
+ * This is the single source of the URL shape: the lockfile writer drops a
17
+ * tarball URL only when this function rebuilds it, and the lockfile reader
18
+ * rebuilds it with this function. Both sides therefore agree by construction,
19
+ * under every {@link RegistryServerType}.
6
20
  */
7
- export declare function getNpmTarballUrl(pkgName: string, pkgVersion: string, opts?: {
8
- registry?: string;
9
- }): string;
21
+ export declare function getNpmTarballUrl(pkgName: string, pkgVersion: string, opts?: TarballUrlOptions): string;
10
22
  /**
11
23
  * Whether `tarball` is the canonical npm registry URL derived from the package
12
24
  * name, version, and registry — i.e. it can be dropped from the lockfile and
@@ -20,10 +32,11 @@ export declare function getNpmTarballUrl(pkgName: string, pkgVersion: string, op
20
32
  * to `getNpmTarballUrl(name, version, { registry })` so nothing host-specific
21
33
  * is persisted to `pnpm-lock.yaml`.
22
34
  *
23
- * Percent-encoding is case-insensitive, so the `%2f` unescape matches both
24
- * `%2f` and `%2F` in the URLs npm produces for scoped packages.
35
+ * A `serverType` the user declared (via `getRegistryServerType` in
36
+ * `@pnpm/config.normalize-registries`) wins; otherwise the built-in layout of
37
+ * a known registry applies, and an unknown registry is read strictly.
25
38
  */
26
39
  export declare function isCanonicalRegistryTarballUrl(tarball: string, pkg: {
27
40
  name: string;
28
41
  version: string;
29
- }, registry: string): boolean;
42
+ }, opts: TarballUrlOptions): boolean;
package/lib/index.js CHANGED
@@ -1,13 +1,29 @@
1
+ const PUBLIC_NPM_REGISTRY = 'https://registry.npmjs.org/';
2
+ /**
3
+ * registry.npmjs.org is the one registry whose layout pnpm knows without being
4
+ * told, so it is a row of data rather than a hostname comparison. A declared
5
+ * `serverType` wins over it.
6
+ */
7
+ const DEFAULT_REGISTRY_SERVER_TYPES = {
8
+ [PUBLIC_NPM_REGISTRY]: 'npm',
9
+ };
1
10
  /**
2
11
  * Build the canonical tarball URL of an npm package — i.e. the URL pnpm derives
3
12
  * from a package's name, version, and registry. Vendored from the
4
13
  * `get-npm-tarball-url` package so the logic and its inverse
5
14
  * ({@link isCanonicalRegistryTarballUrl}) live together in the monorepo.
15
+ *
16
+ * This is the single source of the URL shape: the lockfile writer drops a
17
+ * tarball URL only when this function rebuilds it, and the lockfile reader
18
+ * rebuilds it with this function. Both sides therefore agree by construction,
19
+ * under every {@link RegistryServerType}.
6
20
  */
7
21
  export function getNpmTarballUrl(pkgName, pkgVersion, opts) {
8
22
  const registry = normalizeRegistry(opts?.registry);
9
- const scopelessName = getScopelessName(pkgName);
10
- return `${registry}${pkgName}/-/${scopelessName}-${removeBuildMetadataFromVersion(pkgVersion)}.tgz`;
23
+ // Artifactory keeps the scope in the filename of a scoped package's tarball
24
+ // (`@acme/widget/-/@acme/widget-1.0.0.tgz`); the npm layout strips it.
25
+ const filenameName = opts?.serverType === 'artifactory' ? pkgName : getScopelessName(pkgName);
26
+ return `${registry}${pkgName}/-/${filenameName}-${removeBuildMetadataFromVersion(pkgVersion)}.tgz`;
11
27
  }
12
28
  /**
13
29
  * Whether `tarball` is the canonical npm registry URL derived from the package
@@ -22,17 +38,27 @@ export function getNpmTarballUrl(pkgName, pkgVersion, opts) {
22
38
  * to `getNpmTarballUrl(name, version, { registry })` so nothing host-specific
23
39
  * is persisted to `pnpm-lock.yaml`.
24
40
  *
25
- * Percent-encoding is case-insensitive, so the `%2f` unescape matches both
26
- * `%2f` and `%2F` in the URLs npm produces for scoped packages.
41
+ * A `serverType` the user declared (via `getRegistryServerType` in
42
+ * `@pnpm/config.normalize-registries`) wins; otherwise the built-in layout of
43
+ * a known registry applies, and an unknown registry is read strictly.
27
44
  */
28
- export function isCanonicalRegistryTarballUrl(tarball, pkg, registry) {
29
- const expectedTarball = getNpmTarballUrl(pkg.name, pkg.version, { registry });
30
- const actualTarball = tarball.replace(/%2f/gi, '/');
31
- return removeProtocol(expectedTarball) === removeProtocol(actualTarball);
45
+ export function isCanonicalRegistryTarballUrl(tarball, pkg, opts) {
46
+ const expectedTarball = removeProtocol(getNpmTarballUrl(pkg.name, pkg.version, opts));
47
+ const actualTarball = removeProtocol(tarball);
48
+ if (expectedTarball === actualTarball)
49
+ return true;
50
+ // A registry behaving like registry.npmjs.org serves a scoped package from
51
+ // both the encoded and the unencoded path. A registry that has not been
52
+ // declared to behave like it may serve only the encoded one, so its URL is
53
+ // kept. See https://github.com/pnpm/pnpm/issues/13534.
54
+ return effectiveServerType(opts) === 'npm' && expectedTarball === actualTarball.replace(/%2f/gi, '/');
55
+ }
56
+ function effectiveServerType(opts) {
57
+ return opts.serverType ?? DEFAULT_REGISTRY_SERVER_TYPES[normalizeRegistry(opts.registry)];
32
58
  }
33
59
  function normalizeRegistry(registry) {
34
60
  if (!registry)
35
- return 'https://registry.npmjs.org/';
61
+ return PUBLIC_NPM_REGISTRY;
36
62
  return registry.endsWith('/') ? registry : `${registry}/`;
37
63
  }
38
64
  function removeBuildMetadataFromVersion(version) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/resolving.tarball-url",
3
- "version": "1100.0.0",
3
+ "version": "1101.0.0",
4
4
  "description": "Build and recognize the canonical tarball URL of an npm package",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,9 +28,12 @@
28
28
  "lib",
29
29
  "!*.map"
30
30
  ],
31
+ "dependencies": {
32
+ "@pnpm/types": "1102.0.0"
33
+ },
31
34
  "devDependencies": {
32
35
  "@jest/globals": "30.4.1",
33
- "@pnpm/resolving.tarball-url": "1100.0.0"
36
+ "@pnpm/resolving.tarball-url": "1101.0.0"
34
37
  },
35
38
  "engines": {
36
39
  "node": ">=22.13"