@vltpkg/registry-client 1.0.0-rc.27 → 1.0.0-rc.29

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/dist/auth.d.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import { Keychain } from '@vltpkg/keychain';
2
2
  export type Token = `Bearer ${string}` | `Basic ${string}`;
3
+ /**
4
+ * Normalize a registry URL into a stable key that preserves the
5
+ * path prefix. The result is `origin + pathname` with trailing
6
+ * slashes stripped so that
7
+ * `https://r.io/luke/` and `https://r.io/luke`
8
+ * both produce the same key.
9
+ *
10
+ * For plain-origin registries the result is identical to the old
11
+ * `new URL(url).origin` behaviour (e.g. `https://registry.npmjs.org`).
12
+ */
13
+ export declare const normalizeRegistryKey: (url: string) => string;
3
14
  export declare const keychains: Map<string, Keychain<Token>>;
4
15
  /**
5
16
  * In-memory token store for OIDC-exchanged tokens.
@@ -13,3 +24,13 @@ export declare const isToken: (t: any) => t is Token;
13
24
  export declare const deleteToken: (registry: string, identity: string) => Promise<void>;
14
25
  export declare const setToken: (registry: string, token: Token, identity: string) => Promise<void>;
15
26
  export declare const getToken: (registry: string, identity: string) => Promise<Token | undefined>;
27
+ /**
28
+ * Find the best matching token for a request URL by performing a
29
+ * longest-prefix match against all known registry keys (runtime
30
+ * tokens, env-var registries, and keychain entries).
31
+ *
32
+ * This is used by `RegistryClient.request()` which only has the
33
+ * full request URL — not the configured registry URL that was used
34
+ * to construct it.
35
+ */
36
+ export declare const getTokenByURL: (requestUrl: string, identity: string) => Promise<Token | undefined>;
package/dist/auth.js CHANGED
@@ -1,4 +1,18 @@
1
1
  import { Keychain } from '@vltpkg/keychain';
2
+ /**
3
+ * Normalize a registry URL into a stable key that preserves the
4
+ * path prefix. The result is `origin + pathname` with trailing
5
+ * slashes stripped so that
6
+ * `https://r.io/luke/` and `https://r.io/luke`
7
+ * both produce the same key.
8
+ *
9
+ * For plain-origin registries the result is identical to the old
10
+ * `new URL(url).origin` behaviour (e.g. `https://registry.npmjs.org`).
11
+ */
12
+ export const normalizeRegistryKey = (url) => {
13
+ const u = new URL(url);
14
+ return (u.origin + u.pathname).replace(/\/+$/, '');
15
+ };
2
16
  // just exported for testing
3
17
  export const keychains = new Map();
4
18
  /**
@@ -7,7 +21,7 @@ export const keychains = new Map();
7
21
  */
8
22
  export const runtimeTokens = new Map();
9
23
  export const setRuntimeToken = (registry, token) => {
10
- runtimeTokens.set(new URL(registry).origin, token);
24
+ runtimeTokens.set(normalizeRegistryKey(registry), token);
11
25
  };
