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

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 (69) hide show
  1. package/README.md +57 -54
  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 +62 -14
  11. package/dist/esm/cache-entry.d.ts.map +1 -1
  12. package/dist/esm/cache-entry.js +159 -55
  13. package/dist/esm/cache-entry.js.map +1 -1
  14. package/dist/esm/cache-revalidate.d.ts +2 -0
  15. package/dist/esm/cache-revalidate.d.ts.map +1 -0
  16. package/dist/esm/cache-revalidate.js +66 -0
  17. package/dist/esm/cache-revalidate.js.map +1 -0
  18. package/dist/esm/delete-header.d.ts +2 -0
  19. package/dist/esm/delete-header.d.ts.map +1 -0
  20. package/dist/esm/delete-header.js +32 -0
  21. package/dist/esm/delete-header.js.map +1 -0
  22. package/dist/esm/env.d.ts +0 -4
  23. package/dist/esm/env.d.ts.map +1 -1
  24. package/dist/esm/env.js +0 -3
  25. package/dist/esm/env.js.map +1 -1
  26. package/dist/esm/get-header.js +1 -1
  27. package/dist/esm/get-header.js.map +1 -1
  28. package/dist/esm/handle-304-response.d.ts +2 -2
  29. package/dist/esm/handle-304-response.d.ts.map +1 -1
  30. package/dist/esm/handle-304-response.js.map +1 -1
  31. package/dist/esm/index.d.ts +98 -9
  32. package/dist/esm/index.d.ts.map +1 -1
  33. package/dist/esm/index.js +265 -69
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/is-iterable.d.ts +2 -0
  36. package/dist/esm/is-iterable.d.ts.map +1 -0
  37. package/dist/esm/is-iterable.js +2 -0
  38. package/dist/esm/is-iterable.js.map +1 -0
  39. package/dist/esm/otplease.d.ts +4 -0
  40. package/dist/esm/otplease.d.ts.map +1 -0
  41. package/dist/esm/otplease.js +55 -0
  42. package/dist/esm/otplease.js.map +1 -0
  43. package/dist/esm/raw-header.d.ts +1 -1
  44. package/dist/esm/raw-header.d.ts.map +1 -1
  45. package/dist/esm/redirect.d.ts +2 -2
  46. package/dist/esm/redirect.d.ts.map +1 -1
  47. package/dist/esm/redirect.js +1 -0
  48. package/dist/esm/redirect.js.map +1 -1
  49. package/dist/esm/revalidate.d.ts +5 -0
  50. package/dist/esm/revalidate.d.ts.map +1 -0
  51. package/dist/esm/revalidate.js +49 -0
  52. package/dist/esm/revalidate.js.map +1 -0
  53. package/dist/esm/set-cache-headers.d.ts +2 -2
  54. package/dist/esm/set-cache-headers.d.ts.map +1 -1
  55. package/dist/esm/set-cache-headers.js +1 -1
  56. package/dist/esm/set-cache-headers.js.map +1 -1
  57. package/dist/esm/token-response.d.ts +5 -0
  58. package/dist/esm/token-response.d.ts.map +1 -0
  59. package/dist/esm/token-response.js +5 -0
  60. package/dist/esm/token-response.js.map +1 -0
  61. package/dist/esm/web-auth-challenge.d.ts +6 -0
  62. package/dist/esm/web-auth-challenge.d.ts.map +1 -0
  63. package/dist/esm/web-auth-challenge.js +7 -0
  64. package/dist/esm/web-auth-challenge.js.map +1 -0
  65. package/package.json +30 -21
  66. package/dist/esm/serdes.d.ts +0 -15
  67. package/dist/esm/serdes.d.ts.map +0 -1
  68. package/dist/esm/serdes.js +0 -19
  69. package/dist/esm/serdes.js.map +0 -1
