@vltpkg/registry-client 1.0.0-rc.29 → 1.0.0-rc.30
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 +8 -0
- package/dist/auth.js +11 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +31 -11
- package/dist/oidc.js +1 -2
- package/dist/otplease.d.ts +6 -1
- package/dist/otplease.js +19 -7
- package/package.json +9 -9
package/dist/auth.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export type Token = `Bearer ${string}` | `Basic ${string}`;
|
|
|
11
11
|
* `new URL(url).origin` behaviour (e.g. `https://registry.npmjs.org`).
|
|
12
12
|
*/
|
|
13
13
|
export declare const normalizeRegistryKey: (url: string) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Ensure a registry URL ends with `/` so that `new URL(path, base)`
|
|
16
|
+
* appends under the full path instead of replacing the last segment.
|
|
17
|
+
*
|
|
18
|
+
* registryBase('https://r.io/scope/name')
|
|
19
|
+
* // → 'https://r.io/scope/name/'
|
|
20
|
+
*/
|
|
21
|
+
export declare const registryBase: (url: string) => string;
|
|
14
22
|
export declare const keychains: Map<string, Keychain<Token>>;
|
|
15
23
|
/**
|
|
16
24
|
* In-memory token store for OIDC-exchanged tokens.
|
package/dist/auth.js
CHANGED
|
@@ -13,6 +13,14 @@ export const normalizeRegistryKey = (url) => {
|
|
|
13
13
|
const u = new URL(url);
|
|
14
14
|
return (u.origin + u.pathname).replace(/\/+$/, '');
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Ensure a registry URL ends with `/` so that `new URL(path, base)`
|
|
18
|
+
* appends under the full path instead of replacing the last segment.
|
|
19
|
+
*
|
|
20
|
+
* registryBase('https://r.io/scope/name')
|
|
21
|
+
* // → 'https://r.io/scope/name/'
|
|
22
|
+
*/
|
|
23
|
+
export const registryBase = (url) => url.endsWith('/') ? url : url + '/';
|
|
16
24
|
// just exported for testing
|
|
17
25
|
export const keychains = new Map();
|
|
18
26
|
/**
|
|
@@ -45,7 +53,9 @@ export const deleteToken = async (registry, identity) => {
|
|
|
45
53
|
};
|
|
46
54
|
export const setToken = async (registry, token, identity) => {
|
|
47
55
|
const kc = getKC(identity);
|
|
48
|
-
|
|
56
|
+
await kc.load();
|
|
57
|
+
kc.set(normalizeRegistryKey(registry), token);
|
|
58
|
+
await kc.save();
|
|
49
59
|
};
|
|
50
60
|
export const getToken = async (registry, identity) => {
|
|
51
61
|
const kc = getKC(identity);
|
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, getTokenByURL, isToken, keychains, normalizeRegistryKey, runtimeTokens, setRuntimeToken, setToken } from './auth.ts';
|
|
6
|
+
import { clearRuntimeTokens, deleteToken, getKC, getToken, getTokenByURL, isToken, keychains, normalizeRegistryKey, registryBase, 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, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, runtimeTokens, setRuntimeToken, setToken, type JSONObj, type OidcOptions, type Token, type TokenResponse, type WebAuthChallenge, };
|
|
13
|
+
export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, getToken, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, registryBase, 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, getTokenByURL, isToken, keychains, normalizeRegistryKey, runtimeTokens, setRuntimeToken, setToken, } from "./auth.js";
|
|
13
|
+
import { clearRuntimeTokens, deleteToken, getKC, getToken, getTokenByURL, isToken, keychains, normalizeRegistryKey, registryBase, 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, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, runtimeTokens, setRuntimeToken, setToken, };
|
|
25
|
+
export { CacheEntry, clearRuntimeTokens, deleteToken, getKC, getToken, getTokenByURL, isToken, keychains, normalizeRegistryKey, oidc, registryBase, 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 ??
|
|
@@ -112,13 +112,14 @@ export class RegistryClient {
|
|
|
112
112
|
if (!tok)
|
|
113
113
|
return;
|
|
114
114
|
const s = tok.replace(/^(Bearer|Basic) /i, '');
|
|
115
|
-
const
|
|
115
|
+
const base = registryBase(registry);
|
|
116
|
+
const tokensUrl = new URL('-/npm/v1/tokens', base);
|
|
116
117
|
const record = await this.seek(tokensUrl, ({ token }) => s.startsWith(token), {
|
|
117
118
|
useCache: false,
|
|
118
119
|
}).catch(() => undefined);
|
|
119
120
|
if (record) {
|
|
120
121
|
const { key } = record;
|
|
121
|
-
await this.request(new URL(`-/npm/v1/tokens/token/${key}`,
|
|
122
|
+
await this.request(new URL(`-/npm/v1/tokens/token/${key}`, base), { useCache: false, method: 'DELETE' });
|
|
122
123
|
}
|
|
123
124
|
await deleteToken(registry, this.identity);
|
|
124
125
|
}
|
|
@@ -136,7 +137,7 @@ export class RegistryClient {
|
|
|
136
137
|
// - hang on the doneUrl until done
|
|
137
138
|
//
|
|
138
139
|
// if that fails: fall back to couchdb login
|
|
139
|
-
const webLoginURL = new URL('-/v1/login', registry);
|
|
140
|
+
const webLoginURL = new URL('-/v1/login', registryBase(registry));
|
|
140
141
|
const response = await this.request(webLoginURL, {
|
|
141
142
|
method: 'POST',
|
|
142
143
|
useCache: false,
|
|
@@ -281,10 +282,15 @@ export class RegistryClient {
|
|
|
281
282
|
async #handleResponse(url, options, response, entry) {
|
|
282
283
|
if (handleCacheHitResponse(response, entry))
|
|
283
284
|
return entry;
|
|
285
|
+
let consumedBody;
|
|
284
286
|
if (response.statusCode === 401) {
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
return await this.request(url,
|
|
287
|
+
const otpResult = await otplease(this, options, response);
|
|
288
|
+
if (otpResult && 'retry' in otpResult) {
|
|
289
|
+
return await this.request(url, otpResult.retry);
|
|
290
|
+
}
|
|
291
|
+
if (otpResult && 'bodyConsumed' in otpResult) {
|
|
292
|
+
consumedBody = otpResult.bodyConsumed;
|
|
293
|
+
}
|
|
288
294
|
}
|
|
289
295
|
const h = [];
|
|
290
296
|
for (const [key, value] of Object.entries(response.headers)) {
|
|
@@ -298,15 +304,20 @@ export class RegistryClient {
|
|
|
298
304
|
}
|
|
299
305
|
}
|
|
300
306
|
const { integrity, trustIntegrity } = options;
|
|
307
|
+
// When otplease already consumed the body, use its length
|
|
308
|
+
// instead of the Content-Length header to size the CacheEntry
|
|
309
|
+
// buffer correctly.
|
|
310
|
+
const contentLength = consumedBody !== undefined ? consumedBody.length
|
|
311
|
+
: response.headers['content-length'] ?
|
|
312
|
+
Number(response.headers['content-length'])
|
|
313
|
+
: /* c8 ignore next */ undefined;
|
|
301
314
|
const result = new CacheEntry(
|
|
302
315
|
/* c8 ignore next - should always have a status code */
|
|
303
316
|
response.statusCode || 200, h, {
|
|
304
317
|
integrity,
|
|
305
318
|
trustIntegrity,
|
|
306
319
|
'stale-while-revalidate-factor': this.staleWhileRevalidateFactor,
|
|
307
|
-
contentLength
|
|
308
|
-
Number(response.headers['content-length'])
|
|
309
|
-
: /* c8 ignore next */ undefined,
|
|
320
|
+
contentLength,
|
|
310
321
|
});
|
|
311
322
|
if (isRedirect(result)) {
|
|
312
323
|
response.body.resume();
|
|
@@ -316,6 +327,15 @@ export class RegistryClient {
|
|
|
316
327
|
}
|
|
317
328
|
return result;
|
|
318
329
|
}
|
|
330
|
+
// If otplease already consumed the body (e.g. checking for OTP
|
|
331
|
+
// prompt on a plain 401), use the text it read rather than trying
|
|
332
|
+
// to re-read from the already-drained stream.
|
|
333
|
+
if (consumedBody !== undefined) {
|
|
334
|
+
if (consumedBody.length > 0) {
|
|
335
|
+
result.addBody(new TextEncoder().encode(consumedBody));
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
319
339
|
response.body.on('data', (chunk) => result.addBody(chunk));
|
|
320
340
|
return await new Promise((res, rej) => {
|
|
321
341
|
response.body.on('error', rej);
|
package/dist/oidc.js
CHANGED
|
@@ -47,11 +47,10 @@ const _oidc = async ({ packageName, registry, }) => {
|
|
|
47
47
|
const isGitHub = process.env.GITHUB_ACTIONS === 'true';
|
|
48
48
|
const isGitLab = process.env.GITLAB_CI === 'true';
|
|
49
49
|
const isCircle = process.env.CIRCLECI === 'true';
|
|
50
|
-
log(`CI detected: github=${isGitHub} gitlab=${isGitLab} circle=${isCircle}`);
|
|
51
50
|
if (!isGitHub && !isGitLab && !isCircle) {
|
|
52
|
-
log('Not in a supported CI environment — skipping');
|
|
53
51
|
return undefined;
|
|
54
52
|
}
|
|
53
|
+
log(`CI detected: github=${isGitHub} gitlab=${isGitLab} circle=${isCircle}`);
|
|
55
54
|
// NPM_ID_TOKEN is supported as an override in all CI environments
|
|
56
55
|
let idToken = process.env.NPM_ID_TOKEN;
|
|
57
56
|
log(`NPM_ID_TOKEN: ${idToken ? `set (length=${idToken.length})` : 'not set'}`);
|
package/dist/otplease.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { Dispatcher } from 'undici';
|
|
2
2
|
import type { RegistryClient, RegistryClientRequestOptions } from './index.ts';
|
|
3
|
-
export
|
|
3
|
+
export type OtpResult = {
|
|
4
|
+
retry: RegistryClientRequestOptions;
|
|
5
|
+
} | {
|
|
6
|
+
bodyConsumed: string;
|
|
7
|
+
} | undefined;
|
|
8
|
+
export declare const otplease: (client: RegistryClient, options: RegistryClientRequestOptions, response: Dispatcher.ResponseData) => Promise<OtpResult>;
|
package/dist/otplease.js
CHANGED
|
@@ -27,8 +27,10 @@ export const otplease = async (client, options, response) => {
|
|
|
27
27
|
const challenge = getWebAuthChallenge(await response.body.json().catch(() => null));
|
|
28
28
|
if (challenge) {
|
|
29
29
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
retry: {
|
|
31
|
+
...options,
|
|
32
|
+
otp: (await client.webAuthOpener(challenge)).token,
|
|
33
|
+
},
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
const { 'npm-notice': npmNotice } = response.headers;
|
|
@@ -39,8 +41,10 @@ export const otplease = async (client, options, response) => {
|
|
|
39
41
|
await urlOpen(match[1]);
|
|
40
42
|
log(notice);
|
|
41
43
|
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
retry: {
|
|
45
|
+
...options,
|
|
46
|
+
otp: await question('OTP: '),
|
|
47
|
+
},
|
|
44
48
|
};
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -48,15 +52,23 @@ export const otplease = async (client, options, response) => {
|
|
|
48
52
|
response,
|
|
49
53
|
});
|
|
50
54
|
}
|
|
55
|
+
if (wwwAuth.has('bearer')) {
|
|
56
|
+
throw error('Missing or invalid authentication token. Run `vlt login` or `vlt token add` to authenticate.', { response });
|
|
57
|
+
}
|
|
51
58
|
if (wwwAuth.size) {
|
|
52
59
|
throw error('Unknown authentication challenge', { response });
|
|
53
60
|
}
|
|
54
|
-
//
|
|
61
|
+
// Consume the body to check if it's prompting for OTP.
|
|
62
|
+
// We must return the consumed text so the caller doesn't try to
|
|
63
|
+
// re-read from the already-drained stream.
|
|
55
64
|
const text = await response.body.text().catch(() => '');
|
|
56
65
|
if (text.toLowerCase().includes('one-time pass')) {
|
|
57
66
|
return {
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
retry: {
|
|
68
|
+
...options,
|
|
69
|
+
otp: await question(text),
|
|
70
|
+
},
|
|
60
71
|
};
|
|
61
72
|
}
|
|
73
|
+
return { bodyConsumed: text };
|
|
62
74
|
};
|
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.
|
|
4
|
+
"version": "1.0.0-rc.30",
|
|
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.
|
|
17
|
-
"@vltpkg/cache-unzip": "1.0.0-rc.
|
|
18
|
-
"@vltpkg/error-cause": "1.0.0-rc.
|
|
19
|
-
"@vltpkg/keychain": "1.0.0-rc.
|
|
20
|
-
"@vltpkg/output": "1.0.0-rc.
|
|
21
|
-
"@vltpkg/types": "1.0.0-rc.
|
|
22
|
-
"@vltpkg/url-open": "1.0.0-rc.
|
|
23
|
-
"@vltpkg/xdg": "1.0.0-rc.
|
|
16
|
+
"@vltpkg/cache": "1.0.0-rc.30",
|
|
17
|
+
"@vltpkg/cache-unzip": "1.0.0-rc.30",
|
|
18
|
+
"@vltpkg/error-cause": "1.0.0-rc.30",
|
|
19
|
+
"@vltpkg/keychain": "1.0.0-rc.30",
|
|
20
|
+
"@vltpkg/output": "1.0.0-rc.30",
|
|
21
|
+
"@vltpkg/types": "1.0.0-rc.30",
|
|
22
|
+
"@vltpkg/url-open": "1.0.0-rc.30",
|
|
23
|
+
"@vltpkg/xdg": "1.0.0-rc.30",
|
|
24
24
|
"cache-control-parser": "^2.0.6",
|
|
25
25
|
"package-json-from-dist": "^1.0.1",
|
|
26
26
|
"undici": "^7.16.0"
|