12
26
  export const clearRuntimeTokens = () => {
13
27
  runtimeTokens.clear();
@@ -26,28 +40,69 @@ export const isToken = (t) => typeof t === 'string' &&
26
40
  export const deleteToken = async (registry, identity) => {
27
41
  const kc = getKC(identity);
28
42
  await kc.load();
29
- kc.delete(new URL(registry).origin);
43
+ kc.delete(normalizeRegistryKey(registry));
30
44
  await kc.save();
31
45
  };
32
46
  export const setToken = async (registry, token, identity) => {
33
47
  const kc = getKC(identity);
34
- return kc.set(new URL(registry).origin, token);
48
+ return kc.set(normalizeRegistryKey(registry), token);
35
49
  };
36
50
  export const getToken = async (registry, identity) => {
37
51
  const kc = getKC(identity);
38
- registry = new URL(registry).origin;
52
+ const key = normalizeRegistryKey(registry);
39
53
  // Runtime tokens (e.g. from OIDC exchange) take precedence
40
- const rt = runtimeTokens.get(registry);
54
+ const rt = runtimeTokens.get(key);
41
55
  if (rt)
42
56
  return rt;
43
57
  const envReg = process.env.VLT_REGISTRY;
44
- if (envReg && registry === new URL(envReg).origin) {
58
+ if (envReg && key === normalizeRegistryKey(envReg)) {
45
59
  const envTok = process.env.VLT_TOKEN;
46
60
  if (envTok)
47
61
  return `Bearer ${envTok}`;
48
62
  }
49
- const tok = process.env[`VLT_TOKEN_${registry.replace(/[^a-zA-Z0-9]+/g, '_')}`];
63
+ const tok = process.env[`VLT_TOKEN_${key.replace(/[^a-zA-Z0-9]+/g, '_')}`];
50
64
  if (tok)
51
65
  return `Bearer ${tok}`;
52
- return kc.get(registry);
66
+ return kc.get(key);
67
+ };
68
+ /**
69
+ * Find the best matching token for a request URL by performing a
70
+ * longest-prefix match against all known registry keys (runtime
71
+ * tokens, env-var registries, and keychain entries).
72
+ *
73
+ * This is used by `RegistryClient.request()` which only has the
74
+ * full request URL — not the configured registry URL that was used
75
+ * to construct it.
76
+ */
77
+ export const getTokenByURL = async (requestUrl, identity) => {
78
+ const normalized = normalizeRegistryKey(requestUrl);
79
+ // Collect all known registry keys.
80
+ const candidates = [...runtimeTokens.keys()];
81
+ const envReg = process.env.VLT_REGISTRY;
82
+ if (envReg) {
83
+ candidates.push(normalizeRegistryKey(envReg));
84
+ }
85
+ const kc = getKC(identity);
86
+ // Keychain entries
87
+ for (const k of await kc.keys()) {
88
+ candidates.push(k);
89
+ }
90
+ // Find the longest candidate key that is a prefix of the
91
+ // normalized request URL.
92
+ let bestKey;
93
+ let bestLen = 0;
94
+ for (const candidate of candidates) {
95
+ if (candidate.length > bestLen &&
96
+ (normalized === candidate ||
97
+ normalized.startsWith(candidate + '/'))) {
98
+ bestKey = candidate;
99
+ bestLen = candidate.length;
100
+ }
101
+ }
102
+ if (bestKey) {
103
+ return getToken(bestKey, identity);
104
+ }
105
+ // Fall back to origin-only match (handles VLT_TOKEN_* env vars
106
+ // which we can't enumerate by URL).
107
+ return getToken(requestUrl, identity);
53
108
  };
package/dist/index.d.ts CHANGED
@@ -3,14 +3,14 @@ import type { Integrity } from '@vltpkg/types';
3
3
  import type { Dispatcher } from 'undici';
4
4
  import { RetryAgent } from 'undici';
5
5
  import type { Token } from './auth.ts';
6
- import { clearRuntimeTokens, deleteToken, getKC, isToken, keychains, runtimeTokens, setRuntimeToken, setToken } from './auth.ts';
6
+ import { clearRuntimeTokens, deleteToken, getKC, getTokenByURL, isToken, keychains, normalizeRegistryKey, runtimeTokens, setRuntimeToken, setToken } from './auth.ts';
7
7
  import type { JSONObj } from './cache-entry.ts';
8
8
  import { CacheEntry } from './cache-entry.ts';
9
9
  import type { TokenResponse } from './token-response.ts';
10
10
  import type { WebAuthChallenge } from './web-auth-challenge.ts';
11
11
  import { oidc } from './oidc.ts';
12
12
  import type { OidcOptions } from './oidc.ts';
13
- export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, isToken, keychains, oidc, runtimeTokens, setRuntimeToken, setToken, type JSONObj, type OidcOptions, type Token, type TokenResponse, type WebAuthChallenge, };
13
+ export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, runtimeTokens, setRuntimeToken, setToken, type JSONObj, type OidcOptions, type Token, type TokenResponse, type WebAuthChallenge, };
14
14
  export type CacheableMethod = 'GET' | 'HEAD';
15
15
  export declare const isCacheableMethod: (m: unknown) => m is CacheableMethod;
