@ttsc/metro 0.19.0 → 0.19.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.
package/README.md CHANGED
@@ -74,10 +74,19 @@ For each TypeScript file Metro asks to transform:
74
74
 
75
75
  The plugin contract, `tsconfig` discovery, and per-build cache are identical to every other `ttsc` bundler integration.
76
76
 
77
+ ## Cache invalidation
78
+
79
+ Metro keys its transform cache on each file's own content plus one static transformer key, and its babel-transformer contract has no per-file dependency registration. A `ttsc` transform can depend on a _type_ in another file, so `@ttsc/metro` folds a project fingerprint into that static key: every input file under the project root, plus the reference-graph inputs outside it (`node_modules` declarations, monorepo sibling sources, out-of-root tsconfig `extends` ancestry) recorded under `node_modules/.cache/ttsc-metro`. Editing any of them re-keys the next run, so `metro bundle` and dev-server starts pick up cross-file type changes without `--reset-cache`.
80
+
81
+ The granularity is project-level by necessity: Metro evaluates the transformer key once per run, so any fingerprinted change re-transforms every file on the next run. What remains outside the mechanism's reach:
82
+
83
+ - **Within a running dev server**, Metro re-transforms only files its watcher reports changed. Editing a type in file B updates a dependent file A on A's next transform: save A, or restart the dev server (no `--reset-cache` needed).
84
+ - **Files a plugin declares `volatile`** depend on non-file inputs that no fingerprint can represent; while a volatile declaration is recorded, cross-run cache reuse is disabled entirely.
85
+ - **If `node_modules/.cache` is unwritable**, the recorded input set is unknown, and cross-run cache reuse is disabled rather than made unsound.
86
+
77
87
  ## Caveats (v1)
78
88
 
