ttsc 0.31.1 → 0.31.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/compiler/internal/build/BuildExecution.d.ts +6 -3
- package/lib/compiler/internal/build/BuildExecution.js +40 -12
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/build/RunBuildOptions.d.ts +2 -1
- package/lib/compiler/internal/startResidentTransform.js +5 -4
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +9 -7
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/internal/javascriptRuntimeCapabilities.d.ts +13 -4
- package/lib/internal/javascriptRuntimeCapabilities.js +56 -6
- package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
- package/lib/internal/pluginSource.d.ts +1 -0
- package/lib/internal/pluginSource.js +1 -0
- package/lib/internal/pluginSource.js.map +1 -1
- package/lib/internal/resolveNodeBinary.d.ts +6 -1
- package/lib/internal/resolveNodeBinary.js +6 -2
- package/lib/internal/resolveNodeBinary.js.map +1 -1
- package/lib/internal/runtimeExecutableIdentity.d.ts +22 -13
- package/lib/internal/runtimeExecutableIdentity.js +192 -102
- package/lib/internal/runtimeExecutableIdentity.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +2 -2
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runtime/installRuntimeHooks.js +58 -4
- package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.d.ts +5 -1
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js +7 -2
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js.map +1 -1
- package/lib/launcher/internal/ttscserver/runTtscserver.js +8 -1
- package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/watch/WatchTopology.d.ts +20 -3
- package/lib/launcher/internal/watch/WatchTopology.js +31 -9
- package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
- package/lib/plugin/internal/CapabilityResolutionFormat.d.ts +21 -62
- package/lib/plugin/internal/CapabilityResolutionFormat.js +15 -106
- package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +6 -0
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +4 -4
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
- package/lib/plugin/internal/load/loadProjectPlugins.d.ts +17 -3
- package/lib/plugin/internal/load/loadProjectPlugins.js +86 -23
- package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/internal/readCapabilityResolution.d.ts +6 -0
- package/lib/plugin/internal/readCapabilityResolution.js +8 -5
- package/lib/plugin/internal/readCapabilityResolution.js.map +1 -1
- package/lib/plugin/internal/source/GoEnvironmentReading.d.ts +9 -3
- package/lib/plugin/internal/source/GoEnvironmentReading.js +53 -7
- package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -1
- package/lib/plugin/internal/source/NativeSourcePackages.d.ts +44 -6
- package/lib/plugin/internal/source/NativeSourcePackages.js +150 -8
- package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +21 -3
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +32 -7
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
- package/lib/plugin/internal/source/PluginContentIdentities.d.ts +265 -0
- package/lib/plugin/internal/source/PluginContentIdentities.js +452 -0
- package/lib/plugin/internal/source/PluginContentIdentities.js.map +1 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.d.ts +63 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.js +128 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.js.map +1 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +10 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js +12 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
- package/lib/plugin/internal/source/SourcePluginWorkspace.d.ts +13 -2
- package/lib/plugin/internal/source/SourcePluginWorkspace.js +140 -4
- package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
- package/lib/plugin/internal/source/buildSourcePlugin.d.ts +15 -2
- package/lib/plugin/internal/source/buildSourcePlugin.js +27 -4
- package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/source/computeCacheKey.d.ts +21 -5
- package/lib/plugin/internal/source/computeCacheKey.js +71 -29
- package/lib/plugin/internal/source/computeCacheKey.js.map +1 -1
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.d.ts +7 -3
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.js +74 -35
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildEnvironment.d.ts +8 -3
- package/lib/plugin/internal/source/pluginBuildEnvironment.js +8 -4
- package/lib/plugin/internal/source/pluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js +12 -2
- package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildVersions.d.ts +2 -2
- package/lib/plugin/internal/source/pluginBuildVersions.js +4 -17
- package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
- package/lib/plugin/internal/source/pluginSourceState.d.ts +6 -2
- package/lib/plugin/internal/source/pluginSourceState.js +20 -8
- package/lib/plugin/internal/source/pluginSourceState.js.map +1 -1
- package/lib/plugin/internal/source/pluginSourceStateHolds.d.ts +7 -2
- package/lib/plugin/internal/source/pluginSourceStateHolds.js +12 -7
- package/lib/plugin/internal/source/pluginSourceStateHolds.js.map +1 -1
- package/lib/plugin/internal/source/processPluginBuildEnvironment.d.ts +7 -2
- package/lib/plugin/internal/source/processPluginBuildEnvironment.js +8 -3
- package/lib/plugin/internal/source/processPluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/provenPluginSourceDigest.d.ts +28 -0
- package/lib/plugin/internal/source/provenPluginSourceDigest.js +41 -0
- package/lib/plugin/internal/source/provenPluginSourceDigest.js.map +1 -0
- package/lib/plugin/internal/source/pruneCacheFileRoot.d.ts +3 -2
- package/lib/plugin/internal/source/pruneCacheFileRoot.js +3 -2
- package/lib/plugin/internal/source/pruneCacheFileRoot.js.map +1 -1
- package/lib/plugin/internal/source/ttscPackageVersion.d.ts +26 -0
- package/lib/plugin/internal/source/ttscPackageVersion.js +47 -0
- package/lib/plugin/internal/source/ttscPackageVersion.js.map +1 -0
- package/lib/plugin/internal/writeCapabilityResolution.d.ts +6 -0
- package/lib/plugin/internal/writeCapabilityResolution.js +11 -7
- package/lib/plugin/internal/writeCapabilityResolution.js.map +1 -1
- package/lib/plugin/resolveCapabilityPlugins.js +67 -15
- package/lib/plugin/resolveCapabilityPlugins.js.map +1 -1
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +5 -1
- package/package.json +8 -8
- package/src/compiler/internal/build/BuildExecution.ts +48 -14
- package/src/compiler/internal/build/RunBuildOptions.ts +2 -1
- package/src/compiler/internal/startResidentTransform.ts +12 -3
- package/src/compiler/internal/transformProjectInMemory.ts +10 -2
- package/src/internal/javascriptRuntimeCapabilities.ts +71 -5
- package/src/internal/pluginSource.ts +1 -0
- package/src/internal/resolveNodeBinary.ts +12 -1
- package/src/internal/runtimeExecutableIdentity.ts +227 -141
- package/src/launcher/internal/runTtsc.ts +6 -2
- package/src/launcher/internal/runtime/installRuntimeHooks.ts +68 -3
- package/src/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.ts +11 -2
- package/src/launcher/internal/ttscserver/runTtscserver.ts +18 -1
- package/src/launcher/internal/watch/WatchTopology.ts +38 -9
- package/src/plugin/internal/CapabilityResolutionFormat.ts +23 -107
- package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
- package/src/plugin/internal/load/loadProjectPlugins.ts +226 -98
- package/src/plugin/internal/readCapabilityResolution.ts +17 -6
- package/src/plugin/internal/source/GoEnvironmentReading.ts +74 -5
- package/src/plugin/internal/source/NativeSourcePackages.ts +262 -5
- package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +41 -5
- package/src/plugin/internal/source/PluginContentIdentities.ts +563 -0
- package/src/plugin/internal/source/PluginLoadAnswers.ts +151 -0
- package/src/plugin/internal/source/SourceBuildCacheLayout.ts +14 -0
- package/src/plugin/internal/source/SourcePluginWorkspace.ts +176 -2
- package/src/plugin/internal/source/buildSourcePlugin.ts +43 -3
- package/src/plugin/internal/source/computeCacheKey.ts +103 -47
- package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +144 -29
- package/src/plugin/internal/source/pluginBuildEnvironment.ts +9 -2
- package/src/plugin/internal/source/pluginBuildEnvironmentWorker.ts +18 -2
- package/src/plugin/internal/source/pluginBuildVersions.ts +5 -26
- package/src/plugin/internal/source/pluginSourceState.ts +28 -12
- package/src/plugin/internal/source/pluginSourceStateHolds.ts +15 -8
- package/src/plugin/internal/source/processPluginBuildEnvironment.ts +8 -2
- package/src/plugin/internal/source/provenPluginSourceDigest.ts +45 -0
- package/src/plugin/internal/source/pruneCacheFileRoot.ts +3 -2
- package/src/plugin/internal/source/ttscPackageVersion.ts +45 -0
- package/src/plugin/internal/writeCapabilityResolution.ts +29 -8
- package/src/plugin/resolveCapabilityPlugins.ts +87 -13
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +8 -1
|
@@ -15,11 +15,13 @@ import { runBuild } from "../../../compiler/internal/build/runBuild";
|
|
|
15
15
|
import { readProjectConfig } from "../../../compiler/internal/project/readProjectConfig";
|
|
16
16
|
import { resolveOwningProjectConfig } from "../../../compiler/internal/project/resolveOwningProjectConfig";
|
|
17
17
|
import { resolveTsgo } from "../../../compiler/internal/resolveTsgo";
|
|
18
|
+
import { SidecarEnvironment } from "../../../compiler/internal/sharedHost/SidecarEnvironment";
|
|
18
19
|
import { spawnNative } from "../../../compiler/internal/spawnNative";
|
|
19
20
|
import { E2ETrace } from "../../../internal/E2ETrace";
|
|
20
21
|
import { createCanonicalTempDirectory } from "../../../internal/createCanonicalTempDirectory";
|
|
21
22
|
import { runtimeExecutableIdentity } from "../../../internal/runtimeExecutableIdentity";
|
|
22
23
|
import { moduleResolutionBaseSelects } from "../../../plugin/internal/load/moduleResolutionBaseSelects";
|
|
24
|
+
import { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
|
|
23
25
|
import { recordCacheFileUse } from "../../../plugin/internal/source/recordCacheFileUse";
|
|
24
26
|
import { buildSingleRootProject } from "../buildSingleRootProject";
|
|
25
27
|
import { inlineServedSourceMap } from "../inlineServedSourceMap";
|
|
@@ -684,6 +686,17 @@ function observePluginDescriptorInputOnce(record: {
|
|
|
684
686
|
(record.hash !== undefined && record.hash !== observedHash) ||
|
|
685
687
|
(record.realpath !== undefined && record.realpath !== observedRealpath) ||
|
|
686
688
|
(record.signature !== undefined && record.signature !== afterSignature);
|
|
689
|
+
// A missing path's signature is its nearest existing ancestor's metadata,
|
|
690
|
+
// so a write anywhere in that ancestor refuses the proof; the trace names
|
|
691
|
+
// which observation moved.
|
|
692
|
+
if (unstable)
|
|
693
|
+
E2ETrace.capabilityResolution("plugin-descriptor-input-unstable", {
|
|
694
|
+
resolved: record.resolved,
|
|
695
|
+
recordedSignature: record.signature,
|
|
696
|
+
beforeSignature,
|
|
697
|
+
afterSignature,
|
|
698
|
+
hashChanged: record.hash !== undefined && record.hash !== observedHash,
|
|
699
|
+
});
|
|
687
700
|
return `${JSON.stringify({
|
|
688
701
|
...record,
|
|
689
702
|
hash: observedHash,
|
|
@@ -1479,12 +1492,63 @@ function orphanCacheRoot(): string {
|
|
|
1479
1492
|
* Read afresh at every use, never remembered by path: a long-lived process can
|
|
1480
1493
|
* lower orphans before and after the compiler at that path is replaced, and an
|
|
1481
1494
|
* entry lowered by the new one must not be recorded under the old one's key for
|
|
1482
|
-
* a later process to adopt.
|
|
1495
|
+
* a later process to adopt. A fresh use proves an unchanged compiler from the
|
|
1496
|
+
* run's plugin cache records (`runtimeRecordStore`), which observe its metadata
|
|
1497
|
+
* again each time.
|
|
1483
1498
|
*/
|
|
1484
1499
|
function compilerIdentity(binary: string): string {
|
|
1485
|
-
return
|
|
1500
|
+
return (
|
|
1501
|
+
runtimeExecutableIdentity(binary, runtimeRecordStore()) ??
|
|
1502
|
+
crypto.randomUUID()
|
|
1503
|
+
);
|
|
1486
1504
|
}
|
|
1487
1505
|
|
|
1506
|
+
/**
|
|
1507
|
+
* The record store of this run's plugin cache root, or `undefined` without one.
|
|
1508
|
+
*
|
|
1509
|
+
* WARNING (#1726): every ttsx process and every descriptor evaluation streamed
|
|
1510
|
+
* the whole compiler executable (24.5 MB) to key its orphan and dependency
|
|
1511
|
+
* builds, although the bytes had not changed since the last process. The root
|
|
1512
|
+
* is the one a descriptor evaluator's parent load selected
|
|
1513
|
+
* (`TTSC_PLUGIN_RECORD_ROOT`), or the cache root of this run's manifest; a
|
|
1514
|
+
* process with neither streams as before and never selects a root itself.
|
|
1515
|
+
*/
|
|
1516
|
+
function runtimeRecordStore(): PluginContentIdentities.Store | undefined {
|
|
1517
|
+
const descriptorRoot =
|
|
1518
|
+
process.env.TTSC_PLUGIN_DESCRIPTOR_LOAD === "1"
|
|
1519
|
+
? SidecarEnvironment.read(process.env, "TTSC_PLUGIN_RECORD_ROOT")
|
|
1520
|
+
: undefined;
|
|
1521
|
+
const owner = RuntimeManifestRegistry.runtimeManifests().find(
|
|
1522
|
+
(candidate) =>
|
|
1523
|
+
typeof candidate.orphanCacheDir === "string" &&
|
|
1524
|
+
candidate.orphanCacheDir.length !== 0,
|
|
1525
|
+
);
|
|
1526
|
+
const root =
|
|
1527
|
+
descriptorRoot !== undefined && path.isAbsolute(descriptorRoot)
|
|
1528
|
+
? descriptorRoot
|
|
1529
|
+
: owner === undefined
|
|
1530
|
+
? undefined
|
|
1531
|
+
: path.dirname(owner.orphanCacheDir!);
|
|
1532
|
+
if (root === undefined) return undefined;
|
|
1533
|
+
// One store per root serves the process: its references only grow older,
|
|
1534
|
+
// which can make fewer stamps separable but never more, and each identity
|
|
1535
|
+
// still observes the compiler's metadata afresh.
|
|
1536
|
+
if (runtimeStore?.root !== root)
|
|
1537
|
+
runtimeStore = {
|
|
1538
|
+
root,
|
|
1539
|
+
store: PluginContentIdentities.open({
|
|
1540
|
+
projectRoot: root,
|
|
1541
|
+
cacheDir: root,
|
|
1542
|
+
env: process.env,
|
|
1543
|
+
}),
|
|
1544
|
+
};
|
|
1545
|
+
return runtimeStore.store;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
let runtimeStore:
|
|
1549
|
+
| { root: string; store: PluginContentIdentities.Store | undefined }
|
|
1550
|
+
| undefined;
|
|
1551
|
+
|
|
1488
1552
|
/** The version of this ttsc package, which owns the orphan post-processing. */
|
|
1489
1553
|
function ownPackageVersion(): string {
|
|
1490
1554
|
if (ownPackageVersionCache === undefined) {
|
|
@@ -2020,6 +2084,7 @@ function dependencyCachePaths(
|
|
|
2020
2084
|
try {
|
|
2021
2085
|
compilerProof = runtimeExecutableIdentity(
|
|
2022
2086
|
resolveTsgo({ cwd: path.dirname(tsconfig) }).binary,
|
|
2087
|
+
runtimeRecordStore(),
|
|
2023
2088
|
);
|
|
2024
2089
|
} catch {
|
|
2025
2090
|
// An unobservable executable gets a unique, non-reusable generation key.
|
|
@@ -2045,7 +2110,7 @@ function dependencyCachePaths(
|
|
|
2045
2110
|
function assertCompilerStillCurrent(tsconfig: string, proof?: string): void {
|
|
2046
2111
|
if (proof === undefined) return;
|
|
2047
2112
|
const binary = resolveTsgo({ cwd: path.dirname(tsconfig) }).binary;
|
|
2048
|
-
const current = runtimeExecutableIdentity(binary);
|
|
2113
|
+
const current = runtimeExecutableIdentity(binary, runtimeRecordStore());
|
|
2049
2114
|
if (current !== proof) {
|
|
2050
2115
|
E2ETrace.capabilityResolution("runtime-compiler-publication-refused", {
|
|
2051
2116
|
tsconfig,
|
|
@@ -34,7 +34,8 @@ import { LSPProjectInputDigest } from "./LSPProjectInputDigest";
|
|
|
34
34
|
* detected drift; unavailable markers or changes between reads are not a proof
|
|
35
35
|
* that the complete filesystem stayed fixed throughout capture.
|
|
36
36
|
*
|
|
37
|
-
* @param loaded The plugin load of the session
|
|
37
|
+
* @param loaded The plugin load of the session, with the project whose plugin
|
|
38
|
+
* cache proves its plugin sources from records.
|
|
38
39
|
* @returns The inputs, or `undefined` when the load no longer describes the
|
|
39
40
|
* filesystem and the selection has to be loaded again.
|
|
40
41
|
* @evidence contracts/common.md#principled-implementation The manifest groups candidate/source-file digests by directory. Recorded content and physical-target proofs and all plugin-source states are compared again before return; a detected mismatch rejects capture, while sequential reads and unproven paths do not certify every post-load change.
|
|
@@ -53,6 +54,7 @@ export function captureLSPPluginSelectionInputs(loaded: {
|
|
|
53
54
|
hostInputRealpaths: Readonly<Record<string, string | null>>;
|
|
54
55
|
hostInputs: readonly string[];
|
|
55
56
|
pluginSources: Readonly<Record<string, string>>;
|
|
57
|
+
project?: { root: string };
|
|
56
58
|
}): ILSPPluginSelectionInputs | undefined {
|
|
57
59
|
const excluded = new Set(
|
|
58
60
|
loaded.deferredHostInputs.map((file) => path.resolve(file)),
|
|
@@ -85,8 +87,15 @@ export function captureLSPPluginSelectionInputs(loaded: {
|
|
|
85
87
|
)
|
|
86
88
|
)
|
|
87
89
|
return undefined;
|
|
90
|
+
// The load's project names the plugin cache whose records prove the sources
|
|
91
|
+
// and the SDK, so a session start does not read them again (#1725).
|
|
88
92
|
for (const [directory, state] of Object.entries(loaded.pluginSources))
|
|
89
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
!pluginSourceStateHolds(directory, state, {
|
|
95
|
+
projectRoot: loaded.project?.root,
|
|
96
|
+
})
|
|
97
|
+
)
|
|
98
|
+
return undefined;
|
|
90
99
|
return {
|
|
91
100
|
omittedNames: GoSourceInputs.OMITTED_SOURCE_FILE_NAMES,
|
|
92
101
|
omittedSuffixes: GoSourceInputs.OMITTED_SOURCE_FILE_SUFFIXES,
|
|
@@ -13,6 +13,7 @@ import { spawnNative } from "../../../compiler/internal/spawnNative";
|
|
|
13
13
|
import { E2ETrace } from "../../../internal/E2ETrace";
|
|
14
14
|
import { resolveNodeBinary } from "../../../internal/resolveNodeBinary";
|
|
15
15
|
import { loadProjectPlugins } from "../../../plugin/internal/load/loadProjectPlugins";
|
|
16
|
+
import type { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
|
|
16
17
|
import type { ITtscLoadedNativePlugin } from "../../../structures/internal/ITtscLoadedNativePlugin";
|
|
17
18
|
import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITtscParsedProjectConfig";
|
|
18
19
|
import type { ITtscProjectIdentity } from "../../../structures/internal/ITtscProjectIdentity";
|
|
@@ -165,6 +166,13 @@ type LSPExecutionContext = {
|
|
|
165
166
|
*/
|
|
166
167
|
selectionInputs?: ILSPPluginSelectionInputs;
|
|
167
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Record store the confirming plugin load opened, which proves the Node
|
|
171
|
+
* runtime from metadata instead of streaming it again (#1726); absent for a
|
|
172
|
+
* project without plugins, which gains no cache directory.
|
|
173
|
+
*/
|
|
174
|
+
identities?: PluginContentIdentities.Store;
|
|
175
|
+
|
|
168
176
|
tsgoBinary: string;
|
|
169
177
|
};
|
|
170
178
|
|
|
@@ -196,6 +204,7 @@ function resolveTtscserverEnv(launch: LSPLaunchOptions): TtscserverEnvironment {
|
|
|
196
204
|
const context = resolveLspExecutionContext(launch);
|
|
197
205
|
const env = lspSidecarEnvironment({
|
|
198
206
|
cwd: context.projectContext?.logicalProjectRoot ?? path.resolve(launch.cwd),
|
|
207
|
+
identities: context.identities,
|
|
199
208
|
pluginConfigOrigin: context.projectContext?.pluginConfigOrigin,
|
|
200
209
|
tsgoBinary: context.tsgoBinary,
|
|
201
210
|
});
|
|
@@ -244,6 +253,7 @@ function resolveTtscserverEnv(launch: LSPLaunchOptions): TtscserverEnvironment {
|
|
|
244
253
|
|
|
245
254
|
function lspSidecarEnvironment(options: {
|
|
246
255
|
cwd: string;
|
|
256
|
+
identities: PluginContentIdentities.Store | undefined;
|
|
247
257
|
pluginConfigOrigin: string | undefined;
|
|
248
258
|
tsgoBinary: string;
|
|
249
259
|
}): NodeJS.ProcessEnv {
|
|
@@ -253,7 +263,7 @@ function lspSidecarEnvironment(options: {
|
|
|
253
263
|
SidecarEnvironment.read(process.env, "TTSC_TTSX_BINARY") ??
|
|
254
264
|
path.join(__dirname, "..", "..", "..", "launcher", "ttsx.js"),
|
|
255
265
|
});
|
|
256
|
-
const node = resolveNodeBinary(env, options.cwd);
|
|
266
|
+
const node = resolveNodeBinary(env, options.cwd, options.identities);
|
|
257
267
|
SidecarEnvironment.write(env, "TTSC_NODE_BINARY", node);
|
|
258
268
|
SidecarEnvironment.write(
|
|
259
269
|
env,
|
|
@@ -305,6 +315,7 @@ function resolveLspExecutionContext(
|
|
|
305
315
|
resolveFrom: __filename,
|
|
306
316
|
});
|
|
307
317
|
const initialProjectInputs = captureInitialLSPProjectInputs({
|
|
318
|
+
identities: loaded.identities,
|
|
308
319
|
nativePlugins: loaded.nativePlugins,
|
|
309
320
|
pluginConfigOrigin,
|
|
310
321
|
project: selectedProject,
|
|
@@ -323,6 +334,7 @@ function resolveLspExecutionContext(
|
|
|
323
334
|
resolveFrom: __filename,
|
|
324
335
|
});
|
|
325
336
|
const confirmedProjectInputs = captureInitialLSPProjectInputs({
|
|
337
|
+
identities: confirmation.identities,
|
|
326
338
|
nativePlugins: confirmation.nativePlugins,
|
|
327
339
|
pluginConfigOrigin,
|
|
328
340
|
project: confirmedProject,
|
|
@@ -345,6 +357,9 @@ function resolveLspExecutionContext(
|
|
|
345
357
|
initialProjectInputs: confirmedProjectInputs,
|
|
346
358
|
nativePlugins: confirmation.nativePlugins,
|
|
347
359
|
selectionInputs,
|
|
360
|
+
...(confirmation.identities === undefined
|
|
361
|
+
? {}
|
|
362
|
+
: { identities: confirmation.identities }),
|
|
348
363
|
projectContext: {
|
|
349
364
|
...confirmedProject.identity,
|
|
350
365
|
...(pluginConfigOrigin === undefined ? {} : { pluginConfigOrigin }),
|
|
@@ -377,6 +392,7 @@ function loadLSPProjectPlugins(
|
|
|
377
392
|
}
|
|
378
393
|
|
|
379
394
|
function captureInitialLSPProjectInputs(options: {
|
|
395
|
+
identities: PluginContentIdentities.Store | undefined;
|
|
380
396
|
nativePlugins: readonly ITtscLoadedNativePlugin[];
|
|
381
397
|
pluginConfigOrigin: string | undefined;
|
|
382
398
|
project: ITtscParsedProjectConfig;
|
|
@@ -414,6 +430,7 @@ function captureInitialLSPProjectInputs(options: {
|
|
|
414
430
|
}
|
|
415
431
|
const env = lspSidecarEnvironment({
|
|
416
432
|
cwd: options.project.root,
|
|
433
|
+
identities: options.identities,
|
|
417
434
|
pluginConfigOrigin: options.pluginConfigOrigin,
|
|
418
435
|
tsgoBinary: options.tsgoBinary,
|
|
419
436
|
});
|
|
@@ -14,9 +14,9 @@ import { type ProjectInputPathIdentityContext } from "../../../internal/pathIden
|
|
|
14
14
|
import { createProjectInputPathIdentityContext } from "../../../internal/pathIdentity/createProjectInputPathIdentityContext";
|
|
15
15
|
import { isProjectInputPathIdentityWithin } from "../../../internal/pathIdentity/isProjectInputPathIdentityWithin";
|
|
16
16
|
import { resolveProjectInputPath } from "../../../internal/pathIdentity/resolveProjectInputPath";
|
|
17
|
+
import { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
|
|
17
18
|
import { collectPluginSourceDirectories } from "../../../plugin/internal/source/collectPluginSourceDirectories";
|
|
18
19
|
import { pluginSourceCovers } from "../../../plugin/internal/source/pluginSourceCovers";
|
|
19
|
-
import { pluginSourceDigest } from "../../../plugin/internal/source/pluginSourceDigest";
|
|
20
20
|
import { prunesPluginSourceDirectory } from "../../../plugin/internal/source/prunesPluginSourceDirectory";
|
|
21
21
|
import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITtscParsedProjectConfig";
|
|
22
22
|
import type { ITtscProjectInputSnapshot } from "../../../structures/internal/ITtscProjectInputSnapshot";
|
|
@@ -84,6 +84,13 @@ export class WatchTopology {
|
|
|
84
84
|
*/
|
|
85
85
|
private pluginInputStates = new Map<string, string>();
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The record store of the latest plugin load that reported the inputs, which
|
|
89
|
+
* proves their states from the plugin cache's records (#1728). Absent before
|
|
90
|
+
* a load reports one, or when it opened none; the states are read then.
|
|
91
|
+
*/
|
|
92
|
+
private pluginInputIdentities: PluginContentIdentities.Store | undefined;
|
|
93
|
+
|
|
87
94
|
/**
|
|
88
95
|
* The plugin inputs notifications named since the last decision, by path key,
|
|
89
96
|
* each with the locations noted for it (`notePluginNotification`).
|
|
@@ -331,17 +338,33 @@ export class WatchTopology {
|
|
|
331
338
|
* observation. This does not freeze the source tree or prove every transient
|
|
332
339
|
* edit was seen.
|
|
333
340
|
*
|
|
341
|
+
* WARNING (#1728): take each baseline through the reporting load's record
|
|
342
|
+
* store (`identities`), never by reading the sources again. The load proves
|
|
343
|
+
* the same directories from the plugin cache's records a moment later, and a
|
|
344
|
+
* content read here cost every watch session start 0.3-0.6 s for typia's Go
|
|
345
|
+
* module. The store proves only a digest whose metadata still holds and is
|
|
346
|
+
* separable from its reference, so the baseline is the digest a content read
|
|
347
|
+
* would return at this moment.
|
|
348
|
+
*
|
|
349
|
+
* @param inputs Plugin source directories and files the load reported.
|
|
350
|
+
* @param identities The reporting load's record store, if it opened one.
|
|
334
351
|
* @evidence contracts/common.md#principled-implementation Published plugin inputs define the source corpus, and retaining existing pre-build baselines keeps unreported edits visible.
|
|
335
352
|
* @evidence contracts/common.md#clear-and-simple-design Deduplication, baseline retention and compiler/watch reconciliation remain ordered phases.
|
|
336
353
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The actual plugin source digest applies its owning source-selection and pruning policy; named plugins or fixture output do not replace observed input state.
|
|
337
354
|
* @evidence contracts/common.md#meaningful-documentation Native prose explains baseline timing and why existing inputs are not restamped following the documentation skill.
|
|
338
355
|
* @evidence contracts/portability.md#os-neutral-implementation Native path resolution and canonical backend registration govern source trees; lexical keys preserve declarations and unknown names, folding ASCII only under measured insensitive parents rather than an OS-derived rule.
|
|
339
|
-
* @evidence contracts/performance.md#efficient-algorithms Input/path keys deduplicate with text and native case/ancestor work; only newly admitted inputs compute initial source digests
|
|
340
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Unchanged ordered input sets return immediately; existing inputs reuse their recorded digest until actual notification processing establishes changed consumed state.
|
|
356
|
+
* @evidence contracts/performance.md#efficient-algorithms Input/path keys deduplicate with text and native case/ancestor work; only newly admitted inputs compute initial source digests, each proven from the selected files' metadata when the load's records hold and read from the selected pruned corpus otherwise; refresh(false) also pays project/compiler listing, directory traversal and watcher reconciliation; fixed publication count does not bound delegated bytes or backend work.
|
|
357
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Unchanged ordered input sets return immediately; existing inputs reuse their recorded digest until actual notification processing establishes changed consumed state; a new input's digest is the one the load recorded while its metadata holds.
|
|
341
358
|
* @evidence contracts/performance.md#bound-retention-and-release-resources Publications replace input/digest populations and case-observation caches without a population cap; synchronization attempts obsolete watcher closure. Native/supplied closure has its own lifetime and can throw; owner close does not prove every backend has stopped or every retained snapshot byte was released.
|
|
342
359
|
*/
|
|
343
|
-
public setExtraInputs(
|
|
360
|
+
public setExtraInputs(
|
|
361
|
+
inputs: readonly string[],
|
|
362
|
+
identities?: PluginContentIdentities.Store,
|
|
363
|
+
): void {
|
|
344
364
|
this.beginLexicalTransaction();
|
|
365
|
+
// The latest load's store also serves later notifications; its references
|
|
366
|
+
// only grow older, which can make fewer stamps separable but never more.
|
|
367
|
+
this.pluginInputIdentities = identities;
|
|
345
368
|
const next = uniqueExistingPaths(inputs, this.lexicalIdentities);
|
|
346
369
|
if (arraysEqual(this.extraInputs, next)) return;
|
|
347
370
|
this.extraInputs = next;
|
|
@@ -354,7 +377,8 @@ export class WatchTopology {
|
|
|
354
377
|
const key = this.pathKey(input);
|
|
355
378
|
states.set(
|
|
356
379
|
key,
|
|
357
|
-
this.pluginInputStates.get(key) ??
|
|
380
|
+
this.pluginInputStates.get(key) ??
|
|
381
|
+
pluginInputState(input, this.pluginInputIdentities),
|
|
358
382
|
);
|
|
359
383
|
}
|
|
360
384
|
this.pluginInputStates = states;
|
|
@@ -1664,7 +1688,7 @@ export class WatchTopology {
|
|
|
1664
1688
|
const key = this.pathKey(input);
|
|
1665
1689
|
const locations = pending.get(key);
|
|
1666
1690
|
if (locations === undefined) continue;
|
|
1667
|
-
const state = pluginInputState(input);
|
|
1691
|
+
const state = pluginInputState(input, this.pluginInputIdentities);
|
|
1668
1692
|
if (this.pluginInputStates.get(key) === state) continue;
|
|
1669
1693
|
this.pluginInputStates.set(key, state);
|
|
1670
1694
|
for (const location of locations) {
|
|
@@ -2739,12 +2763,17 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
2739
2763
|
* What a plugin build keys a plugin input on: the digest of the files its build
|
|
2740
2764
|
* reads from a source directory (`pluginSourceDigest`), or a file's bytes. An
|
|
2741
2765
|
* input that cannot be read has a state of its own, so the change that made it
|
|
2742
|
-
* unreadable is reported and the build names the failure.
|
|
2766
|
+
* unreadable is reported and the build names the failure. A directory's digest
|
|
2767
|
+
* comes from the load's record store while the selected files' metadata holds
|
|
2768
|
+
* there (#1728), and from the files otherwise.
|
|
2743
2769
|
*/
|
|
2744
|
-
function pluginInputState(
|
|
2770
|
+
function pluginInputState(
|
|
2771
|
+
input: string,
|
|
2772
|
+
identities: PluginContentIdentities.Store | undefined,
|
|
2773
|
+
): string {
|
|
2745
2774
|
try {
|
|
2746
2775
|
return WatchPaths.isDirectory(input)
|
|
2747
|
-
? `directory:${
|
|
2776
|
+
? `directory:${PluginContentIdentities.sourceDirectory(identities, input)}`
|
|
2748
2777
|
: `file:${fingerprintProjectInputFile(input)}`;
|
|
2749
2778
|
} catch (error) {
|
|
2750
2779
|
return `unreadable:${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
5
4
|
import { SidecarEnvironment } from "../../compiler/internal/sharedHost/SidecarEnvironment";
|
|
6
5
|
import { runtimeExecutableIdentity } from "../../internal/runtimeExecutableIdentity";
|
|
6
|
+
import type { PluginContentIdentities } from "./source/PluginContentIdentities";
|
|
7
7
|
import { SourceBuildCacheLayout } from "./source/SourceBuildCacheLayout";
|
|
8
8
|
import { resolveSourceBuildCachePaths } from "./source/resolveSourceBuildCachePaths";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The on-disk format of the capability-resolution cache, shared by its reader
|
|
12
|
-
* and writer so both agree on the entry's location
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* and writer so both agree on the entry's location and version tag. What proves
|
|
13
|
+
* an entry still describes the project is the host inputs' states and the
|
|
14
|
+
* plugin sources' states, each by the rule its producer applies; a plugin
|
|
15
|
+
* source's metadata signature and the clock reference it is judged against
|
|
16
|
+
* belong to `PluginContentIdentities`, which every cross-process plugin proof
|
|
17
|
+
* shares.
|
|
16
18
|
*
|
|
17
|
-
* @evidence contracts/common.md#principled-implementation Reader and writer share format/key
|
|
18
|
-
* @evidence contracts/common.md#clear-and-simple-design The namespace owns format identity and
|
|
19
|
-
* @evidence contracts/common.md#prohibited-implementation-shortcuts Version tags and NUL delimiters encode the persistence contract
|
|
20
|
-
* @evidence contracts/common.md#meaningful-documentation Native JSDoc explains format evolution, unforgeable path separation and
|
|
21
|
-
* @evidence contracts/portability.md#os-neutral-implementation Native path joins
|
|
22
|
-
* @evidence contracts/performance.md#efficient-algorithms Key construction orders and serializes environment/name bytes, streams runtime content and delegates native ancestor/manifest/cache-layout selection.
|
|
23
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Project/version keys share one answer across requested capabilities
|
|
24
|
-
* @
|
|
19
|
+
* @evidence contracts/common.md#principled-implementation Reader and writer share format/key rules; source-metadata witnesses come from the shared identity owner, so this cache and the build path apply one separable-stamp rule.
|
|
20
|
+
* @evidence contracts/common.md#clear-and-simple-design The namespace owns format identity and entry location, while entry acceptance, answer production and metadata witnesses stay with their owners.
|
|
21
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Version tags and NUL delimiters encode the persistence contract rather than guessed reuse.
|
|
22
|
+
* @evidence contracts/common.md#meaningful-documentation Native JSDoc explains format evolution, unforgeable path separation and the witness owner in separate paragraphs; function/tag spacing follows the documentation skill.
|
|
23
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native path joins and cache-root resolution preserve OS-neutral filesystem behavior.
|
|
24
|
+
* @evidence contracts/performance.md#efficient-algorithms Key construction orders and serializes environment/name bytes, streams runtime content and delegates native ancestor/manifest/cache-layout selection.
|
|
25
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Project/version keys share one answer across requested capabilities.
|
|
26
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Cache entries belong to the source-cache pruning owner; this namespace retains nothing.
|
|
25
27
|
*/
|
|
26
28
|
export namespace CapabilityResolutionFormat {
|
|
27
29
|
/**
|
|
@@ -106,6 +108,13 @@ export namespace CapabilityResolutionFormat {
|
|
|
106
108
|
|
|
107
109
|
/** Effective environment selecting workspace-local or explicit storage. */
|
|
108
110
|
env?: NodeJS.ProcessEnv;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Record store of the project's plugin cache. WARNING (#1725): without it
|
|
114
|
+
* every lookup streamed the whole runtime executable, several times per
|
|
115
|
+
* capability resolution, in every process.
|
|
116
|
+
*/
|
|
117
|
+
identities?: PluginContentIdentities.Store;
|
|
109
118
|
}): string | null {
|
|
110
119
|
const environment = SidecarEnvironment.merge(options.env ?? process.env);
|
|
111
120
|
const runtime =
|
|
@@ -115,7 +124,7 @@ export namespace CapabilityResolutionFormat {
|
|
|
115
124
|
let root: string;
|
|
116
125
|
let runtimeIdentity: string;
|
|
117
126
|
try {
|
|
118
|
-
const observed = runtimeExecutableIdentity(runtime);
|
|
127
|
+
const observed = runtimeExecutableIdentity(runtime, options.identities);
|
|
119
128
|
if (observed === undefined) return null;
|
|
120
129
|
runtimeIdentity = observed;
|
|
121
130
|
root = resolveSourceBuildCachePaths(
|
|
@@ -152,97 +161,4 @@ export namespace CapabilityResolutionFormat {
|
|
|
152
161
|
`${key}.json`,
|
|
153
162
|
);
|
|
154
163
|
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* A clock reference for plugin source signatures, minted by a write of the
|
|
158
|
-
* cache's own beside the entry: the modification stamp a fresh probe file got
|
|
159
|
-
* there, by the device that reported it.
|
|
160
|
-
*
|
|
161
|
-
* A stamp strictly older than the same device's fresh reference satisfies
|
|
162
|
-
* this cache's separation policy. Reuse still assumes the filesystem's
|
|
163
|
-
* reported metadata reflects writes; the comparison cannot certify arbitrary
|
|
164
|
-
* timestamp restoration or future clock behavior. The probe is the cache's
|
|
165
|
-
* own file, named for this call, and removal is attempted at once, so
|
|
166
|
-
* concurrent readers never lend each other a reference. A failed write, or a
|
|
167
|
-
* plugin source on another device, leaves nothing separable, and the proof
|
|
168
|
-
* reads the files.
|
|
169
|
-
*
|
|
170
|
-
* @param entry The entry file, whose directory the probe is written in.
|
|
171
|
-
* @evidence contracts/common.md#principled-implementation A unique freshly-written file supplies its native device/mtime reference; acquisition failure clears the witness, and the source observer requires a matching device and strictly older reported stamp under the metadata policy.
|
|
172
|
-
* @evidence contracts/common.md#clear-and-simple-design The function returns only device-to-stamp evidence and owns its transient probe from creation through cleanup.
|
|
173
|
-
* @evidence contracts/common.md#prohibited-implementation-shortcuts Random probe identity avoids borrowing a concurrent call's witness; failed acquisition does not substitute wall-clock guesses for device evidence.
|
|
174
|
-
* @evidence contracts/common.md#meaningful-documentation Native JSDoc explains ended ticks, cross-device refusal and probe lifetime in separate paragraphs; tag separation follows the documentation skill.
|
|
175
|
-
* @evidence contracts/portability.md#os-neutral-implementation Native joins and bigint lstat use the actual filesystem's device/stamp values; no fixed timestamp precision or OS-specific temp path is assumed.
|
|
176
|
-
* @evidence contracts/performance.md#efficient-algorithms Probe path/UUID/hrtime text construction and mkdir/write/lstat/removal queries produce a witness without reading plugin source bytes; native path/directory work remains and is not bounded by source count.
|
|
177
|
-
*
|
|
178
|
-
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each proof needs a fresh clock reference; sharing an earlier probe would defeat the rollback/separation premise.
|
|
179
|
-
*
|
|
180
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources The unique call-owned probe is removed in finally even after acquisition failure; removal is best effort and the returned map contains at most its single observed device.
|
|
181
|
-
*/
|
|
182
|
-
export function clockReference(entry: string): ReadonlyMap<bigint, bigint> {
|
|
183
|
-
const references = new Map<bigint, bigint>();
|
|
184
|
-
const probe = path.join(
|
|
185
|
-
path.dirname(entry),
|
|
186
|
-
`clock-${process.pid}-${crypto.randomUUID()}.probe`,
|
|
187
|
-
);
|
|
188
|
-
try {
|
|
189
|
-
fs.mkdirSync(path.dirname(probe), { recursive: true });
|
|
190
|
-
fs.writeFileSync(probe, `${process.hrtime.bigint()}\n`);
|
|
191
|
-
const stats = fs.lstatSync(probe, { bigint: true });
|
|
192
|
-
references.set(stats.dev, stats.mtimeNs);
|
|
193
|
-
} catch {
|
|
194
|
-
references.clear();
|
|
195
|
-
} finally {
|
|
196
|
-
try {
|
|
197
|
-
fs.rmSync(probe, { force: true });
|
|
198
|
-
} catch {
|
|
199
|
-
// A leftover probe contains this call's hrtime text, not source data.
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return references;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* One plugin source file's metadata signature, and whether its stamp is
|
|
207
|
-
* separable from `reference`, for `pluginSourceFilesSignature`.
|
|
208
|
-
*
|
|
209
|
-
* The files the digest reads are regular files (`collectPluginSourceFiles`),
|
|
210
|
-
* so these fields are the observer's metadata signature. Native metadata
|
|
211
|
-
* reflecting a write/replacement is a reuse premise; the signature is not
|
|
212
|
-
* itself a content hash or proof against arbitrary timestamp restoration.
|
|
213
|
-
*
|
|
214
|
-
* @param reference The clock reference minted for this proof.
|
|
215
|
-
* @evidence contracts/common.md#principled-implementation lstat device/inode/link/mode/size/timestamps form the regular-file metadata signature; strict mtime-before-reference marks separation for a matching device under the cache's native metadata policy, without observing content.
|
|
216
|
-
* @evidence contracts/common.md#clear-and-simple-design A closure carries one proof's device references and maps each requested file to signature/separation together.
|
|
217
|
-
* @evidence contracts/common.md#prohibited-implementation-shortcuts Unreadable files return no witness, and unmatched device or same/newer stamps cannot authorize digest reuse.
|
|
218
|
-
* @evidence contracts/common.md#meaningful-documentation Native JSDoc states the regular-file premise and reference origin, with separate acknowledgment prose following the documentation skill.
|
|
219
|
-
* @evidence contracts/portability.md#os-neutral-implementation Metadata comes from native bigint lstat and actual device IDs; separators join numeric fields only and no host path is manually parsed.
|
|
220
|
-
* @evidence contracts/performance.md#efficient-algorithms Each invocation performs native path/lstat work and formats seven bigint fields without reading content; fixed field count does not remove native lookup or numeric-text costs.
|
|
221
|
-
* @evidence contracts/performance.md#reuse-equivalent-work The closure reuses one reference map across files in the same proof, while each file's current metadata is read anew.
|
|
222
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources The closure retains the supplied reference map until its consumer releases it; clockReference supplies at most one entry, but arbitrary callers can supply larger or mutable maps. No file handle/global source population is retained and reference immutability is a caller premise.
|
|
223
|
-
*/
|
|
224
|
-
export function sourceEvidence(
|
|
225
|
-
reference: ReadonlyMap<bigint, bigint>,
|
|
226
|
-
): (file: string) => { separable: boolean; signature: string } | undefined {
|
|
227
|
-
return (file) => {
|
|
228
|
-
try {
|
|
229
|
-
const stats = fs.lstatSync(file, { bigint: true });
|
|
230
|
-
const minted = reference.get(stats.dev);
|
|
231
|
-
return {
|
|
232
|
-
separable: minted !== undefined && stats.mtimeNs < minted,
|
|
233
|
-
signature: [
|
|
234
|
-
stats.dev,
|
|
235
|
-
stats.ino,
|
|
236
|
-
stats.nlink,
|
|
237
|
-
stats.mode,
|
|
238
|
-
stats.size,
|
|
239
|
-
stats.mtimeNs,
|
|
240
|
-
stats.ctimeNs,
|
|
241
|
-
].join(":"),
|
|
242
|
-
};
|
|
243
|
-
} catch {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
164
|
}
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { SidecarEnvironment } from "../../../compiler/internal/sharedHost/SidecarEnvironment";
|
|
6
6
|
import { runtimeExecutableIdentity } from "../../../internal/runtimeExecutableIdentity";
|
|
7
|
+
import type { PluginContentIdentities } from "../source/PluginContentIdentities";
|
|
7
8
|
import { SourceBuildCacheLayout } from "../source/SourceBuildCacheLayout";
|
|
8
9
|
import { recordCacheFileUse } from "../source/recordCacheFileUse";
|
|
9
10
|
import { resolveSourceBuildCachePaths } from "../source/resolveSourceBuildCachePaths";
|
|
@@ -127,20 +128,26 @@ export namespace PluginDescriptorEvaluationCache {
|
|
|
127
128
|
|
|
128
129
|
/** Ttsc build version, invalidating answers from another build. */
|
|
129
130
|
version: string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Record store proving the runtimes' bytes from metadata (#1723); without
|
|
134
|
+
* it each runtime is streamed again.
|
|
135
|
+
*/
|
|
136
|
+
identities?: PluginContentIdentities.Store;
|
|
130
137
|
}): string | null {
|
|
131
138
|
const environment = SidecarEnvironment.merge(props.env);
|
|
132
139
|
// Startup preloads can run before the descriptor recorder and read inputs
|
|
133
140
|
// it never sees; a stable option string cannot prove their current meaning.
|
|
134
141
|
if (SidecarEnvironment.read(environment, "NODE_OPTIONS")?.trim())
|
|
135
142
|
return null;
|
|
136
|
-
const runtime = runtimeIdentity(props.runtime);
|
|
143
|
+
const runtime = runtimeIdentity(props.runtime, props.identities);
|
|
137
144
|
if (runtime === null) return null;
|
|
138
145
|
const additionalRuntime =
|
|
139
146
|
props.additionalRuntime === undefined
|
|
140
147
|
? undefined
|
|
141
148
|
: props.additionalRuntime === props.runtime
|
|
142
149
|
? runtime
|
|
143
|
-
: runtimeIdentity(props.additionalRuntime);
|
|
150
|
+
: runtimeIdentity(props.additionalRuntime, props.identities);
|
|
144
151
|
if (additionalRuntime === null) return null;
|
|
145
152
|
let root: string;
|
|
146
153
|
try {
|
|
@@ -358,8 +365,11 @@ export namespace PluginDescriptorEvaluationCache {
|
|
|
358
365
|
* content digest and bracketed lexical/physical metadata, so replaced bytes
|
|
359
366
|
* cannot reuse an evaluation by restoring size/timestamps. Null is unproved.
|
|
360
367
|
*/
|
|
361
|
-
function runtimeIdentity(
|
|
362
|
-
|
|
368
|
+
function runtimeIdentity(
|
|
369
|
+
runtime: string,
|
|
370
|
+
identities: PluginContentIdentities.Store | undefined,
|
|
371
|
+
): string | null {
|
|
372
|
+
return runtimeExecutableIdentity(runtime, identities) ?? null;
|
|
363
373
|
}
|
|
364
374
|
|
|
365
375
|
/**
|