package/README.md CHANGED
@@ -1,71 +1,74 @@
1
- # `@vltpkg/registry-client`
1
+ ![registry-client](https://github.com/user-attachments/assets/664e4107-bf7b-4179-802d-9ec9f1499955)
2
2
 
3
- This is a very light wrapper around undici, optimized for
4
- interfacing with an npm registry.
3
+ # @vltpkg/registry-client
5
4
 
6
- Any response with `immutable` in the `cache-control` header, or
7
- with a `content-type` of `application/octet-stream` or a path
8
- ending in `.tgz`, will be cached forever and never requested
9
- again as long as the cache survives.
5
+ This is a very light wrapper around undici, optimized for interfacing
6
+ with an npm registry.
7
+
8
+ **[Cache Unzipped](#cache-unzipped)** ·
9
+ **[Integrity Options](#integrity-options)** · **[Usage](#usage)**
10
+
11
+ ## Overview
12
+
13
+ Any response with `immutable` in the `cache-control` header, or with a
14
+ `content-type` of `application/octet-stream` or a path ending in
15
+ `.tgz`, will be cached forever and never requested again as long as
16
+ the cache survives.
10
17
 
11
18
  If the request has a cached response:
12
19
 
13
- - Cached responses with `immutable` in the `cache-control`
14
- header will be returned from cache without a network request,
15
- no matter what.
16
- - Cached responses with a `content-type` of
17
- `application/octet-stream` will be returned from cache without
18
- a network request, no matter what, because tarballs are
19
- immutable.
20
+ - Cached responses with `immutable` in the `cache-control` header will
21
+ be returned from cache without a network request, no matter what.
22
+ - Cached responses with a `content-type` of `application/octet-stream`
23
+ will be returned from cache without a network request, no matter
24
+ what, because tarballs are immutable.
20
25
  - Cached responses with `max-age=<n>` or `s-max-age=<n>` will be
21
- served from cache without a network request if it's less than
22
- `<n>` seconds old.
26
+ served from cache without a network request if it's less than `<n>`
27
+ seconds old.
23
28
  - Otherwise, a network request to the registry will be made
24
- - if an `etag` is present in the cached response, it will be
25
- used as the `if-none-match` header.
26
- - If a `last-modified` header is in the response, that will
27
- be used as the `if-modified-since` request header.
28
- - If there is no `last-modified` header, then use the `mtime`
29
- of the cache file as the `if-modified-since` header.
29
+ - if an `etag` is present in the cached response, it will be used as
30
+ the `if-none-match` header.
31
+ - If a `last-modified` header is in the response, that will be used
32
+ as the `if-modified-since` request header.
33
+ - If there is no `last-modified` header, then use the `mtime` of the
34
+ cache file as the `if-modified-since` header.
30
35
 
31
36
  This is the extent of the cache control logic. It is not a
32
- full-featured spec-compliant caching HTTP client, because that is
33
- not needed for this use case. Every response will be cached, even
34
- if the registry headers don't technically allow it.
37
+ full-featured spec-compliant caching HTTP client, because that is not
38
+ needed for this use case. Every response will be cached, even if the
39
+ registry headers don't technically allow it.
35
40
 
36
41
  ## Cache Unzipped
37
42
 
38
- Client always sends `accept-encoding: gzip;q=1.0, *;q=0.5`
39
- header when making requests, to save time on the wire.
43
+ Client always sends `accept-encoding: gzip;q=1.0, *;q=0.5` header when
44
+ making requests, to save time on the wire.
40
45
 
41
- If response has `content-encoding: gzip`, then we swap out the
42
- body for the unzipped response body in the cache, as if it was
43
- not gzipped in the first place. This _must_ be done before
44
- returning the response, because you can't `JSON.parse()` a
45
- gzipped response anyway.
46
+ If response has `content-encoding: gzip`, then we swap out the body
47
+ for the unzipped response body in the cache, as if it was not gzipped
48
+ in the first place. This _must_ be done before returning the response,
49
+ because you can't `JSON.parse()` a gzipped response anyway.
46
50
 
47
- If the response is `content-type: application/octet-stream` and
48
- starts with the gzip header, then we return the raw body as we
49
- received it, but as a best-effort background job, unzip it and
50
- update the cache entry to be an unzipped response body. This is
51
- done in the `@vltpkg/cache-unzip` worker.
51
+ If the response is `content-type: application/octet-stream` and starts
52
+ with the gzip header, then we return the raw body as we received it,
53
+ but as a best-effort background job, unzip it and update the cache
54
+ entry to be an unzipped response body. This is done in the
55
+ `@vltpkg/cache-unzip` worker.
52
56
 
53
57
  So,
54
58
 
55
- - json responses will always be un-zipped, in the response and in
56
- the cache.
57
- - artifact responses _may_ be gzipped (and thus, have to be
58
- unzipped by the unpack operation), but will eventually be
59
- cached as unzipped tarballs.
59
+ - json responses will always be un-zipped, in the response and in the
60
+ cache.
61
+ - artifact responses _may_ be gzipped (and thus, have to be unzipped
62
+ by the unpack operation), but will eventually be cached as unzipped
63
+ tarballs.
60
64
 
61
- Thus, the `content-length` response header will _usually_ not
62
- match the actual byte length of the response body.
65
+ Thus, the `content-length` response header will _usually_ not match
66
+ the actual byte length of the response body.
63
67
 
64
68
  ## Integrity Options
65
69
 
66
- An `integrity` option may be specified in a
67
- `fetchOptions.context` object, or in the options provided to
68
- `cache.set()`. For example:
70
+ An `integrity` option may be specified in a `fetchOptions.context`
71
+ object, or in the options provided to `cache.set()`. For example:
69
72
 
70
73
  ```js
71
74
  const integrity = `sha512-${base64hash}`
@@ -80,19 +83,19 @@ cache.set(key, value, { integrity })
80
83
  const value = await cache.fetch(key, { context: { integrity } })
81
84
  ```
82
85
 
83
- If the integrity provided is obviously not a valid sha512
84
- `Integrity` string, then it is ignored.
86
+ If the integrity provided is obviously not a valid sha512 `Integrity`
87
+ string, then it is ignored.
85
88
 
86
- Integrity values are not calculated or verified. The caller must do this
87
- check, if desired.
89
+ Integrity values are not calculated or verified. The caller must do
90
+ this check, if desired.
88
91
 
89
92
  Note that the integrity provided to `cache.fetch()` or `cache.set()`
90
93
  does _not_ typically match the calculated integrity of the object
91
- being cached. Typically, the integrity is related to the body of
92
- the response that a `@vltpkg/registry-client.CacheEntry` object
94
+ being cached. Typically, the integrity is related to the body of the
95
+ response that a `@vltpkg/registry-client.CacheEntry` object
93
96
  represents.
94
97
 
95
- ## USAGE
98
+ ## Usage
96
99
 
97
100
  ```js
98
101
  import { Cache } from '@vltpkg/cache'
@@ -1,2 +1,2 @@
1
- export declare const addHeader: <H extends [string, string[] | string][] | Iterable<[string, string[] | string | undefined]> | Record<string, string[] | string | undefined> | string[]>(headers: H | null | undefined, key: string, value: string) => H;
1
+ export declare const addHeader: <H extends [string, string[] | string][] | Iterable<[string, string[] | string | undefined]> | Record<string, string[] | string | undefined> | string[]>(headers: H | null | undefined, key: string, value?: string) => H;
2
2
  //# sourceMappingURL=add-header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-header.d.ts","sourceRoot":"","sources":["../../src/add-header.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS,GACpB,CAAC,SACG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,GAC7B,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,GACjD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC,GAC7C,MAAM,EAAE,WAEH,CAAC,GAAG,IAAI,GAAG,SAAS,OACxB,MAAM,SACJ,MAAM,KACZ,CAgBF,CAAA"}
1
+ {"version":3,"file":"add-header.d.ts","sourceRoot":"","sources":["../../src/add-header.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS,GACpB,CAAC,SACG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,GAC7B,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,GACjD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC,GAC7C,MAAM,EAAE,WAEH,CAAC,GAAG,IAAI,GAAG,SAAS,OACxB,MAAM,UACH,MAAM,KACb,CAkBF,CAAA"}
@@ -1,7 +1,10 @@
1
- const isIterable = (o) => !!o && !!o[Symbol.iterator];
1
+ import { deleteHeader } from "./delete-header.js";
2
+ import { isIterable } from "./is-iterable.js";
2
3
  // this does some rude things with types, but not much way around it,
3
4
  // since the opts.headers type is so loosey goosey to begin with.
4
5
  export const addHeader = (headers, key, value) => {
6
+ if (!value)
7
+ return deleteHeader(headers, key);
5
8
  if (!headers)
6
9
  return { [key]: value };
7
10
  if (Array.isArray(headers)) {
@@ -1 +1 @@
1
- {"version":3,"file":"add-header.js","sourceRoot":"","sources":["../../src/add-header.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,CAAI,CAAM,EAAoB,EAAE,CACjD,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AAE7B,qEAAqE;AACrE,iEAAiE;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,CAOvB,OAA6B,EAC7B,GAAW,EACX,KAAa,EACV,EAAE;IACL,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAO,CAAA;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAiB,CAAA;QAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,OAA8B,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;;YAC9C,OAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC3C,OAAO,OAAO,CAAA;IAChB,CAAC;SAAM,IACL,UAAU,CAA0C,OAAO,CAAC,EAC5D,CAAC;QACD,OAAO,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAiB,CAAA;IACnD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACpB,OAAO,OAAO,CAAA;IAChB,CAAC;AACH,CAAC,CAAA","sourcesContent":["const isIterable = <T>(o: any): o is Iterable<T> =>\n !!o && !!o[Symbol.iterator]\n\n// this does some rude things with types, but not much way around it,\n// since the opts.headers type is so loosey goosey to begin with.\nexport const addHeader = <\n H extends\n | [string, string[] | string][]\n | Iterable<[string, string[] | string | undefined]>\n | Record<string, string[] | string | undefined>\n | string[],\n>(\n headers: H | null | undefined,\n key: string,\n value: string,\n): H => {\n if (!headers) return { [key]: value } as H\n if (Array.isArray(headers)) {\n if (!headers.length) return [[key, value]] as unknown as H\n if (Array.isArray(headers[0]))\n (headers as [string, string][]).push([key, value])\n else (headers as string[]).push(key, value)\n return headers\n } else if (\n isIterable<[string, string[] | string | undefined]>(headers)\n ) {\n return [...headers, [key, value]] as unknown as H\n } else {\n headers[key] = value\n return headers\n }\n}\n"]}
1
+ {"version":3,"file":"add-header.js","sourceRoot":"","sources":["../../src/add-header.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,qEAAqE;AACrE,iEAAiE;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,CAOvB,OAA6B,EAC7B,GAAW,EACX,KAAc,EACX,EAAE;IACL,IAAI,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IAE7C,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAO,CAAA;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAiB,CAAA;QAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,OAA8B,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;;YAC9C,OAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC3C,OAAO,OAAO,CAAA;IAChB,CAAC;SAAM,IACL,UAAU,CAA0C,OAAO,CAAC,EAC5D,CAAC;QACD,OAAO,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAiB,CAAA;IACnD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACpB,OAAO,OAAO,CAAA;IAChB,CAAC;AACH,CAAC,CAAA","sourcesContent":["import { deleteHeader } from './delete-header.ts'\nimport { isIterable } from './is-iterable.ts'\n\n// this does some rude things with types, but not much way around it,\n// since the opts.headers type is so loosey goosey to begin with.\nexport const addHeader = <\n H extends\n | [string, string[] | string][]\n | Iterable<[string, string[] | string | undefined]>\n | Record<string, string[] | string | undefined>\n | string[],\n>(\n headers: H | null | undefined,\n key: string,\n value?: string,\n): H => {\n if (!value) return deleteHeader(headers, key)\n\n if (!headers) return { [key]: value } as H\n if (Array.isArray(headers)) {\n if (!headers.length) return [[key, value]] as unknown as H\n if (Array.isArray(headers[0]))\n (headers as [string, string][]).push([key, value])\n else (headers as string[]).push(key, value)\n return headers\n } else if (\n isIterable<[string, string[] | string | undefined]>(headers)\n ) {\n return [...headers, [key, value]] as unknown as H\n } else {\n headers[key] = value\n return headers\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { Keychain } from '@vltpkg/keychain';
2
+ export type Token = `Bearer ${string}` | `Basic ${string}`;
3
+ export declare const keychains: Map<string, Keychain<Token>>;
4
+ export declare const getKC: (identity: string) => Keychain<Token>;
5
+ export declare const isToken: (t: any) => t is Token;
6
+ export declare const deleteToken: (registry: string, identity: string) => Promise<void>;
7
+ export declare const setToken: (registry: string, token: Token, identity: string) => Promise<void>;
8
+ export declare const getToken: (registry: string, identity: string) => Promise<Token | undefined>;
9
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,MAAM,MAAM,KAAK,GAAG,UAAU,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAAA;AAG1D,eAAO,MAAM,SAAS,8BAAqC,CAAA;AAE3D,eAAO,MAAM,KAAK,aAAc,MAAM,oBAOrC,CAAA;AAED,eAAO,MAAM,OAAO,MAAO,GAAG,KAAG,CAAC,IAAI,KAEe,CAAA;AAErD,eAAO,MAAM,WAAW,aACZ,MAAM,YACN,MAAM,KACf,OAAO,CAAC,IAAI,CAKd,CAAA;AAED,eAAO,MAAM,QAAQ,aACT,MAAM,SACT,KAAK,YACF,MAAM,KACf,OAAO,CAAC,IAAI,CAGd,CAAA;AAED,eAAO,MAAM,QAAQ,aACT,MAAM,YACN,MAAM,KACf,OAAO,CAAC,KAAK,GAAG,SAAS,CAc3B,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { Keychain } from '@vltpkg/keychain';
2
+ // just exported for testing
3
+ export const keychains = new Map();
4
+ export const getKC = (identity) => {
5
+ const kc = keychains.get(identity);
6
+ if (kc)
7
+ return kc;
8
+ const i = identity ? `vlt/auth/${identity}` : 'vlt/auth';
9
+ const nkc = new Keychain(i);
10
+ keychains.set(identity, nkc);
11
+ return nkc;
12
+ };
13
+ export const isToken = (t) => typeof t === 'string' &&
14
+ (t.startsWith('Bearer ') || t.startsWith('Basic '));
15
+ export const deleteToken = async (registry, identity) => {
16
+ const kc = getKC(identity);
17
+ await kc.load();
18
+ kc.delete(new URL(registry).origin);
19
+ await kc.save();
20
+ };
21
+ export const setToken = async (registry, token, identity) => {
22
+ const kc = getKC(identity);
23
+ return kc.set(new URL(registry).origin, token);
24
+ };
25
+ export const getToken = async (registry, identity) => {
26
+ const kc = getKC(identity);
27
+ registry = new URL(registry).origin;
28
+ const envReg = process.env.VLT_REGISTRY;
29
+ if (envReg && registry === new URL(envReg).origin) {
30
+ const envTok = process.env.VLT_TOKEN;
31
+ if (envTok)
32
+ return `Bearer ${envTok}`;
33
+ }
34
+ const tok = process.env[`VLT_TOKEN_${registry.replace(/[^a-zA-Z0-9]+/g, '_')}`];
35
+ if (tok)
36
+ return `Bearer ${tok}`;
37
+ return kc.get(registry);
38
+ };
39
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAI3C,4BAA4B;AAC5B,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAA;AAE3D,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,QAAgB,EAAE,EAAE;IACxC,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAA;IACjB,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;IACxD,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAQ,CAAC,CAAC,CAAA;IAClC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAC5B,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAM,EAAc,EAAE,CAC5C,OAAO,CAAC,KAAK,QAAQ;IACrB,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAA;AAErD,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC9B,QAAgB,EAChB,QAAgB,EACD,EAAE;IACjB,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC1B,MAAM,EAAE,CAAC,IAAI,EAAE,CAAA;IACf,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,QAAgB,EAChB,KAAY,EACZ,QAAgB,EACD,EAAE;IACjB,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC1B,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAChD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,QAAgB,EAChB,QAAgB,EACY,EAAE;IAC9B,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC1B,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAA;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAA;IACvC,IAAI,MAAM,IAAI,QAAQ,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAA;QACpC,IAAI,MAAM;YAAE,OAAO,UAAU,MAAM,EAAE,CAAA;IACvC,CAAC;IACD,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CACT,aAAa,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CACvD,CAAA;IACH,IAAI,GAAG;QAAE,OAAO,UAAU,GAAG,EAAE,CAAA;IAC/B,OAAO,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AACzB,CAAC,CAAA","sourcesContent":["import { Keychain } from '@vltpkg/keychain'\n\nexport type Token = `Bearer ${string}` | `Basic ${string}`\n\n// just exported for testing\nexport const keychains = new Map<string, Keychain<Token>>()\n\nexport const getKC = (identity: string) => {\n const kc = keychains.get(identity)\n if (kc) return kc\n const i = identity ? `vlt/auth/${identity}` : 'vlt/auth'\n const nkc = new Keychain<Token>(i)\n keychains.set(identity, nkc)\n return nkc\n}\n\nexport const isToken = (t: any): t is Token =>\n typeof t === 'string' &&\n (t.startsWith('Bearer ') || t.startsWith('Basic '))\n\nexport const deleteToken = async (\n registry: string,\n identity: string,\n): Promise<void> => {\n const kc = getKC(identity)\n await kc.load()\n kc.delete(new URL(registry).origin)\n await kc.save()\n}\n\nexport const setToken = async (\n registry: string,\n token: Token,\n identity: string,\n): Promise<void> => {\n const kc = getKC(identity)\n return kc.set(new URL(registry).origin, token)\n}\n\nexport const getToken = async (\n registry: string,\n identity: string,\n): Promise<Token | undefined> => {\n const kc = getKC(identity)\n registry = new URL(registry).origin\n const envReg = process.env.VLT_REGISTRY\n if (envReg && registry === new URL(envReg).origin) {\n const envTok = process.env.VLT_TOKEN\n if (envTok) return `Bearer ${envTok}`\n }\n const tok =\n process.env[\n `VLT_TOKEN_${registry.replace(/[^a-zA-Z0-9]+/g, '_')}`\n ]\n if (tok) return `Bearer ${tok}`\n return kc.get(registry)\n}\n"]}
@@ -1,33 +1,80 @@
1
- import { Integrity, JSONField } from '@vltpkg/types';
1
+ import type { ErrorCauseOptions } from '@vltpkg/error-cause';
2
+ import type { Integrity, JSONField } from '@vltpkg/types';
3
+ import ccp from 'cache-control-parser';
4
+ import type { InspectOptions } from 'node:util';
5
+ export type JSONObj = Record<string, JSONField>;
2
6
  declare const kCustomInspect: unique symbol;
3
- export declare class CacheEntry {
4
- #private;
5
- constructor(statusCode: number, headers: Buffer[], integrity?: Integrity);
6
- [kCustomInspect](...args: any[]): string;
7
+ export type CacheEntryOptions = {
8
+ /**
9
+ * The expected integrity value for this response body
10
+ */
11
+ integrity?: Integrity;
12
+ /**
13
+ * Whether to trust the integrity, or calculate the actual value.
14
+ *
15
+ * This indicates that we just accept whatever the integrity is as the actual
16
+ * integrity for saving back to the cache, because it's coming directly from
17
+ * the registry that we fetched a packument from, and is an initial gzipped
18
+ * artifact request.
19
+ */
20
+ trustIntegrity?: boolean;
7
21
  /**
8
- * `true` if the entry represents a cached response that is still
9
- * valid to use.
22
+ * If the server does not serve a `stale-while-revalidate` value in the
23
+ * `cache-control` header, then this multiplier is applied to the `max-age`
24
+ * or `s-maxage` values.
25
+ *
26
+ * By default, this is `60`, so for example a response that is cacheable for
27
+ * 5 minutes will allow a stale response while revalidating for up to 5
28
+ * hours.
29
+ *
30
+ * If the server *does* provide a `stale-while-revalidate` value, then that
31
+ * is always used.
32
+ *
33
+ * Set to 0 to prevent any `stale-while-revalidate` behavior unless
34
+ * explicitly allowed by the server's `cache-control` header.
10
35
  */
36
+ 'stale-while-revalidate-factor'?: number;
37
+ };
38
+ export declare class CacheEntry {
39
+ #private;
40
+ constructor(statusCode: number, headers: Buffer[], { integrity, trustIntegrity, 'stale-while-revalidate-factor': staleWhileRevalidateFactor, }?: CacheEntryOptions);
41
+ toJSON(): {
42
+ [k: string]: string | number | boolean | [string, string][] | Date | ccp.CacheControl | undefined;
43
+ };
44
+ [kCustomInspect](depth: number, options: InspectOptions): string;
45
+ get date(): Date | undefined;
46
+ get maxAge(): number;
47
+ get cacheControl(): ccp.CacheControl;
48
+ get staleWhileRevalidate(): boolean;
49
+ get contentType(): string;
11
50
  get valid(): boolean;
12
51
  addBody(b: Buffer): void;
13
52
  get statusCode(): number;
14
- get headers(): Buffer[];
53
+ get headers(): Buffer<ArrayBufferLike>[];
15
54
  /**
16
- * check that the sri integrity string that was provided to the ctor
55
+ * Check that the sri integrity string that was provided to the ctor
17
56
  * matches the body that we actually received. This should only be called
18
57
  * AFTER the entire body has been completely downloaded.
19
58
  *
59
+ * This method **will throw** if the integrity values do not match.
60
+ *
20
61
  * Note that this will *usually* not be true if the value is coming out of
21
62
  * the cache, because the cache entries are un-gzipped in place. It should
22
- * *only* be called for artifacts that come from an actual http response.
63
+ * _only_ be called for artifacts that come from an actual http response.
64
+ *
65
+ * Returns true if anything was actually verified.
23
66
  */
24
- checkIntegrity(): boolean;
67
+ checkIntegrity(context?: ErrorCauseOptions): this is CacheEntry & {
68
+ integrity: Integrity;
69
+ };
25
70
  get integrityActual(): Integrity;
26
- get integrity(): `sha512-${string}` | undefined;
71
+ set integrityActual(i: Integrity);
72
+ set integrity(i: Integrity | undefined);
73
+ get integrity(): Integrity | undefined;
27
74
  /**
28
75
  * Give it a key, and it'll return the buffer of that header value
29
76
  */
30
- getHeader(h: string): Buffer | undefined;
77
+ getHeader(h: string): Buffer<ArrayBufferLike> | undefined;
31
78
  /**
32
79
  * Set a header to a specific value
33
80
  */
@@ -53,13 +100,14 @@ export declare class CacheEntry {
53
100
  /**
54
101
  * Parse the entry body as JSON and return the result
55
102
  */
56
- json(): Record<string, JSONField>;
103
+ json(): JSONObj;
57
104
  /**
58
105
  * Pass the contents of a @vltpkg/cache.Cache object as a buffer,
59
106
  * and this static method will decode it into a CacheEntry representing
60
107
  * the cached response.
61
108
  */
62
109
  static decode(buffer: Buffer): CacheEntry;
110
+ static isGzipEntry(buffer: Buffer): boolean;
63
111
  /**
64
112
  * Encode the entry as a single Buffer for writing to the cache
65
113
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cache-entry.d.ts","sourceRoot":"","sources":["../../src/cache-entry.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AA+BpD,QAAA,MAAM,cAAc,eAA2C,CAAA;AAE/D,qBAAa,UAAU;;gBAUnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,CAAC,EAAE,SAAS;IAiBvB,CAAC,cAAc,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM;IAYxC;;;OAGG;IACH,IAAI,KAAK,IAAI,OAAO,CAsBnB;IAED,OAAO,CAAC,CAAC,EAAE,MAAM;IAKjB,IAAI,UAAU,WAEb;IACD,IAAI,OAAO,aAEV;IAED;;;;;;;;OAQG;IACH,cAAc,IAAI,OAAO;IAIzB,IAAI,eAAe,IAAI,SAAS,CAM/B;IACD,IAAI,SAAS,mCAEZ;IAED;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM;IAInB;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAI3C;;OAEG;IACH,MAAM,IAAI,MAAM;IAWhB,IAAI,IAAI,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAEvC;IAGD,IAAI,MAAM,IAAI,OAAO,CAcpB;IAGD,IAAI,MAAM,IAAI,OAAO,CAcpB;IAED;;;;OAIG;IACH,KAAK;IAmBL;;;OAGG;IACH,IAAI;IAKJ;;OAEG;IACH,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;IAgBjC;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;IAiCzC;;OAEG;IAGH,MAAM,IAAI,MAAM;CAoCjB"}
1
+ {"version":3,"file":"cache-entry.d.ts","sourceRoot":"","sources":["../../src/cache-entry.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,GAAG,MAAM,sBAAsB,CAAA;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAK/C,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAyB/C,QAAA,MAAM,cAAc,eAA2C,CAAA;AAE/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IAExB;;;;;;;;;;;;;;OAcG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAA;CACzC,CAAA;AAED,qBAAa,UAAU;;gBAYnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EAAE,EACjB,EACE,SAAS,EACT,cAAsB,EACtB,+BAA+B,EAC7B,0BAA+B,GAClC,GAAE,iBAAsB;IAmB3B,MAAM;;;IAwCN,CAAC,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM;IAShE,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAK3B;IAGD,IAAI,MAAM,IAAI,MAAM,CAQnB;IAGD,IAAI,YAAY,IAAI,GAAG,CAAC,YAAY,CAKnC;IAGD,IAAI,oBAAoB,IAAI,OAAO,CAWlC;IAGD,IAAI,WAAW,WAKd;IAOD,IAAI,KAAK,IAAI,OAAO,CAmBnB;IAED,OAAO,CAAC,CAAC,EAAE,MAAM;IAKjB,IAAI,UAAU,WAEb;IACD,IAAI,OAAO,8BAEV;IAED;;;;;;;;;;;;OAYG;IACH,cAAc,CACZ,OAAO,GAAE,iBAAsB,GAC9B,IAAI,IAAI,UAAU,GAAG;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE;IAchD,IAAI,eAAe,IAAI,SAAS,CAO/B;IAED,IAAI,eAAe,CAAC,CAAC,EAAE,SAAS,EAG/B;IAED,IAAI,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,EAKrC;IACD,IAAI,SAAS,IANI,SAAS,GAAG,SAAS,CAQrC;IAED;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM;IAInB;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAI3C;;OAEG;IACH,MAAM,IAAI,MAAM;IAWhB,IAAI,IAAI,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAEvC;IAGD,IAAI,MAAM,IAAI,OAAO,CAYpB;IAGD,IAAI,MAAM,IAAI,OAAO,CAcpB;IAED;;;;OAIG;IACH,KAAK;IAmBL;;;OAGG;IACH,IAAI;IAKJ;;OAEG;IACH,IAAI,IAAI,OAAO;IAQf;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;IAsDzC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAO3C;;OAEG;IAGH,MAAM,IAAI,MAAM;CAmCjB"}