@pnpm/crypto.shasums-file 1100.1.4 → 1100.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @pnpm/crypto.shasums-file
2
2
 
3
+ ## 1100.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Resolving a Node.js runtime version (`devEngines.runtime` / `runtime:` specifiers) is now much faster: the per-version release metadata is cached in the pnpm cache directory after its signature is verified, and an exact stable version such as `runtime:22.23.2` no longer downloads the Node.js release index. A pinned runtime whose metadata was fetched once resolves without any network access, which removes the noticeable delay on the first `node` invocation in a project pinning an already-downloaded runtime [#13899](https://github.com/pnpm/pnpm/issues/13899).
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies:
12
+ - @pnpm/error@1100.1.2
13
+
14
+ ## 1100.1.5
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies:
19
+ - @pnpm/error@1100.1.1
20
+
3
21
  ## 1100.1.4
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,59 @@
1
+ /**
2
+ * On-disk cache for per-version runtime `SHASUMS256.txt` bodies.
3
+ *
4
+ * A release's SHASUMS file lives under a version-pinned URL
5
+ * (`.../v22.0.0/SHASUMS256.txt`), so its content is immutable: a body fetched
6
+ * once — and, for signed channels, verified once — never needs to be fetched
7
+ * again. Entries live under
8
+ * `<cacheDir>/v11/runtime-shasums/<trust>/<host>/<url path>` so clearing the
9
+ * cache directory clears them together with the registry metadata mirror. The
10
+ * layout is shared with pacquet, which reads and writes the same files.
11
+ *
12
+ * Only hand immutable URLs to this cache. The cache directory is
13
+ * *project-configurable* (`cacheDir`), so entries under it must never carry
14
+ * more authority than the project that could have written them:
15
+ *
16
+ * - Signed-channel readers persist the detached signature next to the body
17
+ * and re-verify it against the embedded release keys on every read (see
18
+ * `fetchVerifiedNodeShasumsFileCached`), so a pre-seeded entry is only
19
+ * accepted if it is a genuine release body.
20
+ * - Unverified entries are trusted on read, which grants a project nothing
21
+ * new: the unsigned channels' mirrors are already project-configurable
22
+ * (their bodies were project-controllable before the cache existed), and
23
+ * the musl list's download URLs are derived from the hardcoded
24
+ * unofficial-builds base, never from the cached body.
25
+ *
26
+ * The `<trust>` path segment keeps the two classes in disjoint subtrees so
27
+ * their read policies cannot be confused.
28
+ */
29
+ export declare const RUNTIME_SHASUMS_DIR = "v11/runtime-shasums";
30
+ /**
31
+ * How the body of a cache entry was authenticated before it was written.
32
+ * Each class caches into its own subtree.
33
+ */
34
+ export type ShasumsTrust = 'verified' | 'unverified';
35
+ export interface ShasumsCacheOpts {
36
+ cacheDir?: string;
37
+ trust: ShasumsTrust;
38
+ }
39
+ /**
40
+ * The cached body for `url` as UTF-8 text, via {@link readCachedBytes}.
41
+ * Invalid UTF-8 is a miss — a lossy decode would turn a corrupted entry
42
+ * into malformed rows instead of a refetch.
43
+ */
44
+ export declare function readCachedShasums(url: string, opts: ShasumsCacheOpts): Promise<string | undefined>;
45
+ /**
46
+ * The cached bytes for `url`, or `undefined` on any miss — a URL the mapping
47
+ * cannot represent, a missing or non-regular file, unreadable content, an
48
+ * empty file (never a valid entry, so it only signals a torn write), or a
49
+ * file over {@link MAX_CACHED_SHASUMS_LEN}.
50
+ */
51
+ export declare function readCachedBytes(url: string, opts: ShasumsCacheOpts): Promise<Buffer | undefined>;
52
+ /**
53
+ * Best-effort write of `body` for `url`: a cache-write failure only costs a
54
+ * refetch on the next resolve, so errors are deliberately dropped rather than
55
+ * failing the resolution that produced the body. The exclusively-created temp
56
+ * file + rename keeps concurrent writers (two installs resolving the same
57
+ * version) from exposing a torn body.
58
+ */
59
+ export declare function writeCachedShasums(url: string, body: string | Uint8Array, opts: ShasumsCacheOpts): Promise<void>;
@@ -0,0 +1,196 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { threadId } from 'node:worker_threads';
4
+ /**
5
+ * On-disk cache for per-version runtime `SHASUMS256.txt` bodies.
6
+ *
7
+ * A release's SHASUMS file lives under a version-pinned URL
8
+ * (`.../v22.0.0/SHASUMS256.txt`), so its content is immutable: a body fetched
9
+ * once — and, for signed channels, verified once — never needs to be fetched
10
+ * again. Entries live under
11
+ * `<cacheDir>/v11/runtime-shasums/<trust>/<host>/<url path>` so clearing the
12
+ * cache directory clears them together with the registry metadata mirror. The
13
+ * layout is shared with pacquet, which reads and writes the same files.
14
+ *
15
+ * Only hand immutable URLs to this cache. The cache directory is
16
+ * *project-configurable* (`cacheDir`), so entries under it must never carry
17
+ * more authority than the project that could have written them:
18
+ *
19
+ * - Signed-channel readers persist the detached signature next to the body
20
+ * and re-verify it against the embedded release keys on every read (see
21
+ * `fetchVerifiedNodeShasumsFileCached`), so a pre-seeded entry is only
22
+ * accepted if it is a genuine release body.
23
+ * - Unverified entries are trusted on read, which grants a project nothing
24
+ * new: the unsigned channels' mirrors are already project-configurable
25
+ * (their bodies were project-controllable before the cache existed), and
26
+ * the musl list's download URLs are derived from the hardcoded
27
+ * unofficial-builds base, never from the cached body.
28
+ *
29
+ * The `<trust>` path segment keeps the two classes in disjoint subtrees so
30
+ * their read policies cannot be confused.
31
+ */
32
+ export const RUNTIME_SHASUMS_DIR = 'v11/runtime-shasums';
33
+ /**
34
+ * Upper bound on a cache entry's size. Real SHASUMS bodies are a few
35
+ * kilobytes; anything past this bound is not a release asset list and is
36
+ * never read into memory or written.
37
+ */
38
+ const MAX_CACHED_SHASUMS_LEN = 1024 * 1024;
39
+ /**
40
+ * The cached body for `url` as UTF-8 text, via {@link readCachedBytes}.
41
+ * Invalid UTF-8 is a miss — a lossy decode would turn a corrupted entry
42
+ * into malformed rows instead of a refetch.
43
+ */
44
+ export async function readCachedShasums(url, opts) {
45
+ const bytes = await readCachedBytes(url, opts);
46
+ if (bytes == null)
47
+ return undefined;
48
+ try {
49
+ return strictUtf8Decoder.decode(bytes);
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ }
55
+ const strictUtf8Decoder = new TextDecoder('utf8', { fatal: true });
56
+ /**
57
+ * The cached bytes for `url`, or `undefined` on any miss — a URL the mapping
58
+ * cannot represent, a missing or non-regular file, unreadable content, an
59
+ * empty file (never a valid entry, so it only signals a torn write), or a
60
+ * file over {@link MAX_CACHED_SHASUMS_LEN}.
61
+ */
62
+ export async function readCachedBytes(url, opts) {
63
+ if (opts.cacheDir == null)
64
+ return undefined;
65
+ const filePath = shasumsCachePath(opts.cacheDir, opts.trust, url);
66
+ if (filePath == null)
67
+ return undefined;
68
+ try {
69
+ // A FIFO planted at the entry path would block a plain open until a
70
+ // writer appears; O_NONBLOCK (a no-op for regular files, absent on
71
+ // Windows, whose directory entries cannot be named pipes) makes the open
72
+ // return immediately.
73
+ const file = await fs.promises.open(filePath, fs.constants.O_RDONLY | (fs.constants.O_NONBLOCK ?? 0));
74
+ try {
75
+ // Checked on the opened handle, so nothing can swap the regular file
76
+ // for a special one between check and read.
77
+ if (!(await file.stat()).isFile())
78
+ return undefined;
79
+ // A bounded read rather than a stat check keeps the cap race-free: at
80
+ // most one byte past the bound is ever read, whatever the file's size
81
+ // becomes between open and read.
82
+ const buffer = Buffer.allocUnsafe(MAX_CACHED_SHASUMS_LEN + 1);
83
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
84
+ if (bytesRead === 0 || bytesRead > MAX_CACHED_SHASUMS_LEN)
85
+ return undefined;
86
+ return buffer.subarray(0, bytesRead);
87
+ }
88
+ finally {
89
+ await file.close();
90
+ }
91
+ }
92
+ catch {
93
+ return undefined;
94
+ }
95
+ }
96
+ /**
97
+ * Best-effort write of `body` for `url`: a cache-write failure only costs a
98
+ * refetch on the next resolve, so errors are deliberately dropped rather than
99
+ * failing the resolution that produced the body. The exclusively-created temp
100
+ * file + rename keeps concurrent writers (two installs resolving the same
101
+ * version) from exposing a torn body.
102
+ */
103
+ export async function writeCachedShasums(url, body, opts) {
104
+ if (opts.cacheDir == null)
105
+ return;
106
+ if (Buffer.byteLength(body) > MAX_CACHED_SHASUMS_LEN)
107
+ return;
108
+ const filePath = shasumsCachePath(opts.cacheDir, opts.trust, url);
109
+ if (filePath == null)
110
+ return;
111
+ // The process id alone does not make the temp name unique (worker threads
112
+ // share it), so the thread id and a counter join it. The `wx` flag refuses
113
+ // to open a path that already exists — a colliding writer or a pre-seeded
114
+ // symlink fails the open instead of being followed — and any failure just
115
+ // skips the write.
116
+ const tempPath = `${filePath}.tmp-${process.pid.toString()}-${threadId.toString()}-${(tempCounter++).toString()}`;
117
+ try {
118
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
119
+ // Sync before the rename so a crash cannot persist the renamed name
120
+ // pointing at partially-written content — a torn SHASUMS prefix still
121
+ // parses and would otherwise be served (missing platform rows) until the
122
+ // cache is cleared.
123
+ const tempFile = await fs.promises.open(tempPath, 'wx');
124
+ try {
125
+ await tempFile.writeFile(body);
126
+ await tempFile.sync();
127
+ }
128
+ finally {
129
+ await tempFile.close();
130
+ }
131
+ await fs.promises.rename(tempPath, filePath);
132
+ }
133
+ catch {
134
+ try {
135
+ await fs.promises.rm(tempPath, { force: true });
136
+ }
137
+ catch { }
138
+ }
139
+ }
140
+ let tempCounter = 0;
141
+ /**
142
+ * The cache file backing `url`, or `undefined` when the URL has a shape the
143
+ * path mapping does not cover (non-HTTP scheme, embedded credentials, query
144
+ * string, empty or dot-only path segments). Returning `undefined` just
145
+ * disables caching for that URL.
146
+ */
147
+ function shasumsCachePath(cacheDir, trust, url) {
148
+ let rest;
149
+ if (url.startsWith('https://')) {
150
+ rest = url.substring('https://'.length);
151
+ }
152
+ else if (url.startsWith('http://')) {
153
+ rest = url.substring('http://'.length);
154
+ }
155
+ else {
156
+ return undefined;
157
+ }
158
+ if (/[?#@]/.test(rest))
159
+ return undefined;
160
+ const firstSlash = rest.indexOf('/');
161
+ if (firstSlash <= 0)
162
+ return undefined;
163
+ // `:` (a port separator) is not portable in file names; `+` is the same
164
+ // encoding the registry metadata mirror uses for it.
165
+ const host = rest.substring(0, firstSlash).toLowerCase().replaceAll(':', '+');
166
+ const parts = [encodePathSegment(host)];
167
+ for (const segment of rest.substring(firstSlash + 1).split('/')) {
168
+ if (!segment || segment === '.' || segment === '..')
169
+ return undefined;
170
+ parts.push(encodePathSegment(segment));
171
+ }
172
+ if (parts.some((part) => part == null))
173
+ return undefined;
174
+ return path.join(cacheDir, RUNTIME_SHASUMS_DIR, trust, ...parts);
175
+ }
176
+ /**
177
+ * Percent-encode the bytes of `segment` that are not portable across
178
+ * filesystems, keeping `[A-Za-z0-9._+-]` as-is. pacquet applies the same
179
+ * encoding, so both tools address one file.
180
+ */
181
+ function encodePathSegment(segment) {
182
+ if (segment.length > 200)
183
+ return undefined;
184
+ let encoded = '';
185
+ for (const byte of Buffer.from(segment)) {
186
+ const char = String.fromCharCode(byte);
187
+ if (/[\w.+-]/.test(char)) {
188
+ encoded += char;
189
+ }
190
+ else {
191
+ encoded += `%${byte.toString(16).toUpperCase().padStart(2, '0')}`;
192
+ }
193
+ }
194
+ return encoded;
195
+ }
196
+ //# sourceMappingURL=diskCache.js.map
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { FetchFromRegistry } from '@pnpm/fetching.types';
2
- import { fetchVerifiedNodeShasums } from './verifyNodeShasums.js';
3
- export { fetchVerifiedNodeShasums };
2
+ import { RUNTIME_SHASUMS_DIR } from './diskCache.js';
3
+ import { type ArmoredKey, fetchVerifiedNodeShasums } from './verifyNodeShasums.js';
4
+ export { fetchVerifiedNodeShasums, RUNTIME_SHASUMS_DIR };
4
5
  export interface ShasumsFileItem {
5
6
  integrity: string;
6
7
  fileName: string;
@@ -13,6 +14,31 @@ export declare function fetchShasumsFile(fetch: FetchFromRegistry, shasumsUrl: s
13
14
  * fetched from a repository-configurable Node.js mirror.
14
15
  */
15
16
  export declare function fetchVerifiedNodeShasumsFile(fetch: FetchFromRegistry, shasumsUrl: string): Promise<ShasumsFileItem[]>;
17
+ export interface FetchShasumsFileCachedOpts {
18
+ cacheDir?: string;
19
+ }
20
+ export interface FetchVerifiedNodeShasumsFileCachedOpts extends FetchShasumsFileCachedOpts {
21
+ trustedKeys?: readonly ArmoredKey[];
22
+ }
23
+ /**
24
+ * Like {@link fetchVerifiedNodeShasumsFile}, backed by the disk cache when
25
+ * `opts.cacheDir` is given. The cache stores the body together with its
26
+ * detached signature, and a cache hit re-verifies that signature against the
27
+ * embedded release keys: the cache directory is project-configurable, so a
28
+ * pre-seeded entry must prove it is a genuine release body before it is
29
+ * served. Any verification failure is a miss and the pair is refetched.
30
+ * `shasumsUrl` must be version-pinned — a mutable URL must never be handed to
31
+ * the cache.
32
+ */
33
+ export declare function fetchVerifiedNodeShasumsFileCached(fetch: FetchFromRegistry, shasumsUrl: string, opts?: FetchVerifiedNodeShasumsFileCachedOpts): Promise<ShasumsFileItem[]>;
34
+ /**
35
+ * Like {@link fetchShasumsFile}, backed by the disk cache when `opts.cacheDir`
36
+ * is given. For mirrors whose SHASUMS files carry no verifiable signature the
37
+ * cached body is trusted exactly as far as the TLS fetch that produced it.
38
+ * `shasumsUrl` must be version-pinned — a mutable URL must never be handed to
39
+ * the cache.
40
+ */
41
+ export declare function fetchShasumsFileCached(fetch: FetchFromRegistry, shasumsUrl: string, opts?: FetchShasumsFileCachedOpts): Promise<ShasumsFileItem[]>;
16
42
  export declare function parseShasumsFile(shasumsFileContent: string): ShasumsFileItem[];
17
43
  export declare function fetchShasumsFileRaw(fetch: FetchFromRegistry, shasumsUrl: string): Promise<string>;
18
44
  export declare function pickFileChecksumFromShasumsFile(body: string, fileName: string): string;
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { PnpmError } from '@pnpm/error';
2
- import { fetchVerifiedNodeShasums } from './verifyNodeShasums.js';
3
- export { fetchVerifiedNodeShasums };
2
+ import { readCachedBytes, readCachedShasums, RUNTIME_SHASUMS_DIR, writeCachedShasums } from './diskCache.js';
3
+ import { fetchVerifiedNodeShasums, fetchVerifiedNodeShasumsWithSignature, nodeShasumsSignatureVerifies, } from './verifyNodeShasums.js';
4
+ export { fetchVerifiedNodeShasums, RUNTIME_SHASUMS_DIR };
4
5
  export async function fetchShasumsFile(fetch, shasumsUrl) {
5
6
  return parseShasumsFile(await fetchShasumsFileRaw(fetch, shasumsUrl));
6
7
  }
@@ -13,6 +14,50 @@ export async function fetchShasumsFile(fetch, shasumsUrl) {
13
14
  export async function fetchVerifiedNodeShasumsFile(fetch, shasumsUrl) {
14
15
  return parseShasumsFile(await fetchVerifiedNodeShasums(fetch, shasumsUrl));
15
16
  }
17
+ /**
18
+ * Like {@link fetchVerifiedNodeShasumsFile}, backed by the disk cache when
19
+ * `opts.cacheDir` is given. The cache stores the body together with its
20
+ * detached signature, and a cache hit re-verifies that signature against the
21
+ * embedded release keys: the cache directory is project-configurable, so a
22
+ * pre-seeded entry must prove it is a genuine release body before it is
23
+ * served. Any verification failure is a miss and the pair is refetched.
24
+ * `shasumsUrl` must be version-pinned — a mutable URL must never be handed to
25
+ * the cache.
26
+ */
27
+ export async function fetchVerifiedNodeShasumsFileCached(fetch, shasumsUrl, opts) {
28
+ const cacheOpts = { cacheDir: opts?.cacheDir, trust: 'verified' };
29
+ const signatureUrl = `${shasumsUrl}.sig`;
30
+ const [cachedBody, cachedSignature] = await Promise.all([
31
+ readCachedShasums(shasumsUrl, cacheOpts),
32
+ readCachedBytes(signatureUrl, cacheOpts),
33
+ ]);
34
+ if (cachedBody != null && cachedSignature != null &&
35
+ await nodeShasumsSignatureVerifies(Buffer.from(cachedBody, 'utf8'), cachedSignature, opts?.trustedKeys)) {
36
+ return parseShasumsFile(cachedBody);
37
+ }
38
+ const { body, signature } = await fetchVerifiedNodeShasumsWithSignature(fetch, shasumsUrl, opts?.trustedKeys);
39
+ await Promise.all([
40
+ writeCachedShasums(shasumsUrl, body, cacheOpts),
41
+ writeCachedShasums(signatureUrl, signature, cacheOpts),
42
+ ]);
43
+ return parseShasumsFile(body);
44
+ }
45
+ /**
46
+ * Like {@link fetchShasumsFile}, backed by the disk cache when `opts.cacheDir`
47
+ * is given. For mirrors whose SHASUMS files carry no verifiable signature the
48
+ * cached body is trusted exactly as far as the TLS fetch that produced it.
49
+ * `shasumsUrl` must be version-pinned — a mutable URL must never be handed to
50
+ * the cache.
51
+ */
52
+ export async function fetchShasumsFileCached(fetch, shasumsUrl, opts) {
53
+ const cacheOpts = { cacheDir: opts?.cacheDir, trust: 'unverified' };
54
+ const cached = await readCachedShasums(shasumsUrl, cacheOpts);
55
+ if (cached != null)
56
+ return parseShasumsFile(cached);
57
+ const body = await fetchShasumsFileRaw(fetch, shasumsUrl);
58
+ await writeCachedShasums(shasumsUrl, body, cacheOpts);
59
+ return parseShasumsFile(body);
60
+ }
16
61
  export function parseShasumsFile(shasumsFileContent) {
17
62
  const lines = shasumsFileContent.split('\n');
18
63
  const items = [];
@@ -22,3 +22,19 @@ export interface ArmoredKey {
22
22
  * Throws when the signature is missing or does not verify against a trusted key.
23
23
  */
24
24
  export declare function fetchVerifiedNodeShasums(fetch: FetchFromRegistry, shasumsUrl: string, trustedKeys?: readonly ArmoredKey[]): Promise<string>;
25
+ /**
26
+ * {@link fetchVerifiedNodeShasums}, additionally returning the verified
27
+ * detached signature so the disk cache can persist it as the entry's
28
+ * verification evidence.
29
+ */
30
+ export declare function fetchVerifiedNodeShasumsWithSignature(fetch: FetchFromRegistry, shasumsUrl: string, trustedKeys?: readonly ArmoredKey[]): Promise<{
31
+ body: string;
32
+ signature: Uint8Array;
33
+ }>;
34
+ /**
35
+ * Whether `signatureBytes` is a valid detached signature of `content` under
36
+ * the trusted keys, reporting any unreadable input as `false` rather than
37
+ * throwing. This is the read-side check for persisted cache evidence, where
38
+ * any failure just means a cache miss.
39
+ */
40
+ export declare function nodeShasumsSignatureVerifies(content: Uint8Array, signatureBytes: Uint8Array, trustedKeys?: readonly ArmoredKey[]): Promise<boolean>;
@@ -34,6 +34,15 @@ async function readSigningKeyPackets(trustedKeys) {
34
34
  * Throws when the signature is missing or does not verify against a trusted key.
35
35
  */
36
36
  export async function fetchVerifiedNodeShasums(fetch, shasumsUrl, trustedKeys = NODE_RELEASE_KEYS) {
37
+ const { body } = await fetchVerifiedNodeShasumsWithSignature(fetch, shasumsUrl, trustedKeys);
38
+ return body;
39
+ }
40
+ /**
41
+ * {@link fetchVerifiedNodeShasums}, additionally returning the verified
42
+ * detached signature so the disk cache can persist it as the entry's
43
+ * verification evidence.
44
+ */
45
+ export async function fetchVerifiedNodeShasumsWithSignature(fetch, shasumsUrl, trustedKeys = NODE_RELEASE_KEYS) {
37
46
  const [shasumsBytes, signatureBytes] = await Promise.all([
38
47
  fetchBytes(fetch, shasumsUrl, 'SHASUMS256.txt'),
39
48
  fetchBytes(fetch, `${shasumsUrl}.sig`, 'SHASUMS256.txt.sig'),
@@ -42,7 +51,21 @@ export async function fetchVerifiedNodeShasums(fetch, shasumsUrl, trustedKeys =
42
51
  throw new PnpmError('NODE_SHASUMS_SIGNATURE_INVALID', `The OpenPGP signature of ${shasumsUrl} does not match any trusted Node.js release key. ` +
43
52
  'The downloaded Node.js runtime cannot be verified as a genuine release.');
44
53
  }
45
- return Buffer.from(shasumsBytes).toString('utf8');
54
+ return { body: Buffer.from(shasumsBytes).toString('utf8'), signature: signatureBytes };
55
+ }
56
+ /**
57
+ * Whether `signatureBytes` is a valid detached signature of `content` under
58
+ * the trusted keys, reporting any unreadable input as `false` rather than
59
+ * throwing. This is the read-side check for persisted cache evidence, where
60
+ * any failure just means a cache miss.
61
+ */
62
+ export async function nodeShasumsSignatureVerifies(content, signatureBytes, trustedKeys = NODE_RELEASE_KEYS) {
63
+ try {
64
+ return await isSignedByTrustedKey(content, signatureBytes, trustedKeys);
65
+ }
66
+ catch {
67
+ return false;
68
+ }
46
69
  }
47
70
  async function isSignedByTrustedKey(content, signatureBytes, trustedKeys) {
48
71
  let signature;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/crypto.shasums-file",
3
- "version": "1100.1.4",
3
+ "version": "1100.2.0",
4
4
  "description": "Utils for working with shasums files",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -30,14 +30,14 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@pnpm/crypto.hash": "1100.0.2",
33
- "@pnpm/error": "1100.1.0",
33
+ "@pnpm/error": "1100.1.2",
34
34
  "@pnpm/fetching.types": "1100.0.3",
35
35
  "openpgp": "^6.3.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@jest/globals": "30.4.1",
39
39
  "@openpgp/web-stream-tools": "0.3.1",
40
- "@pnpm/crypto.shasums-file": "1100.1.4"
40
+ "@pnpm/crypto.shasums-file": "1100.2.0"
41
41
  },
42
42
  "engines": {
43
43
  "node": ">=22.13"