ttsc 0.35.0 → 0.36.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/driver/compilation_snapshot.go +267 -0
- package/driver/compilation_snapshot_keeps_inputs_across_stages_test.go +130 -0
- package/driver/compilation_snapshot_preserves_checked_emission_test.go +100 -0
- package/driver/graph.go +9 -3
- package/driver/program.go +45 -14
- package/lib/compiler/internal/CompilationSnapshot.d.ts +33 -0
- package/lib/compiler/internal/CompilationSnapshot.js +90 -0
- package/lib/compiler/internal/CompilationSnapshot.js.map +1 -0
- package/lib/compiler/internal/build/BuildExecution.d.ts +7 -4
- package/lib/compiler/internal/build/BuildExecution.js +68 -5
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/build/appendBuildOutput.js +4 -0
- package/lib/compiler/internal/build/appendBuildOutput.js.map +1 -1
- package/lib/compiler/internal/build/runExternalEmitProvenance.d.ts +17 -14
- package/lib/compiler/internal/build/runExternalEmitProvenance.js +71 -42
- package/lib/compiler/internal/build/runExternalEmitProvenance.js.map +1 -1
- package/lib/compiler/internal/parseNativeTransformOutput.js +4 -0
- package/lib/compiler/internal/parseNativeTransformOutput.js.map +1 -1
- package/lib/compiler/internal/sharedHost/inheritedSidecarEnv.d.ts +2 -1
- package/lib/compiler/internal/sharedHost/inheritedSidecarEnv.js +4 -1
- package/lib/compiler/internal/sharedHost/inheritedSidecarEnv.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.d.ts +3 -3
- package/lib/compiler/internal/transformProjectInMemory.js +29 -5
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +5 -0
- package/package.json +8 -8
- package/src/compiler/internal/CompilationSnapshot.ts +97 -0
- package/src/compiler/internal/build/BuildExecution.ts +96 -4
- package/src/compiler/internal/build/appendBuildOutput.ts +4 -0
- package/src/compiler/internal/build/runExternalEmitProvenance.ts +95 -64
- package/src/compiler/internal/parseNativeTransformOutput.ts +5 -0
- package/src/compiler/internal/sharedHost/inheritedSidecarEnv.ts +8 -1
- package/src/compiler/internal/transformProjectInMemory.ts +46 -5
- package/src/structures/ITtscCompilerTransformation.ts +6 -0
|
@@ -16,18 +16,21 @@ import { PassthroughFlags } from "./PassthroughFlags";
|
|
|
16
16
|
* when its read-only option and source probes succeeded. Compiler status,
|
|
17
17
|
* diagnostics and user-requested file-list display remain owned by that run.
|
|
18
18
|
*
|
|
19
|
-
* Admission requires stable effective options
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
19
|
+
* Admission requires stable effective options and physical source paths before
|
|
20
|
+
* and after emission, plus unchanged response files and selected executable
|
|
21
|
+
* bytes. A reported source list proves the emitting selection independently of
|
|
22
|
+
* roots rediscovered by showConfig after emission. When an unreported host's
|
|
23
|
+
* shown roots change, the union of inspected source populations must still
|
|
24
|
+
* predict a unique physical owner for each previously predicted write; new
|
|
25
|
+
* sources can only introduce ambiguity. Response inspection preserves native
|
|
26
|
+
* argument-frame boundaries and operand positions; the producer and its probes
|
|
27
|
+
* receive the original argv. The executable's change time is excluded from
|
|
28
|
+
* cross-command equality because that metadata changed in real executions
|
|
29
|
+
* without changing the executable object or bytes; source contents belong to
|
|
30
|
+
* the compiler's own Program. Each executable read brackets its full metadata.
|
|
31
|
+
* This is an external observation, not an atomic compiler-generation ledger; a
|
|
32
|
+
* concurrent change restored between observations can remain invisible.
|
|
33
|
+
* Unsupported layouts and ambiguous outputs carry an empty ownership list.
|
|
31
34
|
*
|
|
32
35
|
* A nonzero compiler status can still carry proved writes for an unchecked
|
|
33
36
|
* consumer. Ownership metadata never changes that status or its diagnostics.
|
|
@@ -47,12 +50,12 @@ import { PassthroughFlags } from "./PassthroughFlags";
|
|
|
47
50
|
* in emittedSourceProofFailures. These explanations preserve compiler streams
|
|
48
51
|
* and status; they do not replace missing ownership with a successful claim.
|
|
49
52
|
*
|
|
50
|
-
* @evidence contracts/common.md#principled-implementation Actual TSFILE writes are matched against the supported upstream per-source extension and explicit-root layout under matching compiler-selected inputs and stable physical/content observations. No emitted filename alone establishes ownership; observation gaps remain documented.
|
|
53
|
+
* @evidence contracts/common.md#principled-implementation Actual TSFILE writes are matched against the supported upstream per-source extension and explicit-root layout under matching compiler-selected inputs and stable physical source identities and response/executable content observations. Parsed settings exclude rediscovered showConfig.files. A reporting producer proves its emitting selection; an observed host with changed roots rechecks source selection and includes additional candidates only to reject conflicting physical owners of previously predicted writes. No emitted filename alone establishes ownership; observation gaps remain documented.
|
|
51
54
|
* @evidence contracts/common.md#clear-and-simple-design One adapter owns read-only compiler probes, the single delegated emission and admission; private helpers isolate option parsing, physical observation and path prediction.
|
|
52
55
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The selected executable and original arguments remain authoritative. Unknown layouts are reported unknown instead of switching compilers, guessing same-stem ownership or consulting source maps.
|
|
53
56
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain producer preservation, unknown states and the non-atomic observation limitation; the investigation record documents the supported upstream naming subset.
|
|
54
57
|
* @evidence contracts/portability.md#os-neutral-implementation Native node:path and filesystem realpath/stat separate writer spelling from physical source identity. Executables receive separate argv entries through spawnNative, without a shell or OS-based case folding.
|
|
55
|
-
* @evidence contracts/performance.md#efficient-algorithms Source and output indexes avoid cross-product matching. Response inspection uses an iterative frame stack and active physical-path set.
|
|
58
|
+
* @evidence contracts/performance.md#efficient-algorithms Source and output indexes avoid cross-product matching; changed observed roots add one native source probe and a deduplicated union of the two populations. Response inspection uses an iterative frame stack and active physical-path set. Response/executable observations read and hash complete files; source identity checks resolve paths without comparing mutable text; showConfig text, source/output lists, signature JSON, candidate path components and byte buffers are processed at their actual sizes alongside native probes/path operations. No native duration or report-size ceiling follows from using one index.
|
|
56
59
|
* @evidence contracts/performance.md#reuse-equivalent-work Each source observation and predicted output index is shared by all outputs in this invocation. Cross-build reuse is unavailable because arbitrary external producer and filesystem dependencies have no invalidation contract.
|
|
57
60
|
* @evidence contracts/performance.md#bound-retention-and-release-resources Response/config/source tokens and predicted owner sets live for this invocation; returned provenance/refusal records transfer to the caller. Retained bytes grow with complete reports, expanded arguments, paths and observations, while individual file reads allocate full file buffers. Probe capture cleanup is delegated and may be best effort; no timeout, output ceiling or descendant-release certification is provided here.
|
|
58
61
|
*/
|
|
@@ -144,7 +147,7 @@ export function runExternalEmitProvenance(options: {
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
// An observed host lists nothing; it compiles the same selected tsconfig, and
|
|
147
|
-
// the source observations below still prove that selection
|
|
150
|
+
// the source identity observations below still prove that selection retained its physical owners.
|
|
148
151
|
const listed = observed
|
|
149
152
|
? probe.files
|
|
150
153
|
: compilerSourceList(lines, options.cwd);
|
|
@@ -159,6 +162,7 @@ export function runExternalEmitProvenance(options: {
|
|
|
159
162
|
: `Emitting source selection differs from its inspection (${probe.files.length} inspected, ${listed.length} reported); first differing inspected path ${JSON.stringify(probe.files[firstDifference])}, reported path ${JSON.stringify(listed[firstDifference === -1 ? probe.files.length : firstDifference])}.`;
|
|
160
163
|
let stage = "response-file reinspection";
|
|
161
164
|
try {
|
|
165
|
+
let sources = probe.files;
|
|
162
166
|
for (const [file, before] of probe.responseObservations) {
|
|
163
167
|
if (!stable) break;
|
|
164
168
|
stable = CompilerArgumentsInspection.observeInputFile(file) === before;
|
|
@@ -166,10 +170,22 @@ export function runExternalEmitProvenance(options: {
|
|
|
166
170
|
failure = `Compiler response file changed across emission: ${JSON.stringify(file)}.`;
|
|
167
171
|
}
|
|
168
172
|
stage = "effective configuration reinspection";
|
|
169
|
-
if (stable
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
if (stable) {
|
|
174
|
+
const after = readConfig(options);
|
|
175
|
+
if (after?.signature !== probe.signature) {
|
|
176
|
+
stable = false;
|
|
177
|
+
failure =
|
|
178
|
+
"Effective configuration changed or could not be reinspected after emission.";
|
|
179
|
+
} else if (
|
|
180
|
+
observed &&
|
|
181
|
+
JSON.stringify(after.config.files) !==
|
|
182
|
+
JSON.stringify(probe.config.files)
|
|
183
|
+
) {
|
|
184
|
+
stage = "post-emission source-list inspection";
|
|
185
|
+
// An added root can import an output-colliding source, so inspect its
|
|
186
|
+
// full dependency selection rather than only predicting that root.
|
|
187
|
+
sources = [...new Set([...sources, ...readSourceList(options)])];
|
|
188
|
+
}
|
|
173
189
|
}
|
|
174
190
|
stage = `executable observation of ${JSON.stringify(probe.binaryPath)}`;
|
|
175
191
|
if (stable) {
|
|
@@ -185,10 +201,10 @@ export function runExternalEmitProvenance(options: {
|
|
|
185
201
|
for (const [source, before] of probe.observations) {
|
|
186
202
|
if (!stable) break;
|
|
187
203
|
stage = `source observation of ${JSON.stringify(source)}`;
|
|
188
|
-
const after =
|
|
204
|
+
const after = observeSourceIdentity(source);
|
|
189
205
|
stable = after === before;
|
|
190
206
|
if (!stable)
|
|
191
|
-
failure = `Selected source changed across emission: ${JSON.stringify(source)}
|
|
207
|
+
failure = `Selected source identity changed across emission: ${JSON.stringify(source)}.`;
|
|
192
208
|
}
|
|
193
209
|
for (const [file, before] of probe.responseObservations) {
|
|
194
210
|
if (!stable) break;
|
|
@@ -202,14 +218,15 @@ export function runExternalEmitProvenance(options: {
|
|
|
202
218
|
const predictOutputs = createOutputPrediction(probe.config, probe.base);
|
|
203
219
|
let predictedSources = 0;
|
|
204
220
|
let sourceExample: string | undefined;
|
|
205
|
-
for (const source of
|
|
221
|
+
for (const source of sources) {
|
|
206
222
|
const outputs = predictOutputs(source);
|
|
207
223
|
if (outputs.length === 0) continue;
|
|
208
224
|
predictedSources++;
|
|
209
225
|
sourceExample ??= source;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
// Observed writes were predicted before the host ran. A newly selected
|
|
227
|
+
// source can make their ownership ambiguous, never establish a new write.
|
|
228
|
+
const physicalSource =
|
|
229
|
+
probe.observations.get(source) ?? observeSourceIdentity(source);
|
|
213
230
|
for (const output of outputs) {
|
|
214
231
|
const sources = owners.get(output) ?? new Set<string>();
|
|
215
232
|
sources.add(physicalSource);
|
|
@@ -335,43 +352,11 @@ function prepareProbe(
|
|
|
335
352
|
"Original argv does not establish a supported selected configuration directory.",
|
|
336
353
|
);
|
|
337
354
|
stage = "compiler source-list inspection";
|
|
338
|
-
const
|
|
339
|
-
options.binary,
|
|
340
|
-
[
|
|
341
|
-
...options.args,
|
|
342
|
-
"--listFilesOnly",
|
|
343
|
-
"true",
|
|
344
|
-
"--listEmittedFiles",
|
|
345
|
-
"false",
|
|
346
|
-
],
|
|
347
|
-
options,
|
|
348
|
-
);
|
|
349
|
-
if (listed.error || listed.status === null || listed.signal !== null)
|
|
350
|
-
return unavailable(
|
|
351
|
-
`Selected producer source-list inspection did not complete normally (status ${listed.status}, signal ${listed.signal}, error ${listed.error?.message ?? "none"}).`,
|
|
352
|
-
);
|
|
353
|
-
const files = compilerSourceList(
|
|
354
|
-
String(listed.stdout).split(/\r?\n/),
|
|
355
|
-
options.cwd,
|
|
356
|
-
);
|
|
357
|
-
if (
|
|
358
|
-
files.length === 0 ||
|
|
359
|
-
!files.every(
|
|
360
|
-
(file) =>
|
|
361
|
-
path.isAbsolute(file) &&
|
|
362
|
-
/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$/.test(file),
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
return unavailable(
|
|
366
|
-
`Source-list inspection returned ${files.length} paths and no complete supported source population; first unsupported candidate: ${JSON.stringify(files.find((file) => !path.isAbsolute(file) || !/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$/.test(file)))}.`,
|
|
367
|
-
);
|
|
355
|
+
const files = readSourceList(options);
|
|
368
356
|
const observations = new Map<string, string>();
|
|
369
357
|
for (const source of files) {
|
|
370
358
|
stage = `initial source observation of ${JSON.stringify(source)}`;
|
|
371
|
-
observations.set(
|
|
372
|
-
source,
|
|
373
|
-
CompilerArgumentsInspection.observeInputFile(source),
|
|
374
|
-
);
|
|
359
|
+
observations.set(source, observeSourceIdentity(source));
|
|
375
360
|
}
|
|
376
361
|
for (const [file, before] of inspected.observations)
|
|
377
362
|
if (CompilerArgumentsInspection.observeInputFile(file) !== before)
|
|
@@ -394,6 +379,37 @@ function prepareProbe(
|
|
|
394
379
|
}
|
|
395
380
|
}
|
|
396
381
|
|
|
382
|
+
/** Both source inspections use the same native reporting and admission rules. */
|
|
383
|
+
function readSourceList(
|
|
384
|
+
options: Parameters<typeof runExternalEmitProvenance>[0],
|
|
385
|
+
): string[] {
|
|
386
|
+
const listed = spawnNative(
|
|
387
|
+
options.binary,
|
|
388
|
+
[...options.args, "--listFilesOnly", "true", "--listEmittedFiles", "false"],
|
|
389
|
+
options,
|
|
390
|
+
);
|
|
391
|
+
if (listed.error || listed.status === null || listed.signal !== null)
|
|
392
|
+
throw new Error(
|
|
393
|
+
`Selected producer source-list inspection did not complete normally (status ${listed.status}, signal ${listed.signal}, error ${listed.error?.message ?? "none"}).`,
|
|
394
|
+
);
|
|
395
|
+
const files = compilerSourceList(
|
|
396
|
+
String(listed.stdout).split(/\r?\n/),
|
|
397
|
+
options.cwd,
|
|
398
|
+
);
|
|
399
|
+
if (
|
|
400
|
+
files.length === 0 ||
|
|
401
|
+
!files.every(
|
|
402
|
+
(file) =>
|
|
403
|
+
path.isAbsolute(file) &&
|
|
404
|
+
/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$/.test(file),
|
|
405
|
+
)
|
|
406
|
+
)
|
|
407
|
+
throw new Error(
|
|
408
|
+
`Source-list inspection returned ${files.length} paths and no complete supported source population; first unsupported candidate: ${JSON.stringify(files.find((file) => !path.isAbsolute(file) || !/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$/.test(file)))}.`,
|
|
409
|
+
);
|
|
410
|
+
return files;
|
|
411
|
+
}
|
|
412
|
+
|
|
397
413
|
/**
|
|
398
414
|
* Compiler diagnostics may start with an absolute filename too. Exclude only
|
|
399
415
|
* recognized diagnostic renderings, preserving every other absolute candidate
|
|
@@ -411,14 +427,20 @@ function compilerSourceList(lines: readonly string[], cwd: string): string[] {
|
|
|
411
427
|
);
|
|
412
428
|
}
|
|
413
429
|
|
|
414
|
-
/**
|
|
430
|
+
/**
|
|
431
|
+
* ShowConfig resolves inherited settings and argv overrides in the producer.
|
|
432
|
+
* Its expanded files describe the current directory, not the completed emit.
|
|
433
|
+
* Root changes are handled separately from settings: reporting producers name
|
|
434
|
+
* their emitting sources, while observed hosts recheck possible contributors.
|
|
435
|
+
*/
|
|
415
436
|
function readConfig(options: Parameters<typeof runExternalEmitProvenance>[0]):
|
|
416
437
|
| {
|
|
417
438
|
config: {
|
|
418
439
|
compilerOptions: Record<string, unknown>;
|
|
419
440
|
references?: unknown[];
|
|
441
|
+
files?: string[];
|
|
420
442
|
};
|
|
421
|
-
|
|
443
|
+
signature: string;
|
|
422
444
|
}
|
|
423
445
|
| undefined {
|
|
424
446
|
const result = spawnNative(
|
|
@@ -427,8 +449,7 @@ function readConfig(options: Parameters<typeof runExternalEmitProvenance>[0]):
|
|
|
427
449
|
options,
|
|
428
450
|
);
|
|
429
451
|
if (result.error || result.status !== 0) return undefined;
|
|
430
|
-
const
|
|
431
|
-
const config = JSON.parse(text);
|
|
452
|
+
const config = JSON.parse(String(result.stdout));
|
|
432
453
|
if (
|
|
433
454
|
!config ||
|
|
434
455
|
typeof config !== "object" ||
|
|
@@ -438,7 +459,9 @@ function readConfig(options: Parameters<typeof runExternalEmitProvenance>[0]):
|
|
|
438
459
|
(config.references !== undefined && !Array.isArray(config.references))
|
|
439
460
|
)
|
|
440
461
|
return undefined;
|
|
441
|
-
|
|
462
|
+
const settings = { ...config };
|
|
463
|
+
delete settings.files;
|
|
464
|
+
return { config, signature: JSON.stringify(settings) };
|
|
442
465
|
}
|
|
443
466
|
|
|
444
467
|
/** ShowConfig path options are relative to the selected config's directory. */
|
|
@@ -644,3 +667,11 @@ function writtenJavaScript(stdout: string, cwd: string): string[] {
|
|
|
644
667
|
}
|
|
645
668
|
return [...outputs];
|
|
646
669
|
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Source ownership is a physical path; text freshness belongs to cache
|
|
673
|
+
* admission.
|
|
674
|
+
*/
|
|
675
|
+
function observeSourceIdentity(source: string): string {
|
|
676
|
+
return fs.realpathSync.native(source);
|
|
677
|
+
}
|
|
@@ -339,6 +339,7 @@ export function parseReferenceGraph(
|
|
|
339
339
|
return undefined;
|
|
340
340
|
}
|
|
341
341
|
const section = value as {
|
|
342
|
+
compilationSnapshot?: unknown;
|
|
342
343
|
candidates?: unknown;
|
|
343
344
|
configs?: unknown;
|
|
344
345
|
edges?: unknown;
|
|
@@ -420,6 +421,10 @@ export function parseReferenceGraph(
|
|
|
420
421
|
}
|
|
421
422
|
return {
|
|
422
423
|
...(Object.keys(candidates).length === 0 ? {} : { candidates }),
|
|
424
|
+
...(typeof section.compilationSnapshot === "string" &&
|
|
425
|
+
section.compilationSnapshot.length !== 0
|
|
426
|
+
? { compilationSnapshot: section.compilationSnapshot }
|
|
427
|
+
: {}),
|
|
423
428
|
configs,
|
|
424
429
|
edges,
|
|
425
430
|
globals,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CompilationSnapshot } from "../CompilationSnapshot";
|
|
1
2
|
import { SidecarEnvironment } from "./SidecarEnvironment";
|
|
2
3
|
import { clearInheritedSemanticConfigPath } from "./clearInheritedSemanticConfigPath";
|
|
3
4
|
import { clearInheritedTsgoArgs } from "./clearInheritedTsgoArgs";
|
|
@@ -6,7 +7,8 @@ import { publishLinkedTransformPlugins } from "./publishLinkedTransformPlugins";
|
|
|
6
7
|
/**
|
|
7
8
|
* Merge inherited and caller environments with native variable-name identity,
|
|
8
9
|
* then clear per-invocation payloads this lane never published: forwarded tsgo
|
|
9
|
-
* arguments, semantic config ownership and the
|
|
10
|
+
* arguments, compilation snapshot, semantic config ownership and the
|
|
11
|
+
* linked-plugin manifest.
|
|
10
12
|
*
|
|
11
13
|
* Use it at every spawn whose child can reach `driver.LoadProgram` — the
|
|
12
14
|
* `api-compile` / `api-transform` hosts, and the plugin loader, whose `ttsx`
|
|
@@ -40,6 +42,11 @@ export function inheritedSidecarEnv(
|
|
|
40
42
|
);
|
|
41
43
|
clearInheritedTsgoArgs(env, callerEnv);
|
|
42
44
|
clearInheritedSemanticConfigPath(env, callerEnv);
|
|
45
|
+
SidecarEnvironment.write(
|
|
46
|
+
env,
|
|
47
|
+
CompilationSnapshot.ENV,
|
|
48
|
+
SidecarEnvironment.read(callerEnv, CompilationSnapshot.ENV),
|
|
49
|
+
);
|
|
43
50
|
publishLinkedTransformPlugins(env, callerEnv, []);
|
|
44
51
|
return env;
|
|
45
52
|
}
|
|
@@ -11,6 +11,7 @@ import type { ITtscCompilerTransformation } from "../../structures/ITtscCompiler
|
|
|
11
11
|
import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
|
|
12
12
|
import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscParsedProjectConfig";
|
|
13
13
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
14
|
+
import { CompilationSnapshot } from "./CompilationSnapshot";
|
|
14
15
|
import { appendBuildOutput, mergeCheckGraphs } from "./build/appendBuildOutput";
|
|
15
16
|
import { normalizeBuildOutput } from "./build/normalizeBuildOutput";
|
|
16
17
|
import { buildNativeCompiler } from "./buildNativeCompiler";
|
|
@@ -60,10 +61,10 @@ import { spawnNative } from "./spawnNative";
|
|
|
60
61
|
* @evidence contracts/common.md#clear-and-simple-design One router composes built-in and plugin-backed transforms; envelope parsers, environment construction, proof merging and negative-only observation limits have separate shared helpers.
|
|
61
62
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Missing input proof cannot be repaired by a postcompile baseline; conventional deferred config reads accept only the actual native consumer's proof, while malformed required TypeScript output is a protocol error.
|
|
62
63
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs describe transform lanes and stage ordering; separated result members distinguish output, advisory graph/dependency data, evaluation witnesses and unstable source markers under documentation guidance.
|
|
63
|
-
* @evidence contracts/performance.md#efficient-algorithms Stage filtering, declared-input Sets and proof maps scale with plugin/input counts; merged paths are sorted with path-text comparison costs. Each stage can rehash/re-realpath declared inputs, serialize plugin/config records and parse complete output; discovery, native capability probes, source builds and child execution remain delegated costs of this call. No byte/work ceiling or measured dominant-cost ranking is asserted.
|
|
64
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Source-plugin artifacts are shared by their owning loader; this transform intentionally produces a current generation and gives downstream consumers the graph, source states and host witnesses required to qualify reuse.
|
|
64
|
+
* @evidence contracts/performance.md#efficient-algorithms Stage filtering, declared-input Sets and proof maps scale with plugin/input counts; merged paths are sorted with path-text comparison costs. Each stage can rehash/re-realpath declared inputs, serialize plugin/config records and parse complete output; discovery, native capability probes, source builds and child execution remain delegated costs of this call. The invocation snapshot adds full captured-population parsing and dirty document encoding/writes per native stage. No byte/work ceiling or measured dominant-cost ranking is asserted.
|
|
65
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Descriptor/source discovery precedes snapshot allocation so invocation-private paths do not vary its environment cache identity. Source-plugin artifacts are shared by their owning loader; this transform intentionally produces a current generation and gives downstream consumers the graph, source states and host witnesses required to qualify reuse.
|
|
65
66
|
*
|
|
66
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources This router retains discovery records, per-stage proof maps and accumulated diagnostics/output until synchronous stage completion, then transfers the returned envelope.
|
|
67
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources This router retains discovery records, per-stage proof maps and accumulated diagnostics/output until synchronous stage completion, then transfers the returned envelope. The invocation coordinator attempts identity-checked snapshot removal and propagates or aggregates removal failure. Other native capture/observation owners attempt their own cleanup; suppressed cleanup failures and descendant release remain unconfirmed where those owners do not join them. Artifact-cache retention belongs to the builder, and no stage timeout or input/output byte ceiling is supplied here.
|
|
67
68
|
*
|
|
68
69
|
* @evidence contracts/portability.md#os-neutral-implementation Native paths remain filesystem coordinates, child argv are arrays, and environment helpers preserve Windows key equivalence and explicit Node/tsgo/config anchors without shell parsing or native method patching.
|
|
69
70
|
*/
|
|
@@ -123,6 +124,16 @@ export function transformProjectInMemory(options: ITtscCompilerContext): {
|
|
|
123
124
|
projectRoot: options.projectRoot,
|
|
124
125
|
tsconfig: options.tsconfig,
|
|
125
126
|
});
|
|
127
|
+
return CompilationSnapshot.run(options.env, (env) =>
|
|
128
|
+
transformWithSnapshot({ ...options, env }, loaded),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Run native checks and transformation after descriptor/source discovery. */
|
|
133
|
+
function transformWithSnapshot(
|
|
134
|
+
options: ITtscCompilerContext,
|
|
135
|
+
loaded: ReturnType<typeof loadProjectPlugins>,
|
|
136
|
+
): ReturnType<typeof transformProjectInMemory> {
|
|
126
137
|
if (loaded.nativePlugins.length !== 0) {
|
|
127
138
|
// Every step below runs binaries built from these sources, so the output
|
|
128
139
|
// is a function of their state, which the envelope carries for a consumer
|
|
@@ -393,7 +404,11 @@ function transformProjectWithPlugins(
|
|
|
393
404
|
);
|
|
394
405
|
return {
|
|
395
406
|
...envelopeSideChannels(transformed),
|
|
396
|
-
graph:
|
|
407
|
+
graph: mergeTransformGraphs(
|
|
408
|
+
checked.graph,
|
|
409
|
+
transformed.graph,
|
|
410
|
+
checks.length,
|
|
411
|
+
),
|
|
397
412
|
...observationLimitations(
|
|
398
413
|
[loaded, checked, transformed],
|
|
399
414
|
hostInputHashes,
|
|
@@ -476,7 +491,7 @@ function transformProjectWithPlugins(
|
|
|
476
491
|
);
|
|
477
492
|
return {
|
|
478
493
|
...envelopeSideChannels(output),
|
|
479
|
-
graph:
|
|
494
|
+
graph: mergeTransformGraphs(checked.graph, output.graph, checks.length),
|
|
480
495
|
...observationLimitations(
|
|
481
496
|
[loaded, checked, output],
|
|
482
497
|
hostInputHashes,
|
|
@@ -708,6 +723,7 @@ function runNativeChecks(
|
|
|
708
723
|
stdout: "",
|
|
709
724
|
stderr: "",
|
|
710
725
|
};
|
|
726
|
+
let snapshotsComplete = true;
|
|
711
727
|
for (const plugin of checks) {
|
|
712
728
|
const checked = runNativeCheckWithObservations(plugin, (extraArgs) => {
|
|
713
729
|
const res = spawnNative(
|
|
@@ -748,7 +764,12 @@ function runNativeChecks(
|
|
|
748
764
|
project.root,
|
|
749
765
|
);
|
|
750
766
|
});
|
|
767
|
+
snapshotsComplete &&= checked.graph?.compilationSnapshot !== undefined;
|
|
751
768
|
result = appendBuildOutput(result, checked);
|
|
769
|
+
if (!snapshotsComplete && result.graph?.compilationSnapshot !== undefined) {
|
|
770
|
+
const { compilationSnapshot: _snapshot, ...unqualified } = result.graph;
|
|
771
|
+
result.graph = unqualified;
|
|
772
|
+
}
|
|
752
773
|
if (result.status !== 0) {
|
|
753
774
|
return result;
|
|
754
775
|
}
|
|
@@ -877,3 +898,23 @@ function nativePluginEnv(
|
|
|
877
898
|
);
|
|
878
899
|
return env;
|
|
879
900
|
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* A legacy check with no graph cannot certify the transform's shared input
|
|
904
|
+
* view.
|
|
905
|
+
*/
|
|
906
|
+
function mergeTransformGraphs(
|
|
907
|
+
checked: TtscBuildResult["graph"],
|
|
908
|
+
transformed: TtscBuildResult["graph"],
|
|
909
|
+
checks: number,
|
|
910
|
+
): TtscBuildResult["graph"] {
|
|
911
|
+
const graph = mergeCheckGraphs(checked, transformed);
|
|
912
|
+
if (
|
|
913
|
+
graph === undefined ||
|
|
914
|
+
checks === 0 ||
|
|
915
|
+
checked?.compilationSnapshot !== undefined
|
|
916
|
+
)
|
|
917
|
+
return graph;
|
|
918
|
+
const { compilationSnapshot: _snapshot, ...unqualified } = graph;
|
|
919
|
+
return unqualified;
|
|
920
|
+
}
|
|
@@ -51,6 +51,12 @@ export namespace ITtscCompilerTransformation {
|
|
|
51
51
|
* @evidence contracts/portability.md#os-neutral-implementation Graph keys use the producer's cwd-relative slash spelling with absolute slash paths for outside-root or cross-volume inputs; this protocol spelling is distinct from reported native realpaths carried by inputRealpaths and observations, which can retain lexical fallback on native VFS errors. The reported compiler case policy governs compiler membership comparisons rather than an OS-name assumption; absent policy or proof does not establish native filesystem capabilities.
|
|
52
52
|
*/
|
|
53
53
|
export interface IReferenceGraph {
|
|
54
|
+
/**
|
|
55
|
+
* Shared native read-once input view; current delivery authority, never
|
|
56
|
+
* disk freshness.
|
|
57
|
+
*/
|
|
58
|
+
compilationSnapshot?: string;
|
|
59
|
+
|
|
54
60
|
/**
|
|
55
61
|
* Effective native possible-root suffixes and output-directory exclusions.
|
|
56
62
|
* A report that differs from a host's provisional policy requires a new
|