@ttsc/metro 0.30.4 → 0.31.1
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 +11 -6
- package/lib/core/SnapshotReadOperations.d.ts +119 -0
- package/lib/core/SnapshotReadOperations.js +3 -0
- package/lib/core/SnapshotReadOperations.js.map +1 -0
- package/lib/core/SnapshotReadOperations.mjs +2 -0
- package/lib/core/SnapshotReadOperations.mjs.map +1 -0
- package/lib/core/TtscMetroOptions.d.ts +119 -0
- package/lib/core/TtscMetroOptions.js +3 -0
- package/lib/core/TtscMetroOptions.js.map +1 -0
- package/lib/core/TtscMetroOptions.mjs +2 -0
- package/lib/core/TtscMetroOptions.mjs.map +1 -0
- package/lib/core/TtscMetroProjectView.d.ts +64 -0
- package/lib/core/TtscMetroProjectView.js +3 -0
- package/lib/core/TtscMetroProjectView.js.map +1 -0
- package/lib/core/TtscMetroProjectView.mjs +2 -0
- package/lib/core/TtscMetroProjectView.mjs.map +1 -0
- package/lib/core/UpstreamTransformer.d.ts +127 -0
- package/lib/core/UpstreamTransformer.js +3 -0
- package/lib/core/UpstreamTransformer.js.map +1 -0
- package/lib/core/UpstreamTransformer.mjs +2 -0
- package/lib/core/UpstreamTransformer.mjs.map +1 -0
- package/lib/core/fingerprint.d.ts +442 -97
- package/lib/core/fingerprint.js +838 -83
- package/lib/core/fingerprint.js.map +1 -1
- package/lib/core/fingerprint.mjs +839 -84
- package/lib/core/fingerprint.mjs.map +1 -1
- package/lib/core/options.d.ts +81 -58
- package/lib/core/options.js +81 -4
- package/lib/core/options.js.map +1 -1
- package/lib/core/options.mjs +81 -4
- package/lib/core/options.mjs.map +1 -1
- package/lib/core/remapAstLocations.d.ts +76 -0
- package/lib/core/remapAstLocations.js +205 -0
- package/lib/core/remapAstLocations.js.map +1 -0
- package/lib/core/remapAstLocations.mjs +203 -0
- package/lib/core/remapAstLocations.mjs.map +1 -0
- package/lib/core/upstream.d.ts +97 -27
- package/lib/core/upstream.js +109 -8
- package/lib/core/upstream.js.map +1 -1
- package/lib/core/upstream.mjs +109 -9
- package/lib/core/upstream.mjs.map +1 -1
- package/lib/index.d.ts +50 -2
- package/lib/index.js +88 -24
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +89 -25
- package/lib/index.mjs.map +1 -1
- package/lib/transformer.d.ts +183 -6
- package/lib/transformer.js +209 -14
- package/lib/transformer.js.map +1 -1
- package/lib/transformer.mjs +211 -16
- package/lib/transformer.mjs.map +1 -1
- package/package.json +7 -18
- package/src/core/SnapshotReadOperations.ts +121 -0
- package/src/core/TtscMetroOptions.ts +127 -0
- package/src/core/TtscMetroProjectView.ts +74 -0
- package/src/core/UpstreamTransformer.ts +126 -0
- package/src/core/fingerprint.ts +987 -109
- package/src/core/options.ts +85 -62
- package/src/core/remapAstLocations.ts +219 -0
- package/src/core/upstream.ts +110 -26
- package/src/index.ts +99 -28
- package/src/transformer.ts +214 -17
|
@@ -1,60 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* process at `Transformer` construction). A ttsc transform's output can depend
|
|
7
|
-
* on inputs Metro never keys: other project sources reached through type-only
|
|
8
|
-
* edges, `node_modules` declarations, monorepo sibling sources, and the
|
|
9
|
-
* tsconfig `extends` ancestry. This module folds all of them into the static
|
|
10
|
-
* key so the cache key incorporates every input that can influence a
|
|
11
|
-
* transform's output:
|
|
12
|
-
*
|
|
13
|
-
* - **Project walk.** Every input file under the fingerprint roots (Metro's
|
|
14
|
-
* `projectRoot` plus the resolved tsconfig's directory when it lies outside),
|
|
15
|
-
* hashed with the exact walk universe the `@ttsc/unplugin` transform core
|
|
16
|
-
* validates its own cache against.
|
|
17
|
-
* - **Recorded transform inputs.** The host-owned reference graph
|
|
18
|
-
* (samchon/ttsc#718) reports each transform's derived inputs. Workers retain
|
|
19
|
-
* them under `node_modules/.cache/ttsc-metro`, compare their generation state
|
|
20
|
-
* with the exact main-process key baseline, and batch one durable write per
|
|
21
|
-
* delivered module.
|
|
22
|
-
*
|
|
23
|
-
* Snapshot layout: one main file carrying a random epoch id plus per-worker
|
|
24
|
-
* files with unique names, so concurrent workers never race a shared write.
|
|
25
|
-
* `withTtsc` compacts worker files into the main file before its workers exist,
|
|
26
|
-
* under a process-shared lock for builds using the same project cache. Readers
|
|
27
|
-
* take the union of every file, reading the worker files strictly before the
|
|
28
|
-
* main file: the compactor renames the merged main into place strictly before
|
|
29
|
-
* deleting a worker file, so a worker file that disappears mid-read is always
|
|
30
|
-
* already merged into the main the reader loads afterwards.
|
|
31
|
-
*
|
|
32
|
-
* Sound degradations, by design:
|
|
33
|
-
*
|
|
34
|
-
* - No readable snapshot (first run, wiped cache dir, unwritable filesystem)
|
|
35
|
-
* folds a random nonce: that run shares no cache entries with any other run.
|
|
36
|
-
* - A failed snapshot write persists the pending observation beside the snapshot
|
|
37
|
-
* directory. While any such recovery document exists, readers fold a nonce;
|
|
38
|
-
* successful compaction merges it and mints a fresh epoch.
|
|
39
|
-
* - A recreated snapshot carries a fresh epoch id, so it can never alias a key
|
|
40
|
-
* from an older epoch whose recorded set is unknown.
|
|
41
|
-
* - A plugin-declared volatile output (non-file inputs; unrepresentable in any
|
|
42
|
-
* file fingerprint) marks the snapshot volatile, which also folds a nonce
|
|
43
|
-
* until a later run records the volatile declaration gone.
|
|
44
|
-
* - A recorded input that disappears hashes as a stable `missing` marker, so
|
|
45
|
-
* deletion and reappearance both move the key.
|
|
46
|
-
* - A worker state that differs from the static key's run baseline taints the
|
|
47
|
-
* observation; compaction rotates the epoch so even A -> B -> A cannot reuse
|
|
48
|
-
* output stored under the earlier A key.
|
|
49
|
-
*/
|
|
50
|
-
import { readProjectMembershipPolicy } from "@ttsc/unplugin/api";
|
|
51
|
-
import type { TtscProjectDiscoveryFilesystem, TtscProjectTreeDiscoveryFilesystem, TtscWatchInput } from "@ttsc/unplugin/api";
|
|
1
|
+
import type { TtscProjectDiscoveryFilesystem, TtscProjectTreeDiscoveryFilesystem, TtscWatchInput, TtscWatchInputBaseline } from "@ttsc/unplugin/api";
|
|
2
|
+
import type { SnapshotReadOperations } from "./SnapshotReadOperations";
|
|
3
|
+
import type { TtscMetroProjectView } from "./TtscMetroProjectView";
|
|
4
|
+
/** Requested native codecs, separate from compiler-accessible entry listings. */
|
|
5
|
+
type NativeInputKind = keyof NonNullable<TtscWatchInputBaseline["nativePredicates"]>;
|
|
52
6
|
/** Union of the snapshot state readable on disk. */
|
|
53
7
|
interface SnapshotState {
|
|
8
|
+
/** Native version-one kinds to observe again for each recorded path. */
|
|
9
|
+
nativePredicates: Record<string, NativeInputKind[]>;
|
|
10
|
+
/** Paths whose compiler-visible immediate entry listings affect output. */
|
|
11
|
+
accessibleEntries: string[];
|
|
54
12
|
/** Random epoch id minted when the main snapshot was created. */
|
|
55
13
|
id: string;
|
|
56
14
|
/** Absolute paths of every recorded derived transform input. */
|
|
57
15
|
files: string[];
|
|
16
|
+
/**
|
|
17
|
+
* The recorded inputs that are plugin source directories, each also in
|
|
18
|
+
* `files`, whose state the static key carries (samchon/ttsc#1487).
|
|
19
|
+
*/
|
|
20
|
+
trees: string[];
|
|
58
21
|
/** Whether any recorded transform declared volatile output. */
|
|
59
22
|
volatile: boolean;
|
|
60
23
|
/** Whether a transform observed state different from its run's static key. */
|
|
@@ -66,52 +29,146 @@ interface SnapshotState {
|
|
|
66
29
|
* from Metro's cache-key options, the transformer from each file's transform
|
|
67
30
|
* options — all the same value in a real Metro run), else the working directory
|
|
68
31
|
* Metro was launched from.
|
|
32
|
+
*
|
|
33
|
+
* @evidence contracts/common.md#principled-implementation
|
|
34
|
+
* Node path.resolve provides one agreed base for config preparation, static
|
|
35
|
+
* keying and worker recording. A nonempty projectRoot wins; absent or empty
|
|
36
|
+
* input uses cwd.
|
|
37
|
+
*
|
|
38
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
39
|
+
* One helper defines the shared base fallback for preparation, keying and
|
|
40
|
+
* worker recording instead of giving each boundary its own anchor policy.
|
|
41
|
+
*
|
|
42
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms
|
|
43
|
+
* This operation delegates one path resolution and chooses a fallback; it
|
|
44
|
+
* owns no collection-processing strategy.
|
|
45
|
+
*
|
|
46
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
47
|
+
* The base depends on caller input and current cwd; this pure mapping
|
|
48
|
+
* coordinates no shared computation.
|
|
49
|
+
*
|
|
50
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
51
|
+
* It returns a directory string and owns no retained state or handle.
|
|
52
|
+
*
|
|
53
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
54
|
+
* It computes a directory value without changing the filesystem, foreign
|
|
55
|
+
* APIs or consumer-specific behavior.
|
|
56
|
+
*
|
|
57
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
58
|
+
* Node path.resolve and process.cwd handle native path roots and separators
|
|
59
|
+
* on supported OSes. No URL conversion or process spawning occurs.
|
|
60
|
+
*
|
|
61
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
62
|
+
* The native JSDoc explains shared base ownership and the absent/empty
|
|
63
|
+
* projectRoot fallback. Checked against the documentation skill: separate
|
|
64
|
+
* paragraphs state the contract and why its nonobvious boundary matters;
|
|
65
|
+
* field comments retain their own useful facts.
|
|
69
66
|
*/
|
|
70
67
|
export declare function resolveFingerprintBase(projectRoot: string | undefined): string;
|
|
71
68
|
/**
|
|
72
|
-
* The directories whose walk universes the fingerprint hashes
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
69
|
+
* The directories whose walk universes the fingerprint hashes. Implicit
|
|
70
|
+
* selection searches from the base upward, so a config at the base uses that
|
|
71
|
+
* walk and a config above it replaces the base walk with its own directory,
|
|
72
|
+
* which contains the base. An explicit project inside the base uses the base
|
|
73
|
+
* walk; one above the base replaces it; one elsewhere adds its directory. The
|
|
74
|
+
* separate per-file project view can select a nested config below the base.
|
|
75
|
+
* Matching the transform core's validation universe keeps the invariant simple:
|
|
76
|
+
* everything it treats as an input is fingerprinted by the walk, the recorded
|
|
77
|
+
* snapshot, or both.
|
|
78
|
+
*
|
|
79
|
+
* @evidence contracts/common.md#principled-implementation
|
|
80
|
+
* The shared project resolver owns tsconfig selection; lexical path
|
|
81
|
+
* containment is separate from program membership.
|
|
82
|
+
* Blank explicit options mean implicit discovery. An in-root config avoids a
|
|
83
|
+
* duplicate whole-tree walk; a config above the base walks its own directory,
|
|
84
|
+
* which contains the base, instead of hashing the base subtree twice; an
|
|
85
|
+
* unrelated out-of-root config adds its directory because it can supply
|
|
86
|
+
* transform inputs.
|
|
87
|
+
*
|
|
88
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
89
|
+
* Project selection delegates to the shared resolver and containment to
|
|
90
|
+
* projectViewRoots, separating config ownership from walk-root choice.
|
|
91
|
+
*
|
|
92
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms
|
|
93
|
+
* It delegates shared project discovery and computes at most two roots;
|
|
94
|
+
* traversal cost belongs to that resolver and the fingerprint operation.
|
|
95
|
+
*
|
|
96
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
97
|
+
* This mapping retains no discovery result between calls and coordinates
|
|
98
|
+
* no completed or in-flight computation.
|
|
99
|
+
*
|
|
100
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
101
|
+
* Roots transfer to the caller; no retained state or handle is acquired.
|
|
102
|
+
*
|
|
103
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
104
|
+
* No fixture-specific roots or patched filesystem APIs are used.
|
|
105
|
+
*
|
|
106
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
107
|
+
* Node resolve and dirname handle Windows drives and POSIX roots. Lexical
|
|
108
|
+
* containment compares complete path components with only volume-root case
|
|
109
|
+
* normalization; it does not merge distinct native names by OS assumption.
|
|
110
|
+
*
|
|
111
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
112
|
+
* The native JSDoc explains walk roots, implicit selection and why outside
|
|
113
|
+
* configs extend coverage. Checked against the documentation skill:
|
|
114
|
+
* separate paragraphs state the contract and why its nonobvious boundary
|
|
115
|
+
* matters; field comments retain their own useful facts.
|
|
79
116
|
*/
|
|
80
117
|
export declare function fingerprintRoots(base: string, explicitProject: string | undefined): string[];
|
|
81
|
-
/**
|
|
82
|
-
* One project, and the membership policy that describes it.
|
|
83
|
-
*
|
|
84
|
-
* The recorder's question is whether the project walk already covers an input,
|
|
85
|
-
* so it needs both the walk's roots and the policy that walk used, and it is
|
|
86
|
-
* wrong exactly when those two describe different projects. Passing them
|
|
87
|
-
* separately made that mismatch expressible — the policy for one project
|
|
88
|
-
* alongside the root of another — and passing the policy alone made it
|
|
89
|
-
* expressible in a quieter way still, since a recorder that resolved its own
|
|
90
|
-
* could describe a different program than the walk hashed. Both halves travel
|
|
91
|
-
* together so neither can be supplied without the other (samchon/ttsc#1316).
|
|
92
|
-
*/
|
|
93
|
-
export interface TtscMetroProjectView {
|
|
94
|
-
/** The base directory both fingerprint sides agree on. */
|
|
95
|
-
readonly base: string;
|
|
96
|
-
/** Config candidates observed while selecting this transform's project. */
|
|
97
|
-
readonly discoveryInputs: readonly TtscWatchInput[];
|
|
98
|
-
/** The caller's explicit `project`, if any. */
|
|
99
|
-
readonly explicitProject: string | undefined;
|
|
100
|
-
/** The membership policy resolved for that project. */
|
|
101
|
-
readonly policy: ReturnType<typeof readProjectMembershipPolicy>;
|
|
102
|
-
/** The policy used by the routed static walk. */
|
|
103
|
-
readonly walkPolicy: ReturnType<typeof readProjectMembershipPolicy>;
|
|
104
|
-
/** Lexical roots whose fingerprint uses this project's policy. */
|
|
105
|
-
readonly roots: readonly string[];
|
|
106
|
-
/** The exact config selected for this project. */
|
|
107
|
-
readonly tsconfig: string;
|
|
108
|
-
}
|
|
109
118
|
/**
|
|
110
119
|
* Resolve one transform's project view, once, for every watch input it reports.
|
|
111
120
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
121
|
+
* Discovery, referenced-project selection and membership policy are resolved
|
|
122
|
+
* once per delivered module and shared by its recorder batch. The policy is
|
|
123
|
+
* read from source rather than trusting a cached mtime/size observation, which
|
|
124
|
+
* could miss same-stamp edits.
|
|
125
|
+
*
|
|
126
|
+
* @evidence contracts/common.md#principled-implementation
|
|
127
|
+
* Shared project discovery, reference selection and membership-policy APIs
|
|
128
|
+
* select the same config as the transform core, retaining positive and
|
|
129
|
+
* negative discovery predicates. Consulted routing configs retain current
|
|
130
|
+
* content, since their references or admission rules can change the owner.
|
|
131
|
+
* Compiler-option overlays are merged through that owner.
|
|
132
|
+
*
|
|
133
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
134
|
+
* Selection records the predicates that chose the project, then one
|
|
135
|
+
* createProjectView helper assembles its policy and roots for every input
|
|
136
|
+
* in the module's recorder batch.
|
|
137
|
+
*
|
|
138
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
139
|
+
* Discovery follows the ancestor candidates and reference graph; the
|
|
140
|
+
* resulting policy and roots are assembled once for the delivered module.
|
|
141
|
+
* Candidate predicates and consulted config content are captured once for
|
|
142
|
+
* this view, with a lexical-key map merging their overlapping paths in
|
|
143
|
+
* linear map operations. Capturing content performs the shared baseline's
|
|
144
|
+
* two observations and scales with the consulted config bytes.
|
|
145
|
+
*
|
|
146
|
+
* @evidence contracts/performance.md#reuse-equivalent-work
|
|
147
|
+
* The returned view is reused by the module's recorder batch; its selected
|
|
148
|
+
* tsconfig is passed to the transform options. Config sources are reread for
|
|
149
|
+
* a later module because matching mtime and size cannot establish continued
|
|
150
|
+
* equivalence.
|
|
151
|
+
*
|
|
152
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
153
|
+
* Returned project data transfers to the caller. This operation retains no
|
|
154
|
+
* process-wide state or handle after discovery completes.
|
|
155
|
+
*
|
|
156
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
157
|
+
* The view is resolved once per module and shared with its batch, avoiding
|
|
158
|
+
* independent recorder discovery without hardcoded project or test names.
|
|
159
|
+
*
|
|
160
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
161
|
+
* Node absolute paths and host discovery predicates retain native
|
|
162
|
+
* filesystem identity. The injected discovery filesystem is an explicit
|
|
163
|
+
* observation boundary, and path-relative helpers respect native roots; no
|
|
164
|
+
* shell command or global filesystem patch is used.
|
|
165
|
+
*
|
|
166
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
167
|
+
* The native JSDoc explains selection inputs, returned coupled view,
|
|
168
|
+
* discovery evidence and per-module reuse. Checked against the
|
|
169
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
170
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
171
|
+
* facts.
|
|
115
172
|
*/
|
|
116
173
|
export declare function resolveProjectView(props: {
|
|
117
174
|
compilerOptions?: Record<string, unknown>;
|
|
@@ -124,6 +181,63 @@ export declare function resolveProjectView(props: {
|
|
|
124
181
|
* Compute the fingerprint `getCacheKey` folds into Metro's static transformer
|
|
125
182
|
* key. Never throws: any failure degrades to a nonce, which soundly disables
|
|
126
183
|
* cross-run cache reuse for this run instead of serving stale output.
|
|
184
|
+
*
|
|
185
|
+
* A successful call makes two complete observations of the routed projects,
|
|
186
|
+
* config sources and recorded inputs. Work scales with their files and bytes;
|
|
187
|
+
* it is paid at Metro's static-key boundary, not once per delivered module. The
|
|
188
|
+
* run baseline retains exact hashes, identities and discovery predicates so a
|
|
189
|
+
* worker can prove that its generation matches the state keyed here.
|
|
190
|
+
*
|
|
191
|
+
* @evidence contracts/common.md#principled-implementation
|
|
192
|
+
* The shared project walker/policy and input-state APIs supply real file,
|
|
193
|
+
* config, identity and recorded-input observations. Two complete
|
|
194
|
+
* observations must agree before Node sha256 publishes the key and immutable
|
|
195
|
+
* run baseline. Missing, volatile, incomplete or changing state yields
|
|
196
|
+
* randomBytes nonce, a required withdrawal of reuse rather than a successful
|
|
197
|
+
* stale fallback. Injected discovery changes observation only, not
|
|
198
|
+
* production results.
|
|
199
|
+
*
|
|
200
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
201
|
+
* The entry point compares two observations and publishes their baseline;
|
|
202
|
+
* observeProjectFingerprint owns collecting the complete input universe.
|
|
203
|
+
* Nonreusable state has one nonce exit rather than partial-success keys.
|
|
204
|
+
*
|
|
205
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
206
|
+
* Two observations scan project files and recorded inputs, with time driven
|
|
207
|
+
* by their bytes and config graph size. Overlapping lexical project walks
|
|
208
|
+
* are excluded by routed roots; maps deduplicate config sources. Exact
|
|
209
|
+
* content observations are necessary because timestamps do not prove state.
|
|
210
|
+
*
|
|
211
|
+
* @evidence contracts/performance.md#reuse-equivalent-work
|
|
212
|
+
* The static key shares transforms only for equal options, config contents,
|
|
213
|
+
* file hashes, identities, recorded generation state and requested exact
|
|
214
|
+
* compiler-accessible entry listings. Volatile, tainted
|
|
215
|
+
* or unstable observations take a fresh nonce; a run baseline must be
|
|
216
|
+
* immutable before workers can claim its coverage.
|
|
217
|
+
*
|
|
218
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources
|
|
219
|
+
* Observation maps are local to key computation and are released on return.
|
|
220
|
+
* The persisted run baseline is owned by snapshot preparation, which sweeps
|
|
221
|
+
* old baselines. A long-running worker can retain its loaded baseline until
|
|
222
|
+
* worker termination; it owns no open handle here.
|
|
223
|
+
*
|
|
224
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
225
|
+
* Decision values come from the documented inputs and product protocol
|
|
226
|
+
* rather than expected test answers. No compensating path is introduced to
|
|
227
|
+
* make a known example pass.
|
|
228
|
+
*
|
|
229
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
230
|
+
* Node paths/fs preserve native roots and physical identities, including
|
|
231
|
+
* symlink-aware discovery. Sorted key representation is deterministic; file
|
|
232
|
+
* bytes and host predicates remain inputs. Filesystem failure withdraws
|
|
233
|
+
* reuse consistently instead of becoming an OS-specific successful key.
|
|
234
|
+
*
|
|
235
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
236
|
+
* The native JSDoc explains hex digest versus nonce, repeated observation,
|
|
237
|
+
* private baseline publication and failure policy. Checked against the
|
|
238
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
239
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
240
|
+
* facts.
|
|
127
241
|
*/
|
|
128
242
|
export declare function computeProjectFingerprint(props: {
|
|
129
243
|
compilerOptions?: Record<string, unknown>;
|
|
@@ -148,24 +262,208 @@ export declare function computeProjectFingerprint(props: {
|
|
|
148
262
|
* token crosses bundle and process boundaries, so `getCacheKey` degrades to a
|
|
149
263
|
* nonce even when neither snapshot location can persist the failure and an old
|
|
150
264
|
* main later reappears.
|
|
265
|
+
*
|
|
266
|
+
* Compaction reads the accumulated worker and recovery documents and unions
|
|
267
|
+
* their recorded paths. Workers have unique names; completed observations are
|
|
268
|
+
* published before removal. Locked claimed files remain until a later retry.
|
|
269
|
+
* Old temporary files and run baselines are swept, but recorded input paths
|
|
270
|
+
* accumulate in the main snapshot and dead-owner election directories remain to
|
|
271
|
+
* prevent delayed contenders from moving a newer lock. Those populations are
|
|
272
|
+
* not bounded by a fixed retention budget in this implementation.
|
|
273
|
+
*
|
|
274
|
+
* @evidence contracts/common.md#principled-implementation
|
|
275
|
+
* Node filesystem APIs atomically publish complete snapshot documents and a
|
|
276
|
+
* token-owned compaction lock. Worker claims are merged before deletion;
|
|
277
|
+
* retained claims are identified in the main document to prevent replay.
|
|
278
|
+
* Definite dead-PID recovery preserves an election record; contention or
|
|
279
|
+
* failed persistence returns a private nonreusable token.
|
|
280
|
+
*
|
|
281
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
282
|
+
* Preparation owns one lock-protected main rewrite. Helpers separate worker
|
|
283
|
+
* claims, document parsing, atomic publication and recovery storage, so
|
|
284
|
+
* concurrent readers can use the same persisted protocol.
|
|
285
|
+
*
|
|
286
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
287
|
+
* Sets union recorded paths in time proportional to document paths, then
|
|
288
|
+
* sorting gives deterministic output. Each claimed worker is merged once;
|
|
289
|
+
* readdir and document parsing remain necessary to find pending publishers.
|
|
290
|
+
*
|
|
291
|
+
* @evidence contracts/performance.md#reuse-equivalent-work
|
|
292
|
+
* The main document records claimed worker names before removal, preventing
|
|
293
|
+
* locked files from replaying completed compaction. The epoch survives only
|
|
294
|
+
* trusted, untainted observations; corrupt or lost evidence rotates it.
|
|
295
|
+
*
|
|
296
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources
|
|
297
|
+
* Preparation owns its compaction lock and retires it in finally. Claimed
|
|
298
|
+
* workers and recovery files are removed after publication; failed removals
|
|
299
|
+
* stay represented for retry. Temporary files and baselines have age sweeps,
|
|
300
|
+
* but cumulative input paths and stale-owner election records have no fixed
|
|
301
|
+
* bound. Removing these without a generation protocol could erase evidence.
|
|
302
|
+
*
|
|
303
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
304
|
+
* Retries serve locked-file/recovery contracts rather than masking an
|
|
305
|
+
* uncorrected successful-state assumption.
|
|
306
|
+
*
|
|
307
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
308
|
+
* Node join, mkdir, rename and rm implement native cache paths.
|
|
309
|
+
* Windows-locked files remain represented for a later removal attempt; only
|
|
310
|
+
* a definite process-query absence permits dead-owner recovery. No shell
|
|
311
|
+
* process checks or patched fs methods are used.
|
|
312
|
+
*
|
|
313
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
314
|
+
* The native JSDoc explains new-run preparation, contention, epoch
|
|
315
|
+
* rotation, recovery storage and nonreusable tokens. Checked against the
|
|
316
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
317
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
318
|
+
* facts.
|
|
151
319
|
*/
|
|
152
320
|
export declare function prepareSnapshot(projectRoot: string | undefined): string;
|
|
153
321
|
/**
|
|
154
322
|
* Read the unioned snapshot state, or `undefined` when the main snapshot is
|
|
155
|
-
* missing
|
|
156
|
-
* recorded set cannot be trusted, so the caller degrades to a nonce)
|
|
323
|
+
* missing, any snapshot file is corrupt (a torn or foreign write means the
|
|
324
|
+
* recorded set cannot be trusted, so the caller degrades to a nonce), or a
|
|
325
|
+
* concurrent compaction keeps renaming worker files so the listing never
|
|
326
|
+
* settles or a compaction lock stays held.
|
|
327
|
+
*
|
|
328
|
+
* `operations` is the read boundary (existence, listing, file text); the
|
|
329
|
+
* default reads the real filesystem, and a test passes its own to interleave a
|
|
330
|
+
* compaction with a listing. A pass is retried at most
|
|
331
|
+
* {@link SNAPSHOT_LISTING_ATTEMPTS} (8) times with a 5 ms pause between passes,
|
|
332
|
+
* so a held lock costs a call at most about 40 ms before the state is reported
|
|
333
|
+
* untrusted; the retry is bounded and holds no resource between passes.
|
|
334
|
+
*
|
|
335
|
+
* The result contains sorted absolute file and tree paths, the epoch identity,
|
|
336
|
+
* and tainted/volatile flags. It reads persisted evidence without revalidating
|
|
337
|
+
* current file bytes; the fingerprint operation owns that validation. Reading
|
|
338
|
+
* costs the number and size of outstanding documents plus their unioned paths.
|
|
339
|
+
*
|
|
340
|
+
* @evidence contracts/common.md#principled-implementation
|
|
341
|
+
* Strict snapshot parsing and Set union read worker documents before the
|
|
342
|
+
* main publication, retaining file/tree/volatile/tainted state without
|
|
343
|
+
* replaying already compacted claims. This is the owned persisted-input
|
|
344
|
+
* protocol, not a separate compiler dependency model.
|
|
345
|
+
* A pass is accepted only when no compaction lock existed before or after
|
|
346
|
+
* it and the main file is unchanged, because a listing concurrent with a
|
|
347
|
+
* rename can return the renamed entry under neither name.
|
|
348
|
+
* The read boundary is an optional parameter, so the interleaving can be
|
|
349
|
+
* driven deterministically while production reads the real filesystem.
|
|
350
|
+
*
|
|
351
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
352
|
+
* This reader validates persisted documents and unions their paths without
|
|
353
|
+
* recomputing file state. The fingerprint owner handles live validation;
|
|
354
|
+
* both reader and compactor share uncompacted-worker classification.
|
|
355
|
+
*
|
|
356
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
357
|
+
* Reading costs document bytes plus Set insertion and final path sorting.
|
|
358
|
+
* The compacted-name Set avoids replay and avoids a linear search per worker.
|
|
359
|
+
*
|
|
360
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
361
|
+
* This read must observe new worker publications; it coordinates no cache
|
|
362
|
+
* between calls. Cross-run reuse belongs to computeProjectFingerprint.
|
|
363
|
+
*
|
|
364
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
365
|
+
* Returned snapshot data transfers to the caller. This reader retains no
|
|
366
|
+
* state or handle after synchronous reads finish.
|
|
367
|
+
*
|
|
368
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
369
|
+
* Unknown, corrupt or recovery-pending state returns undefined, never an
|
|
370
|
+
* invented complete empty set.
|
|
371
|
+
*
|
|
372
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
373
|
+
* Node path and fs read native snapshot locations. Locked or unreadable
|
|
374
|
+
* directory/documents invalidate the proof on all supported OSes; sorted
|
|
375
|
+
* lexical paths remain values, not shell arguments.
|
|
376
|
+
*
|
|
377
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
378
|
+
* The native JSDoc explains unioned snapshot fields, read ordering and the
|
|
379
|
+
* undefined result for untrusted state. Checked against the documentation
|
|
380
|
+
* skill: separate paragraphs state the contract and why its nonobvious
|
|
381
|
+
* boundary matters; field comments retain their own useful facts.
|
|
157
382
|
*/
|
|
158
|
-
export declare function readSnapshotState(base: string): SnapshotState | undefined;
|
|
383
|
+
export declare function readSnapshotState(base: string, operations?: SnapshotReadOperations): SnapshotState | undefined;
|
|
159
384
|
/**
|
|
160
385
|
* Recorder held by each Metro worker. It persists every derived watch input and
|
|
161
386
|
* any volatile declaration, compares compiler-generation evidence with the
|
|
162
387
|
* matching main-process run baseline, and marks any temporal mismatch tainted.
|
|
163
388
|
* A clean transform also writes a document so it can clear a volatile
|
|
164
|
-
* declaration from an earlier run.
|
|
165
|
-
*
|
|
166
|
-
*
|
|
389
|
+
* declaration from an earlier run. The first delivery, a delivery that adds a
|
|
390
|
+
* path, and every delivery once the state is tainted flush one cumulative
|
|
391
|
+
* document; the unique name makes worker writes race-free, and `withTtsc`
|
|
392
|
+
* compacts the files on the next run.
|
|
393
|
+
*
|
|
394
|
+
* `record` accepts one lexical path without generation evidence; `recordMany`
|
|
395
|
+
* accepts a module's generation evidence; `recordVolatile` withdraws the file
|
|
396
|
+
* proof for non-file inputs. Without a run identity every input is retained;
|
|
397
|
+
* production passes the identity whose immutable baseline was keyed in the main
|
|
398
|
+
* process. An unknown or mismatching baseline marks the snapshot tainted, and
|
|
399
|
+
* since `record` carries no evidence, a run identity always reads it as a
|
|
400
|
+
* mismatch. Listing predicates retain their paths for the next run's key
|
|
401
|
+
* observation; native kind requests survive the same persistence path without
|
|
402
|
+
* becoming compiler listings or gaining watch scope. The worker never adds a
|
|
403
|
+
* new disk read to its earlier immutable baseline.
|
|
404
|
+
*
|
|
405
|
+
* Sets and baseline maps live for the recorder's worker lifetime and grow with
|
|
406
|
+
* observed projects and distinct inputs. A flushing delivery serializes the
|
|
407
|
+
* cumulative recorded set once, not once per input, but a tainted state flushes
|
|
408
|
+
* on every delivery, so its cost grows with the recorded set for each module. A
|
|
409
|
+
* failed write remains dirty for retry and tries recovery storage. If both
|
|
410
|
+
* stores fail, a reusable run throws rather than publishing output backed by
|
|
411
|
+
* lost evidence.
|
|
412
|
+
*
|
|
413
|
+
* @evidence contracts/common.md#principled-implementation
|
|
414
|
+
* One worker closure retains derived inputs and compares their
|
|
415
|
+
* compiler-generation evidence with its immutable run baseline through
|
|
416
|
+
* shared validation APIs. Static coverage is omitted only after a matching
|
|
417
|
+
* proof; unknown/volatile inputs persist taint.
|
|
418
|
+
* Listing and plugin-tree paths stay in the retained file universe even
|
|
419
|
+
* when a static proof already covers them, so optional payloads survive
|
|
420
|
+
* subsequent compaction and key capture.
|
|
421
|
+
* A first or changed module batch publishes one cumulative atomic document,
|
|
422
|
+
* with recovery storage and AggregateError when a reusable generation cannot
|
|
423
|
+
* persist either record.
|
|
424
|
+
*
|
|
425
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
426
|
+
* One worker closure owns recorder state; shared recordMany owns evidence
|
|
427
|
+
* comparison and batch publication for single and multiple inputs. flush
|
|
428
|
+
* owns durability, so volatile observations use the same failure policy.
|
|
429
|
+
*
|
|
430
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
431
|
+
* Sets insert distinct inputs and a cached static-input Set checks coverage
|
|
432
|
+
* without searching the whole baseline. A flushing module serializes the
|
|
433
|
+
* cumulative paths once, costing their count and encoded bytes rather than
|
|
434
|
+
* one cumulative serialization for every input. A tainted state flushes on
|
|
435
|
+
* every module, so that cost recurs per delivery until the worker ends.
|
|
436
|
+
*
|
|
437
|
+
* @evidence contracts/performance.md#reuse-equivalent-work
|
|
438
|
+
* Each base loads its immutable run baseline once and shares it across
|
|
439
|
+
* module batches. Only matching generation evidence authorizes omission of
|
|
440
|
+
* static paths; missing or differing evidence records taint instead.
|
|
441
|
+
*
|
|
442
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources
|
|
443
|
+
* The worker owns maps of bases, baseline bytes and distinct recorded paths
|
|
444
|
+
* until termination. Population grows with projects and inputs observed in
|
|
445
|
+
* that worker; there is no fixed eviction budget. Persisted files transfer
|
|
446
|
+
* to the next preparation's compactor. No native handle remains open.
|
|
447
|
+
*
|
|
448
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
449
|
+
* The legacy no-run handshake records all paths without claiming static
|
|
450
|
+
* coverage.
|
|
451
|
+
*
|
|
452
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
453
|
+
* Node resolve/join and atomic rename use native paths; unique PID/random
|
|
454
|
+
* worker names separate publishers. Windows-locked claims remain readable
|
|
455
|
+
* until later compaction, and persistence failures follow the same reuse
|
|
456
|
+
* withdrawal contract. No foreign filesystem or process method is replaced.
|
|
457
|
+
*
|
|
458
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
459
|
+
* The native JSDoc explains record, batch and volatile operations,
|
|
460
|
+
* run-baseline matching, cumulative persistence and failure ownership.
|
|
461
|
+
* Checked against the documentation skill: separate paragraphs state the
|
|
462
|
+
* contract and why its nonobvious boundary matters; field comments retain
|
|
463
|
+
* their own useful facts.
|
|
167
464
|
*/
|
|
168
465
|
export declare function createSnapshotRecorder(runId?: string): {
|
|
466
|
+
/** Record one lexical input without claiming compiler-generation evidence. */
|
|
169
467
|
record: (props: {
|
|
170
468
|
input: string;
|
|
171
469
|
/**
|
|
@@ -185,18 +483,65 @@ export declare function createSnapshotRecorder(runId?: string): {
|
|
|
185
483
|
*/
|
|
186
484
|
project: TtscMetroProjectView;
|
|
187
485
|
}) => void;
|
|
486
|
+
/** Record one module's input evidence and publish its cumulative snapshot. */
|
|
188
487
|
recordMany: (props: {
|
|
189
488
|
inputs: readonly TtscWatchInput[];
|
|
190
489
|
project: TtscMetroProjectView;
|
|
191
490
|
}) => void;
|
|
491
|
+
/** Persist that a project's output depends on inputs a file key cannot prove. */
|
|
192
492
|
recordVolatile: (props: {
|
|
193
493
|
project: TtscMetroProjectView;
|
|
194
494
|
}) => void;
|
|
195
495
|
};
|
|
196
496
|
/**
|
|
197
|
-
* JSON-
|
|
198
|
-
*
|
|
497
|
+
* Represent acyclic JSON-like records with recursively sorted keys for hashing.
|
|
498
|
+
* Arrays preserve order and JSON primitives use JSON escaping. This is an
|
|
499
|
+
* internal key representation, not JSON for interchange.
|
|
500
|
+
*
|
|
501
|
+
* Undefined uses an unquoted marker and sparse array slots use a separate
|
|
502
|
+
* unquoted hole marker, preserving cardinality and the distinction from null
|
|
503
|
+
* and literal strings. Non-finite numbers, functions, symbols, BigInt and
|
|
504
|
+
* cyclic structures are unsupported and throw rather than supplying a key.
|
|
199
505
|
* Shared with the transformer's option digest.
|
|
506
|
+
*
|
|
507
|
+
* @evidence contracts/common.md#principled-implementation
|
|
508
|
+
* Recursive Object.entries sorting and JSON primitive escaping provide a
|
|
509
|
+
* stable internal key representation, not another transport parser. The
|
|
510
|
+
* undefined and hole markers cannot collide with quoted string values.
|
|
511
|
+
* Array.from visits each slot, preserving length and explicit absence.
|
|
512
|
+
* Unsupported primitive values fail instead of aliasing null or absence.
|
|
513
|
+
*
|
|
514
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
515
|
+
* One recursive encoder owns array, record and primitive representations;
|
|
516
|
+
* sorted record entries keep ordering policy in that one boundary.
|
|
517
|
+
*
|
|
518
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
519
|
+
* Each visited value is encoded once. Each record sorts its own keys in
|
|
520
|
+
* O(k log k), and encoding space follows output bytes plus recursion depth.
|
|
521
|
+
* Sorting is necessary for insertion-order-independent record keys.
|
|
522
|
+
*
|
|
523
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
524
|
+
* This encoder accepts potentially mutable caller records and coordinates
|
|
525
|
+
* no cross-call computation; consumers own whether encoded values can recur.
|
|
526
|
+
*
|
|
527
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
528
|
+
* The encoder returns its owned string and retains no caller data or handle
|
|
529
|
+
* after return; temporary recursion state is computation space.
|
|
530
|
+
*
|
|
531
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
532
|
+
* Absence is represented at its owning serializer rather than patched in
|
|
533
|
+
* individual key consumers. Unsupported primitives fail explicitly.
|
|
534
|
+
*
|
|
535
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation
|
|
536
|
+
* This operation sorts and encodes caller values for a key; it has no
|
|
537
|
+
* native path, filesystem or process boundary.
|
|
538
|
+
*
|
|
539
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
540
|
+
* The native JSDoc explains sorted keys, the acyclic value domain,
|
|
541
|
+
* unsupported JSON interchange and explicit absence markers. Checked
|
|
542
|
+
* against the documentation skill: separate paragraphs state the contract
|
|
543
|
+
* and why its nonobvious boundary matters; field comments retain their own
|
|
544
|
+
* useful facts.
|
|
200
545
|
*/
|
|
201
546
|
export declare function stableStringify(value: unknown): string;
|
|
202
547
|
export {};
|