@ttsc/unplugin 0.27.0 → 0.28.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 CHANGED
@@ -312,7 +312,7 @@ The transform host reports the program's reference graph (the transform envelope
312
312
 
313
313
  Transform plugins may additionally report, per file, the source files they consulted (the envelope's `dependencies` field); the adapter registers those as watch files too, union semantics. A plugin that declares such a list [complete](https://ttsc.dev/docs/development/concepts/protocol#dependency-completeness) for a file narrows the registration instead: only its own list plus the tsconfig chain, so files the transform never consulted stop invalidating it. The JavaScript host also reports the descriptor's loaded CommonJS graph, every package manifest inspected by auto-discovery, and plugin-declared `hostInputs`. First-party plugins use that last field for implicit config discovery and evaluated config dependencies. These remain universal without turning arbitrary project assets into configuration. Files a plugin declares `volatile` (output depending on non-file inputs such as environment or time) bypass the adapter's transform cache and are marked uncacheable where the bundler exposes that control.
314
314
 
315
- The transform cache snapshots every regular file reached by the non-following project walk plus graph-reported inputs outside that walk (`node_modules` declarations, monorepo sibling sources, files reached through symlinks or Windows junctions, and out-of-root `extends` ancestry). One whole-project compile already contains every module's output. Adapters with a guaranteed build boundary clear the previous generation there, then check only the supplied source on each module's first delivery from a complete generation in that build. An incomplete generation never takes that shortcut. Later deliveries of a graph-bearing generation validate that file's reference closure, globals, configs, resolution candidates, plugin dependencies, and exact host inputs. Generation-scoped directory notifications detect new, removed, or renamed project inputs without repeating a directory-stat pass per module; Windows isolates those notifications so deletion of a watched temporary tree cannot crash the host. The project is snapshotted before and after compilation, while every graph member carries the compiler filesystem's content and physical-identity proof; only a complete generation that still matches both may authorize narrow reuse. This also rejects an in-project or external A→B→A change whose restored post-compile bytes would otherwise hide the transient state used by the compiler. `dependenciesComplete` narrows file inputs by the same contract as watch registration. Envelopes without a graph, an unavailable watcher, or an unstable/incomplete generation retain complete-snapshot validation. Bun runtime setup defines one process-scoped loading session. Long-lived hosts without either boundary (Metro workers, the Turbopack loader, and Vite's development server, whose initial `buildStart` spans later HMR edits) apply the file-specific validation on every generation hit.
315
+ The transform cache snapshots every regular file reached by the non-following project walk plus graph-reported inputs outside that walk (`node_modules` declarations, monorepo sibling sources, files reached through symlinks or Windows junctions, and out-of-root `extends` ancestry). One whole-project compile already contains every module's output. Adapters with a guaranteed build boundary clear the previous generation there, then check only the supplied source on each module's first delivery from a complete generation in that build. An incomplete generation never takes that shortcut. Later deliveries of a graph-bearing generation validate that file's reference closure, globals, configs, resolution candidates, plugin dependencies, and exact host inputs. Every input a generation has already proven carries the nanosecond metadata signature captured around the read that proved it, so a later delivery that finds the signature unchanged reuses that proof instead of re-reading the bytes; any signature change falls back to the full content comparison. A signature is recorded only for a read nothing raced, only for an input whose recorded state came from bytes that were read, since the metadata of a path nothing could read holds still while the bytes behind it appear, and only once the observed filesystem's own clock has provably left the tick that minted the input's modification stamp. A filesystem stamps writes once per clock tick, so a same-length rewrite landing inside the recorded stamp's tick would leave the signature unchanged; until some stamp the same filesystem minted later separates that tick, the content comparison keeps running, and the signature is re-earned the moment it can be. The reference instants come from the filesystem itself, never from the process clock: every stamp the adapter observes raises a floor kept per reporting device, and the adapter also stamps a probe in its own scratch directory (the way git separates racily-clean index entries with the index file's own timestamp) to cover a tree whose files were all written inside one tick. The probe counts only when the scratch volume is the inputs' volume, so on a split-volume layout the observed stamps carry the rule alone; an input they cannot separate is re-read rather than trusted. Both sides of every comparison are therefore stamps of equal granularity minted by one clock, so a filesystem clock running behind the host changes nothing. The floor is a maximum over stamps it merely observed, so what defeats it is a stamp that was set into the future rather than minted, such as a stamp-preserving extraction or copy from a machine whose clock ran ahead. A restored past stamp is harmless, since it never raises the floor. A clock that jumps backwards strands the floor above the present the same way, which is a different hazard from a clock running at a constant offset: an offset moves both stamps being compared and changes nothing, while a jump moves only the present. No stamp-based freshness proof survives either one. Sibling modules therefore share one proof of the closure and the globals rather than repeating it per module. Generation-scoped directory notifications detect new, removed, or renamed project inputs without repeating a directory-stat pass per module; Windows isolates those notifications so deletion of a watched temporary tree cannot crash the host. An absent resolution candidate is the one input a metadata signature cannot stand for, since a path that is not there has no metadata to compare. The generation therefore registers those names with a watcher of their own, along with the directory components of the spelling that lead to them, down to the project's own root, and a delivery reads the notification instead of re-probing each candidate. That makes the notification the sole positive evidence for this one input class, which is why the registration reaches past the candidate itself: a watcher opened on a path that traverses a link follows it, so retargeting the link would otherwise move the answer without disturbing what is watched, and a package link inside `node_modules` is exactly that. It stops at the project root, and a candidate whose spelling leaves the project subtree before reaching it is not claimed at all: above that line the components are the machine's own layout rather than the project's, which nobody retargets and which changes for reasons no generation should hear about. It listens for renames alone, since every event that can change a candidate's answer is one — the file appearing, a component being created, replaced or retargeted — while the directories carrying them have their attributes moved by anything written below. A candidate whose watch could not be opened, whose watcher has since failed, or that belongs to a set spanning more directories than the generation will watch, is probed exactly as before: a host that runs out of watch descriptors would fail the tracker outright, which costs every delivery a whole-project comparison rather than one probe. What none of that covers is a filesystem that accepts a watch and then reports nothing, which some network mounts do; that is the same assumption the project-membership proof beside it already rests on, though a wider one here: the project walk skips `node_modules`, so a candidate under it was never covered by that proof and kept a notification-independent probe until now. A project on such a mount should be treated as one where notifications do not work rather than one where they merely have not fired. The barrier that lets a synchronous edit reach those watchers before a delivery reads their verdict is the watcher's own acknowledgement rather than a fixed wait: an in-process watcher answers on the next turn of the loop its callbacks are queued on, and the Windows broker answers by an ordered round-trip, so a delivery waits for the crossing instead of guessing at it. The project is snapshotted before and after compilation, while every graph member carries the compiler filesystem's content and physical-identity proof; only a complete generation that still matches both may authorize narrow reuse. This also rejects an in-project or external A→B→A change whose restored post-compile bytes would otherwise hide the transient state used by the compiler. `dependenciesComplete` narrows file inputs by the same contract as watch registration, and a project reaches it without a type-driven plugin: ttsc's own transform lanes print syntactically, so the host declares every file complete when no plugin can contribute to it, and the first-party `@ttsc/banner` and `@ttsc/strip` declare their own contribution the same way. Such a delivery validates the universal inputs and the file itself instead of its reference closure. A narrowed file's diagnostics narrow with it: an edit to a file the declaration dropped no longer re-runs the compile, so a type error introduced there surfaces at the next compile the build runs for another reason rather than at the edit. Run the compiler's own check beside the bundler when you want it at the edit. Envelopes without a graph, and generations whose watchers could not be opened or have since failed, retain complete-snapshot validation: losing a notification is the absence of a membership proof rather than evidence of a change, so such a generation keeps validating against its own recorded state (the directory snapshot, the input hashes, and the universal descriptor inputs whose every rejection is evidence of a change rather than an inability to prove one) instead of being discarded. A reported membership event is evidence, and still replaces the generation. Only an unstable or incomplete generation is refused outright. Bun runtime setup defines one process-scoped loading session. A dev server started without a watcher takes the build-scoped lifecycle instead of persistent validation. `server.watch: null` leaves the session no channel through which a file change could ever reach it, so what per-delivery validation buys there is incoherence rather than freshness: modules delivered before an edit and after it would come from two different compilations of one program. Each module's first delivery in such a session is therefore settled against the generation the session started from, exactly as under a build, while a module the session already delivered keeps revalidating on its next request. Long-lived hosts without either boundary (Metro workers, the Turbopack loader, and a watching Vite development server, whose initial `buildStart` spans later HMR edits) apply the file-specific validation on every generation hit.
316
316
 
317
317
  ## Sponsors
318
318
 
@@ -10,7 +10,7 @@ import { beginTtscTransformBuild, collectExternalInputHashes, collectProjectInpu
10
10
  export declare const sourceFilePattern: RegExp;
11
11
  declare const unplugin: UnpluginInstance<TtscUnpluginOptions | undefined, false>;
12
12
  export type { TtscUnpluginCompilerOptionsJson, TtscUnpluginOptions, } from "./options";
13
- export type { TtscTransformFilesystemOperations, TtscTransformHooks, } from "./transform";
13
+ export type { TtscTransformFilesystemOperations, TtscTransformHooks, TtscWatchInputEvidence, } from "./transform";
14
14
  export { beginTtscTransformBuild, collectExternalInputHashes, collectProjectInputHashes, createTtscTransformCache, isProjectWalkPath, resetTtscTransformCache, resolveOptions, transformTtsc, unplugin, };
15
15
  export default unplugin;
16
16
  /**
package/lib/core/index.js CHANGED
@@ -30,8 +30,10 @@ const virtualModulePattern = /\0/;
30
30
  * The factory resolves raw options once, creates a per-build transform cache,
31
31
  * and captures Vite alias configuration via the `vite.configResolved` hook so
32
32
  * that path aliases are forwarded to the generated tsconfig overlay. Real build
33
- * lifecycles use a per-build cache; Vite's development server keeps persistent
34
- * validation because its one `buildStart` spans later HMR edits.
33
+ * lifecycles use a per-build cache; a watching Vite development server keeps
34
+ * persistent validation because its one `buildStart` spans later HMR edits,
35
+ * while a dev server configured without a watcher takes the build-scoped path
36
+ * with them, having declared it will observe no edit at all.
35
37
  */
36
38
  const unpluginFactory = (rawOptions = {}) => {
37
39
  const options$1 = options.resolveOptions(rawOptions);
@@ -39,6 +41,7 @@ const unpluginFactory = (rawOptions = {}) => {
39
41
  const missingInputs = viteServe.createViteServeMissingInputWatch();
40
42
  let aliases;
41
43
  let viteCommand;
44
+ let viteWatching = true;
42
45
  return {
43
46
  name,
44
47
  enforce: "pre",
@@ -50,6 +53,16 @@ const unpluginFactory = (rawOptions = {}) => {
50
53
  // serve-time poll, even though the closed server stays attached
51
54
  // (see the dispose note in viteServe.ts).
52
55
  viteCommand = config.command;
56
+ // `server.watch: null` disables Vite's watcher outright, which is how
57
+ // a one-shot consumer (a `vitest --run` suite above all) configures the
58
+ // dev server. Nothing can then deliver a change event, so every watch
59
+ // registration is dead weight, and not cheap dead weight: Vite's
60
+ // import analysis resolves each registered path like a real import of
61
+ // the transformed module, once per module, which is the dominant cost
62
+ // of a delivered module in a project with a real dependency graph
63
+ // (samchon/ttsc#1246).
64
+ viteWatching =
65
+ config.server?.watch !== null;
53
66
  },
54
67
  // Vite serve funnels every transform-context `addWatchFile()` into the
55
68
  // module's added-import graph (`_addedImports`), which import-analysis
@@ -67,7 +80,22 @@ const unpluginFactory = (rawOptions = {}) => {
67
80
  },
68
81
  },
69
82
  buildStart() {
70
- if (viteCommand === "serve") {
83
+ // Persistent validation exists for a session that spans edits it can
84
+ // observe, and a dev server told to open no watcher is not one:
85
+ // `server.watch: null` leaves Vite with no change channel at all, so no
86
+ // edit can reach the session, nothing invalidates what one touched, and
87
+ // no client is hot-updated. Validating each delivery there does not buy
88
+ // freshness, it buys incoherence — modules delivered before an edit and
89
+ // after it would come from two different compilations of one program —
90
+ // while costing a full derived-input proof per delivered module. The
91
+ // build-scoped lifecycle settles each module's first delivery against the
92
+ // generation the session started from, exactly as a build does, and still
93
+ // revalidates a module this session already delivered. A one-shot suite
94
+ // configures precisely this server (`vitest --run` sets `server.watch =
95
+ // null`) and is the workload behind samchon/ttsc#970
96
+ // (samchon/ttsc#1260). The neighbouring watch-registration decision reads
97
+ // the same two properties for the same reason.
98
+ if (viteCommand === "serve" && viteWatching) {
71
99
  transform.resetTtscTransformCache(transformCache);
72
100
  }
73
101
  else {
@@ -94,11 +122,24 @@ const unpluginFactory = (rawOptions = {}) => {
94
122
  // (a superseding resolution candidate, a not-yet-generated
95
123
  // dependency), so those are watched on the filesystem instead and
96
124
  // invalidate this module when created.
97
- addWatchFile: (watched) => {
98
- if (viteCommand === "serve" &&
99
- missingInputs.serving() &&
100
- !fs.existsSync(watched)) {
101
- missingInputs.watch(watched, path.resolve(file));
125
+ addWatchFile: (watched, evidence) => {
126
+ if (viteCommand === "serve" && missingInputs.serving()) {
127
+ // Trust the generation's recorded existence when it supplied one:
128
+ // every cache hit revalidates it, and probing each input again
129
+ // costs one `existsSync` per input per delivered module.
130
+ const missing = evidence?.missing ?? !fs.existsSync(watched);
131
+ if (missing) {
132
+ missingInputs.watch(watched, path.resolve(file), evidence?.identity);
133
+ return;
134
+ }
135
+ }
136
+ // A dev server configured without a watcher can never deliver a
137
+ // change event, so a registration here buys nothing, and it is not
138
+ // free: Vite's import analysis resolves every registered path like a
139
+ // real import of the transformed module, once per module. The
140
+ // adapter's own missing-input poll above stays active either way,
141
+ // because it never depended on Vite's watcher.
142
+ if (viteCommand === "serve" && !viteWatching) {
102
143
  return;
103
144
  }
104
145
  this.addWatchFile(watched);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/core/index.ts"],"sourcesContent":[null],"names":["options","resolveOptions","createTtscTransformCache","createViteServeMissingInputWatch","resetTtscTransformCache","beginTtscTransformBuild","stripQuery","transformTtsc","createUnplugin","isDeclarationFile"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/core/index.ts"],"sourcesContent":[null],"names":["options","resolveOptions","createTtscTransformCache","createViteServeMissingInputWatch","resetTtscTransformCache","beginTtscTransformBuild","stripQuery","transformTtsc","createUnplugin","isDeclarationFile"],"mappings":"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"}
@@ -27,8 +27,10 @@ const virtualModulePattern = /\0/;
27
27
  * The factory resolves raw options once, creates a per-build transform cache,
28
28
  * and captures Vite alias configuration via the `vite.configResolved` hook so
29
29
  * that path aliases are forwarded to the generated tsconfig overlay. Real build
30
- * lifecycles use a per-build cache; Vite's development server keeps persistent
31
- * validation because its one `buildStart` spans later HMR edits.
30
+ * lifecycles use a per-build cache; a watching Vite development server keeps
31
+ * persistent validation because its one `buildStart` spans later HMR edits,
32
+ * while a dev server configured without a watcher takes the build-scoped path
33
+ * with them, having declared it will observe no edit at all.
32
34
  */
33
35
  const unpluginFactory = (rawOptions = {}) => {
34
36
  const options = resolveOptions(rawOptions);
@@ -36,6 +38,7 @@ const unpluginFactory = (rawOptions = {}) => {
36
38
  const missingInputs = createViteServeMissingInputWatch();
37
39
  let aliases;
38
40
  let viteCommand;
41
+ let viteWatching = true;
39
42
  return {
40
43
  name,
41
44
  enforce: "pre",
@@ -47,6 +50,16 @@ const unpluginFactory = (rawOptions = {}) => {
47
50
  // serve-time poll, even though the closed server stays attached
48
51
  // (see the dispose note in viteServe.ts).
49
52
  viteCommand = config.command;
53
+ // `server.watch: null` disables Vite's watcher outright, which is how
54
+ // a one-shot consumer (a `vitest --run` suite above all) configures the
55
+ // dev server. Nothing can then deliver a change event, so every watch
56
+ // registration is dead weight, and not cheap dead weight: Vite's
57
+ // import analysis resolves each registered path like a real import of
58
+ // the transformed module, once per module, which is the dominant cost
59
+ // of a delivered module in a project with a real dependency graph
60
+ // (samchon/ttsc#1246).
61
+ viteWatching =
62
+ config.server?.watch !== null;
50
63
  },
51
64
  // Vite serve funnels every transform-context `addWatchFile()` into the
52
65
  // module's added-import graph (`_addedImports`), which import-analysis
@@ -64,7 +77,22 @@ const unpluginFactory = (rawOptions = {}) => {
64
77
  },
65
78
  },
66
79
  buildStart() {
67
- if (viteCommand === "serve") {
80
+ // Persistent validation exists for a session that spans edits it can
81
+ // observe, and a dev server told to open no watcher is not one:
82
+ // `server.watch: null` leaves Vite with no change channel at all, so no
83
+ // edit can reach the session, nothing invalidates what one touched, and
84
+ // no client is hot-updated. Validating each delivery there does not buy
85
+ // freshness, it buys incoherence — modules delivered before an edit and
86
+ // after it would come from two different compilations of one program —
87
+ // while costing a full derived-input proof per delivered module. The
88
+ // build-scoped lifecycle settles each module's first delivery against the
89
+ // generation the session started from, exactly as a build does, and still
90
+ // revalidates a module this session already delivered. A one-shot suite
91
+ // configures precisely this server (`vitest --run` sets `server.watch =
92
+ // null`) and is the workload behind samchon/ttsc#970
93
+ // (samchon/ttsc#1260). The neighbouring watch-registration decision reads
94
+ // the same two properties for the same reason.
95
+ if (viteCommand === "serve" && viteWatching) {
68
96
  resetTtscTransformCache(transformCache);
69
97
  }
70
98
  else {
@@ -91,11 +119,24 @@ const unpluginFactory = (rawOptions = {}) => {
91
119
  // (a superseding resolution candidate, a not-yet-generated
92
120
  // dependency), so those are watched on the filesystem instead and
93
121
  // invalidate this module when created.
94
- addWatchFile: (watched) => {
95
- if (viteCommand === "serve" &&
96
- missingInputs.serving() &&
97
- !fs.existsSync(watched)) {
98
- missingInputs.watch(watched, path.resolve(file));
122
+ addWatchFile: (watched, evidence) => {
123
+ if (viteCommand === "serve" && missingInputs.serving()) {
124
+ // Trust the generation's recorded existence when it supplied one:
125
+ // every cache hit revalidates it, and probing each input again
126
+ // costs one `existsSync` per input per delivered module.
127
+ const missing = evidence?.missing ?? !fs.existsSync(watched);
128
+ if (missing) {
129
+ missingInputs.watch(watched, path.resolve(file), evidence?.identity);
130
+ return;
131
+ }
132
+ }
133
+ // A dev server configured without a watcher can never deliver a
134
+ // change event, so a registration here buys nothing, and it is not
135
+ // free: Vite's import analysis resolves every registered path like a
136
+ // real import of the transformed module, once per module. The
137
+ // adapter's own missing-input poll above stays active either way,
138
+ // because it never depended on Vite's watcher.
139
+ if (viteCommand === "serve" && !viteWatching) {
99
140
  return;
100
141
  }
101
142
  this.addWatchFile(watched);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../../src/core/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../../src/core/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -32,6 +32,27 @@ interface TtscProjectDirectorySnapshot {
32
32
  /** Generation-scoped directory watchers used to detect membership changes. */
33
33
  interface TtscProjectMutationTracker {
34
34
  close: () => void;
35
+ /**
36
+ * Absolute spellings whose creation, change or removal this tracker would
37
+ * report, when it watches exact names rather than whole directories.
38
+ *
39
+ * A validation that finds an input here needs no filesystem call of its own:
40
+ * the tracker is the evidence, and every path that leaves this set falls back
41
+ * to being proven by hand. Empty for a tracker that watches directories as a
42
+ * whole, which cannot answer for one name.
43
+ */
44
+ covered?: ReadonlySet<string>;
45
+ /**
46
+ * Wait until every event this tracker's watcher has already dispatched has
47
+ * been applied to it.
48
+ *
49
+ * An in-process watcher drains on the next macrotask turn, because its
50
+ * callbacks are already queued on this loop. A watcher living in the Windows
51
+ * broker drains by round-trip instead: the child replies after its own turn,
52
+ * and IPC preserves order, so the reply cannot overtake an event the child
53
+ * had already sent (samchon/ttsc#1272).
54
+ */
55
+ drain?: () => Promise<void>;
35
56
  failed: boolean;
36
57
  membershipChanged: boolean;
37
58
  settle?: Promise<void>;
@@ -73,15 +94,68 @@ export interface TtscCachedProjectTransform {
73
94
  * compiler reported rather than a normalized replacement spelling.
74
95
  */
75
96
  externalInputPaths?: string[];
97
+ /**
98
+ * Metadata signature of each out-of-walk input, captured around the read that
99
+ * proved its {@link externalInputHashes} entry and recorded only once the
100
+ * observed filesystem's clock provably left the stamp's tick
101
+ * ({@link stampSeparable}). An input whose signature still holds carries the
102
+ * recorded content, so revalidation may skip the read.
103
+ *
104
+ * Keyed by lexical spelling rather than by physical identity, for the reason
105
+ * {@link TtscHostInputValidation} states: a symlink or junction spelling and
106
+ * its selected target deliberately share one identity but have different
107
+ * metadata, so an identity key would let the two overwrite each other's
108
+ * signature and force both to be re-read on every delivery.
109
+ */
110
+ externalInputSignatures?: Record<string, string>;
76
111
  /**
77
112
  * SHA-256 hash of each project-relative input path at the time of the
78
113
  * transform.
79
114
  */
80
115
  inputHashes: Record<string, string>;
116
+ /**
117
+ * Metadata signature of each {@link inputHashes} entry whose hash was proven
118
+ * against an unracing read of the file on disk, in a tick the observed
119
+ * filesystem's clock had provably left ({@link stampSeparable}).
120
+ *
121
+ * The generation's own current file is absent at capture: its recorded hash
122
+ * comes from the bundler's in-memory source, so the walk that produced it
123
+ * compared nothing. A later delivery of a sibling does compare that file's
124
+ * disk bytes against the recorded hash, and may record a signature then.
125
+ */
126
+ inputSignatures?: Record<string, string>;
81
127
  /** Metadata snapshot of every directory in the stable generation walk. */
82
128
  projectDirectories?: TtscProjectDirectorySnapshot[];
83
129
  /** Live notification state for universal host-input changes. */
84
130
  hostInputMutationTracker?: TtscProjectMutationTracker;
131
+ /**
132
+ * Live notification state for the generation's absent resolution candidates
133
+ * and the directories that carry them.
134
+ *
135
+ * Separate from the universal-input tracker because it listens for a
136
+ * different thing. Every event that can make an absent candidate present is a
137
+ * rename — the file appearing, a component of the path being created,
138
+ * replaced, or retargeted — so a change event on one of these names is never
139
+ * evidence this tracker exists to collect. What it is, on a backend that
140
+ * reports a write below a directory as a change to that directory's own entry
141
+ * (Windows does), is a dev server's steady traffic: listening for every event
142
+ * would replace the generation each time a bundler wrote inside
143
+ * `node_modules`. The filter therefore drops noise without dropping proof.
144
+ * The one appearance it cannot see is a Windows junction retargeted in place
145
+ * through `FSCTL_SET_REPARSE_POINT`, which no mainstream tool does; every
146
+ * package manager replaces the entry instead, which is a rename.
147
+ */
148
+ candidateMutationTracker?: TtscProjectMutationTracker;
149
+ /**
150
+ * Universal descriptor/config inputs proven once at generation time, then by
151
+ * metadata.
152
+ *
153
+ * Recorded state of the generation, like the input hashes and the directory
154
+ * snapshot beside it, rather than state derived from the envelope: an entry
155
+ * carries the manifest that proved it, so nothing can present one
156
+ * generation's recorded inputs under another envelope's proof.
157
+ */
158
+ hostInputValidation?: TtscHostInputValidation;
85
159
  /** Live notification state for file/directory creation, deletion, and rename. */
86
160
  projectMutationTracker?: TtscProjectMutationTracker;
87
161
  /**
@@ -137,6 +211,23 @@ export interface TtscTransformFilesystemOperations {
137
211
  statBigInt(location: string): fs.BigIntStats;
138
212
  /** Override path parsing when the observed filesystem is not the host. */
139
213
  platform?: NodeJS.Platform;
214
+ /**
215
+ * Open one directory's change notification, or throw when the observed
216
+ * filesystem cannot provide one.
217
+ *
218
+ * Left undefined, generations watch the host filesystem: `fs.watch` on POSIX
219
+ * and an isolated broker process on Windows. An embedder observing another
220
+ * filesystem supplies its own; a generation whose watch cannot be opened
221
+ * keeps validating from recorded state instead of losing its cache.
222
+ *
223
+ * Supplying one replaces the Windows broker as well, so an embedder that
224
+ * wraps Node's own `fs.watch` there gives up the isolation that contains the
225
+ * native abort Node's Windows fs-event backend can raise when a watched
226
+ * temporary tree is deleted.
227
+ */
228
+ watch?(directory: string, listener: (eventType: string, filename: string | null) => void, onError: () => void): {
229
+ close: () => void;
230
+ };
140
231
  }
141
232
  /** Normalize one directory entry under the owning filesystem's case policy. */
142
233
  export declare function normalizeHostInputName(name: string, caseSensitive: boolean): string;
@@ -159,6 +250,23 @@ export declare function beginTtscTransformBuild(cache: TtscTransformCache): void
159
250
  * many edits, so that callback cannot authorize build-scoped shortcuts.
160
251
  */
161
252
  export declare function resetTtscTransformCache(cache: TtscTransformCache): void;
253
+ /**
254
+ * What the generation already knows about one derived watch input, handed to
255
+ * the adapter so it does not rederive it per input per delivery.
256
+ *
257
+ * Both facts are generation state: the identity is the memoized
258
+ * {@link pathIdentityKey} of the input, and `missing` is the existence the
259
+ * generation recorded and every cache hit revalidates. An adapter that computes
260
+ * them itself pays a `realpath`, a case-sensitivity directory listing, and an
261
+ * `existsSync` for every input of every delivered module, which is O(modules x
262
+ * inputs) for one build (samchon/ttsc#1246).
263
+ */
264
+ export interface TtscWatchInputEvidence {
265
+ /** Memoized filesystem identity of the input. */
266
+ identity: string;
267
+ /** Whether the generation recorded this input as absent. */
268
+ missing: boolean;
269
+ }
162
270
  /**
163
271
  * Hooks the bundler adapter passes into {@link transformTtsc} so transform
164
272
  * side-channels (plugin-reported dependencies and host resolution candidates)
@@ -178,7 +286,7 @@ export interface TtscTransformHooks {
178
286
  * persistent-cache invalidation. See {@link selectWatchInputs} for the exact
179
287
  * derivation.
180
288
  */
181
- addWatchFile?: (file: string) => void;
289
+ addWatchFile?: (file: string, evidence?: TtscWatchInputEvidence) => void;
182
290
  /**
183
291
  * Invoked when the plugin declared the transformed file volatile (the
184
292
  * envelope's `volatile` list): its output depends on non-file inputs that no
@@ -213,6 +321,41 @@ export interface TtscTransformHooks {
213
321
  * per build, not per compilation.
214
322
  */
215
323
  export declare function transformTtsc(id: string, source: string, options: ResolvedTtscUnpluginOptions, aliases?: unknown, cache?: TtscTransformCache, hooks?: TtscTransformHooks): Promise<TtscTransformResult | undefined>;
324
+ interface TtscHostInputValidation {
325
+ /** Lexical input spellings that existed when the generation was captured. */
326
+ readonly entries: Map<string, {
327
+ path: string;
328
+ /**
329
+ * Whether the recorded state of this input came from reading its bytes.
330
+ * An input that existed but could not be read records a missing state, so
331
+ * no signature may stand in for it: its metadata holds still while the
332
+ * bytes behind it appear.
333
+ */
334
+ readable: boolean;
335
+ realpath: string | null;
336
+ /**
337
+ * The signature that may stand in for this entry's content comparison, or
338
+ * `undefined` when none may. A blocker keeps one regardless: it proves a
339
+ * kind and an identity rather than content.
340
+ */
341
+ signature: string | undefined;
342
+ strict?: true;
343
+ }>;
344
+ /**
345
+ * Lexical spellings the manifest accounts for, omitted from the per-module
346
+ * dependency loop below.
347
+ *
348
+ * Spellings, not identities: a symlink and its target share one identity but
349
+ * are two inputs, and skipping the alias because the manifest proved the
350
+ * target would leave the alias's own retarget unvalidated.
351
+ */
352
+ readonly covered: Set<string>;
353
+ /**
354
+ * Missing paths grouped by the nearest directory whose listing proves them
355
+ * absent.
356
+ */
357
+ readonly missing: Map<string, Set<string>>;
358
+ }
216
359
  /**
217
360
  * Strip a query string or hash fragment from a bundler module id.
218
361
  *