@vltpkg/registry-client 0.0.0-0.1730239248325 → 0.0.0-2

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.
Files changed (61) hide show
  1. package/README.md +14 -8
  2. package/dist/esm/add-header.d.ts +1 -1
  3. package/dist/esm/add-header.d.ts.map +1 -1
  4. package/dist/esm/add-header.js +4 -1
  5. package/dist/esm/add-header.js.map +1 -1
  6. package/dist/esm/auth.d.ts +9 -0
  7. package/dist/esm/auth.d.ts.map +1 -0
  8. package/dist/esm/auth.js +39 -0
  9. package/dist/esm/auth.js.map +1 -0
  10. package/dist/esm/cache-entry.d.ts +8 -6
  11. package/dist/esm/cache-entry.d.ts.map +1 -1
  12. package/dist/esm/cache-entry.js +9 -20
  13. package/dist/esm/cache-entry.js.map +1 -1
  14. package/dist/esm/delete-header.d.ts +2 -0
  15. package/dist/esm/delete-header.d.ts.map +1 -0
  16. package/dist/esm/delete-header.js +32 -0
  17. package/dist/esm/delete-header.js.map +1 -0
  18. package/dist/esm/env.d.ts +0 -4
  19. package/dist/esm/env.d.ts.map +1 -1
  20. package/dist/esm/env.js +0 -3
  21. package/dist/esm/env.js.map +1 -1
  22. package/dist/esm/get-header.js +1 -1
  23. package/dist/esm/get-header.js.map +1 -1
  24. package/dist/esm/handle-304-response.d.ts +2 -2
  25. package/dist/esm/handle-304-response.d.ts.map +1 -1
  26. package/dist/esm/handle-304-response.js.map +1 -1
  27. package/dist/esm/index.d.ts +67 -9
  28. package/dist/esm/index.d.ts.map +1 -1
  29. package/dist/esm/index.js +215 -57
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/is-iterable.d.ts +2 -0
  32. package/dist/esm/is-iterable.d.ts.map +1 -0
  33. package/dist/esm/is-iterable.js +2 -0
  34. package/dist/esm/is-iterable.js.map +1 -0
  35. package/dist/esm/otplease.d.ts +4 -0
  36. package/dist/esm/otplease.d.ts.map +1 -0
  37. package/dist/esm/otplease.js +55 -0
  38. package/dist/esm/otplease.js.map +1 -0
  39. package/dist/esm/raw-header.d.ts +1 -1
  40. package/dist/esm/raw-header.d.ts.map +1 -1
  41. package/dist/esm/redirect.d.ts +2 -2
  42. package/dist/esm/redirect.d.ts.map +1 -1
  43. package/dist/esm/redirect.js +1 -0
  44. package/dist/esm/redirect.js.map +1 -1
  45. package/dist/esm/set-cache-headers.d.ts +2 -2
  46. package/dist/esm/set-cache-headers.d.ts.map +1 -1
  47. package/dist/esm/set-cache-headers.js +1 -1
  48. package/dist/esm/set-cache-headers.js.map +1 -1
  49. package/dist/esm/token-response.d.ts +5 -0
  50. package/dist/esm/token-response.d.ts.map +1 -0
  51. package/dist/esm/token-response.js +5 -0
  52. package/dist/esm/token-response.js.map +1 -0
  53. package/dist/esm/web-auth-challenge.d.ts +6 -0
  54. package/dist/esm/web-auth-challenge.d.ts.map +1 -0
  55. package/dist/esm/web-auth-challenge.js +7 -0
  56. package/dist/esm/web-auth-challenge.js.map +1 -0
  57. package/package.json +28 -17
  58. package/dist/esm/serdes.d.ts +0 -15
  59. package/dist/esm/serdes.d.ts.map +0 -1
  60. package/dist/esm/serdes.js +0 -19
  61. package/dist/esm/serdes.js.map +0 -1
package/dist/esm/index.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import { Cache } from '@vltpkg/cache';
2
2
  import { register } from '@vltpkg/cache-unzip';
3
+ import { error } from '@vltpkg/error-cause';
4
+ import { urlOpen } from '@vltpkg/url-open';
3
5
  import { XDG } from '@vltpkg/xdg';
6
+ import { setTimeout } from 'node:timers/promises';
4
7
  import { loadPackageJson } from 'package-json-from-dist';
5
- import { Agent } from 'undici';
6
- import { addHeader } from './add-header.js';
7
- import { CacheEntry } from './cache-entry.js';
8
- import { bun, deno, node } from './env.js';
9
- import { handle304Response } from './handle-304-response.js';
10
- import { isRedirect, redirect } from './redirect.js';
11
- import { setCacheHeaders } from './set-cache-headers.js';
12
- const { version } = loadPackageJson(import.meta.filename);
8
+ import { Agent, RetryAgent } from 'undici';
9
+ import { addHeader } from "./add-header.js";
10
+ import { deleteToken, getKC, getToken, isToken, keychains, setToken, } from "./auth.js";
11
+ import { CacheEntry } from "./cache-entry.js";
12
+ import { bun, deno, node } from "./env.js";
13
+ import { handle304Response } from "./handle-304-response.js";
14
+ import { otplease } from "./otplease.js";
15
+ import { isRedirect, redirect } from "./redirect.js";
16
+ import { setCacheHeaders } from "./set-cache-headers.js";
17
+ import { logRequest } from '@vltpkg/output';
18
+ import { isTokenResponse } from "./token-response.js";
19
+ import { isWebAuthChallenge } from "./web-auth-challenge.js";
20
+ export { keychains, getKC, setToken, deleteToken, isToken };
21
+ const { version } = loadPackageJson(import.meta.filename, process.env.__VLT_INTERNAL_REGISTRY_CLIENT_PACKAGE_JSON);
13
22
  const nua = globalThis.navigator?.userAgent ??