79
89
  - **Cost model.** This release reuses `@ttsc/unplugin`'s transform core, which type-checks the whole `tsconfig` project and caches the result per process. Metro runs transforms in a multi-process worker pool, so the project is compiled once per worker (on that worker's first file). A resident, incremental, per-file compiler shared across workers is the planned optimization, tracked in [samchon/ttsc#255](https://github.com/samchon/ttsc/issues/255).
80
- - **Cache invalidation.** Metro keys its transform cache on per-file content plus a static transformer key. A `ttsc` transform can depend on a _type_ in another file; editing that type does not change the dependent file's content, so Metro may serve a stale transform. After changing `tsconfig`/plugin configuration or a depended-upon type, restart Metro with `--reset-cache`.
81
90
  - **Type errors fail the build.** The `ttsc` pass type-checks; a project type error surfaces as a Metro build error, matching the other `ttsc` bundler integrations.
82
91
 
83
92
  ## Sponsors
@@ -0,0 +1,81 @@
1
+ /** Union of the snapshot state readable on disk. */
2
+ interface SnapshotState {
3
+ /** Random epoch id minted when the main snapshot was created. */
4
+ id: string;
5
+ /** Absolute paths of every recorded out-of-walk input. */
6
+ files: string[];
7
+ /** Whether any recorded transform declared volatile output. */
8
+ volatile: boolean;
9
+ }
10
+ /**
11
+ * Resolve the base directory both fingerprint sides agree on: Metro's
12
+ * `projectRoot` when known (`withTtsc` reads it from the config, `getCacheKey`
13
+ * from Metro's cache-key options, the transformer from each file's transform
14
+ * options — all the same value in a real Metro run), else the working directory
15
+ * Metro was launched from.
16
+ */
17
+ export declare function resolveFingerprintBase(projectRoot: string | undefined): string;
18
+ /**
19
+ * The directories whose walk universes the fingerprint hashes: the base
20
+ * directory, plus the resolved tsconfig's directory when the tsconfig is not
21
+ * already inside the base walk (an explicit out-of-root `project`, or a
22
+ * monorepo-root tsconfig discovered above the app). Matching the transform
23
+ * core's own validation universe keeps the invariant simple: everything the
24
+ * core treats as an input is fingerprinted, either by a walk here or by the
25
+ * recorded out-of-walk snapshot.
26
+ */
27
+ export declare function fingerprintRoots(base: string, explicitProject: string | undefined): string[];
28
+ /**
29
+ * Compute the fingerprint `getCacheKey` folds into Metro's static transformer
30
+ * key. Never throws: any failure degrades to a nonce, which soundly disables
31
+ * cross-run cache reuse for this run instead of serving stale output.
32
+ */
33
+ export declare function computeProjectFingerprint(props: {
34
+ explicitProject?: string;
35
+ projectRoot?: string;
36
+ }): string;
37
+ /**
38
+ * Prepare the snapshot for a new run. Called from `withTtsc` in the single
39
+ * Metro config process, before any worker exists: creates the main snapshot
40
+ * (fresh epoch id) when missing or corrupt, compacts leftover worker files into
41
+ * it, and sweeps unparseable worker files plus crash-leftover temp files. An
42
+ * unparseable worker file's recordings are unrecoverable, so its removal mints
43
+ * a fresh epoch id — every key that might have depended on the lost recordings
44
+ * is soundly orphaned, and later runs stabilize instead of degrading to a nonce
45
+ * forever. Never throws — an unwritable cache directory leaves the snapshot
46
+ * unreadable and `getCacheKey` degrades to a nonce.
47
+ */
48
+ export declare function prepareSnapshot(projectRoot: string | undefined): void;
49
+ /**
50
+ * Read the unioned snapshot state, or `undefined` when the main snapshot is
51
+ * missing or any snapshot file is corrupt (a torn or foreign write means the
52
+ * recorded set cannot be trusted, so the caller degrades to a nonce).
53
+ */
54
+ export declare function readSnapshotState(base: string): SnapshotState | undefined;
55
+ /**
56
+ * Recorder held by each Metro worker. Collects the out-of-walk inputs derived
57
+ * for every transformed file (the plugin-reported dependencies unioned with the
58
+ * reference graph's reach, globals, and configs — delivered through the
59
+ * transform core's `addWatchFile` hook) plus any volatile declaration, and
60
+ * persists them to this worker's uniquely named snapshot file whenever the
61
+ * observed state grows. The unique name makes worker writes race-free;
62
+ * `withTtsc` compacts the files on the next run.
63
+ */
64
+ export declare function createSnapshotRecorder(): {
65
+ record: (props: {
66
+ explicitProject?: string;
67
+ input: string;
68
+ projectRoot?: string;
69
+ }) => void;
70
+ recordVolatile: (props: {
71
+ explicitProject?: string;
72
+ projectRoot?: string;
73
+ }) => void;
74
+ };
75
+ /**
76
+ * JSON-serialise with object keys sorted recursively, so two semantically equal
77
+ * records always hash to the same fingerprint regardless of property order.
78
+ * Shared with the transformer's option digest.
79
+ */
80
+ export declare function stableStringify(value: unknown): string;
81
+ export {};
@@ -0,0 +1,449 @@
1
+ 'use strict';
2
+
3
+ var api = require('@ttsc/unplugin/api');
4
+ var node_crypto = require('node:crypto');
5
+ var fs = require('node:fs');
6
+ var path = require('node:path');
7
+
8
+ /**
9
+ * Project fingerprint and reference-graph snapshot for `@ttsc/metro`.
10
+ *
11
+ * Metro's transform cache keys each file on its own content plus one static
12
+ * transformer key computed once per run (`getCacheKey`, called on the main
13
+ * process at `Transformer` construction). A ttsc transform's output can depend
14
+ * on inputs Metro never keys: other project sources reached through type-only
15
+ * edges, `node_modules` declarations, monorepo sibling sources, and the
16
+ * tsconfig `extends` ancestry. This module folds all of them into the static
17
+ * key so the cache key incorporates every input that can influence a
18
+ * transform's output:
19
+ *
20
+ * - **Project walk.** Every input file under the fingerprint roots (Metro's
21
+ * `projectRoot` plus the resolved tsconfig's directory when it lies outside),
22
+ * hashed with the exact walk universe the `@ttsc/unplugin` transform core
23
+ * validates its own cache against.
24
+ * - **Recorded out-of-walk inputs.** The transform core cannot walk files outside
25
+ * the roots or under ignored directories, but the host-owned reference graph
26
+ * (samchon/ttsc#718) reports them per transform. Workers record them into a
27
+ * snapshot under `node_modules/.cache/ttsc-metro`; the next run's
28
+ * `getCacheKey` re-hashes the recorded set.
29
+ *
30
+ * Snapshot layout: one main file carrying a random epoch id plus per-worker
31
+ * files with unique names, so concurrent workers never race a shared write.
32
+ * `withTtsc` (the single config process, before workers exist) compacts worker
33
+ * files into the main file. Readers take the union of every file, reading the
34
+ * worker files strictly before the main file: the compactor renames the merged
35
+ * main into place strictly before deleting a worker file, so a worker file that
36
+ * disappears mid-read is always already merged into the main the reader loads
37
+ * afterwards.
38
+ *
39
+ * Sound degradations, by design:
40
+ *
41
+ * - No readable snapshot (first run, wiped cache dir, unwritable filesystem)
42
+ * folds a random nonce: that run shares no cache entries with any other run.
43
+ * - A recreated snapshot carries a fresh epoch id, so it can never alias a key
44
+ * from an older epoch whose recorded set is unknown.
45
+ * - A plugin-declared volatile output (non-file inputs; unrepresentable in any
46
+ * file fingerprint) marks the snapshot volatile, which also folds a nonce
47
+ * until a later run records the volatile declaration gone.
48
+ * - A recorded input that disappears hashes as a stable `missing` marker, so
49
+ * deletion and reappearance both move the key.
50
+ */
51
+ /** Bumped when the snapshot JSON shape changes; mismatches read as corrupt. */
52
+ const SNAPSHOT_VERSION = 1;
53
+ /** Snapshot directory segments under the fingerprint base directory. */
54
+ const SNAPSHOT_DIRECTORY = ["node_modules", ".cache", "ttsc-metro"];
55
+ /** Main snapshot file name (epoch id + compacted recorded inputs). */
56
+ const MAIN_SNAPSHOT = "graph-inputs.json";
57
+ /** Worker snapshot file prefix; each worker appends a unique suffix. */
58
+ const WORKER_SNAPSHOT_PREFIX = "graph-inputs.worker-";
59
+ /**
60
+ * Resolve the base directory both fingerprint sides agree on: Metro's
61
+ * `projectRoot` when known (`withTtsc` reads it from the config, `getCacheKey`
62
+ * from Metro's cache-key options, the transformer from each file's transform
63
+ * options — all the same value in a real Metro run), else the working directory
64
+ * Metro was launched from.
65
+ */
66
+ function resolveFingerprintBase(projectRoot) {
67
+ return path.resolve(typeof projectRoot === "string" && projectRoot.length !== 0
68
+ ? projectRoot
69
+ : process.cwd());
70
+ }
71
+ /**
72
+ * The directories whose walk universes the fingerprint hashes: the base
73
+ * directory, plus the resolved tsconfig's directory when the tsconfig is not
74
+ * already inside the base walk (an explicit out-of-root `project`, or a
75
+ * monorepo-root tsconfig discovered above the app). Matching the transform
76
+ * core's own validation universe keeps the invariant simple: everything the
77
+ * core treats as an input is fingerprinted, either by a walk here or by the
78
+ * recorded out-of-walk snapshot.
79
+ */
80
+ function fingerprintRoots(base, explicitProject) {
81
+ const tsconfig = resolveProjectTsconfig(base, explicitProject);
82
+ if (api.isProjectWalkPath(base, tsconfig)) {
83
+ return [base];
84
+ }
85
+ return [base, path.dirname(tsconfig)];
86
+ }
87
+ /**
88
+ * Locate the tsconfig governing the project, mirroring the transform core's
89
+ * discovery: an explicit `project` resolves against the working directory;
90
+ * otherwise ancestor directories starting at `base` are searched for a
91
+ * `tsconfig.json`, falling back to `<base>/tsconfig.json`.
92
+ */
93
+ function resolveProjectTsconfig(base, explicitProject) {
94
+ if (explicitProject !== undefined && explicitProject.length !== 0) {
95
+ return path.isAbsolute(explicitProject)
96
+ ? explicitProject
97
+ : path.resolve(process.cwd(), explicitProject);
98
+ }
99
+ let current = base;
100
+ while (true) {
101
+ const candidate = path.join(current, "tsconfig.json");
102
+ if (fs.existsSync(candidate)) {
103
+ return candidate;
104
+ }
105
+ const parent = path.dirname(current);
106
+ if (parent === current) {
107
+ break;
108
+ }
109
+ current = parent;
110
+ }
111
+ return path.resolve(base, "tsconfig.json");
112
+ }
113
+ /**
114
+ * Compute the fingerprint `getCacheKey` folds into Metro's static transformer
115
+ * key. Never throws: any failure degrades to a nonce, which soundly disables
116
+ * cross-run cache reuse for this run instead of serving stale output.
117
+ */
118
+ function computeProjectFingerprint(props) {
119
+ try {
120
+ const base = resolveFingerprintBase(props.projectRoot);
121
+ const hash = node_crypto.createHash("sha256");
122
+ for (const root of fingerprintRoots(base, props.explicitProject)) {
123
+ hash.update(stableStringify(api.collectProjectInputHashes(root)));
124
+ }
125
+ const snapshot = readSnapshotState(base);
126
+ if (snapshot === undefined || snapshot.volatile) {
127
+ hash.update(nonce());
128
+ }
129
+ else {
130
+ hash.update(`snapshot:${snapshot.id}`);
131
+ hash.update(stableStringify(api.collectExternalInputHashes(snapshot.files)));
132
+ }
133
+ return hash.digest("hex");
134
+ }
135
+ catch {
136
+ return nonce();
137
+ }
138
+ }
139
+ /**
140
+ * A value no other run can reproduce. Folding it means this run's cache entries
141
+ * are written but never reused by later runs, and this run reuses nothing from
142
+ * earlier ones — the sound fallback whenever the recorded out-of-walk input set
143
+ * is unknown or unrepresentable.
144
+ */
145
+ function nonce() {
146
+ return `nonce:${node_crypto.randomBytes(32).toString("hex")}`;
147
+ }
148
+ /**
149
+ * Prepare the snapshot for a new run. Called from `withTtsc` in the single
150
+ * Metro config process, before any worker exists: creates the main snapshot
151
+ * (fresh epoch id) when missing or corrupt, compacts leftover worker files into
152
+ * it, and sweeps unparseable worker files plus crash-leftover temp files. An
153
+ * unparseable worker file's recordings are unrecoverable, so its removal mints
154
+ * a fresh epoch id — every key that might have depended on the lost recordings
155
+ * is soundly orphaned, and later runs stabilize instead of degrading to a nonce
156
+ * forever. Never throws — an unwritable cache directory leaves the snapshot
157
+ * unreadable and `getCacheKey` degrades to a nonce.
158
+ */
159
+ function prepareSnapshot(projectRoot) {
160
+ try {
161
+ const base = resolveFingerprintBase(projectRoot);
162
+ // A nonexistent base can never be a working Metro setup (Metro verifies
163
+ // the project root exists), so preparing a snapshot there would only
164
+ // materialize directory trees at arbitrary paths.
165
+ if (!fs.existsSync(base)) {
166
+ return;
167
+ }
168
+ const directory = snapshotDirectory(base);
169
+ fs.mkdirSync(directory, { recursive: true });
170
+ // Read the worker files strictly before the main file (see the module doc
171
+ // comment): a concurrent compactor deletes a worker file only after the
172
+ // merged main is renamed into place, so whatever this enumeration misses
173
+ // is already inside the main read below.
174
+ const workers = readWorkerFiles(directory);
175
+ const main = readMainDocument(directory);
176
+ const files = new Set(main?.files ?? []);
177
+ const volatile = workers.entries.length === 0
178
+ ? (main?.volatile ?? false)
179
+ : // Worker files carry the previous run's fresh observations, so they
180
+ // own the volatile verdict: a removed volatile declaration must be
181
+ // able to clear the sticky flag.
182
+ workers.entries.some((entry) => entry.volatile);
183
+ for (const entry of workers.entries) {
184
+ for (const file of entry.files) {
185
+ files.add(file);
186
+ }
187
+ }
188
+ writeSnapshotDocument(path.join(directory, MAIN_SNAPSHOT), {
189
+ files: [...files].sort(),
190
+ id: workers.corruptPaths.length === 0
191
+ ? (main?.id ?? node_crypto.randomBytes(16).toString("hex"))
192
+ : node_crypto.randomBytes(16).toString("hex"),
193
+ version: SNAPSHOT_VERSION,
194
+ volatile,
195
+ });
196
+ for (const file of [
197
+ ...workers.paths,
198
+ ...workers.corruptPaths,
199
+ ...listTemporaryFiles(directory),
200
+ ]) {
201
+ try {
202
+ fs.rmSync(file, { force: true });
203
+ }
204
+ catch {
205
+ // A locked worker file stays behind; readers union it, so nothing is
206
+ // lost, and the next compaction retries.
207
+ }
208
+ }
209
+ }
210
+ catch {
211
+ // Snapshot maintenance must never break the Metro config process.
212
+ }
213
+ }
214
+ /**
215
+ * Crash-leftover temp files from the atomic writer, swept at compaction. Only
216
+ * files older than a day qualify: a young temp file may belong to a live writer
217
+ * in a concurrently running Metro instance, and deleting it mid-write would
218
+ * silently drop that writer's recordings.
219
+ */
220
+ function listTemporaryFiles(directory) {
221
+ const horizon = Date.now() - 24 * 60 * 60 * 1000;
222
+ try {
223
+ return fs
224
+ .readdirSync(directory)
225
+ .filter((name) => name.endsWith(".tmp"))
226
+ .map((name) => path.join(directory, name))
227
+ .filter((file) => {
228
+ try {
229
+ return fs.statSync(file).mtimeMs < horizon;
230
+ }
231
+ catch {
232
+ return false;
233
+ }
234
+ });
235
+ }
236
+ catch {
237
+ return [];
238
+ }
239
+ }
240
+ /**
241
+ * Read the unioned snapshot state, or `undefined` when the main snapshot is
242
+ * missing or any snapshot file is corrupt (a torn or foreign write means the
243
+ * recorded set cannot be trusted, so the caller degrades to a nonce).
244
+ */
245
+ function readSnapshotState(base) {
246
+ const directory = snapshotDirectory(base);
247
+ // Worker files strictly before the main file — see the module doc comment.
248
+ const workers = readWorkerFiles(directory);
249
+ if (workers.corruptPaths.length !== 0) {
250
+ return undefined;
251
+ }
252
+ const main = readMainDocument(directory);
253
+ if (main === undefined || typeof main.id !== "string") {
254
+ return undefined;
255
+ }
256
+ const files = new Set(main.files);
257
+ let volatile = main.volatile;
258
+ for (const entry of workers.entries) {
259
+ for (const file of entry.files) {
260
+ files.add(file);
261
+ }
262
+ volatile ||= entry.volatile;
263
+ }
264
+ return { files: [...files].sort(), id: main.id, volatile };
265
+ }
266
+ /**
267
+ * Recorder held by each Metro worker. Collects the out-of-walk inputs derived
268
+ * for every transformed file (the plugin-reported dependencies unioned with the
269
+ * reference graph's reach, globals, and configs — delivered through the
270
+ * transform core's `addWatchFile` hook) plus any volatile declaration, and
271
+ * persists them to this worker's uniquely named snapshot file whenever the
272
+ * observed state grows. The unique name makes worker writes race-free;
273
+ * `withTtsc` compacts the files on the next run.
274
+ */
275
+ function createSnapshotRecorder() {
276
+ const suffix = `${process.pid.toString(36)}-${node_crypto.randomBytes(6).toString("hex")}`;
277
+ const states = new Map();
278
+ function stateFor(projectRoot, explicitProject) {
279
+ const base = resolveFingerprintBase(projectRoot);
280
+ let state = states.get(base);
281
+ if (state === undefined) {
282
+ state = {
283
+ dirty: false,
284
+ files: new Set(),
285
+ roots: fingerprintRoots(base, explicitProject),
286
+ volatile: false,
287
+ };
288
+ states.set(base, state);
289
+ }
290
+ return state;
291
+ }
292
+ function flush(base, state) {
293
+ if (!state.dirty) {
294
+ return;
295
+ }
296
+ try {
297
+ const directory = snapshotDirectory(base);
298
+ fs.mkdirSync(directory, { recursive: true });
299
+ writeSnapshotDocument(path.join(directory, `${WORKER_SNAPSHOT_PREFIX}${suffix}.json`), {
300
+ files: [...state.files].sort(),
301
+ version: SNAPSHOT_VERSION,
302
+ volatile: state.volatile,
303
+ });
304
+ // Cleared only on success so a transient write failure retries on the
305
+ // next recording instead of silently dropping the observed state.
306
+ state.dirty = false;
307
+ }
308
+ catch {
309
+ // An unwritable snapshot leaves the main snapshot unreadable or stale;
310
+ // getCacheKey degrades to a nonce rather than serving stale output.
311
+ }
312
+ }
313
+ return {
314
+ record(props) {
315
+ const base = resolveFingerprintBase(props.projectRoot);
316
+ const state = stateFor(props.projectRoot, props.explicitProject);
317
+ const input = path.resolve(props.input);
318
+ if (state.files.has(input) ||
319
+ state.roots.some((root) => api.isProjectWalkPath(root, input))) {
320
+ return;
321
+ }
322
+ state.files.add(input);
323
+ state.dirty = true;
324
+ flush(base, state);
325
+ },
326
+ recordVolatile(props) {
327
+ const base = resolveFingerprintBase(props.projectRoot);
328
+ const state = stateFor(props.projectRoot, props.explicitProject);
329
+ if (state.volatile) {
330
+ return;
331
+ }
332
+ state.volatile = true;
333
+ state.dirty = true;
334
+ flush(base, state);
335
+ },
336
+ };
337
+ }
338
+ function snapshotDirectory(base) {
339
+ return path.join(base, ...SNAPSHOT_DIRECTORY);
340
+ }
341
+ /**
342
+ * Read every worker snapshot file in `directory`. A file that disappears
343
+ * mid-read was compacted (merged into the main snapshot first) and is skipped;
344
+ * a file that exists but does not parse is reported in `corruptPaths` so
345
+ * readers can degrade to a nonce and the compactor can sweep it.
346
+ */
347
+ function readWorkerFiles(directory) {
348
+ let names;
349
+ try {
350
+ names = fs.readdirSync(directory);
351
+ }
352
+ catch {
353
+ return { corruptPaths: [], entries: [], paths: [] };
354
+ }
355
+ const entries = [];
356
+ const paths = [];
357
+ const corruptPaths = [];
358
+ for (const name of names) {
359
+ if (!name.startsWith(WORKER_SNAPSHOT_PREFIX) || !name.endsWith(".json")) {
360
+ continue;
361
+ }
362
+ const file = path.join(directory, name);
363
+ let text;
364
+ try {
365
+ text = fs.readFileSync(file, "utf8");
366
+ }
367
+ catch {
368
+ continue;
369
+ }
370
+ const parsed = parseSnapshotDocument(text);
371
+ if (parsed === undefined) {
372
+ corruptPaths.push(file);
373
+ continue;
374
+ }
375
+ entries.push(parsed);
376
+ paths.push(file);
377
+ }
378
+ return { corruptPaths, entries, paths };
379
+ }
380
+ function readMainDocument(directory) {
381
+ let text;
382
+ try {
383
+ text = fs.readFileSync(path.join(directory, MAIN_SNAPSHOT), "utf8");
384
+ }
385
+ catch {
386
+ return undefined;
387
+ }
388
+ return parseSnapshotDocument(text);
389
+ }
390
+ function parseSnapshotDocument(text) {
391
+ let value;
392
+ try {
393
+ value = JSON.parse(text);
394
+ }
395
+ catch {
396
+ return undefined;
397
+ }
398
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
399
+ return undefined;
400
+ }
401
+ const document = value;
402
+ if (document.version !== SNAPSHOT_VERSION || !Array.isArray(document.files)) {
403
+ return undefined;
404
+ }
405
+ return {
406
+ files: document.files.filter((entry) => typeof entry === "string"),
407
+ ...(typeof document.id === "string" ? { id: document.id } : {}),
408
+ version: SNAPSHOT_VERSION,
409
+ volatile: document.volatile === true,
410
+ };
411
+ }
412
+ /** Write a snapshot document atomically (unique temp file, then rename). */
413
+ function writeSnapshotDocument(file, document) {
414
+ const temp = `${file}.${node_crypto.randomBytes(6).toString("hex")}.tmp`;
415
+ fs.writeFileSync(temp, JSON.stringify(document), "utf8");
416
+ try {
417
+ fs.renameSync(temp, file);
418
+ }
419
+ catch (error) {
420
+ fs.rmSync(temp, { force: true });
421
+ throw error;
422
+ }
423
+ }
424
+ /**
425
+ * JSON-serialise with object keys sorted recursively, so two semantically equal
426
+ * records always hash to the same fingerprint regardless of property order.
427
+ * Shared with the transformer's option digest.
428
+ */
429
+ function stableStringify(value) {
430
+ if (Array.isArray(value)) {
431
+ return `[${value.map(stableStringify).join(",")}]`;
432
+ }
433
+ if (value !== null && typeof value === "object") {
434
+ return `{${Object.entries(value)
435
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
436
+ .map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`)
437
+ .join(",")}}`;
438
+ }
439
+ return JSON.stringify(value);
440
+ }
441
+
442
+ exports.computeProjectFingerprint = computeProjectFingerprint;
443
+ exports.createSnapshotRecorder = createSnapshotRecorder;
444
+ exports.fingerprintRoots = fingerprintRoots;
445
+ exports.prepareSnapshot = prepareSnapshot;
446
+ exports.readSnapshotState = readSnapshotState;
447
+ exports.resolveFingerprintBase = resolveFingerprintBase;
448
+ exports.stableStringify = stableStringify;
449
+ //# sourceMappingURL=fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.js","sources":["../../src/core/fingerprint.ts"],"sourcesContent":[null],"names":["isProjectWalkPath","createHash","collectProjectInputHashes","collectExternalInputHashes","randomBytes"],"mappings":"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"}