16
16
  export type RegistryClientOptions = {
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { setTimeout } from 'node:timers/promises';
10
10
  import { loadPackageJson } from 'package-json-from-dist';
11
11
  import { Agent, RetryAgent } from 'undici';
12
12
  import { addHeader } from "./add-header.js";
13
- import { clearRuntimeTokens, deleteToken, getKC, getToken, isToken, keychains, runtimeTokens, setRuntimeToken, setToken, } from "./auth.js";
13
+ import { clearRuntimeTokens, deleteToken, getKC, getToken, getTokenByURL, isToken, keychains, normalizeRegistryKey, runtimeTokens, setRuntimeToken, setToken, } from "./auth.js";
14
14
  import { CacheEntry } from "./cache-entry.js";
15
15
  import { register } from "./cache-revalidate.js";
16
16
  import { bun, deno, node } from "./env.js";
@@ -22,7 +22,7 @@ import { getTokenResponse } from "./token-response.js";
22
22
  import { getWebAuthChallenge } from "./web-auth-challenge.js";
23
23
  import { getEncondedValue } from "./string-encoding.js";
24
24
  import { oidc } from "./oidc.js";
25
- export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, isToken, keychains, oidc, runtimeTokens, setRuntimeToken, setToken, };
25
+ export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, runtimeTokens, setRuntimeToken, setToken, };
26
26
  export const isCacheableMethod = (m) => m === 'GET' || m === 'HEAD';
27
27
  const { version } = loadPackageJson(import.meta.filename, process.env.__VLT_INTERNAL_REGISTRY_CLIENT_PACKAGE_JSON);
28
28
  const nua = globalThis.navigator?.userAgent ??
@@ -213,7 +213,6 @@ export class RegistryClient {
213
213
  const { useCache = !!m } = options;
214
214
  signal?.throwIfAborted();
215
215
  // first, try to get from the cache before making any request.
216
- const { origin } = u;
217
216
  const key = `${method !== 'GET' ? method + ' ' : ''}${u}`;
218
217
  const buffer = useCache ?
219
218
  await this.cache.fetch(key, { context: { integrity } })
@@ -247,7 +246,7 @@ export class RegistryClient {
247
246
  }
248
247
  options.method = options.method ?? 'GET';
249
248
  // will remove if we don't have a token.
250
- options.headers = addHeader(options.headers, 'authorization', await getToken(origin, this.identity));
249
+ options.headers = addHeader(options.headers, 'authorization', await getTokenByURL(String(u), this.identity));
251
250
  let response = null;
252
251
  try {
253
252
  response = await this.agent.request(options);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/registry-client",
3
3
  "description": "Fetch package artifacts and metadata from registries",
4
- "version": "1.0.0-rc.27",
4
+ "version": "1.0.0-rc.29",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,14 +13,14 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/cache": "1.0.0-rc.27",
17
- "@vltpkg/cache-unzip": "1.0.0-rc.27",
18
- "@vltpkg/error-cause": "1.0.0-rc.27",
19
- "@vltpkg/keychain": "1.0.0-rc.27",
20
- "@vltpkg/output": "1.0.0-rc.27",
21
- "@vltpkg/types": "1.0.0-rc.27",
22
- "@vltpkg/url-open": "1.0.0-rc.27",
23
- "@vltpkg/xdg": "1.0.0-rc.27",
16
+ "@vltpkg/cache": "1.0.0-rc.29",
17
+ "@vltpkg/cache-unzip": "1.0.0-rc.29",
18
+ "@vltpkg/error-cause": "1.0.0-rc.29",
19
+ "@vltpkg/keychain": "1.0.0-rc.29",
20
+ "@vltpkg/output": "1.0.0-rc.29",
21
+ "@vltpkg/types": "1.0.0-rc.29",
22
+ "@vltpkg/url-open": "1.0.0-rc.29",
23
+ "@vltpkg/xdg": "1.0.0-rc.29",
24
24
  "cache-control-parser": "^2.0.6",
25
25
  "package-json-from-dist": "^1.0.1",
26
26
  "undici": "^7.16.0"