ttsc 0.31.1 → 0.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/compiler/internal/build/BuildExecution.d.ts +4 -2
- package/lib/compiler/internal/build/BuildExecution.js +40 -12
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -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/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/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 +12 -1
- package/lib/plugin/internal/load/loadProjectPlugins.js +80 -20
- 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 +5 -2
- package/lib/plugin/internal/source/SourcePluginWorkspace.js +5 -3
- 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/package.json +8 -8
- package/src/compiler/internal/build/BuildExecution.ts +46 -13
- 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/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/plugin/internal/CapabilityResolutionFormat.ts +23 -107
- package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
- package/src/plugin/internal/load/loadProjectPlugins.ts +212 -94
- 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 +6 -1
- 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
|
@@ -9,6 +9,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const captureProcessOutput_1 = require("../compiler/internal/captureProcessOutput");
|
|
11
11
|
const SidecarEnvironment_1 = require("../compiler/internal/sharedHost/SidecarEnvironment");
|
|
12
|
+
const PluginLoadAnswers_1 = require("../plugin/internal/source/PluginLoadAnswers");
|
|
12
13
|
const E2ETrace_1 = require("./E2ETrace");
|
|
13
14
|
const OwnedSynchronousProcess_1 = require("./OwnedSynchronousProcess");
|
|
14
15
|
const isSpawnSyncFdExhaustion_1 = require("./isSpawnSyncFdExhaustion");
|
|
@@ -23,24 +24,48 @@ const spawnSyncWithLowDescriptors_1 = require("./spawnSyncWithLowDescriptors");
|
|
|
23
24
|
* Cache-ineligible candidates do not acquire executable proofs used only by
|
|
24
25
|
* cache lookup and publication.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
+
* With a record store, the executable identity is proven from metadata and a
|
|
28
|
+
* probe answer recorded for that identity is reused by a later process (#1723);
|
|
29
|
+
* a recorded answer must still name an executable this runtime is.
|
|
30
|
+
*
|
|
31
|
+
* @param runtime The interpreter to probe.
|
|
32
|
+
* @param env Caller environment merged over this process's.
|
|
33
|
+
* @param cwd Working directory of the probe.
|
|
34
|
+
* @param identities Optional record store of the caller's plugin cache.
|
|
35
|
+
* @evidence contracts/common.md#principled-implementation A child reports its own feature availability and executable; byte-aware before/after identity and same-executable proof authorize reuse only for a stable absolute runtime without NODE_OPTIONS preload authority, in process or through an answer recorded under that identity.
|
|
27
36
|
* @evidence contracts/common.md#clear-and-simple-design One probe owner coordinates identity, spawning and parsing; the shared fingerprint helper owns file-content proof and the low-descriptor helper owns the POSIX launch distinction.
|
|
28
37
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Feature availability comes from the actual interpreter rather than its name; descriptor fallback handles a supported kernel constraint and failures produce unsupported capabilities, not fabricated success.
|
|
29
38
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain measured capabilities, reuse restrictions and freshness, with comments separating platform launch and cache permission from tags.
|
|
30
39
|
* @evidence contracts/portability.md#os-neutral-implementation Shared environment merge and lookup apply native name identity to caller precedence and NODE_OPTIONS preload authority, including Windows aliases; Node spawn receives an executable and argv without shell syntax, and only POSIX descriptor exhaustion uses the isolated broker.
|
|
31
|
-
* @evidence contracts/performance.md#efficient-algorithms Environment merge/name handling first decides cache eligibility. Eligible hits read B executable bytes; eligible misses acquire before/after
|
|
32
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Stable absolute candidates that report their own executable share measured capability results, including false feature flags; changed executable bytes, link/target identity, preload options or wrapper identity require another probe, while failed probes without executable identity are not reused.
|
|
40
|
+
* @evidence contracts/performance.md#efficient-algorithms Environment merge/name handling first decides cache eligibility. Eligible hits prove the executable from a record (three stats and one small read) or read B executable bytes; eligible misses acquire before/after identities around the actual probe. Ineligible calls retain the fresh child without reading executable bytes solely for the forbidden cache. Probe spawning/parsing, a possible descriptor-exhaustion retry and capture-file reads add text/environment/native lookup costs and transient output storage; this adapter sets no explicit byte quota.
|
|
41
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Stable absolute candidates that report their own executable share measured capability results, including false feature flags, within the process and across processes through the record store keyed by the full identity; changed executable bytes, link/target identity, preload options or wrapper identity require another probe, while failed probes without executable identity are not reused.
|
|
33
42
|
* @evidence contracts/performance.md#bound-retention-and-release-resources The synchronous probe owns child completion and fallback captures; finally attempts capture close/removal, whose helper suppresses cleanup failures, so native/file release is unconfirmed on failure. Capability entries retain absolute spellings and path/identity text with no eviction quota; failed identity validation can remove an entry. Returned capability copies transfer to callers.
|
|
34
43
|
*/
|
|
35
|
-
function javascriptRuntimeCapabilities(runtime, env, cwd) {
|
|
44
|
+
function javascriptRuntimeCapabilities(runtime, env, cwd, identities) {
|
|
36
45
|
const effectiveEnv = SidecarEnvironment_1.SidecarEnvironment.merge(process.env, env);
|
|
37
46
|
const cacheKey = runtimeCapabilityCacheKey(runtime, effectiveEnv);
|
|
38
|
-
const beforeIdentity = cacheKey === undefined
|
|
47
|
+
const beforeIdentity = cacheKey === undefined
|
|
48
|
+
? undefined
|
|
49
|
+
: (0, runtimeExecutableIdentity_1.runtimeExecutableIdentity)(runtime, identities);
|
|
39
50
|
if (cacheKey !== undefined && beforeIdentity !== undefined) {
|
|
40
51
|
const cached = runtimeCapabilityCache.get(cacheKey);
|
|
41
52
|
if (cached?.identity === beforeIdentity)
|
|
42
53
|
return { ...cached.capabilities };
|
|
43
54
|
runtimeCapabilityCache.delete(cacheKey);
|
|
55
|
+
// WARNING (#1723): a launch is a new process, so the memo above is empty
|
|
56
|
+
// on every CLI run. The same proven executable answers the same probe; the
|
|
57
|
+
// record store carries that answer across processes.
|
|
58
|
+
const recorded = recordedCapabilities(PluginLoadAnswers_1.PluginLoadAnswers.read(identities, RUNTIME_CAPABILITIES, [
|
|
59
|
+
cacheKey,
|
|
60
|
+
beforeIdentity,
|
|
61
|
+
]), runtime);
|
|
62
|
+
if (recorded !== undefined) {
|
|
63
|
+
runtimeCapabilityCache.set(cacheKey, {
|
|
64
|
+
capabilities: { ...recorded },
|
|
65
|
+
identity: beforeIdentity,
|
|
66
|
+
});
|
|
67
|
+
return recorded;
|
|
68
|
+
}
|
|
44
69
|
}
|
|
45
70
|
const args = [
|
|
46
71
|
"-e",
|
|
@@ -104,7 +129,9 @@ function javascriptRuntimeCapabilities(runtime, env, cwd) {
|
|
|
104
129
|
// false for either feature and still be reused under the executable proof.
|
|
105
130
|
// This removes a process spawn from the common path without
|
|
106
131
|
// authorizing a replaced or redirected runtime in a long-lived host.
|
|
107
|
-
const afterIdentity = cacheKey === undefined
|
|
132
|
+
const afterIdentity = cacheKey === undefined
|
|
133
|
+
? undefined
|
|
134
|
+
: (0, runtimeExecutableIdentity_1.runtimeExecutableIdentity)(runtime, identities);
|
|
108
135
|
if (cacheKey !== undefined &&
|
|
109
136
|
capabilities.executable !== undefined &&
|
|
110
137
|
beforeIdentity !== undefined &&
|
|
@@ -114,9 +141,32 @@ function javascriptRuntimeCapabilities(runtime, env, cwd) {
|
|
|
114
141
|
capabilities: { ...capabilities },
|
|
115
142
|
identity: afterIdentity,
|
|
116
143
|
});
|
|
144
|
+
PluginLoadAnswers_1.PluginLoadAnswers.write(identities, RUNTIME_CAPABILITIES, [cacheKey, afterIdentity], capabilities);
|
|
117
145
|
}
|
|
118
146
|
return capabilities;
|
|
119
147
|
}
|
|
148
|
+
/** Answer family of measured runtime capabilities (`PluginLoadAnswers`). */
|
|
149
|
+
const RUNTIME_CAPABILITIES = "javascript-runtime-capabilities";
|
|
150
|
+
/**
|
|
151
|
+
* A recorded probe answer in the shape the probe produces, still naming an
|
|
152
|
+
* executable this runtime is, or `undefined`.
|
|
153
|
+
*/
|
|
154
|
+
function recordedCapabilities(value, runtime) {
|
|
155
|
+
if (typeof value !== "object" || value === null)
|
|
156
|
+
return undefined;
|
|
157
|
+
const recorded = value;
|
|
158
|
+
if (typeof recorded.bun !== "boolean" ||
|
|
159
|
+
typeof recorded.registerHooks !== "boolean" ||
|
|
160
|
+
typeof recorded.executable !== "string" ||
|
|
161
|
+
!node_path_1.default.isAbsolute(recorded.executable) ||
|
|
162
|
+
!sameRuntimeExecutable(runtime, recorded.executable))
|
|
163
|
+
return undefined;
|
|
164
|
+
return {
|
|
165
|
+
bun: recorded.bun,
|
|
166
|
+
executable: recorded.executable,
|
|
167
|
+
registerHooks: recorded.registerHooks,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
120
170
|
const runtimeCapabilityCache = new Map();
|
|
121
171
|
/** True when a probe ran the candidate itself rather than a mutable wrapper. */
|
|
122
172
|
function sameRuntimeExecutable(candidate, executable) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"javascriptRuntimeCapabilities.js","sourceRoot":"","sources":["../../src/internal/javascriptRuntimeCapabilities.ts"],"names":[],"mappings":";;;;;;AAAA,4EAA8C;AAC9C,sDAAyB;AACzB,0DAA6B;AAE7B,oFAAiF;AACjF,2FAAwF;
|
|
1
|
+
{"version":3,"file":"javascriptRuntimeCapabilities.js","sourceRoot":"","sources":["../../src/internal/javascriptRuntimeCapabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,5 +22,6 @@ export * from "../plugin/internal/source/pluginSourceDigest";
|
|
|
22
22
|
export * from "../plugin/internal/source/pluginSourceState";
|
|
23
23
|
export * from "../plugin/internal/source/pluginSourceStateHolds";
|
|
24
24
|
export * from "../plugin/internal/source/processPluginBuildEnvironment";
|
|
25
|
+
export * from "../plugin/internal/source/provenPluginSourceDigest";
|
|
25
26
|
export * from "../plugin/internal/source/PluginBuildEnvironmentReadings";
|
|
26
27
|
export * from "../plugin/internal/source/prunesPluginSourceDirectory";
|
|
@@ -38,6 +38,7 @@ __exportStar(require("../plugin/internal/source/pluginSourceDigest"), exports);
|
|
|
38
38
|
__exportStar(require("../plugin/internal/source/pluginSourceState"), exports);
|
|
39
39
|
__exportStar(require("../plugin/internal/source/pluginSourceStateHolds"), exports);
|
|
40
40
|
__exportStar(require("../plugin/internal/source/processPluginBuildEnvironment"), exports);
|
|
41
|
+
__exportStar(require("../plugin/internal/source/provenPluginSourceDigest"), exports);
|
|
41
42
|
__exportStar(require("../plugin/internal/source/PluginBuildEnvironmentReadings"), exports);
|
|
42
43
|
__exportStar(require("../plugin/internal/source/prunesPluginSourceDirectory"), exports);
|
|
43
44
|
//# sourceMappingURL=pluginSource.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pluginSource.js","sourceRoot":"","sources":["../../src/internal/pluginSource.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;GAeG;AACH,qFAAmE;AACnE,gFAA8D;AAC9D,+EAA6D;AAC7D,uFAAqE;AACrE,+EAA6D;AAC7D,8EAA4D;AAC5D,mFAAiE;AACjE,0FAAwE;AACxE,2FAAyE;AACzE,wFAAsE"}
|
|
1
|
+
{"version":3,"file":"pluginSource.js","sourceRoot":"","sources":["../../src/internal/pluginSource.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;GAeG;AACH,qFAAmE;AACnE,gFAA8D;AAC9D,+EAA6D;AAC7D,uFAAqE;AACrE,+EAA6D;AAC7D,8EAA4D;AAC5D,mFAAiE;AACjE,0FAAwE;AACxE,qFAAmE;AACnE,2FAAyE;AACzE,wFAAsE"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { PluginContentIdentities } from "../plugin/internal/source/PluginContentIdentities";
|
|
1
2
|
/**
|
|
2
3
|
* Locate a real Node runtime for ttsx and native JavaScript config loaders.
|
|
3
4
|
*
|
|
4
5
|
* Bun can directly evaluate a descriptor, but it does not implement the
|
|
5
6
|
* synchronous `module.registerHooks` contract used by those loaders.
|
|
6
7
|
*
|
|
8
|
+
* @param env Effective environment naming `TTSC_NODE_BINARY`.
|
|
9
|
+
* @param cwd Working directory of the probes.
|
|
10
|
+
* @param identities Optional record store that lets probes of an unchanged
|
|
11
|
+
* runtime be answered across processes (#1723).
|
|
7
12
|
* @evidence contracts/common.md#principled-implementation Ordered candidates are accepted only after an actual runtime probe reports non-Bun registerHooks support and its absolute executable; names alone do not establish the loader capability.
|
|
8
13
|
* @evidence contracts/common.md#clear-and-simple-design Candidate precedence and duplicate suppression remain here, while the shared probe owns launch, capability parsing and freshness policy.
|
|
9
14
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The node spelling is a supported final discovery candidate, not an assumed successful runtime; incompatible candidates are skipped without patching modules or globals.
|
|
@@ -13,4 +18,4 @@
|
|
|
13
18
|
* @evidence contracts/performance.md#efficient-algorithms At most four candidate slots are inspected with first-usable short-circuiting and exact-spelling deduplication. Environment-key/spelling work and delegated executable-byte identity, environment merge, native probing/parsing or descriptor-exhaustion retry costs dominate; four slots do not bound executable bytes, output text or native work.
|
|
14
19
|
* @evidence contracts/performance.md#reuse-equivalent-work Exact duplicate spellings share one candidate probe within this call. The probe owner additionally reuses stable absolute same-executable capabilities only while byte/link/target identity and no-preload authority hold; relative, wrapper, changed and unproven failed candidates need fresh observation.
|
|
15
20
|
*/
|
|
16
|
-
export declare function resolveNodeBinary(env?: NodeJS.ProcessEnv, cwd?: string): string | undefined;
|
|
21
|
+
export declare function resolveNodeBinary(env?: NodeJS.ProcessEnv, cwd?: string, identities?: PluginContentIdentities.Store): string | undefined;
|
|
@@ -9,6 +9,10 @@ const javascriptRuntimeCapabilities_1 = require("./javascriptRuntimeCapabilities
|
|
|
9
9
|
* Bun can directly evaluate a descriptor, but it does not implement the
|
|
10
10
|
* synchronous `module.registerHooks` contract used by those loaders.
|
|
11
11
|
*
|
|
12
|
+
* @param env Effective environment naming `TTSC_NODE_BINARY`.
|
|
13
|
+
* @param cwd Working directory of the probes.
|
|
14
|
+
* @param identities Optional record store that lets probes of an unchanged
|
|
15
|
+
* runtime be answered across processes (#1723).
|
|
12
16
|
* @evidence contracts/common.md#principled-implementation Ordered candidates are accepted only after an actual runtime probe reports non-Bun registerHooks support and its absolute executable; names alone do not establish the loader capability.
|
|
13
17
|
* @evidence contracts/common.md#clear-and-simple-design Candidate precedence and duplicate suppression remain here, while the shared probe owns launch, capability parsing and freshness policy.
|
|
14
18
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The node spelling is a supported final discovery candidate, not an assumed successful runtime; incompatible candidates are skipped without patching modules or globals.
|
|
@@ -18,7 +22,7 @@ const javascriptRuntimeCapabilities_1 = require("./javascriptRuntimeCapabilities
|
|
|
18
22
|
* @evidence contracts/performance.md#efficient-algorithms At most four candidate slots are inspected with first-usable short-circuiting and exact-spelling deduplication. Environment-key/spelling work and delegated executable-byte identity, environment merge, native probing/parsing or descriptor-exhaustion retry costs dominate; four slots do not bound executable bytes, output text or native work.
|
|
19
23
|
* @evidence contracts/performance.md#reuse-equivalent-work Exact duplicate spellings share one candidate probe within this call. The probe owner additionally reuses stable absolute same-executable capabilities only while byte/link/target identity and no-preload authority hold; relative, wrapper, changed and unproven failed candidates need fresh observation.
|
|
20
24
|
*/
|
|
21
|
-
function resolveNodeBinary(env = process.env, cwd = process.cwd()) {
|
|
25
|
+
function resolveNodeBinary(env = process.env, cwd = process.cwd(), identities) {
|
|
22
26
|
const candidates = [
|
|
23
27
|
SidecarEnvironment_1.SidecarEnvironment.read(env, "TTSC_NODE_BINARY"),
|
|
24
28
|
SidecarEnvironment_1.SidecarEnvironment.read(process.env, "TTSC_NODE_BINARY"),
|
|
@@ -32,7 +36,7 @@ function resolveNodeBinary(env = process.env, cwd = process.cwd()) {
|
|
|
32
36
|
if (seen.has(candidate))
|
|
33
37
|
continue;
|
|
34
38
|
seen.add(candidate);
|
|
35
|
-
const capabilities = (0, javascriptRuntimeCapabilities_1.javascriptRuntimeCapabilities)(candidate, env, cwd);
|
|
39
|
+
const capabilities = (0, javascriptRuntimeCapabilities_1.javascriptRuntimeCapabilities)(candidate, env, cwd, identities);
|
|
36
40
|
if (!capabilities.bun &&
|
|
37
41
|
capabilities.registerHooks &&
|
|
38
42
|
capabilities.executable !== undefined) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveNodeBinary.js","sourceRoot":"","sources":["../../src/internal/resolveNodeBinary.ts"],"names":[],"mappings":";;;AAAA,2FAAwF;
|
|
1
|
+
{"version":3,"file":"resolveNodeBinary.js","sourceRoot":"","sources":["../../src/internal/resolveNodeBinary.ts"],"names":[],"mappings":";;;AAAA,2FAAwF;AAExF,mFAAgF;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,2BACE,GAAG,GAAsB,OAAO,CAAC,GAAG,EACpC,GAAG,GAAW,OAAO,CAAC,GAAG,EAAE,EAC3B,UAA0C;IAE1C,MAAM,UAAU,GAAG;QACjB,uCAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC;QAChD,uCAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC;QACxD,OAAO,CAAC,QAAQ;QAChB,MAAM;KACP,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACjE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,SAAS;QAClC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpB,MAAM,YAAY,GAAG,IAAA,6DAA6B,EAChD,SAAS,EACT,GAAG,EACH,GAAG,EACH,UAAU,CACX,CAAC;QACF,IACE,CAAC,YAAY,CAAC,GAAG;YACjB,YAAY,CAAC,aAAa;YAC1B,YAAY,CAAC,UAAU,KAAK,SAAS,EACrC,CAAC;YACD,OAAO,YAAY,CAAC,UAAU,CAAC;QACjC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
import { PluginContentIdentities } from "../plugin/internal/source/PluginContentIdentities";
|
|
1
2
|
/**
|
|
2
3
|
* Fingerprint an absolute executable's spelling, target and actual bytes.
|
|
3
4
|
*
|
|
4
5
|
* A changed link, file, or read failure yields no reusable identity. Metadata
|
|
5
|
-
* brackets the streamed read
|
|
6
|
-
*
|
|
7
|
-
* snapshot guarantee.
|
|
6
|
+
* brackets the streamed read. Concurrent writes that evade every filesystem
|
|
7
|
+
* observation remain outside this snapshot guarantee.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
9
|
+
* With a record store, the bytes of an unchanged executable are proven from its
|
|
10
|
+
* metadata instead of streamed again (`PluginContentIdentities`, #1723): the
|
|
11
|
+
* recorded digest stands only while the physical file's identity, size and both
|
|
12
|
+
* stamps match the record and its modification stamp is separable from a
|
|
13
|
+
* reference the store minted. A plugin load asks for this identity several
|
|
14
|
+
* times, and every launch is a new process, so streaming an 85 MB runtime each
|
|
15
|
+
* time cost about 0.9 s per launch. Without a store, or when that proof is
|
|
16
|
+
* unavailable, the file is streamed as before.
|
|
17
17
|
*
|
|
18
|
-
* @
|
|
18
|
+
* @param runtime The executable, absolute.
|
|
19
|
+
* @param identities Optional record store of the caller's plugin cache.
|
|
20
|
+
* @evidence contracts/common.md#principled-implementation The identity joins lexical-link metadata, the selected physical file metadata and the SHA-256 of that file's bytes, streamed under metadata brackets or proven by a record whose separable signature still matches; descriptor and pathname observations must still agree after a read.
|
|
21
|
+
* @evidence contracts/common.md#clear-and-simple-design This one native owner supplies the same identity to runtime capability and plugin caches; the record store owns metadata reuse and a private helper owns the bracketed stream.
|
|
22
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Metadata alone never stands for bytes: reuse needs a record written under unchanged separable metadata around an actual read, which a same-tick rewrite or restored timestamps cannot satisfy because change time and the minted reference move.
|
|
23
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains absolute-path scope, failure meaning, the record proof and the remaining concurrent-mutation limitation, with separate prose and tags.
|
|
24
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node native realpath and bigint stat preserve link/target identity on supported hosts; absolute paths use native platform semantics, and unreadable or non-regular targets cannot be reused.
|
|
25
|
+
* @evidence contracts/performance.md#efficient-algorithms A proven record costs three stats and one small file read; otherwise a sequential read hashes B bytes with one 64 KiB buffer instead of a complete executable allocation. Native path/realpath/stat observations and serialized metadata add text/lookup costs.
|
|
26
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Processes share an executable digest only through the record store's separable-signature proof; without a store every call streams the file.
|
|
27
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources One descriptor and one fixed buffer belong to a streamed read; finally attempts closure after read/observation failures as well as success. A close failure returns no identity but cannot confirm native release. Opt-in opened/closed/close-failed observations carry one trace-only call id and actual descriptor, PID/thread and already observed physical identity. Records belong to the single-file collector.
|
|
19
28
|
*/
|
|
20
|
-
export declare function runtimeExecutableIdentity(runtime: string): string | undefined;
|
|
29
|
+
export declare function runtimeExecutableIdentity(runtime: string, identities?: PluginContentIdentities.Store): string | undefined;
|
|
@@ -8,55 +8,41 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const node_worker_threads_1 = require("node:worker_threads");
|
|
11
|
+
const PluginContentIdentities_1 = require("../plugin/internal/source/PluginContentIdentities");
|
|
11
12
|
const E2ETrace_1 = require("./E2ETrace");
|
|
12
13
|
/**
|
|
13
14
|
* Fingerprint an absolute executable's spelling, target and actual bytes.
|
|
14
15
|
*
|
|
15
16
|
* A changed link, file, or read failure yields no reusable identity. Metadata
|
|
16
|
-
* brackets the streamed read
|
|
17
|
-
*
|
|
18
|
-
* snapshot guarantee.
|
|
17
|
+
* brackets the streamed read. Concurrent writes that evade every filesystem
|
|
18
|
+
* observation remain outside this snapshot guarantee.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
20
|
+
* With a record store, the bytes of an unchanged executable are proven from its
|
|
21
|
+
* metadata instead of streamed again (`PluginContentIdentities`, #1723): the
|
|
22
|
+
* recorded digest stands only while the physical file's identity, size and both
|
|
23
|
+
* stamps match the record and its modification stamp is separable from a
|
|
24
|
+
* reference the store minted. A plugin load asks for this identity several
|
|
25
|
+
* times, and every launch is a new process, so streaming an 85 MB runtime each
|
|
26
|
+
* time cost about 0.9 s per launch. Without a store, or when that proof is
|
|
27
|
+
* unavailable, the file is streamed as before.
|
|
28
28
|
*
|
|
29
|
-
* @
|
|
29
|
+
* @param runtime The executable, absolute.
|
|
30
|
+
* @param identities Optional record store of the caller's plugin cache.
|
|
31
|
+
* @evidence contracts/common.md#principled-implementation The identity joins lexical-link metadata, the selected physical file metadata and the SHA-256 of that file's bytes, streamed under metadata brackets or proven by a record whose separable signature still matches; descriptor and pathname observations must still agree after a read.
|
|
32
|
+
* @evidence contracts/common.md#clear-and-simple-design This one native owner supplies the same identity to runtime capability and plugin caches; the record store owns metadata reuse and a private helper owns the bracketed stream.
|
|
33
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Metadata alone never stands for bytes: reuse needs a record written under unchanged separable metadata around an actual read, which a same-tick rewrite or restored timestamps cannot satisfy because change time and the minted reference move.
|
|
34
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains absolute-path scope, failure meaning, the record proof and the remaining concurrent-mutation limitation, with separate prose and tags.
|
|
35
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node native realpath and bigint stat preserve link/target identity on supported hosts; absolute paths use native platform semantics, and unreadable or non-regular targets cannot be reused.
|
|
36
|
+
* @evidence contracts/performance.md#efficient-algorithms A proven record costs three stats and one small file read; otherwise a sequential read hashes B bytes with one 64 KiB buffer instead of a complete executable allocation. Native path/realpath/stat observations and serialized metadata add text/lookup costs.
|
|
37
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Processes share an executable digest only through the record store's separable-signature proof; without a store every call streams the file.
|
|
38
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources One descriptor and one fixed buffer belong to a streamed read; finally attempts closure after read/observation failures as well as success. A close failure returns no identity but cannot confirm native release. Opt-in opened/closed/close-failed observations carry one trace-only call id and actual descriptor, PID/thread and already observed physical identity. Records belong to the single-file collector.
|
|
30
39
|
*/
|
|
31
|
-
function runtimeExecutableIdentity(runtime) {
|
|
40
|
+
function runtimeExecutableIdentity(runtime, identities) {
|
|
32
41
|
if (!node_path_1.default.isAbsolute(runtime))
|
|
33
42
|
return undefined;
|
|
34
43
|
const startedAt = new Date().toISOString();
|
|
35
|
-
let descriptor;
|
|
36
|
-
let traceLease;
|
|
37
44
|
let stage = "lexical-stat";
|
|
38
|
-
|
|
39
|
-
E2ETrace_1.E2ETrace.capabilityResolution("runtime-executable-identity-unavailable", {
|
|
40
|
-
runtime,
|
|
41
|
-
stage,
|
|
42
|
-
reason,
|
|
43
|
-
expected,
|
|
44
|
-
observed,
|
|
45
|
-
expectedAtimeNs: observations === undefined
|
|
46
|
-
? undefined
|
|
47
|
-
: String(observations.before.atimeNs),
|
|
48
|
-
observedAtimeNs: observations === undefined
|
|
49
|
-
? undefined
|
|
50
|
-
: String(observations.after.atimeNs),
|
|
51
|
-
expectedBirthtimeNs: observations === undefined
|
|
52
|
-
? undefined
|
|
53
|
-
: String(observations.before.birthtimeNs),
|
|
54
|
-
observedBirthtimeNs: observations === undefined
|
|
55
|
-
? undefined
|
|
56
|
-
: String(observations.after.birthtimeNs),
|
|
57
|
-
});
|
|
58
|
-
return undefined;
|
|
59
|
-
};
|
|
45
|
+
let refused;
|
|
60
46
|
try {
|
|
61
47
|
const lexical = node_fs_1.default.lstatSync(runtime, { bigint: true });
|
|
62
48
|
stage = "physical-realpath";
|
|
@@ -64,68 +50,23 @@ function runtimeExecutableIdentity(runtime) {
|
|
|
64
50
|
stage = "physical-stat";
|
|
65
51
|
const physical = node_fs_1.default.statSync(physicalPath, { bigint: true });
|
|
66
52
|
if (!physical.isFile())
|
|
67
|
-
return unavailable(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const opened = node_fs_1.default.fstatSync(descriptor, { bigint: true });
|
|
85
|
-
if (fileIdentity(opened) !== fileIdentity(physical))
|
|
86
|
-
return unavailable("opened-file-changed", fileIdentity(physical), fileIdentity(opened));
|
|
87
|
-
const hash = node_crypto_1.default.createHash("sha256");
|
|
88
|
-
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
89
|
-
let remaining = opened.size;
|
|
90
|
-
stage = "read";
|
|
91
|
-
while (remaining > 0n) {
|
|
92
|
-
const requested = remaining > BigInt(buffer.length) ? buffer.length : Number(remaining);
|
|
93
|
-
const length = node_fs_1.default.readSync(descriptor, buffer, 0, requested, null);
|
|
94
|
-
if (length === 0)
|
|
95
|
-
return unavailable("premature-eof", String(opened.size), String(opened.size - remaining));
|
|
96
|
-
hash.update(buffer.subarray(0, length));
|
|
97
|
-
remaining -= BigInt(length);
|
|
98
|
-
}
|
|
99
|
-
stage = "post-read-opened-stat";
|
|
100
|
-
const afterOpened = node_fs_1.default.fstatSync(descriptor, { bigint: true });
|
|
101
|
-
if (fileIdentity(afterOpened) !== fileIdentity(opened))
|
|
102
|
-
return unavailable("opened-file-changed-during-read", fileIdentity(opened), fileIdentity(afterOpened), { before: opened, after: afterOpened });
|
|
103
|
-
stage = "post-read-realpath";
|
|
104
|
-
const afterPath = node_fs_1.default.realpathSync.native(runtime);
|
|
105
|
-
if (afterPath !== physicalPath)
|
|
106
|
-
return unavailable("physical-target-changed", physicalPath, afterPath);
|
|
107
|
-
stage = "post-read-lexical-stat";
|
|
108
|
-
const afterLexical = node_fs_1.default.lstatSync(runtime, { bigint: true });
|
|
109
|
-
if (fileIdentity(afterLexical) !== fileIdentity(lexical))
|
|
110
|
-
return unavailable("lexical-file-changed-during-read", fileIdentity(lexical), fileIdentity(afterLexical));
|
|
111
|
-
stage = "post-read-physical-stat";
|
|
112
|
-
const afterPhysical = node_fs_1.default.statSync(physicalPath, { bigint: true });
|
|
113
|
-
if (fileIdentity(afterPhysical) !== fileIdentity(physical))
|
|
114
|
-
return unavailable("physical-file-changed-during-read", fileIdentity(physical), fileIdentity(afterPhysical));
|
|
115
|
-
const digest = hash.digest("hex");
|
|
116
|
-
E2ETrace_1.E2ETrace.capabilityResolution("runtime-executable-identity-observed", {
|
|
117
|
-
runtime,
|
|
118
|
-
physicalPath,
|
|
119
|
-
startedAt,
|
|
120
|
-
finishedAt: new Date().toISOString(),
|
|
121
|
-
lexical: fileIdentity(lexical),
|
|
122
|
-
physical: fileIdentity(physical),
|
|
123
|
-
digest,
|
|
124
|
-
openedAtimeNs: String(opened.atimeNs),
|
|
125
|
-
afterOpenedAtimeNs: String(afterOpened.atimeNs),
|
|
126
|
-
openedBirthtimeNs: String(opened.birthtimeNs),
|
|
127
|
-
afterOpenedBirthtimeNs: String(afterOpened.birthtimeNs),
|
|
128
|
-
});
|
|
53
|
+
return unavailable(runtime, {
|
|
54
|
+
stage,
|
|
55
|
+
reason: "not-regular",
|
|
56
|
+
observed: fileIdentity(physical),
|
|
57
|
+
});
|
|
58
|
+
const read = () => {
|
|
59
|
+
const streamed = streamDigest(runtime, physicalPath, physical, lexical, {
|
|
60
|
+
startedAt,
|
|
61
|
+
});
|
|
62
|
+
if (typeof streamed === "string")
|
|
63
|
+
return streamed;
|
|
64
|
+
refused = streamed;
|
|
65
|
+
throw new Error(streamed.reason);
|
|
66
|
+
};
|
|
67
|
+
const digest = identities === undefined
|
|
68
|
+
? read()
|
|
69
|
+
: PluginContentIdentities_1.PluginContentIdentities.digest(identities, "executable", physicalPath, (references) => PluginContentIdentities_1.PluginContentIdentities.fileObservation(references, physicalPath), read);
|
|
129
70
|
return [
|
|
130
71
|
physicalPath,
|
|
131
72
|
fileIdentity(lexical),
|
|
@@ -134,7 +75,48 @@ function runtimeExecutableIdentity(runtime) {
|
|
|
134
75
|
].join("\0");
|
|
135
76
|
}
|
|
136
77
|
catch (error) {
|
|
137
|
-
return unavailable(
|
|
78
|
+
return unavailable(runtime, refused ?? {
|
|
79
|
+
stage,
|
|
80
|
+
reason: "filesystem-error",
|
|
81
|
+
observed: error instanceof Error ? error.message : String(error),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function unavailable(runtime, failure) {
|
|
86
|
+
E2ETrace_1.E2ETrace.capabilityResolution("runtime-executable-identity-unavailable", {
|
|
87
|
+
runtime,
|
|
88
|
+
stage: failure.stage,
|
|
89
|
+
reason: failure.reason,
|
|
90
|
+
expected: failure.expected,
|
|
91
|
+
observed: failure.observed,
|
|
92
|
+
expectedAtimeNs: failure.observations === undefined
|
|
93
|
+
? undefined
|
|
94
|
+
: String(failure.observations.before.atimeNs),
|
|
95
|
+
observedAtimeNs: failure.observations === undefined
|
|
96
|
+
? undefined
|
|
97
|
+
: String(failure.observations.after.atimeNs),
|
|
98
|
+
expectedBirthtimeNs: failure.observations === undefined
|
|
99
|
+
? undefined
|
|
100
|
+
: String(failure.observations.before.birthtimeNs),
|
|
101
|
+
observedBirthtimeNs: failure.observations === undefined
|
|
102
|
+
? undefined
|
|
103
|
+
: String(failure.observations.after.birthtimeNs),
|
|
104
|
+
});
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Stream the physical file's bytes between metadata observations of the open
|
|
109
|
+
* descriptor, the physical path and the lexical spelling, returning the digest
|
|
110
|
+
* or what moved. The descriptor is closed in finally; a failed close refuses
|
|
111
|
+
* the identity.
|
|
112
|
+
*/
|
|
113
|
+
function streamDigest(runtime, physicalPath, physical, lexical, timing) {
|
|
114
|
+
let stage = "open";
|
|
115
|
+
let descriptor;
|
|
116
|
+
let traceLease;
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = observe();
|
|
138
120
|
}
|
|
139
121
|
finally {
|
|
140
122
|
if (descriptor !== undefined) {
|
|
@@ -149,11 +131,119 @@ function runtimeExecutableIdentity(runtime) {
|
|
|
149
131
|
...traceLease,
|
|
150
132
|
error: error instanceof Error ? error.message : String(error),
|
|
151
133
|
});
|
|
152
|
-
|
|
153
|
-
|
|
134
|
+
// A digest whose descriptor could not be closed is refused, as before.
|
|
135
|
+
result = {
|
|
136
|
+
stage: "close",
|
|
137
|
+
reason: "descriptor-close-error",
|
|
138
|
+
observed: error instanceof Error ? error.message : String(error),
|
|
139
|
+
};
|
|
154
140
|
}
|
|
155
141
|
}
|
|
156
142
|
}
|
|
143
|
+
return result;
|
|
144
|
+
function observe() {
|
|
145
|
+
try {
|
|
146
|
+
descriptor = node_fs_1.default.openSync(physicalPath, "r");
|
|
147
|
+
if (process.env.TTSC_E2E_TRACE) {
|
|
148
|
+
traceLease = {
|
|
149
|
+
callId: String(++identityTraceOrdinal),
|
|
150
|
+
runtime,
|
|
151
|
+
physicalPath,
|
|
152
|
+
descriptor,
|
|
153
|
+
pid: process.pid,
|
|
154
|
+
threadId: node_worker_threads_1.threadId,
|
|
155
|
+
dev: String(physical.dev),
|
|
156
|
+
ino: String(physical.ino),
|
|
157
|
+
};
|
|
158
|
+
E2ETrace_1.E2ETrace.capabilityResolution("runtime-executable-identity-opened", traceLease);
|
|
159
|
+
}
|
|
160
|
+
stage = "opened-stat";
|
|
161
|
+
const opened = node_fs_1.default.fstatSync(descriptor, { bigint: true });
|
|
162
|
+
if (fileIdentity(opened) !== fileIdentity(physical))
|
|
163
|
+
return {
|
|
164
|
+
stage,
|
|
165
|
+
reason: "opened-file-changed",
|
|
166
|
+
expected: fileIdentity(physical),
|
|
167
|
+
observed: fileIdentity(opened),
|
|
168
|
+
};
|
|
169
|
+
const hash = node_crypto_1.default.createHash("sha256");
|
|
170
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
171
|
+
let remaining = opened.size;
|
|
172
|
+
stage = "read";
|
|
173
|
+
while (remaining > 0n) {
|
|
174
|
+
const requested = remaining > BigInt(buffer.length) ? buffer.length : Number(remaining);
|
|
175
|
+
const length = node_fs_1.default.readSync(descriptor, buffer, 0, requested, null);
|
|
176
|
+
if (length === 0)
|
|
177
|
+
return {
|
|
178
|
+
stage,
|
|
179
|
+
reason: "premature-eof",
|
|
180
|
+
expected: String(opened.size),
|
|
181
|
+
observed: String(opened.size - remaining),
|
|
182
|
+
};
|
|
183
|
+
hash.update(buffer.subarray(0, length));
|
|
184
|
+
remaining -= BigInt(length);
|
|
185
|
+
}
|
|
186
|
+
stage = "post-read-opened-stat";
|
|
187
|
+
const afterOpened = node_fs_1.default.fstatSync(descriptor, { bigint: true });
|
|
188
|
+
if (fileIdentity(afterOpened) !== fileIdentity(opened))
|
|
189
|
+
return {
|
|
190
|
+
stage,
|
|
191
|
+
reason: "opened-file-changed-during-read",
|
|
192
|
+
expected: fileIdentity(opened),
|
|
193
|
+
observed: fileIdentity(afterOpened),
|
|
194
|
+
observations: { before: opened, after: afterOpened },
|
|
195
|
+
};
|
|
196
|
+
stage = "post-read-realpath";
|
|
197
|
+
const afterPath = node_fs_1.default.realpathSync.native(runtime);
|
|
198
|
+
if (afterPath !== physicalPath)
|
|
199
|
+
return {
|
|
200
|
+
stage,
|
|
201
|
+
reason: "physical-target-changed",
|
|
202
|
+
expected: physicalPath,
|
|
203
|
+
observed: afterPath,
|
|
204
|
+
};
|
|
205
|
+
stage = "post-read-lexical-stat";
|
|
206
|
+
const afterLexical = node_fs_1.default.lstatSync(runtime, { bigint: true });
|
|
207
|
+
if (fileIdentity(afterLexical) !== fileIdentity(lexical))
|
|
208
|
+
return {
|
|
209
|
+
stage,
|
|
210
|
+
reason: "lexical-file-changed-during-read",
|
|
211
|
+
expected: fileIdentity(lexical),
|
|
212
|
+
observed: fileIdentity(afterLexical),
|
|
213
|
+
};
|
|
214
|
+
stage = "post-read-physical-stat";
|
|
215
|
+
const afterPhysical = node_fs_1.default.statSync(physicalPath, { bigint: true });
|
|
216
|
+
if (fileIdentity(afterPhysical) !== fileIdentity(physical))
|
|
217
|
+
return {
|
|
218
|
+
stage,
|
|
219
|
+
reason: "physical-file-changed-during-read",
|
|
220
|
+
expected: fileIdentity(physical),
|
|
221
|
+
observed: fileIdentity(afterPhysical),
|
|
222
|
+
};
|
|
223
|
+
const digest = hash.digest("hex");
|
|
224
|
+
E2ETrace_1.E2ETrace.capabilityResolution("runtime-executable-identity-observed", {
|
|
225
|
+
runtime,
|
|
226
|
+
physicalPath,
|
|
227
|
+
startedAt: timing.startedAt,
|
|
228
|
+
finishedAt: new Date().toISOString(),
|
|
229
|
+
lexical: fileIdentity(lexical),
|
|
230
|
+
physical: fileIdentity(physical),
|
|
231
|
+
digest,
|
|
232
|
+
openedAtimeNs: String(opened.atimeNs),
|
|
233
|
+
afterOpenedAtimeNs: String(afterOpened.atimeNs),
|
|
234
|
+
openedBirthtimeNs: String(opened.birthtimeNs),
|
|
235
|
+
afterOpenedBirthtimeNs: String(afterOpened.birthtimeNs),
|
|
236
|
+
});
|
|
237
|
+
return digest;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
stage,
|
|
242
|
+
reason: "filesystem-error",
|
|
243
|
+
observed: error instanceof Error ? error.message : String(error),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
157
247
|
}
|
|
158
248
|
// Trace-only ordinal distinguishes sequential uses of a reused descriptor.
|
|
159
249
|
let identityTraceOrdinal = 0;
|