@pnpm/installing.deps-installer 1101.1.2 → 1101.3.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/lib/install/extendInstallOptions.d.ts +52 -1
- package/lib/install/extendInstallOptions.js +1 -0
- package/lib/install/index.d.ts +14 -1
- package/lib/install/index.js +201 -21
- package/lib/install/link.js +7 -1
- package/lib/install/recordLockfileVerified.d.ts +22 -0
- package/lib/install/recordLockfileVerified.js +24 -0
- package/lib/install/verifyLockfileResolutions.d.ts +59 -0
- package/lib/install/verifyLockfileResolutions.js +225 -0
- package/lib/install/verifyLockfileResolutionsCache.d.ts +80 -0
- package/lib/install/verifyLockfileResolutionsCache.js +335 -0
- package/lib/install/writeLockfilesAndRecordVerified.d.ts +14 -0
- package/lib/install/writeLockfilesAndRecordVerified.js +32 -0
- package/lib/install/writeWantedLockfileAndRecordVerified.d.ts +12 -0
- package/lib/install/writeWantedLockfileAndRecordVerified.js +28 -0
- package/package.json +53 -53
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import util from 'node:util';
|
|
4
|
+
import { logger } from '@pnpm/logger';
|
|
5
|
+
/**
|
|
6
|
+
* On-disk cache of verifyLockfileResolutions results, keyed by lockfile
|
|
7
|
+
* content hash. Lets repeat installs against an unchanged lockfile skip
|
|
8
|
+
* the per-package registry round trips entirely — including across git
|
|
9
|
+
* worktrees, where the same lockfile content lives at different paths.
|
|
10
|
+
*
|
|
11
|
+
* Two indexes share the same JSONL records:
|
|
12
|
+
*
|
|
13
|
+
* - **by content hash** — the primary index. Recognizing the same
|
|
14
|
+
* lockfile content regardless of where it sits on disk is what makes
|
|
15
|
+
* worktrees and lockfile copies hit.
|
|
16
|
+
* - **by absolute path** — a same-machine stat shortcut. When we've
|
|
17
|
+
* seen this exact path before with these exact stat values, we
|
|
18
|
+
* trust the cached hash and skip reading the lockfile entirely
|
|
19
|
+
* (microseconds vs. ms-per-MB). Worktrees that get reinstalled in
|
|
20
|
+
* pay the hash cost once, then hit the stat fast path.
|
|
21
|
+
*
|
|
22
|
+
* All filesystem operations are synchronous: the cache is consulted
|
|
23
|
+
* once before verification fan-out and recorded once after — there's
|
|
24
|
+
* no concurrent install work to overlap with, so blocking the event
|
|
25
|
+
* loop for the brief read/stat/hash is fine and keeps the call sites
|
|
26
|
+
* straight-line.
|
|
27
|
+
*
|
|
28
|
+
* Persisted as JSON Lines: each verification appends one record;
|
|
29
|
+
* later records overwrite earlier ones on key collision when read.
|
|
30
|
+
* Appends of a single line are atomic on POSIX and NTFS, so parallel
|
|
31
|
+
* pnpm processes (monorepo installs, CI matrices sharing a cache) can
|
|
32
|
+
* write without coordination.
|
|
33
|
+
*
|
|
34
|
+
* Policy-neutral. Every active verifier's `policy` contribution merges
|
|
35
|
+
* into a single `policy` bag on the record; verifiers sharing a
|
|
36
|
+
* logical policy (same field name) share the slot — no resolver-level
|
|
37
|
+
* namespacing.
|
|
38
|
+
*/
|
|
39
|
+
const CACHE_FILE_NAME = 'lockfile-verified.jsonl';
|
|
40
|
+
// Cap the file before it grows large enough to slow down reads. When the
|
|
41
|
+
// cap is exceeded we rewrite the file keeping the N most recently
|
|
42
|
+
// verified entries. The number is generous — a developer machine that
|
|
43
|
+
// touches a thousand distinct (path, content) tuples is far past steady
|
|
44
|
+
// state.
|
|
45
|
+
const MAX_CACHE_ENTRIES = 1000;
|
|
46
|
+
// Records cluster around 250–400 bytes; budget 1 KiB per entry as a
|
|
47
|
+
// conservative upper bound. The compaction check uses `stat().size` to
|
|
48
|
+
// decide whether to read+rewrite, so we never parse the file unless it
|
|
49
|
+
// has actually grown past the cap.
|
|
50
|
+
const COMPACT_TRIGGER_BYTES = MAX_CACHE_ENTRIES * 1024 * 3 / 2;
|
|
51
|
+
/**
|
|
52
|
+
* Build two indexes over the JSONL records in one pass: by content
|
|
53
|
+
* hash (primary) and by absolute path (stat shortcut). Records are
|
|
54
|
+
* walked in file order so the last record for any key wins.
|
|
55
|
+
*/
|
|
56
|
+
function readCache(cacheDir) {
|
|
57
|
+
const cacheFilePath = path.join(cacheDir, CACHE_FILE_NAME);
|
|
58
|
+
let contents;
|
|
59
|
+
try {
|
|
60
|
+
contents = fs.readFileSync(cacheFilePath, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (isNodeError(err) && err.code === 'ENOENT')
|
|
64
|
+
return { byHash: new Map(), byPath: new Map() };
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
const byHash = new Map();
|
|
68
|
+
const byPath = new Map();
|
|
69
|
+
for (const line of contents.split('\n')) {
|
|
70
|
+
if (!line)
|
|
71
|
+
continue;
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(line);
|
|
74
|
+
const hash = parsed?.lockfile?.hash;
|
|
75
|
+
const lockfilePath = parsed?.lockfile?.path;
|
|
76
|
+
if (typeof hash !== 'string' || typeof lockfilePath !== 'string')
|
|
77
|
+
continue;
|
|
78
|
+
const record = normalizeRecord(parsed);
|
|
79
|
+
byHash.set(hash, record);
|
|
80
|
+
byPath.set(lockfilePath, record);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Skip malformed lines; the next clean append will still work.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { byHash, byPath };
|
|
87
|
+
}
|
|
88
|
+
function normalizeRecord(parsed) {
|
|
89
|
+
const lockfile = parsed.lockfile ?? {};
|
|
90
|
+
return {
|
|
91
|
+
lockfile: {
|
|
92
|
+
hash: lockfile.hash ?? '',
|
|
93
|
+
path: lockfile.path ?? '',
|
|
94
|
+
size: lockfile.size ?? -1,
|
|
95
|
+
mtimeNs: lockfile.mtimeNs ?? '',
|
|
96
|
+
inode: lockfile.inode ?? '',
|
|
97
|
+
},
|
|
98
|
+
verifiedAt: parsed.verifiedAt ?? '',
|
|
99
|
+
policy: parsed.policy && typeof parsed.policy === 'object' ? parsed.policy : {},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function statLockfile(lockfilePath) {
|
|
103
|
+
try {
|
|
104
|
+
const stat = fs.statSync(lockfilePath, { bigint: true });
|
|
105
|
+
return {
|
|
106
|
+
size: Number(stat.size),
|
|
107
|
+
mtimeNs: stat.mtimeNs.toString(),
|
|
108
|
+
inode: stat.ino.toString(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (isNodeError(err) && err.code === 'ENOENT')
|
|
113
|
+
return null;
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function statMatches(stat, lockfile) {
|
|
118
|
+
return stat.size === lockfile.size &&
|
|
119
|
+
stat.mtimeNs === lockfile.mtimeNs &&
|
|
120
|
+
stat.inode === lockfile.inode;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Try to confirm a cached verification covers the lockfile as it
|
|
124
|
+
* currently sits on disk and the policies currently in effect. Returns
|
|
125
|
+
* `{ hit: true }` to skip the gate; `{ hit: false }` means the caller
|
|
126
|
+
* should run the verifier and persist the result with
|
|
127
|
+
* {@link recordVerification}.
|
|
128
|
+
*
|
|
129
|
+
* Lookup order:
|
|
130
|
+
*
|
|
131
|
+
* 1. **Stat shortcut** — if we've previously verified this exact path
|
|
132
|
+
* with these exact stat values, trust the cached hash and skip
|
|
133
|
+
* reading the lockfile.
|
|
134
|
+
* 2. **Content lookup** — hash the lockfile and look up by hash.
|
|
135
|
+
* Catches the worktree case (same content, different path) and
|
|
136
|
+
* CI checkouts where stat fields got reset. Refreshes the
|
|
137
|
+
* stat-shortcut entry on hit so the next install at this path
|
|
138
|
+
* skips the hash.
|
|
139
|
+
*
|
|
140
|
+
* Every active verifier must agree the cached policy snapshot is still
|
|
141
|
+
* trustworthy under what it currently demands; if any rejects, the
|
|
142
|
+
* full gate runs.
|
|
143
|
+
*/
|
|
144
|
+
export function tryLockfileVerificationCache(cacheDir, key) {
|
|
145
|
+
let indexes;
|
|
146
|
+
try {
|
|
147
|
+
indexes = readCache(cacheDir);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
// A corrupt cache file should never block the install; fall
|
|
151
|
+
// through to verification so the gate still runs.
|
|
152
|
+
logger.debug({ msg: 'lockfile-verified cache: read failed', err });
|
|
153
|
+
return { hit: false, precomputed: {} };
|
|
154
|
+
}
|
|
155
|
+
const stat = statLockfile(key.lockfilePath);
|
|
156
|
+
if (!stat)
|
|
157
|
+
return { hit: false, precomputed: {} };
|
|
158
|
+
// Stat shortcut: same path + same stat means we trust the cached
|
|
159
|
+
// hash without reading the file. Microseconds.
|
|
160
|
+
const byPathRecord = indexes.byPath.get(key.lockfilePath);
|
|
161
|
+
if (byPathRecord && statMatches(stat, byPathRecord.lockfile)) {
|
|
162
|
+
return {
|
|
163
|
+
hit: everyVerifierTrustsCachedRun(byPathRecord, key.verifiers),
|
|
164
|
+
// The stat-match implies the file content is unchanged since the
|
|
165
|
+
// cached record was written, so its hash is still correct. Pass
|
|
166
|
+
// it through to skip hashing on the miss-then-record path.
|
|
167
|
+
precomputed: { stat, hash: byPathRecord.lockfile.hash },
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Content lookup: hash the in-memory lockfile, look up by content
|
|
171
|
+
// hash. Catches worktrees (same content, different path) and CI
|
|
172
|
+
// checkouts (same content, reset stat). On hit, refresh the
|
|
173
|
+
// path/stat entry so the next install at this path takes the stat
|
|
174
|
+
// shortcut above.
|
|
175
|
+
let hash;
|
|
176
|
+
try {
|
|
177
|
+
hash = key.hashLockfile();
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger.debug({ msg: 'lockfile-verified cache: lockfile hash failed', err });
|
|
181
|
+
return { hit: false, precomputed: { stat } };
|
|
182
|
+
}
|
|
183
|
+
const byHashRecord = indexes.byHash.get(hash);
|
|
184
|
+
if (!byHashRecord)
|
|
185
|
+
return { hit: false, precomputed: { stat, hash } };
|
|
186
|
+
if (!everyVerifierTrustsCachedRun(byHashRecord, key.verifiers)) {
|
|
187
|
+
return { hit: false, precomputed: { stat, hash } };
|
|
188
|
+
}
|
|
189
|
+
appendRecord(cacheDir, {
|
|
190
|
+
...byHashRecord,
|
|
191
|
+
lockfile: { ...byHashRecord.lockfile, path: key.lockfilePath, size: stat.size, mtimeNs: stat.mtimeNs, inode: stat.inode },
|
|
192
|
+
});
|
|
193
|
+
return { hit: true, precomputed: { stat, hash } };
|
|
194
|
+
}
|
|
195
|
+
function everyVerifierTrustsCachedRun(record, verifiers) {
|
|
196
|
+
for (const verifier of verifiers) {
|
|
197
|
+
if (!verifier.canTrustPastCheck(record.policy))
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
function mergePolicies(verifiers) {
|
|
203
|
+
// Later verifiers overwrite earlier ones on conflict — a shared field
|
|
204
|
+
// should carry the same value across verifiers by convention; mismatch
|
|
205
|
+
// is a config bug and we don't try to reconcile it here.
|
|
206
|
+
const merged = {};
|
|
207
|
+
for (const verifier of verifiers) {
|
|
208
|
+
Object.assign(merged, verifier.policy);
|
|
209
|
+
}
|
|
210
|
+
return merged;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Persist a successful verification. Called after the gate passes; the
|
|
214
|
+
* lockfile is hashed once and the resulting record is appended to the
|
|
215
|
+
* cache file. If the file is past {@link MAX_CACHE_ENTRIES}, it is
|
|
216
|
+
* rewritten keeping the most recent entries.
|
|
217
|
+
*
|
|
218
|
+
* Reuses `precomputed` values from a prior
|
|
219
|
+
* {@link tryLockfileVerificationCache} lookup so we don't re-stat or
|
|
220
|
+
* (especially) re-hash the lockfile a second time on the miss-then-
|
|
221
|
+
* record path.
|
|
222
|
+
*/
|
|
223
|
+
export function recordVerification(cacheDir, key, precomputed) {
|
|
224
|
+
let stat;
|
|
225
|
+
let hash;
|
|
226
|
+
try {
|
|
227
|
+
stat = precomputed?.stat ?? statLockfile(key.lockfilePath);
|
|
228
|
+
if (!stat)
|
|
229
|
+
return;
|
|
230
|
+
hash = precomputed?.hash ?? key.hashLockfile();
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
// The gate has already passed; if we can't record the cache entry we
|
|
234
|
+
// just won't get the speedup next time. Not a reason to fail install.
|
|
235
|
+
logger.debug({ msg: 'lockfile-verified cache: could not record verification', err });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const record = {
|
|
239
|
+
lockfile: {
|
|
240
|
+
hash,
|
|
241
|
+
path: key.lockfilePath,
|
|
242
|
+
size: stat.size,
|
|
243
|
+
mtimeNs: stat.mtimeNs,
|
|
244
|
+
inode: stat.inode,
|
|
245
|
+
},
|
|
246
|
+
verifiedAt: new Date().toISOString(),
|
|
247
|
+
policy: mergePolicies(key.verifiers),
|
|
248
|
+
};
|
|
249
|
+
appendRecord(cacheDir, record);
|
|
250
|
+
}
|
|
251
|
+
function appendRecord(cacheDir, record) {
|
|
252
|
+
const cacheFilePath = path.join(cacheDir, CACHE_FILE_NAME);
|
|
253
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
254
|
+
try {
|
|
255
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
256
|
+
fs.appendFileSync(cacheFilePath, line);
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
logger.debug({ msg: 'lockfile-verified cache: append failed', err });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
maybeCompactCache(cacheDir);
|
|
263
|
+
}
|
|
264
|
+
function maybeCompactCache(cacheDir) {
|
|
265
|
+
const cacheFilePath = path.join(cacheDir, CACHE_FILE_NAME);
|
|
266
|
+
// Decide whether to compact from the file size alone — avoids reading
|
|
267
|
+
// and parsing the file on every successful install. Records cluster
|
|
268
|
+
// around a few hundred bytes; the byte budget translates directly to
|
|
269
|
+
// the entry cap with generous slack so we don't trigger a rewrite on
|
|
270
|
+
// every append once we cross the line.
|
|
271
|
+
let size;
|
|
272
|
+
try {
|
|
273
|
+
size = fs.statSync(cacheFilePath).size;
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
if (isNodeError(err) && err.code === 'ENOENT')
|
|
277
|
+
return;
|
|
278
|
+
logger.debug({ msg: 'lockfile-verified cache: stat for compaction failed', err });
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (size <= COMPACT_TRIGGER_BYTES)
|
|
282
|
+
return;
|
|
283
|
+
let contents;
|
|
284
|
+
try {
|
|
285
|
+
contents = fs.readFileSync(cacheFilePath, 'utf8');
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
if (isNodeError(err) && err.code === 'ENOENT')
|
|
289
|
+
return;
|
|
290
|
+
logger.debug({ msg: 'lockfile-verified cache: read for compaction failed', err });
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const lines = contents.split('\n').filter(Boolean);
|
|
294
|
+
// Dedup by (path, hash) — that's the unit both indexes care about.
|
|
295
|
+
// Walking reverse keeps the newest record per tuple; we then trim to
|
|
296
|
+
// MAX_CACHE_ENTRIES and write back in original order.
|
|
297
|
+
const seen = new Set();
|
|
298
|
+
const reversed = [];
|
|
299
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
300
|
+
const line = lines[i];
|
|
301
|
+
try {
|
|
302
|
+
const parsed = JSON.parse(line);
|
|
303
|
+
const lockfilePath = parsed?.lockfile?.path;
|
|
304
|
+
const hash = parsed?.lockfile?.hash;
|
|
305
|
+
if (typeof lockfilePath !== 'string' || typeof hash !== 'string')
|
|
306
|
+
continue;
|
|
307
|
+
const tupleKey = `${lockfilePath}${hash}`;
|
|
308
|
+
if (seen.has(tupleKey))
|
|
309
|
+
continue;
|
|
310
|
+
seen.add(tupleKey);
|
|
311
|
+
reversed.push(line);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
// Skip malformed lines.
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
reversed.reverse();
|
|
318
|
+
const kept = reversed.slice(-MAX_CACHE_ENTRIES);
|
|
319
|
+
try {
|
|
320
|
+
// Write to a sibling tempfile + rename so a concurrent pnpm process
|
|
321
|
+
// can't observe a half-written file.
|
|
322
|
+
const tmpPath = `${cacheFilePath}.${process.pid}.tmp`;
|
|
323
|
+
fs.writeFileSync(tmpPath, kept.map((line) => `${line}\n`).join(''));
|
|
324
|
+
fs.renameSync(tmpPath, cacheFilePath);
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
logger.debug({ msg: 'lockfile-verified cache: compaction failed', err });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function isNodeError(err) {
|
|
331
|
+
// `instanceof Error` is unreliable across realms (Jest's VM context), so
|
|
332
|
+
// route through util.types.isNativeError per the repo guideline.
|
|
333
|
+
return util.types.isNativeError(err) && 'code' in err;
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=verifyLockfileResolutionsCache.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type LockfileObject, type WriteLockfilesResult } from '@pnpm/lockfile.fs';
|
|
2
|
+
import type { ResolutionVerifier } from '@pnpm/resolving.resolver-base';
|
|
3
|
+
export interface WriteLockfilesAndRecordVerifiedOptions {
|
|
4
|
+
wantedLockfile: LockfileObject;
|
|
5
|
+
wantedLockfileDir: string;
|
|
6
|
+
currentLockfile: LockfileObject;
|
|
7
|
+
currentLockfileDir: string;
|
|
8
|
+
useGitBranchLockfile?: boolean;
|
|
9
|
+
mergeGitBranchLockfiles?: boolean;
|
|
10
|
+
cacheDir?: string;
|
|
11
|
+
resolutionVerifiers: readonly ResolutionVerifier[] | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Plural counterpart of {@link writeWantedLockfileAndRecordVerified}. */
|
|
14
|
+
export declare function writeLockfilesAndRecordVerified(opts: WriteLockfilesAndRecordVerifiedOptions): Promise<WriteLockfilesResult>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getWantedLockfileName, writeLockfiles } from '@pnpm/lockfile.fs';
|
|
3
|
+
import { recordLockfileVerified } from './recordLockfileVerified.js';
|
|
4
|
+
/** Plural counterpart of {@link writeWantedLockfileAndRecordVerified}. */
|
|
5
|
+
export async function writeLockfilesAndRecordVerified(opts) {
|
|
6
|
+
const cacheActive = opts.cacheDir != null && (opts.resolutionVerifiers?.length ?? 0) > 0;
|
|
7
|
+
const wantedLockfileName = cacheActive
|
|
8
|
+
? await getWantedLockfileName({
|
|
9
|
+
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
10
|
+
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
11
|
+
})
|
|
12
|
+
: undefined;
|
|
13
|
+
const written = await writeLockfiles({
|
|
14
|
+
wantedLockfile: opts.wantedLockfile,
|
|
15
|
+
wantedLockfileDir: opts.wantedLockfileDir,
|
|
16
|
+
currentLockfile: opts.currentLockfile,
|
|
17
|
+
currentLockfileDir: opts.currentLockfileDir,
|
|
18
|
+
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
19
|
+
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
20
|
+
wantedLockfileName,
|
|
21
|
+
});
|
|
22
|
+
if (cacheActive) {
|
|
23
|
+
recordLockfileVerified({
|
|
24
|
+
cacheDir: opts.cacheDir,
|
|
25
|
+
lockfilePath: path.resolve(opts.wantedLockfileDir, wantedLockfileName),
|
|
26
|
+
lockfile: written.wantedLockfile,
|
|
27
|
+
resolutionVerifiers: opts.resolutionVerifiers,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return written;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=writeLockfilesAndRecordVerified.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type LockfileObject } from '@pnpm/lockfile.fs';
|
|
2
|
+
import type { ResolutionVerifier } from '@pnpm/resolving.resolver-base';
|
|
3
|
+
export interface WriteWantedLockfileAndRecordVerifiedOptions {
|
|
4
|
+
lockfileDir: string;
|
|
5
|
+
lockfile: LockfileObject;
|
|
6
|
+
cacheDir?: string;
|
|
7
|
+
resolutionVerifiers: readonly ResolutionVerifier[] | undefined;
|
|
8
|
+
useGitBranchLockfile?: boolean;
|
|
9
|
+
mergeGitBranchLockfiles?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Combines {@link writeWantedLockfile} and {@link recordLockfileVerified} — see each for semantics. */
|
|
12
|
+
export declare function writeWantedLockfileAndRecordVerified(opts: WriteWantedLockfileAndRecordVerifiedOptions): Promise<LockfileObject>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getWantedLockfileName, writeWantedLockfile } from '@pnpm/lockfile.fs';
|
|
3
|
+
import { recordLockfileVerified } from './recordLockfileVerified.js';
|
|
4
|
+
/** Combines {@link writeWantedLockfile} and {@link recordLockfileVerified} — see each for semantics. */
|
|
5
|
+
export async function writeWantedLockfileAndRecordVerified(opts) {
|
|
6
|
+
const cacheActive = opts.cacheDir != null && (opts.resolutionVerifiers?.length ?? 0) > 0;
|
|
7
|
+
const lockfileName = cacheActive
|
|
8
|
+
? await getWantedLockfileName({
|
|
9
|
+
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
10
|
+
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
11
|
+
})
|
|
12
|
+
: undefined;
|
|
13
|
+
const written = await writeWantedLockfile(opts.lockfileDir, opts.lockfile, {
|
|
14
|
+
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
15
|
+
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
16
|
+
lockfileName,
|
|
17
|
+
});
|
|
18
|
+
if (cacheActive) {
|
|
19
|
+
recordLockfileVerified({
|
|
20
|
+
cacheDir: opts.cacheDir,
|
|
21
|
+
lockfilePath: path.resolve(opts.lockfileDir, lockfileName),
|
|
22
|
+
lockfile: written,
|
|
23
|
+
resolutionVerifiers: opts.resolutionVerifiers,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return written;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=writeWantedLockfileAndRecordVerified.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/installing.deps-installer",
|
|
3
|
-
"version": "1101.
|
|
3
|
+
"version": "1101.3.0",
|
|
4
4
|
"description": "Fast, disk space efficient installation engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -62,60 +62,60 @@
|
|
|
62
62
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
63
63
|
"run-groups": "^5.0.0",
|
|
64
64
|
"semver": "^7.7.2",
|
|
65
|
-
"@pnpm/
|
|
66
|
-
"@pnpm/
|
|
67
|
-
"@pnpm/
|
|
68
|
-
"@pnpm/
|
|
69
|
-
"@pnpm/building.
|
|
70
|
-
"@pnpm/building.
|
|
65
|
+
"@pnpm/agent.client": "1.0.7",
|
|
66
|
+
"@pnpm/bins.linker": "1100.0.8",
|
|
67
|
+
"@pnpm/bins.remover": "1100.0.5",
|
|
68
|
+
"@pnpm/building.after-install": "1101.0.14",
|
|
69
|
+
"@pnpm/building.during-install": "1101.0.12",
|
|
70
|
+
"@pnpm/building.policy": "1100.0.6",
|
|
71
71
|
"@pnpm/catalogs.protocol-parser": "1100.0.0",
|
|
72
72
|
"@pnpm/catalogs.resolver": "1100.0.0",
|
|
73
|
-
"@pnpm/config.matcher": "1100.0.1",
|
|
74
73
|
"@pnpm/catalogs.types": "1100.0.0",
|
|
75
|
-
"@pnpm/config.
|
|
76
|
-
"@pnpm/
|
|
74
|
+
"@pnpm/config.matcher": "1100.0.1",
|
|
75
|
+
"@pnpm/config.normalize-registries": "1100.0.4",
|
|
77
76
|
"@pnpm/config.parse-overrides": "1100.0.1",
|
|
77
|
+
"@pnpm/constants": "1100.0.0",
|
|
78
|
+
"@pnpm/core-loggers": "1100.1.1",
|
|
78
79
|
"@pnpm/crypto.hash": "1100.0.1",
|
|
79
|
-
"@pnpm/
|
|
80
|
-
"@pnpm/
|
|
81
|
-
"@pnpm/deps.graph-hasher": "1100.1.5",
|
|
80
|
+
"@pnpm/deps.graph-hasher": "1100.2.1",
|
|
81
|
+
"@pnpm/deps.path": "1100.0.4",
|
|
82
82
|
"@pnpm/deps.graph-sequencer": "1100.0.0",
|
|
83
|
-
"@pnpm/deps.path": "1100.0.3",
|
|
84
83
|
"@pnpm/error": "1100.0.0",
|
|
85
|
-
"@pnpm/exec.lifecycle": "1100.0.
|
|
84
|
+
"@pnpm/exec.lifecycle": "1100.0.12",
|
|
86
85
|
"@pnpm/fs.read-modules-dir": "1100.0.1",
|
|
87
|
-
"@pnpm/fs.symlink-dependency": "1100.0.
|
|
88
|
-
"@pnpm/
|
|
89
|
-
"@pnpm/
|
|
90
|
-
"@pnpm/installing.deps-
|
|
91
|
-
"@pnpm/
|
|
92
|
-
"@pnpm/installing.
|
|
93
|
-
"@pnpm/installing.
|
|
94
|
-
"@pnpm/installing.linking.
|
|
95
|
-
"@pnpm/installing.linking.
|
|
96
|
-
"@pnpm/installing.
|
|
97
|
-
"@pnpm/installing.
|
|
98
|
-
"@pnpm/lockfile.filtering": "1100.1.
|
|
99
|
-
"@pnpm/
|
|
100
|
-
"@pnpm/lockfile.
|
|
101
|
-
"@pnpm/lockfile.
|
|
102
|
-
"@pnpm/lockfile.
|
|
103
|
-
"@pnpm/lockfile.
|
|
104
|
-
"@pnpm/lockfile.utils": "1100.0.
|
|
105
|
-
"@pnpm/lockfile.
|
|
106
|
-
"@pnpm/lockfile.
|
|
107
|
-
"@pnpm/
|
|
108
|
-
"@pnpm/
|
|
109
|
-
"@pnpm/
|
|
86
|
+
"@pnpm/fs.symlink-dependency": "1100.0.5",
|
|
87
|
+
"@pnpm/installing.context": "1100.0.12",
|
|
88
|
+
"@pnpm/crypto.object-hasher": "1100.0.0",
|
|
89
|
+
"@pnpm/installing.deps-restorer": "1101.1.4",
|
|
90
|
+
"@pnpm/hooks.types": "1100.0.8",
|
|
91
|
+
"@pnpm/installing.deps-resolver": "1100.1.1",
|
|
92
|
+
"@pnpm/installing.linking.direct-dep-linker": "1100.0.5",
|
|
93
|
+
"@pnpm/installing.linking.hoist": "1100.0.8",
|
|
94
|
+
"@pnpm/installing.linking.modules-cleaner": "1100.1.3",
|
|
95
|
+
"@pnpm/installing.package-requester": "1101.0.8",
|
|
96
|
+
"@pnpm/installing.modules-yaml": "1100.0.5",
|
|
97
|
+
"@pnpm/lockfile.filtering": "1100.1.2",
|
|
98
|
+
"@pnpm/hooks.read-package-hook": "1100.0.4",
|
|
99
|
+
"@pnpm/lockfile.fs": "1100.1.1",
|
|
100
|
+
"@pnpm/lockfile.preferred-versions": "1100.0.11",
|
|
101
|
+
"@pnpm/lockfile.pruner": "1100.0.7",
|
|
102
|
+
"@pnpm/lockfile.to-pnp": "1100.0.10",
|
|
103
|
+
"@pnpm/lockfile.utils": "1100.0.9",
|
|
104
|
+
"@pnpm/lockfile.walker": "1100.0.7",
|
|
105
|
+
"@pnpm/lockfile.settings-checker": "1100.0.12",
|
|
106
|
+
"@pnpm/lockfile.verification": "1100.0.12",
|
|
107
|
+
"@pnpm/patching.config": "1100.0.4",
|
|
108
|
+
"@pnpm/pkg-manifest.utils": "1100.2.0",
|
|
110
109
|
"@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
|
|
111
|
-
"@pnpm/
|
|
110
|
+
"@pnpm/resolving.resolver-base": "1100.3.0",
|
|
111
|
+
"@pnpm/store.controller-types": "1100.1.1",
|
|
112
112
|
"@pnpm/store.index": "1100.1.0",
|
|
113
|
-
"@pnpm/
|
|
114
|
-
"@pnpm/workspace.project-manifest-reader": "1100.0.
|
|
113
|
+
"@pnpm/types": "1101.1.1",
|
|
114
|
+
"@pnpm/workspace.project-manifest-reader": "1100.0.7"
|
|
115
115
|
},
|
|
116
116
|
"peerDependencies": {
|
|
117
117
|
"@pnpm/logger": ">=1001.0.0 <1002.0.0",
|
|
118
|
-
"@pnpm/worker": "^1100.1.
|
|
118
|
+
"@pnpm/worker": "^1100.1.7"
|
|
119
119
|
},
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@jest/globals": "30.3.0",
|
|
@@ -138,21 +138,21 @@
|
|
|
138
138
|
"symlink-dir": "^10.0.1",
|
|
139
139
|
"write-json-file": "^7.0.0",
|
|
140
140
|
"write-yaml-file": "^6.0.0",
|
|
141
|
-
"@pnpm/assert-project": "1100.0.
|
|
142
|
-
"@pnpm/
|
|
143
|
-
"@pnpm/
|
|
144
|
-
"@pnpm/lockfile.types": "1100.0.
|
|
145
|
-
"@pnpm/network.git-utils": "1100.0.1",
|
|
141
|
+
"@pnpm/assert-project": "1100.0.10",
|
|
142
|
+
"@pnpm/assert-store": "1100.0.10",
|
|
143
|
+
"@pnpm/installing.deps-installer": "1101.3.0",
|
|
144
|
+
"@pnpm/lockfile.types": "1100.0.7",
|
|
146
145
|
"@pnpm/logger": "1100.0.0",
|
|
147
|
-
"@pnpm/
|
|
148
|
-
"@pnpm/pkg-manifest.reader": "1100.0.
|
|
149
|
-
"@pnpm/
|
|
150
|
-
"@pnpm/
|
|
146
|
+
"@pnpm/network.git-utils": "1100.0.1",
|
|
147
|
+
"@pnpm/pkg-manifest.reader": "1100.0.4",
|
|
148
|
+
"@pnpm/prepare": "1100.0.10",
|
|
149
|
+
"@pnpm/resolving.registry.types": "1100.0.4",
|
|
151
150
|
"@pnpm/store.path": "1100.0.1",
|
|
151
|
+
"@pnpm/store.cafs": "1100.1.6",
|
|
152
152
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
153
153
|
"@pnpm/test-ipc-server": "1100.0.0",
|
|
154
|
-
"@pnpm/testing.mock-agent": "1100.0.
|
|
155
|
-
"@pnpm/testing.temp-store": "1100.
|
|
154
|
+
"@pnpm/testing.mock-agent": "1100.0.6",
|
|
155
|
+
"@pnpm/testing.temp-store": "1100.1.1"
|
|
156
156
|
},
|
|
157
157
|
"engines": {
|
|
158
158
|
"node": ">=22.13"
|