14
23
  (bun ? `Bun/${bun}`
15
24
  : deno ? `Deno/${deno}`
@@ -35,7 +44,10 @@ const xdg = new XDG('vlt');
35
44
  export class RegistryClient {
36
45
  agent;
37
46
  cache;
38
- constructor({ cache = xdg.cache('registry-client'), }) {
47
+ identity;
48
+ constructor(options) {
49
+ const { cache = xdg.cache('registry-client'), 'fetch-retry-factor': timeoutFactor = 2, 'fetch-retry-mintimeout': minTimeout = 0, 'fetch-retry-maxtimeout': maxTimeout = 30_000, 'fetch-retries': maxRetries = 3, identity = '', } = options;
50
+ this.identity = identity;
39
51
  this.cache = new Cache({
40
52
  path: cache,
41
53
  onDiskWrite(_path, key, data) {
@@ -43,17 +55,157 @@ export class RegistryClient {
43
55
  register(cache, key);
44
56
  },
45
57
  });
46
- this.agent = new Agent(agentOptions);
58
+ const dispatch = new Agent(agentOptions);
59
+ this.agent = new RetryAgent(dispatch, {
60
+ maxRetries,
61
+ timeoutFactor,
62
+ minTimeout,
63
+ maxTimeout,
64
+ retryAfter: true,
65
+ errorCodes: [
66
+ 'ECONNREFUSED',
67
+ 'ECONNRESET',
68
+ 'EHOSTDOWN',
69
+ 'ENETDOWN',
70
+ 'ENETUNREACH',
71
+ 'ENOTFOUND',
72
+ 'EPIPE',
73
+ 'UND_ERR_SOCKET',
74
+ ],
75
+ });
76
+ }
77
+ /**
78
+ * Fetch the entire set of a paginated list of objects
79
+ */
80
+ async scroll(url, options = {}, seek) {
81
+ const resp = await this.request(url, options);
82
+ const { objects, urls } = resp.json();
83
+ // if we have more, and haven't found our target, fetch more
84
+ return urls.next && !(seek && objects.some(seek)) ?
85
+ objects.concat(await this.scroll(urls.next, options, seek))
86
+ : objects;
87
+ }
88
+ /**
89
+ * find a given item in a paginated set
90
+ */
91
+ async seek(url, seek, options = {}) {
92
+ return (await this.scroll(url, options, seek)).find(seek);
93
+ }
94
+ /**
95
+ * Log out from the registry specified, attempting to destroy the
96
+ * token if the registry supports that endpoint.
97
+ */
98
+ async logout(registry) {
99
+ // if we have no token for that registry, nothing to do
100
+ const tok = await getToken(registry, this.identity);
101
+ if (!tok)
102
+ return;
103
+ const s = tok.replace(/^(Bearer|Basic) /i, '');
104
+ const tokensUrl = new URL('-/npm/v1/tokens', registry);
105
+ const record = await this.seek(tokensUrl, ({ token }) => s.startsWith(token), {
106
+ cache: false,
107
+ }).catch(() => undefined);
108
+ if (record) {
109
+ const { key } = record;
110
+ await this.request(new URL(`-/npm/v1/tokens/token/${key}`, registry), { cache: false, method: 'DELETE' });
111
+ }
112
+ await deleteToken(registry, this.identity);
113
+ }
114
+ /**
115
+ * Log into the registry specified
116
+ *
117
+ * Does not return the token or expose it, just saves to the auth keychain
118
+ * and returns void if it worked. Otherwise, error is raised.
119
+ */
120
+ async login(registry) {
121
+ // - make POST to '/-/v1/login'
122
+ // - include a body of {} and npm-auth-type:web
123
+ // - get a {doneUrl, loginUrl}
124
+ // - open the loginUrl
125
+ // - hang on the doneUrl until done
126
+ //
127
+ // if that fails: fall back to couchdb login
128
+ const webLoginURL = new URL('-/v1/login', registry);
129
+ const response = await this.request(webLoginURL, {
130
+ method: 'POST',
131
+ cache: false,
132
+ headers: {
133
+ 'content-type': 'application/json',
134
+ 'npm-auth-type': 'web',
135
+ },
136
+ body: '{}',
137
+ });
138
+ if (response.statusCode === 200) {
139
+ const challenge = response.json();
140
+ if (isWebAuthChallenge(challenge)) {
141
+ const result = await this.webAuthOpener(challenge);
142
+ await setToken(registry, `Bearer ${result.token}`, this.identity);
143
+ return;
144
+ }
145
+ }
146
+ /* c8 ignore start */
147
+ // TODO: fall back to username/password login, and/or couchdb PUT login
148
+ throw error('Failed to perform web login', { response });
149
+ }
150
+ /* c8 ignore stop */
151
+ /**
152
+ * Given a {@link WebAuthChallenge}, open the `loginUrl` in a browser and
153
+ * hang on the `doneUrl` until it returns a {@link TokenResponse} object.
154
+ */
155
+ async webAuthOpener({ doneUrl, loginUrl }) {
156
+ const ac = new AbortController();
157
+ const { signal } = ac;
158
+ /* c8 ignore start - race condition */
159
+ const [result] = await Promise.all([
160
+ this.#checkLogin(doneUrl, { signal }).then(result => {
161
+ ac.abort();
162
+ return result;
163
+ }),
164
+ urlOpen(loginUrl, { signal }).catch((er) => {
165
+ if (er.name === 'AbortError')
166
+ return;
167
+ ac.abort();
168
+ throw er;
169
+ }),
170
+ ]);
171
+ /* c8 ignore stop */
172
+ return result;
173
+ }
174
+ async #checkLogin(url, options = {}) {
175
+ const response = await this.request(url, {
176
+ ...options,
177
+ cache: false,
178
+ });
179
+ const { signal } = options;
180
+ if (response.statusCode === 202) {
181
+ const rt = response.getHeader('retry-after');
182
+ const retryAfter = rt ? Number(rt.toString()) : -1;
183
+ if (retryAfter > 0) {
184
+ await setTimeout(retryAfter * 1000, null, { signal });
185
+ }
186
+ return await this.#checkLogin(url, options);
187
+ }
188
+ if (response.statusCode === 200) {
189
+ const body = response.json();
190
+ if (isTokenResponse(body))
191
+ return body;
192
+ }
193
+ throw error('Invalid response from web login endpoint', {
194
+ response,
195
+ });
47
196
  }
48
197
  async request(url, options = {}) {
198
+ logRequest(url, 'start');
49
199
  const u = typeof url === 'string' ? new URL(url) : url;
50
- const { method = 'GET', integrity, redirections = new Set(), } = options;
200
+ const { method = 'GET', integrity, redirections = new Set(), signal, otp = (process.env.VLT_OTP ?? '').trim(), } = options;
201
+ const { cache = method === 'GET' || method === 'HEAD' } = options;
202
+ signal?.throwIfAborted();
51
203
  // first, try to get from the cache before making any request.
52
204
  const { origin, pathname } = u;
53
205
  const key = JSON.stringify([origin, method, pathname]);
54
- const buffer = await this.cache.fetch(key, {
55
- context: { integrity },
56
- });
206
+ const buffer = cache ?
207
+ await this.cache.fetch(key, { context: { integrity } })
208
+ : undefined;
57
209
  const entry = buffer ? CacheEntry.decode(buffer) : undefined;
58
210
  if (entry?.valid)
59
211
  return entry;
@@ -69,54 +221,60 @@ export class RegistryClient {
69
221
  });
70
222
  options.origin = u.origin;
71
223
  options.headers = addHeader(addHeader(options.headers, 'accept-encoding', 'gzip;q=1.0, identity;q=0.5'), 'user-agent', userAgent);
224
+ if (otp) {
225
+ options.headers = addHeader(options.headers, 'npm-otp', otp);
226
+ }
72
227
  options.method = options.method ?? 'GET';
73
- const result = await new Promise((res, rej) => {
74
- /* c8 ignore start - excessive type setting for eslint */
75
- this.agent
76
- .request(options)
77
- .then(res)
78
- .catch((er) => rej(er));
79
- /* c8 ignore stop */
80
- }).then(resp => {
81
- if (handle304Response(resp, entry))
82
- return entry;
83
- const h = [];
84
- for (const [key, value] of Object.entries(resp.headers)) {
85
- /* c8 ignore start - theoretical */
86
- if (Array.isArray(value)) {
87
- h.push(Buffer.from(key), Buffer.from(value.join(', ')));
88
- /* c8 ignore stop */
89
- }
90
- else if (typeof value === 'string') {
91
- h.push(Buffer.from(key), Buffer.from(value));
92
- }
228
+ // will remove if we don't have a token.
229
+ options.headers = addHeader(options.headers, 'authorization', await getToken(origin, this.identity));
230
+ const result = await this.#handleResponse(u, options, await this.agent.request(options), entry);
231
+ if (cache)
232
+ this.cache.set(key, result.encode());
233
+ return result;
234
+ }
235
+ async #handleResponse(url, options, response, entry) {
236
+ if (handle304Response(response, entry))
237
+ return entry;
238
+ if (response.statusCode === 401) {
239
+ const repeatRequest = await otplease(this, options, response);
240
+ if (repeatRequest)
241
+ return await this.request(url, repeatRequest);
242
+ }
243
+ const h = [];
244
+ for (const [key, value] of Object.entries(response.headers)) {
245
+ /* c8 ignore start - theoretical */
246
+ if (Array.isArray(value)) {
247
+ h.push(Buffer.from(key), Buffer.from(value.join(', ')));
248
+ /* c8 ignore stop */
93
249
  }
94
- const result = new CacheEntry(
95
- /* c8 ignore next - should always have a status code */
96
- resp.statusCode || 200, h, options.integrity);
97
- if (isRedirect(result)) {
98
- resp.body.resume();
99
- try {
100
- const [nextURL, nextOptions] = redirect(options, result, u);
101
- if (nextOptions && nextURL) {
102
- return this.request(nextURL, nextOptions);
103
- }
104
- return result;
105
- }
106
- catch (er) {
107
- /* c8 ignore start */
108
- throw er instanceof Error ? er : (new Error(typeof er === 'string' ? er : 'Unknown error'));
109
- /* c8 ignore stop */
250
+ else if (typeof value === 'string') {
251
+ h.push(Buffer.from(key), Buffer.from(value));
252
+ }
253
+ }
254
+ const result = new CacheEntry(
255
+ /* c8 ignore next - should always have a status code */
256
+ response.statusCode || 200, h, options.integrity);
257
+ if (isRedirect(result)) {
258
+ response.body.resume();
259
+ // remove the try/catch once rebasing onto main with Luke's error-cause stuff
260
+ try {
261
+ const [nextURL, nextOptions] = redirect(options, result, url);
262
+ if (nextOptions && nextURL) {
263
+ return await this.request(nextURL, nextOptions);
110
264
  }
265
+ return result;
266
+ }
267
+ catch (er) {
268
+ /* c8 ignore start */
269
+ throw er instanceof Error ? er : (new Error(typeof er === 'string' ? er : 'Unknown error'));
270
+ /* c8 ignore stop */
111
271
  }
112
- resp.body.on('data', (chunk) => result.addBody(chunk));
113
- return new Promise((res, rej) => {
114
- resp.body.on('error', rej);
115
- resp.body.on('end', () => res(result));
116
- });
272
+ }
273
+ response.body.on('data', (chunk) => result.addBody(chunk));
274
+ return await new Promise((res, rej) => {
275
+ response.body.on('error', rej);
276
+ response.body.on('end', () => res(result));
117
277
  });
118
- this.cache.set(key, result.encode());
119
- return result;
120
278
  }
121
279
  }
122
280
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,KAAK,EAAc,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAsDxD,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AACzD,MAAM,GAAG,GACN,UAAU,CAAC,SAAmC,EAAE,SAAS;IAC1D,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE;QACnB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE;YACvB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE;gBAC1B,CAAC,CAAC,oBAAoB,CAAC,CAAA;AACzB,MAAM,CAAC,MAAM,SAAS,GAAG,2BAA2B,OAAO,IAAI,GAAG,EAAE,CAAA;AAEpE,MAAM,YAAY,GAAkB;IAClC,WAAW,EAAE,OAAO;IACpB,cAAc,EAAE,OAAO;IACvB,mBAAmB,EAAE,SAAS;IAC9B,gBAAgB,EAAE,OAAO;IACzB,yBAAyB,EAAE,MAAM;IACjC,OAAO,EAAE;QACP,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,IAAI;QACf,qBAAqB,EAAE,MAAM;QAC7B,cAAc,EAAE,GAAG;KACpB;IACD,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,EAAE;CACf,CAAA;AAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;AAE1B,MAAM,OAAO,cAAc;IACzB,KAAK,CAAO;IACZ,KAAK,CAAO;IAEZ,YAAY,EACV,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,GACd;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC;YACrB,IAAI,EAAE,KAAK;YACX,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI;gBAC1B,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;oBAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YAC1D,CAAC;SACF,CAAC,CAAA;QACF,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,GAAiB,EACjB,UAAwC,EAAE;QAE1C,MAAM,CAAC,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QACtD,MAAM,EACJ,MAAM,GAAG,KAAK,EACd,SAAS,EACT,YAAY,GAAG,IAAI,GAAG,EAAE,GACzB,GAAG,OAAO,CAAA;QAEX,8DAA8D;QAC9D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE;YACzC,OAAO,EAAE,EAAE,SAAS,EAAE;SACvB,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC5D,IAAI,KAAK,EAAE,KAAK;YAAE,OAAO,KAAK,CAAA;QAC9B,+DAA+D;QAC/D,6DAA6D;QAC7D,4CAA4C;QAE5C,mDAAmD;QACnD,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAE/B,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAE7B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;YACrB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM;YAC/C,GAAG,YAAY;SAChB,CAAC,CAAA;QAEF,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;QACzB,OAAO,CAAC,OAAO,GAAG,SAAS,CACzB,SAAS,CACP,OAAO,CAAC,OAAO,EACf,iBAAiB,EACjB,4BAA4B,CAC7B,EACD,YAAY,EACZ,SAAS,CACV,CAAA;QACD,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAA;QAExC,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAC9B,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACX,yDAAyD;YACzD,IAAI,CAAC,KAAK;iBACP,OAAO,CAAC,OAAoC,CAAC;iBAC7C,IAAI,CAAC,GAAG,CAAC;iBACT,KAAK,CAAC,CAAC,EAAW,EAAE,EAAE,CAAC,GAAG,CAAC,EAAW,CAAC,CAAC,CAAA;YAC3C,oBAAoB;QACtB,CAAC,CACF,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACZ,IAAI,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YAEhD,MAAM,CAAC,GAAa,EAAE,CAAA;YACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACxD,mCAAmC;gBACnC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;oBACvD,oBAAoB;gBACtB,CAAC;qBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACrC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC9C,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,UAAU;YAC3B,uDAAuD;YACvD,IAAI,CAAC,UAAU,IAAI,GAAG,EACtB,CAAC,EACD,OAAO,CAAC,SAAS,CAClB,CAAA;YAED,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;gBAClB,IAAI,CAAC;oBACH,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;oBAC3D,IAAI,WAAW,IAAI,OAAO,EAAE,CAAC;wBAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;oBAC3C,CAAC;oBACD,OAAO,MAAM,CAAA;gBACf,CAAC;gBAAC,OAAO,EAAE,EAAE,CAAC;oBACZ,qBAAqB;oBACrB,MAAM,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAC7B,IAAI,KAAK,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CACzD,CAAA;oBACH,oBAAoB;gBACtB,CAAC;YACH,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;YAC9D,OAAO,IAAI,OAAO,CAAa,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBAC1C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;gBAC1B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;YACxC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QACpC,OAAO,MAAM,CAAA;IACf,CAAC;CACF","sourcesContent":["import { Cache } from '@vltpkg/cache'\nimport { register } from '@vltpkg/cache-unzip'\nimport { Integrity } from '@vltpkg/types'\nimport { XDG } from '@vltpkg/xdg'\nimport { loadPackageJson } from 'package-json-from-dist'\nimport { Agent, Dispatcher } from 'undici'\nimport { addHeader } from './add-header.js'\nimport { CacheEntry } from './cache-entry.js'\nimport { bun, deno, node } from './env.js'\nimport { handle304Response } from './handle-304-response.js'\nimport { isRedirect, redirect } from './redirect.js'\nimport { setCacheHeaders } from './set-cache-headers.js'\n\nexport { type CacheEntry }\n\nexport type RegistryClientOptions = {\n /**\n * Path on disk where the cache should be stored\n *\n * @default `$HOME/.config/vlt/cache`\n */\n cache?: string\n}\n\nexport type RegistryClientRequestOptions = Omit<\n Dispatcher.DispatchOptions,\n 'method' | 'path'\n> & {\n /**\n * `path` should not be set when using the RegistryClient.\n * It will be overwritten with the path on the URL being requested.\n * This only here for compliance with the DispatchOptions base type.\n * @deprecated\n */\n path?: string\n\n /**\n * Method is optional, defaults to 'GET'\n */\n method?: Dispatcher.DispatchOptions['method']\n /**\n * Provide an SRI string to verify integrity of the item being fetched.\n *\n * This is only relevant when it must make a request to the registry. Once in\n * the local disk cache, items are assumed to be trustworthy.\n */\n integrity?: Integrity\n\n /**\n * Follow up to 10 redirections by default. Set this to 0 to just return\n * the 3xx response. If the max redirections are expired, and we still get\n * a redirection response, then fail the request. Redirection cycles are\n * always treated as an error.\n */\n maxRedirections?: number\n\n /**\n * the number of redirections that have already been seen. This is used\n * internally, and should always start at 0.\n *\n * @internal\n */\n redirections?: Set<string>\n}\n\nconst { version } = loadPackageJson(import.meta.filename)\nconst nua =\n (globalThis.navigator as Navigator | undefined)?.userAgent ??\n (bun ? `Bun/${bun}`\n : deno ? `Deno/${deno}`\n : node ? `Node.js/${node}`\n : '(unknown platform)')\nexport const userAgent = `@vltpkg/registry-client/${version} ${nua}`\n\nconst agentOptions: Agent.Options = {\n bodyTimeout: 600_000,\n headersTimeout: 600_000,\n keepAliveMaxTimeout: 1_200_000,\n keepAliveTimeout: 600_000,\n keepAliveTimeoutThreshold: 30_000,\n connect: {\n timeout: 600_000,\n keepAlive: true,\n keepAliveInitialDelay: 30_000,\n sessionTimeout: 600,\n },\n connections: 128,\n pipelining: 10,\n}\n\nconst xdg = new XDG('vlt')\n\nexport class RegistryClient {\n agent: Agent\n cache: Cache\n\n constructor({\n cache = xdg.cache('registry-client'),\n }: RegistryClientOptions) {\n this.cache = new Cache({\n path: cache,\n onDiskWrite(_path, key, data) {\n if (CacheEntry.decode(data).isGzip) register(cache, key)\n },\n })\n this.agent = new Agent(agentOptions)\n }\n\n async request(\n url: URL | string,\n options: RegistryClientRequestOptions = {},\n ): Promise<CacheEntry> {\n const u = typeof url === 'string' ? new URL(url) : url\n const {\n method = 'GET',\n integrity,\n redirections = new Set(),\n } = options\n\n // first, try to get from the cache before making any request.\n const { origin, pathname } = u\n const key = JSON.stringify([origin, method, pathname])\n const buffer = await this.cache.fetch(key, {\n context: { integrity },\n })\n\n const entry = buffer ? CacheEntry.decode(buffer) : undefined\n if (entry?.valid) return entry\n // TODO: stale-while-revalidate timeout, say 1 day, where we'll\n // use the cached response even if it's invalid, and validate\n // in the background without waiting for it.\n\n // either no cache entry, or need to revalidate it.\n setCacheHeaders(options, entry)\n\n redirections.add(String(url))\n\n Object.assign(options, {\n path: u.pathname.replace(/\\/+$/, '') + u.search,\n ...agentOptions,\n })\n\n options.origin = u.origin\n options.headers = addHeader(\n addHeader(\n options.headers,\n 'accept-encoding',\n 'gzip;q=1.0, identity;q=0.5',\n ),\n 'user-agent',\n userAgent,\n )\n options.method = options.method ?? 'GET'\n\n const result = await new Promise<Dispatcher.ResponseData>(\n (res, rej) => {\n /* c8 ignore start - excessive type setting for eslint */\n this.agent\n .request(options as Dispatcher.RequestOptions)\n .then(res)\n .catch((er: unknown) => rej(er as Error))\n /* c8 ignore stop */\n },\n ).then(resp => {\n if (handle304Response(resp, entry)) return entry\n\n const h: Buffer[] = []\n for (const [key, value] of Object.entries(resp.headers)) {\n /* c8 ignore start - theoretical */\n if (Array.isArray(value)) {\n h.push(Buffer.from(key), Buffer.from(value.join(', ')))\n /* c8 ignore stop */\n } else if (typeof value === 'string') {\n h.push(Buffer.from(key), Buffer.from(value))\n }\n }\n const result = new CacheEntry(\n /* c8 ignore next - should always have a status code */\n resp.statusCode || 200,\n h,\n options.integrity,\n )\n\n if (isRedirect(result)) {\n resp.body.resume()\n try {\n const [nextURL, nextOptions] = redirect(options, result, u)\n if (nextOptions && nextURL) {\n return this.request(nextURL, nextOptions)\n }\n return result\n } catch (er) {\n /* c8 ignore start */\n throw er instanceof Error ? er : (\n new Error(typeof er === 'string' ? er : 'Unknown error')\n )\n /* c8 ignore stop */\n }\n }\n\n resp.body.on('data', (chunk: Buffer) => result.addBody(chunk))\n return new Promise<CacheEntry>((res, rej) => {\n resp.body.on('error', rej)\n resp.body.on('end', () => res(result))\n })\n })\n\n this.cache.set(key, result.encode())\n return result\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { Cache } from '@vltpkg/cache'\nimport { register } from '@vltpkg/cache-unzip'\nimport { error } from '@vltpkg/error-cause'\nimport type { Integrity } from '@vltpkg/types'\nimport { urlOpen } from '@vltpkg/url-open'\nimport { XDG } from '@vltpkg/xdg'\nimport { setTimeout } from 'node:timers/promises'\nimport { loadPackageJson } from 'package-json-from-dist'\nimport { Agent, RetryAgent } from 'undici'\nimport type { Dispatcher } from 'undici'\nimport { addHeader } from './add-header.ts'\nimport {\n deleteToken,\n getKC,\n getToken,\n isToken,\n keychains,\n setToken,\n} from './auth.ts'\nimport type { Token } from './auth.ts'\nimport { CacheEntry } from './cache-entry.ts'\nimport type { JSONObj } from './cache-entry.ts'\nimport { bun, deno, node } from './env.ts'\nimport { handle304Response } from './handle-304-response.ts'\nimport { otplease } from './otplease.ts'\nimport { isRedirect, redirect } from './redirect.ts'\nimport { setCacheHeaders } from './set-cache-headers.ts'\nimport { logRequest } from '@vltpkg/output'\nimport { isTokenResponse } from './token-response.ts'\nimport type { TokenResponse } from './token-response.ts'\nimport { isWebAuthChallenge } from './web-auth-challenge.ts'\nimport type { WebAuthChallenge } from './web-auth-challenge.ts'\nexport {\n type JSONObj,\n type CacheEntry,\n type Token,\n type WebAuthChallenge,\n type TokenResponse,\n}\nexport { keychains, getKC, setToken, deleteToken, isToken }\n\nexport type RegistryClientOptions = {\n /**\n * Path on disk where the cache should be stored\n * @default `$HOME/.config/vlt/cache`\n */\n cache?: string\n /**\n * Number of retries to perform when encountering network errors or\n * likely-transient errors from git hosts.\n */\n 'fetch-retries'?: number\n /** The exponential backoff factor to use when retrying git hosts */\n 'fetch-retry-factor'?: number\n /** Number of milliseconds before starting first retry */\n 'fetch-retry-mintimeout'?: number\n /** Maximum number of milliseconds between two retries */\n 'fetch-retry-maxtimeout'?: number\n\n /** the identity to use for storing auth tokens */\n identity?: string\n}\n\nexport type RegistryClientRequestOptions = Omit<\n Dispatcher.RequestOptions,\n 'method' | 'path'\n> & {\n /**\n * `path` should not be set when using the RegistryClient.\n * It will be overwritten with the path on the URL being requested.\n * This only here for compliance with the DispatchOptions base type.\n * @deprecated\n */\n path?: string\n\n /**\n * Method is optional, defaults to 'GET'\n */\n method?: Dispatcher.DispatchOptions['method']\n /**\n * Provide an SRI string to verify integrity of the item being fetched.\n *\n * This is only relevant when it must make a request to the registry. Once in\n * the local disk cache, items are assumed to be trustworthy.\n */\n integrity?: Integrity\n\n /**\n * Follow up to 10 redirections by default. Set this to 0 to just return\n * the 3xx response. If the max redirections are expired, and we still get\n * a redirection response, then fail the request. Redirection cycles are\n * always treated as an error.\n */\n maxRedirections?: number\n\n /**\n * the number of redirections that have already been seen. This is used\n * internally, and should always start at 0.\n * @internal\n */\n redirections?: Set<string>\n\n /**\n * Set to `false` to suppress ANY lookups from cache. This will also\n * prevent storing the result to the cache.\n */\n cache?: false\n\n /**\n * Set to pass an `npm-otp` header on the request.\n *\n * This should not be set except by the RegistryClient itself, when\n * we receive a 401 response with an OTP challenge.\n * @internal\n */\n otp?: string\n}\n\nconst { version } = loadPackageJson(\n import.meta.filename,\n process.env.__VLT_INTERNAL_REGISTRY_CLIENT_PACKAGE_JSON,\n) as {\n version: string\n}\nconst nua =\n (globalThis.navigator as Navigator | undefined)?.userAgent ??\n (bun ? `Bun/${bun}`\n : deno ? `Deno/${deno}`\n : node ? `Node.js/${node}`\n : '(unknown platform)')\nexport const userAgent = `@vltpkg/registry-client/${version} ${nua}`\n\nconst agentOptions: Agent.Options = {\n bodyTimeout: 600_000,\n headersTimeout: 600_000,\n keepAliveMaxTimeout: 1_200_000,\n keepAliveTimeout: 600_000,\n keepAliveTimeoutThreshold: 30_000,\n connect: {\n timeout: 600_000,\n keepAlive: true,\n keepAliveInitialDelay: 30_000,\n sessionTimeout: 600,\n },\n connections: 128,\n pipelining: 10,\n}\n\nconst xdg = new XDG('vlt')\n\nexport class RegistryClient {\n agent: RetryAgent\n cache: Cache\n identity: string\n\n constructor(options: RegistryClientOptions) {\n const {\n cache = xdg.cache('registry-client'),\n 'fetch-retry-factor': timeoutFactor = 2,\n 'fetch-retry-mintimeout': minTimeout = 0,\n 'fetch-retry-maxtimeout': maxTimeout = 30_000,\n 'fetch-retries': maxRetries = 3,\n identity = '',\n } = options\n this.identity = identity\n this.cache = new Cache({\n path: cache,\n onDiskWrite(_path, key, data) {\n if (CacheEntry.decode(data).isGzip) register(cache, key)\n },\n })\n const dispatch = new Agent(agentOptions)\n this.agent = new RetryAgent(dispatch, {\n maxRetries,\n timeoutFactor,\n minTimeout,\n maxTimeout,\n retryAfter: true,\n errorCodes: [\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTDOWN',\n 'ENETDOWN',\n 'ENETUNREACH',\n 'ENOTFOUND',\n 'EPIPE',\n 'UND_ERR_SOCKET',\n ],\n })\n }\n\n /**\n * Fetch the entire set of a paginated list of objects\n */\n async scroll<T>(\n url: URL | string,\n options: RegistryClientRequestOptions = {},\n seek?: (obj: T) => boolean,\n ): Promise<T[]> {\n const resp = await this.request(url, options)\n const { objects, urls } = resp.json() as {\n objects: T[]\n urls: { next?: string }\n }\n // if we have more, and haven't found our target, fetch more\n return urls.next && !(seek && objects.some(seek)) ?\n objects.concat(await this.scroll<T>(urls.next, options, seek))\n : objects\n }\n\n /**\n * find a given item in a paginated set\n */\n async seek<T>(\n url: URL | string,\n seek: (obj: T) => boolean,\n options: RegistryClientRequestOptions = {},\n ): Promise<T | undefined> {\n return (await this.scroll(url, options, seek)).find(seek)\n }\n\n /**\n * Log out from the registry specified, attempting to destroy the\n * token if the registry supports that endpoint.\n */\n async logout(registry: string) {\n // if we have no token for that registry, nothing to do\n const tok = await getToken(registry, this.identity)\n if (!tok) return\n\n const s = tok.replace(/^(Bearer|Basic) /i, '')\n\n const tokensUrl = new URL('-/npm/v1/tokens', registry)\n const record = await this.seek<{\n key: string\n token: string\n }>(tokensUrl, ({ token }) => s.startsWith(token), {\n cache: false,\n }).catch(() => undefined)\n\n if (record) {\n const { key } = record\n await this.request(\n new URL(`-/npm/v1/tokens/token/${key}`, registry),\n { cache: false, method: 'DELETE' },\n )\n }\n\n await deleteToken(registry, this.identity)\n }\n\n /**\n * Log into the registry specified\n *\n * Does not return the token or expose it, just saves to the auth keychain\n * and returns void if it worked. Otherwise, error is raised.\n */\n async login(registry: string) {\n // - make POST to '/-/v1/login'\n // - include a body of {} and npm-auth-type:web\n // - get a {doneUrl, loginUrl}\n // - open the loginUrl\n // - hang on the doneUrl until done\n //\n // if that fails: fall back to couchdb login\n const webLoginURL = new URL('-/v1/login', registry)\n const response = await this.request(webLoginURL, {\n method: 'POST',\n cache: false,\n headers: {\n 'content-type': 'application/json',\n 'npm-auth-type': 'web',\n },\n body: '{}',\n })\n\n if (response.statusCode === 200) {\n const challenge = response.json()\n if (isWebAuthChallenge(challenge)) {\n const result = await this.webAuthOpener(challenge)\n await setToken(\n registry,\n `Bearer ${result.token}`,\n this.identity,\n )\n return\n }\n }\n /* c8 ignore start */\n // TODO: fall back to username/password login, and/or couchdb PUT login\n throw error('Failed to perform web login', { response })\n }\n /* c8 ignore stop */\n\n /**\n * Given a {@link WebAuthChallenge}, open the `loginUrl` in a browser and\n * hang on the `doneUrl` until it returns a {@link TokenResponse} object.\n */\n async webAuthOpener({ doneUrl, loginUrl }: WebAuthChallenge) {\n const ac = new AbortController()\n const { signal } = ac\n /* c8 ignore start - race condition */\n const [result] = await Promise.all([\n this.#checkLogin(doneUrl, { signal }).then(result => {\n ac.abort()\n return result\n }),\n urlOpen(loginUrl, { signal }).catch((er: unknown) => {\n if ((er as Error).name === 'AbortError') return\n ac.abort()\n throw er\n }),\n ])\n /* c8 ignore stop */\n return result\n }\n\n async #checkLogin(\n url: URL | string,\n options: RegistryClientRequestOptions = {},\n ): Promise<TokenResponse> {\n const response = await this.request(url, {\n ...options,\n cache: false,\n })\n const { signal } = options as { signal?: AbortSignal }\n if (response.statusCode === 202) {\n const rt = response.getHeader('retry-after')\n const retryAfter = rt ? Number(rt.toString()) : -1\n if (retryAfter > 0) {\n await setTimeout(retryAfter * 1000, null, { signal })\n }\n return await this.#checkLogin(url, options)\n }\n if (response.statusCode === 200) {\n const body = response.json()\n if (isTokenResponse(body)) return body\n }\n throw error('Invalid response from web login endpoint', {\n response,\n })\n }\n\n async request(\n url: URL | string,\n options: RegistryClientRequestOptions = {},\n ): Promise<CacheEntry> {\n logRequest(url, 'start')\n\n const u = typeof url === 'string' ? new URL(url) : url\n const {\n method = 'GET',\n integrity,\n redirections = new Set(),\n signal,\n otp = (process.env.VLT_OTP ?? '').trim(),\n } = options\n\n const { cache = method === 'GET' || method === 'HEAD' } = options\n\n ;(signal as AbortSignal | null)?.throwIfAborted()\n\n // first, try to get from the cache before making any request.\n const { origin, pathname } = u\n const key = JSON.stringify([origin, method, pathname])\n const buffer =\n cache ?\n await this.cache.fetch(key, { context: { integrity } })\n : undefined\n\n const entry = buffer ? CacheEntry.decode(buffer) : undefined\n if (entry?.valid) return entry\n // TODO: stale-while-revalidate timeout, say 1 day, where we'll\n // use the cached response even if it's invalid, and validate\n // in the background without waiting for it.\n\n // either no cache entry, or need to revalidate it.\n setCacheHeaders(options, entry)\n\n redirections.add(String(url))\n\n Object.assign(options, {\n path: u.pathname.replace(/\\/+$/, '') + u.search,\n ...agentOptions,\n })\n\n options.origin = u.origin\n options.headers = addHeader(\n addHeader(\n options.headers,\n 'accept-encoding',\n 'gzip;q=1.0, identity;q=0.5',\n ),\n 'user-agent',\n userAgent,\n )\n if (otp) {\n options.headers = addHeader(options.headers, 'npm-otp', otp)\n }\n options.method = options.method ?? 'GET'\n\n // will remove if we don't have a token.\n options.headers = addHeader(\n options.headers,\n 'authorization',\n await getToken(origin, this.identity),\n )\n\n const result = await this.#handleResponse(\n u,\n options,\n await this.agent.request(options as Dispatcher.RequestOptions),\n entry,\n )\n\n if (cache) this.cache.set(key, result.encode())\n return result\n }\n\n async #handleResponse(\n url: URL,\n options: RegistryClientRequestOptions,\n response: Dispatcher.ResponseData,\n entry?: CacheEntry,\n ): Promise<CacheEntry> {\n if (handle304Response(response, entry)) return entry\n\n if (response.statusCode === 401) {\n const repeatRequest = await otplease(this, options, response)\n if (repeatRequest) return await this.request(url, repeatRequest)\n }\n\n const h: Buffer[] = []\n for (const [key, value] of Object.entries(response.headers)) {\n /* c8 ignore start - theoretical */\n if (Array.isArray(value)) {\n h.push(Buffer.from(key), Buffer.from(value.join(', ')))\n /* c8 ignore stop */\n } else if (typeof value === 'string') {\n h.push(Buffer.from(key), Buffer.from(value))\n }\n }\n\n const result = new CacheEntry(\n /* c8 ignore next - should always have a status code */\n response.statusCode || 200,\n h,\n options.integrity,\n )\n\n if (isRedirect(result)) {\n response.body.resume()\n // remove the try/catch once rebasing onto main with Luke's error-cause stuff\n try {\n const [nextURL, nextOptions] = redirect(options, result, url)\n if (nextOptions && nextURL) {\n return await this.request(nextURL, nextOptions)\n }\n return result\n } catch (er) {\n /* c8 ignore start */\n throw er instanceof Error ? er : (\n new Error(typeof er === 'string' ? er : 'Unknown error')\n )\n /* c8 ignore stop */\n }\n }\n\n response.body.on('data', (chunk: Buffer) => result.addBody(chunk))\n return await new Promise<CacheEntry>((res, rej) => {\n response.body.on('error', rej)\n response.body.on('end', () => res(result))\n })\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const isIterable: <T>(o: unknown) => o is Iterable<T>;
2
+ //# sourceMappingURL=is-iterable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-iterable.d.ts","sourceRoot":"","sources":["../../src/is-iterable.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,CAAC,KAAK,OAAO,KAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CACJ,CAAA"}
@@ -0,0 +1,2 @@
1
+ export const isIterable = (o) => !!o && typeof o === 'object' && Symbol.iterator in o;
2
+ //# sourceMappingURL=is-iterable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-iterable.js","sourceRoot":"","sources":["../../src/is-iterable.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAI,CAAU,EAAoB,EAAE,CAC5D,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAA","sourcesContent":["export const isIterable = <T>(o: unknown): o is Iterable<T> =>\n !!o && typeof o === 'object' && Symbol.iterator in o\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Dispatcher } from 'undici';
2
+ import type { RegistryClient, RegistryClientRequestOptions } from './index.ts';
3
+ export declare const otplease: (client: RegistryClient, options: RegistryClientRequestOptions, response: Dispatcher.ResponseData) => Promise<RegistryClientRequestOptions | undefined>;
4
+ //# sourceMappingURL=otplease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otplease.d.ts","sourceRoot":"","sources":["../../src/otplease.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EACV,cAAc,EACd,4BAA4B,EAC7B,MAAM,YAAY,CAAA;AASnB,eAAO,MAAM,QAAQ,WACX,cAAc,WACb,4BAA4B,YAC3B,UAAU,CAAC,YAAY,KAChC,OAAO,CAAC,4BAA4B,GAAG,SAAS,CAuDlD,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { error } from '@vltpkg/error-cause';
2
+ import { isWebAuthChallenge } from "./web-auth-challenge.js";
3
+ import { urlOpen } from '@vltpkg/url-open';
4
+ import { createInterface } from 'node:readline/promises';
5
+ const otpChallengeNotice = /^Open ([^ ]+) to use your security key for authentication or enter OTP from your authenticator app/i;
6
+ export const otplease = async (client, options, response) => {
7
+ const waHeader = String(response.headers['www-authenticate'] ?? '');
8
+ const wwwAuth = new Set(waHeader ? waHeader.toLowerCase().split(/,\s*/) : []);
9
+ if (wwwAuth.has('ipaddress')) {
10
+ throw error('Authorization is not allowed from your ip address', {
11
+ response,
12
+ });
13
+ }
14
+ if (wwwAuth.has('otp')) {
15
+ // do a web auth opener to get otp token
16
+ const challenge = await response.body.json();
17
+ if (isWebAuthChallenge(challenge)) {
18
+ return {
19
+ ...options,
20
+ otp: (await client.webAuthOpener(challenge)).token,
21
+ };
22
+ }
23
+ else {
24
+ const { 'npm-notice': npmNotice } = response.headers;
25
+ if (npmNotice) {
26
+ const n = String(npmNotice);
27
+ const match = otpChallengeNotice.exec(n);
28
+ if (match) {
29
+ void urlOpen(match[1]);
30
+ const otp = await createInterface({
31
+ input: process.stdin,
32
+ output: process.stdout,
33
+ }).question(n);
34
+ return { ...options, otp };
35
+ }
36
+ }
37
+ throw error('Unrecognized OTP authentication challenge', {
38
+ response,
39
+ });
40
+ }
41
+ }
42
+ if (wwwAuth.size) {
43
+ throw error('Unknown authentication challenge', { response });
44
+ }
45
+ // see if the body is prompting for otp
46
+ const text = await response.body.text();
47
+ if (text.toLowerCase().includes('one-time pass')) {
48
+ const otp = await createInterface({
49
+ input: process.stdin,
50
+ output: process.stdout,
51
+ }).question(text);
52
+ return { ...options, otp };
53
+ }
54
+ };
55
+ //# sourceMappingURL=otplease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otplease.js","sourceRoot":"","sources":["../../src/otplease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAM3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,MAAM,kBAAkB,GACtB,qGAAqG,CAAA;AAEvG,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,MAAsB,EACtB,OAAqC,EACrC,QAAiC,EACkB,EAAE;IACrD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAA;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CACrD,CAAA;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,MAAM,KAAK,CAAC,mDAAmD,EAAE;YAC/D,QAAQ;SACT,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,wCAAwC;QACxC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAC5C,IAAI,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,OAAO;gBACL,GAAG,OAAO;gBACV,GAAG,EAAE,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;aACnD,CAAA;QACH,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAA;YACpD,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;gBAC3B,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAEnB,CAAA;gBACpB,IAAI,KAAK,EAAE,CAAC;oBACV,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;oBACtB,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC;wBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;qBACvB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;oBACd,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,CAAA;gBAC5B,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC,2CAA2C,EAAE;gBACvD,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,KAAK,CAAC,kCAAkC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED,uCAAuC;IACvC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IACvC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC;YAChC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACjB,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,CAAA;IAC5B,CAAC;AACH,CAAC,CAAA","sourcesContent":["import { error } from '@vltpkg/error-cause'\nimport type { Dispatcher } from 'undici'\nimport type {\n RegistryClient,\n RegistryClientRequestOptions,\n} from './index.ts'\nimport { isWebAuthChallenge } from './web-auth-challenge.ts'\n\nimport { urlOpen } from '@vltpkg/url-open'\nimport { createInterface } from 'node:readline/promises'\n\nconst otpChallengeNotice =\n /^Open ([^ ]+) to use your security key for authentication or enter OTP from your authenticator app/i\n\nexport const otplease = async (\n client: RegistryClient,\n options: RegistryClientRequestOptions,\n response: Dispatcher.ResponseData,\n): Promise<RegistryClientRequestOptions | undefined> => {\n const waHeader = String(response.headers['www-authenticate'] ?? '')\n const wwwAuth = new Set(\n waHeader ? waHeader.toLowerCase().split(/,\\s*/) : [],\n )\n\n if (wwwAuth.has('ipaddress')) {\n throw error('Authorization is not allowed from your ip address', {\n response,\n })\n }\n\n if (wwwAuth.has('otp')) {\n // do a web auth opener to get otp token\n const challenge = await response.body.json()\n if (isWebAuthChallenge(challenge)) {\n return {\n ...options,\n otp: (await client.webAuthOpener(challenge)).token,\n }\n } else {\n const { 'npm-notice': npmNotice } = response.headers\n if (npmNotice) {\n const n = String(npmNotice)\n const match = otpChallengeNotice.exec(n) as\n | null\n | [string, string]\n if (match) {\n void urlOpen(match[1])\n const otp = await createInterface({\n input: process.stdin,\n output: process.stdout,\n }).question(n)\n return { ...options, otp }\n }\n }\n throw error('Unrecognized OTP authentication challenge', {\n response,\n })\n }\n }\n\n if (wwwAuth.size) {\n throw error('Unknown authentication challenge', { response })\n }\n\n // see if the body is prompting for otp\n const text = await response.body.text()\n if (text.toLowerCase().includes('one-time pass')) {\n const otp = await createInterface({\n input: process.stdin,\n output: process.stdout,\n }).question(text)\n return { ...options, otp }\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Give it a key, and it'll return the buffer of that header value
3
3
  */
4
- export declare const getRawHeader: (headers: Buffer[], k: string) => Buffer | undefined;
4
+ export declare const getRawHeader: (headers: Buffer[], k: string) => Buffer<ArrayBufferLike> | undefined;
5
5
  /**
6
6
  * Give it a key and value, and it'll overwrite or add the header entry
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"raw-header.d.ts","sourceRoot":"","sources":["../../src/raw-header.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,YAAa,MAAM,EAAE,KAAK,MAAM,uBAYxD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,YACd,MAAM,EAAE,KACd,MAAM,KACN,MAAM,GAAG,MAAM,KACjB,MAAM,EAkBR,CAAA"}
1
+ {"version":3,"file":"raw-header.d.ts","sourceRoot":"","sources":["../../src/raw-header.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,YAAa,MAAM,EAAE,KAAK,MAAM,wCAYxD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,YACd,MAAM,EAAE,KACd,MAAM,KACN,MAAM,GAAG,MAAM,KACjB,MAAM,EAkBR,CAAA"}
@@ -1,5 +1,5 @@
1
- import { CacheEntry } from './cache-entry.js';
2
- import { RegistryClientRequestOptions } from './index.js';
1
+ import type { CacheEntry } from './cache-entry.ts';
2
+ import type { RegistryClientRequestOptions } from './index.ts';
3
3
  export type RedirectStatus = 301 | 302 | 303 | 307 | 308;
4
4
  export type RedirectResponse = CacheEntry & {
5
5
  statusCode: RedirectStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/redirect.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAEzD,MAAM,MAAM,cAAc,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAIxD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,UAAU,EAAE,cAAc,CAAA;IAC1B,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,UAAU,aACX,UAAU,KACnB,QAAQ,IAAI,gBAE2B,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,YACV,4BAA4B,YAC3B,gBAAgB,QACpB,GAAG,KACR,EAAE,GAAG,CAAC,GAAG,EAAE,4BAA4B,CAwCzC,CAAA"}
1
+ {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/redirect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAE9D,MAAM,MAAM,cAAc,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAIxD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,UAAU,EAAE,cAAc,CAAA;IAC1B,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,UAAU,aACX,UAAU,KACnB,QAAQ,IAAI,gBAE2B,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,YACV,4BAA4B,YAC3B,gBAAgB,QACpB,GAAG,KACR,EAAE,GAAG,CAAC,GAAG,EAAE,4BAA4B,CAyCzC,CAAA"}
@@ -40,6 +40,7 @@ export const redirect = (options, response, from) => {
40
40
  ...options,
41
41
  redirections,
42
42
  };
43
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- thats why we are deleting it
43
44
  delete nextOptions.path;
44
45
  redirections.add(String(nextURL));
45
46
  switch (response.statusCode) {
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/redirect.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAM3C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAO3D,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,QAAoB,EACU,EAAE,CAChC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;IACzC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAqC,EACrC,QAA0B,EAC1B,IAAS,EACiC,EAAE;IAC5C,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,EAAU,EAAE,eAAe,GAAG,EAAE,EAAE,GAC9D,OAAO,CAAA;IACT,IAAI,eAAe,IAAI,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,IAAI,YAAY,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,CAAC,+BAA+B,EAAE;YAC3C,GAAG,EAAE,eAAe;YACpB,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC;YACxB,GAAG,EAAE,IAAI;SACV,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAA;IACvD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IACvC,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,CAAC,4BAA4B,EAAE;YACxC,GAAG,EAAE,eAAe;YACpB,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC;YACxB,GAAG,EAAE,IAAI;SACV,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,WAAW,GAAiC;QAChD,GAAG,OAAO;QACV,YAAY;KACb,CAAA;IACD,OAAO,WAAW,CAAC,IAAI,CAAA;IACvB,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IACjC,QAAQ,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC5B,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,kCAAkC;YAClC,WAAW,CAAC,MAAM,GAAG,KAAK,CAAA;YAC1B,WAAW,CAAC,IAAI,GAAG,SAAS,CAAA;YAC5B,cAAc;QAChB,CAAC;QACD,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,CAAC,mDAAmD;QAC7D,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;AACH,CAAC,CAAA","sourcesContent":["// given a RegistryClientOptions object, and a redirection response,\n\nimport { error } from '@vltpkg/error-cause'\nimport { CacheEntry } from './cache-entry.js'\nimport { RegistryClientRequestOptions } from './index.js'\n\nexport type RedirectStatus = 301 | 302 | 303 | 307 | 308\n\nconst redirectStatuses = new Set([301, 302, 303, 307, 308])\n\nexport type RedirectResponse = CacheEntry & {\n statusCode: RedirectStatus\n getHeader(key: 'location'): Buffer\n}\n\nexport const isRedirect = (\n response: CacheEntry,\n): response is RedirectResponse =>\n redirectStatuses.has(response.statusCode) &&\n !!response.getHeader('location')?.length\n\n/**\n * If this response is allowed to follow the redirect (because max has not\n * been hit, and the new location has not been seen already), then return\n * the [url, options] to use for the subsequent request.\n *\n * Return [] if the response should be returned as-is.\n *\n * Throws an error if maxRedirections is hit or the redirections set already\n * contains the new location.\n *\n * Ensure that the response is in fact a redirection first, by calling\n * {@link isRedirect} on it.\n */\nexport const redirect = (\n options: RegistryClientRequestOptions,\n response: RedirectResponse,\n from: URL,\n): [] | [URL, RegistryClientRequestOptions] => {\n const { redirections = new Set<string>(), maxRedirections = 10 } =\n options\n if (maxRedirections <= 0) return []\n if (redirections.size >= maxRedirections) {\n throw error('Maximum redirections exceeded', {\n max: maxRedirections,\n found: [...redirections],\n url: from,\n })\n }\n const location = String(response.getHeader('location'))\n const nextURL = new URL(location, from)\n if (redirections.has(String(nextURL))) {\n throw error('Redirection cycle detected', {\n max: maxRedirections,\n found: [...redirections],\n url: from,\n })\n }\n const nextOptions: RegistryClientRequestOptions = {\n ...options,\n redirections,\n }\n delete nextOptions.path\n redirections.add(String(nextURL))\n switch (response.statusCode) {\n case 303: {\n // drop body, change method to GET\n nextOptions.method = 'GET'\n nextOptions.body = undefined\n // fallthrough\n }\n case 301:\n case 302: // some user agents treat as 303, but they're wrong\n case 307:\n case 308: {\n return [nextURL, nextOptions]\n }\n }\n}\n"]}
1
+ {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/redirect.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAM3C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAO3D,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,QAAoB,EACU,EAAE,CAChC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;IACzC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAqC,EACrC,QAA0B,EAC1B,IAAS,EACiC,EAAE;IAC5C,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,EAAU,EAAE,eAAe,GAAG,EAAE,EAAE,GAC9D,OAAO,CAAA;IACT,IAAI,eAAe,IAAI,CAAC;QAAE,OAAO,EAAE,CAAA;IACnC,IAAI,YAAY,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,CAAC,+BAA+B,EAAE;YAC3C,GAAG,EAAE,eAAe;YACpB,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC;YACxB,GAAG,EAAE,IAAI;SACV,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAA;IACvD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IACvC,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,CAAC,4BAA4B,EAAE;YACxC,GAAG,EAAE,eAAe;YACpB,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC;YACxB,GAAG,EAAE,IAAI;SACV,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,WAAW,GAAiC;QAChD,GAAG,OAAO;QACV,YAAY;KACb,CAAA;IACD,4FAA4F;IAC5F,OAAO,WAAW,CAAC,IAAI,CAAA;IACvB,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IACjC,QAAQ,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC5B,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,kCAAkC;YAClC,WAAW,CAAC,MAAM,GAAG,KAAK,CAAA;YAC1B,WAAW,CAAC,IAAI,GAAG,SAAS,CAAA;YAC5B,cAAc;QAChB,CAAC;QACD,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,CAAC,mDAAmD;QAC7D,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;AACH,CAAC,CAAA","sourcesContent":["// given a RegistryClientOptions object, and a redirection response,\n\nimport { error } from '@vltpkg/error-cause'\nimport type { CacheEntry } from './cache-entry.ts'\nimport type { RegistryClientRequestOptions } from './index.ts'\n\nexport type RedirectStatus = 301 | 302 | 303 | 307 | 308\n\nconst redirectStatuses = new Set([301, 302, 303, 307, 308])\n\nexport type RedirectResponse = CacheEntry & {\n statusCode: RedirectStatus\n getHeader(key: 'location'): Buffer\n}\n\nexport const isRedirect = (\n response: CacheEntry,\n): response is RedirectResponse =>\n redirectStatuses.has(response.statusCode) &&\n !!response.getHeader('location')?.length\n\n/**\n * If this response is allowed to follow the redirect (because max has not\n * been hit, and the new location has not been seen already), then return\n * the [url, options] to use for the subsequent request.\n *\n * Return [] if the response should be returned as-is.\n *\n * Throws an error if maxRedirections is hit or the redirections set already\n * contains the new location.\n *\n * Ensure that the response is in fact a redirection first, by calling\n * {@link isRedirect} on it.\n */\nexport const redirect = (\n options: RegistryClientRequestOptions,\n response: RedirectResponse,\n from: URL,\n): [] | [URL, RegistryClientRequestOptions] => {\n const { redirections = new Set<string>(), maxRedirections = 10 } =\n options\n if (maxRedirections <= 0) return []\n if (redirections.size >= maxRedirections) {\n throw error('Maximum redirections exceeded', {\n max: maxRedirections,\n found: [...redirections],\n url: from,\n })\n }\n const location = String(response.getHeader('location'))\n const nextURL = new URL(location, from)\n if (redirections.has(String(nextURL))) {\n throw error('Redirection cycle detected', {\n max: maxRedirections,\n found: [...redirections],\n url: from,\n })\n }\n const nextOptions: RegistryClientRequestOptions = {\n ...options,\n redirections,\n }\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- thats why we are deleting it\n delete nextOptions.path\n redirections.add(String(nextURL))\n switch (response.statusCode) {\n case 303: {\n // drop body, change method to GET\n nextOptions.method = 'GET'\n nextOptions.body = undefined\n // fallthrough\n }\n case 301:\n case 302: // some user agents treat as 303, but they're wrong\n case 307:\n case 308: {\n return [nextURL, nextOptions]\n }\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { CacheEntry } from './cache-entry.js';
2
- import { RegistryClientRequestOptions } from './index.js';
1
+ import type { CacheEntry } from './cache-entry.ts';
2
+ import type { RegistryClientRequestOptions } from './index.ts';
3
3
  export declare const setCacheHeaders: (options: RegistryClientRequestOptions, entry?: CacheEntry) => void;
4
4
  //# sourceMappingURL=set-cache-headers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"set-cache-headers.d.ts","sourceRoot":"","sources":["../../src/set-cache-headers.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAEzD,eAAO,MAAM,eAAe,YACjB,4BAA4B,UAC7B,UAAU,KACjB,IAqBF,CAAA"}
1
+ {"version":3,"file":"set-cache-headers.d.ts","sourceRoot":"","sources":["../../src/set-cache-headers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAE9D,eAAO,MAAM,eAAe,YACjB,4BAA4B,UAC7B,UAAU,KACjB,IAqBF,CAAA"}
@@ -1,6 +1,6 @@
1
1
  // Set the cache headers on a request options object
2
2
  // based on what we know from a CacheEntry
3
- import { addHeader } from './add-header.js';
3
+ import { addHeader } from "./add-header.js";
4
4
  export const setCacheHeaders = (options, entry) => {
5
5
  if (!entry)
6
6
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"set-cache-headers.js","sourceRoot":"","sources":["../../src/set-cache-headers.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,0CAA0C;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAI3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAqC,EACrC,KAAkB,EACZ,EAAE;IACR,IAAI,CAAC,KAAK;QAAE,OAAM;IAClB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAA;IAChD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,OAAO,GAAG,SAAS,CACzB,OAAO,CAAC,OAAO,EACf,eAAe,EACf,IAAI,CACL,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GACR,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE;QACnC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAA;IAC9C,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,OAAO,GAAG,SAAS,CACzB,OAAO,CAAC,OAAO,EACf,mBAAmB,EACnB,IAAI,CACL,CAAA;IACH,CAAC;AACH,CAAC,CAAA","sourcesContent":["// Set the cache headers on a request options object\n// based on what we know from a CacheEntry\n\nimport { addHeader } from './add-header.js'\nimport { CacheEntry } from './cache-entry.js'\nimport { RegistryClientRequestOptions } from './index.js'\n\nexport const setCacheHeaders = (\n options: RegistryClientRequestOptions,\n entry?: CacheEntry,\n): void => {\n if (!entry) return\n const etag = entry.getHeader('etag')?.toString()\n if (etag) {\n options.headers = addHeader(\n options.headers,\n 'if-none-match',\n etag,\n )\n }\n\n const date =\n entry.getHeader('date')?.toString() ??\n entry.getHeader('last-modified')?.toString()\n if (date) {\n options.headers = addHeader(\n options.headers,\n 'if-modified-since',\n date,\n )\n }\n}\n"]}
1
+ {"version":3,"file":"set-cache-headers.js","sourceRoot":"","sources":["../../src/set-cache-headers.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,0CAA0C;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAI3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAqC,EACrC,KAAkB,EACZ,EAAE;IACR,IAAI,CAAC,KAAK;QAAE,OAAM;IAClB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAA;IAChD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,OAAO,GAAG,SAAS,CACzB,OAAO,CAAC,OAAO,EACf,eAAe,EACf,IAAI,CACL,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GACR,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE;QACnC,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAA;IAC9C,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,OAAO,GAAG,SAAS,CACzB,OAAO,CAAC,OAAO,EACf,mBAAmB,EACnB,IAAI,CACL,CAAA;IACH,CAAC;AACH,CAAC,CAAA","sourcesContent":["// Set the cache headers on a request options object\n// based on what we know from a CacheEntry\n\nimport { addHeader } from './add-header.ts'\nimport type { CacheEntry } from './cache-entry.ts'\nimport type { RegistryClientRequestOptions } from './index.ts'\n\nexport const setCacheHeaders = (\n options: RegistryClientRequestOptions,\n entry?: CacheEntry,\n): void => {\n if (!entry) return\n const etag = entry.getHeader('etag')?.toString()\n if (etag) {\n options.headers = addHeader(\n options.headers,\n 'if-none-match',\n etag,\n )\n }\n\n const date =\n entry.getHeader('date')?.toString() ??\n entry.getHeader('last-modified')?.toString()\n if (date) {\n options.headers = addHeader(\n options.headers,\n 'if-modified-since',\n date,\n )\n }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export type TokenResponse = {
2
+ token: string;
3
+ };
4
+ export declare const isTokenResponse: (b: unknown) => b is TokenResponse;
5
+ //# sourceMappingURL=token-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-response.d.ts","sourceRoot":"","sources":["../../src/token-response.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,eAAe,MAAO,OAAO,KAAG,CAAC,IAAI,aAIpB,CAAA"}
@@ -0,0 +1,5 @@
1
+ export const isTokenResponse = (b) => !!b &&
2
+ typeof b === 'object' &&
3
+ typeof b.token === 'string' &&
4
+ !!b.token;
5
+ //# sourceMappingURL=token-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-response.js","sourceRoot":"","sources":["../../src/token-response.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAU,EAAsB,EAAE,CAChE,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,QAAQ;IACrB,OAAQ,CAAmB,CAAC,KAAK,KAAK,QAAQ;IAC9C,CAAC,CAAE,CAAmB,CAAC,KAAK,CAAA","sourcesContent":["export type TokenResponse = {\n token: string\n}\n\nexport const isTokenResponse = (b: unknown): b is TokenResponse =>\n !!b &&\n typeof b === 'object' &&\n typeof (b as TokenResponse).token === 'string' &&\n !!(b as TokenResponse).token\n"]}
@@ -0,0 +1,6 @@
1
+ export type WebAuthChallenge = {
2
+ doneUrl: string;
3
+ loginUrl: string;
4
+ };
5
+ export declare const isWebAuthChallenge: (o: unknown) => o is WebAuthChallenge;
6
+ //# sourceMappingURL=web-auth-challenge.d.ts.map