ttsc 0.31.0 → 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/cmd/platform/main.go +2 -2
- package/internal/sourceprocess/contain_windows.go +10 -13
- package/internal/sourceprocess/started_windows.go +41 -0
- package/internal/sourceprocess/windows_started_publication_distinguishes_pending_from_refusal_test.go +76 -0
- package/internal/watchhelper/run.go +30 -20
- package/internal/watchhelper/run_other.go +4 -4
- package/internal/watchhelper/run_windows.go +471 -0
- package/internal/watchhelper/watchhelper.go +8 -2
- package/internal/watchhelper/windows_notifications_preserve_mutations_without_access_noise_test.go +506 -0
- 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/nativeBinary.d.ts +3 -3
- package/lib/internal/nativeBinary.js +3 -3
- 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 +24 -14
- package/lib/plugin/internal/load/loadProjectPlugins.js +141 -56
- 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 +48 -0
- package/lib/plugin/internal/source/GoEnvironmentReading.js +156 -0
- package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -0
- package/lib/plugin/internal/source/NativeSourcePackages.d.ts +51 -13
- package/lib/plugin/internal/source/NativeSourcePackages.js +182 -27
- package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +42 -3
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +68 -7
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.d.ts +5 -3
- package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.js +14 -8
- package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.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 +53 -19
- package/lib/plugin/internal/source/SourcePluginWorkspace.js +111 -22
- package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
- package/lib/plugin/internal/source/buildSourcePlugin.d.ts +34 -18
- package/lib/plugin/internal/source/buildSourcePlugin.js +52 -24
- package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/source/computeCacheKey.d.ts +25 -7
- package/lib/plugin/internal/source/computeCacheKey.js +85 -34
- 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 +75 -102
- 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/pluginModuleReplaceDirectories.d.ts +6 -5
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +10 -17
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.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/nativeBinary.ts +3 -3
- 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 +421 -211
- package/src/plugin/internal/readCapabilityResolution.ts +17 -6
- package/src/plugin/internal/source/GoEnvironmentReading.ts +178 -0
- package/src/plugin/internal/source/NativeSourcePackages.ts +365 -48
- package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +83 -5
- package/src/plugin/internal/source/PluginBuildEnvironmentWitness.ts +18 -8
- 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 +148 -25
- package/src/plugin/internal/source/buildSourcePlugin.ts +104 -36
- package/src/plugin/internal/source/computeCacheKey.ts +129 -55
- package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +152 -105
- 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/pluginModuleReplaceDirectories.ts +13 -25
- 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
|
@@ -2,12 +2,11 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { resolvePhysicalPath } from "../../../internal/pathIdentity/resolvePhysicalPath";
|
|
5
|
-
import { GoSourceInputs } from "./GoSourceInputs";
|
|
6
5
|
import { GoToolResolution } from "./GoToolResolution";
|
|
7
6
|
import type { IPluginModuleReplaceDirectory } from "./IPluginModuleReplaceDirectory";
|
|
7
|
+
import { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
8
8
|
import { resolveGoCompiler } from "./resolveGoCompiler";
|
|
9
9
|
import { selectPluginModuleReplaceDirectories } from "./selectPluginModuleReplaceDirectories";
|
|
10
|
-
import { spawnGoTool } from "./spawnGoTool";
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* The local directories outside a plugin's Go module that its `go.mod`
|
|
@@ -38,19 +37,21 @@ import { spawnGoTool } from "./spawnGoTool";
|
|
|
38
37
|
* fails or supplies an unusable JSON result. A no-marker manifest skips Go
|
|
39
38
|
* parsing here; this is not general go.mod validation.
|
|
40
39
|
* @evidence contracts/common.md#principled-implementation When the replace marker triggers a query, Go parses its grammar. Unversioned local targets selected outside best-effort root/target spellings are reported, resolving relative paths from the original module; unavailable identity resolution does not certify complete physical containment.
|
|
41
|
-
* @evidence contracts/common.md#clear-and-simple-design A cheap no-replace scan avoids
|
|
40
|
+
* @evidence contracts/common.md#clear-and-simple-design A cheap no-replace scan avoids unnecessary parsing; the common selected-tool/load reader owns Go syntax, and this operation owns current physical containment.
|
|
42
41
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The implementation uses Go's supported mod-edit interface instead of a handwritten grammar or special-casing particular dependency names.
|
|
43
42
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain external versus internal replacement ownership, original-directory resolution and returned spellings; failure behavior is documented.
|
|
44
43
|
* @evidence contracts/portability.md#os-neutral-implementation Node path and physical-path resolution determine containment; Go local-path syntax accepts native absolute paths and Windows-relative backslashes only on Windows.
|
|
45
|
-
* @evidence contracts/performance.md#efficient-algorithms Full manifest read/UTF-8 marker scan can skip Go parsing. Otherwise
|
|
44
|
+
* @evidence contracts/performance.md#efficient-algorithms Full manifest read/UTF-8 marker scan can skip Go parsing. Otherwise selected-tool reader acquisition and a current manifest read precede a pass over all parsed directives, not only returned ones. Equivalent bytes share Go JSON acquisition only in the supplied fixed-environment load registry; a new syntax record pays native tool/env/capture and full JSON costs; each selected local target uses native best-effort identity resolution, which can traverse ancestors/probe case. Sorting returned module/version strings processes their bytes, with complete manifest/output/path arrays retained transiently and no replacement content digest scan here.
|
|
46
45
|
*
|
|
47
|
-
* @
|
|
48
|
-
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources This operation acquires no retained handle
|
|
46
|
+
* @evidence contracts/performance.md#reuse-equivalent-work The optional pinned-tool/load reader or registry shares Go parsing of identical current bytes with package observation and workspace creation. Physical replacement containment is recomputed every call, never borrowed from that syntax memo.
|
|
47
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources This operation acquires no retained handle; the supplied load registry retains syntax readers until its caller releases the load. Without it the reader is call-owned. Native capture cleanup remains delegated and can fail; current physical identity contexts are call-owned and returned directories do not pin future state.
|
|
49
48
|
*/
|
|
50
49
|
export function pluginModuleReplaceDirectories(
|
|
51
50
|
moduleRoot: string,
|
|
52
51
|
env: NodeJS.ProcessEnv,
|
|
53
52
|
goBinary?: string,
|
|
53
|
+
reader?: SourcePluginWorkspace.GoModReader,
|
|
54
|
+
readers?: Map<string, SourcePluginWorkspace.GoModReader>,
|
|
54
55
|
): IPluginModuleReplaceDirectory[] {
|
|
55
56
|
const root = path.resolve(moduleRoot);
|
|
56
57
|
let text: string;
|
|
@@ -70,27 +71,14 @@ export function pluginModuleReplaceDirectories(
|
|
|
70
71
|
env,
|
|
71
72
|
root,
|
|
72
73
|
);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
79
|
-
if (result.error !== undefined || result.status !== 0)
|
|
80
|
-
throw new Error(
|
|
81
|
-
`ttsc: reading ${path.join(root, "go.mod")} failed: ${
|
|
82
|
-
result.error?.message ?? (result.stderr || result.stdout)
|
|
83
|
-
}`,
|
|
84
|
-
);
|
|
85
|
-
const parsed = JSON.parse(result.stdout) as {
|
|
86
|
-
Replace?: readonly {
|
|
87
|
-
New?: { Path?: string; Version?: string };
|
|
88
|
-
Old?: { Path?: string; Version?: string };
|
|
89
|
-
}[];
|
|
90
|
-
};
|
|
74
|
+
const parsed = (
|
|
75
|
+
reader ?? SourcePluginWorkspace.createGoModReader(go, root, env, readers)
|
|
76
|
+
).read(root);
|
|
77
|
+
// Parsed syntax may be shared; physical containment is current native state
|
|
78
|
+
// and must be recomputed even when these manifest bytes are unchanged.
|
|
91
79
|
return selectPluginModuleReplaceDirectories(
|
|
92
80
|
root,
|
|
93
|
-
parsed.
|
|
81
|
+
parsed.directives,
|
|
94
82
|
resolvePhysicalPath,
|
|
95
83
|
);
|
|
96
84
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
|
|
3
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
3
4
|
import { pluginBuildEnvironment } from "./pluginBuildEnvironment";
|
|
4
|
-
import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
5
5
|
import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
6
|
+
import { provenPluginSourceDigest } from "./provenPluginSourceDigest";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The state of one plugin source directory as a plugin binary is built from it:
|
|
@@ -25,7 +26,9 @@ import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
|
25
26
|
* process's own environment the environment is read once per directory and set
|
|
26
27
|
* of variables (`processPluginBuildEnvironment`), and a proof that must not
|
|
27
28
|
* accept a stale one compares through `pluginSourceStateHolds`, which reads it
|
|
28
|
-
* again before it refutes a state.
|
|
29
|
+
* again before it refutes a state. A caller that names its project proves the
|
|
30
|
+
* sources and the SDK from that project's plugin cache records instead of
|
|
31
|
+
* reading them in every new process (#1725).
|
|
29
32
|
*
|
|
30
33
|
* @param directory The source directory.
|
|
31
34
|
* @param options.env An effective environment other than this process's, which
|
|
@@ -34,6 +37,7 @@ import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
|
34
37
|
* caller already read it.
|
|
35
38
|
* @param options.environment The directory's `pluginBuildEnvironment`, when the
|
|
36
39
|
* caller already read it, as a plugin build's key does.
|
|
40
|
+
* @param options.projectRoot Project whose plugin cache root holds the records.
|
|
37
41
|
* @returns The state, as lowercase hex.
|
|
38
42
|
* @throws When a listed source file cannot be read, as the build itself would.
|
|
39
43
|
* @evidence contracts/common.md#principled-implementation The state combines source and environment digests because both affect compiled plugin behavior; supplied readings preserve the exact inputs a build already keyed on.
|
|
@@ -42,7 +46,7 @@ import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
|
42
46
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain module-root versus overlay environment meaning and the provenance required for supplied digests.
|
|
43
47
|
* @evidence contracts/portability.md#os-neutral-implementation Native source and toolchain identity are delegated to the shared filesystem/process readers; the final digest is platform-independent serialized text.
|
|
44
48
|
* @evidence contracts/performance.md#efficient-algorithms Composition serializes/hashes supplied digest strings whose public inputs are not length-validated; owning generated digests are fixed-width hex. Missing readings delegate native source enumeration/full reads and environment variable/witness/tool/SDK work, without establishing an unmeasured dominant cost ranking.
|
|
45
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Supplied source/environment strings are trusted producer readings, not independently revalidated here. Process-environment memos require their owner's variable/path witness checks; explicit injected environments are read fresh. Equality inherits the selected-source and native metadata premises.
|
|
49
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Supplied source/environment strings are trusted producer readings, not independently revalidated here. Process-environment memos require their owner's variable/path witness checks; explicit injected environments are read fresh. A named project lets missing source and SDK readings come from its record store across processes. Equality inherits the selected-source and native metadata premises.
|
|
46
50
|
*
|
|
47
51
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources This function retains no resource or state after returning; process-environment memo lifetime belongs to its separate owner.
|
|
48
52
|
*/
|
|
@@ -51,19 +55,31 @@ export function pluginSourceState(
|
|
|
51
55
|
options: {
|
|
52
56
|
env?: NodeJS.ProcessEnv;
|
|
53
57
|
environment?: string;
|
|
58
|
+
projectRoot?: string;
|
|
54
59
|
sourceDigest?: string;
|
|
55
60
|
} = {},
|
|
56
61
|
): string {
|
|
62
|
+
const sourceDigest =
|
|
63
|
+
options.sourceDigest ??
|
|
64
|
+
provenPluginSourceDigest(directory, options.projectRoot, options.env);
|
|
65
|
+
const environment =
|
|
66
|
+
options.environment ??
|
|
67
|
+
(options.env === undefined
|
|
68
|
+
? processPluginBuildEnvironment(directory, false, options.projectRoot)
|
|
69
|
+
: pluginBuildEnvironment(
|
|
70
|
+
directory,
|
|
71
|
+
options.env,
|
|
72
|
+
undefined,
|
|
73
|
+
options.projectRoot === undefined
|
|
74
|
+
? undefined
|
|
75
|
+
: PluginContentIdentities.open({
|
|
76
|
+
projectRoot: options.projectRoot,
|
|
77
|
+
env: options.env,
|
|
78
|
+
sources: [directory],
|
|
79
|
+
}),
|
|
80
|
+
));
|
|
57
81
|
return crypto
|
|
58
82
|
.createHash("sha256")
|
|
59
|
-
.update(
|
|
60
|
-
`source=${options.sourceDigest ?? pluginSourceDigest(directory)}\n` +
|
|
61
|
-
`environment=${
|
|
62
|
-
options.environment ??
|
|
63
|
-
(options.env === undefined
|
|
64
|
-
? processPluginBuildEnvironment(directory)
|
|
65
|
-
: pluginBuildEnvironment(directory, options.env))
|
|
66
|
-
}\n`,
|
|
67
|
-
)
|
|
83
|
+
.update(`source=${sourceDigest}\nenvironment=${environment}\n`)
|
|
68
84
|
.digest("hex");
|
|
69
85
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
2
1
|
import { pluginSourceState } from "./pluginSourceState";
|
|
3
2
|
import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
3
|
+
import { provenPluginSourceDigest } from "./provenPluginSourceDigest";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Whether one plugin source directory still holds the state a transform
|
|
@@ -16,12 +16,16 @@ import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
|
16
16
|
* is refuted. A compile keys its binaries on a fresh read, so a state it
|
|
17
17
|
* reported after a change gets one comparison with a refreshed environment.
|
|
18
18
|
* That retry does not guarantee equality for a changing or differently keyed
|
|
19
|
-
* environment, and a supplied source digest remains the caller's premise.
|
|
19
|
+
* environment, and a supplied source digest remains the caller's premise. A
|
|
20
|
+
* caller that names its project proves the sources and the SDK from that
|
|
21
|
+
* project's plugin cache records instead of reading them in every new process
|
|
22
|
+
* (#1725).
|
|
20
23
|
*
|
|
21
24
|
* @param directory The source directory, as the envelope names it.
|
|
22
25
|
* @param state The state the envelope reported.
|
|
23
26
|
* @param options.sourceDigest The directory's `pluginSourceDigest`, when the
|
|
24
27
|
* caller holds one it can vouch for.
|
|
28
|
+
* @param options.projectRoot Project whose plugin cache root holds the records.
|
|
25
29
|
* @throws When a listed source file cannot be read, as the build itself would.
|
|
26
30
|
* @evidence contracts/common.md#principled-implementation Equality uses shared state composition and one real environment refresh after mismatch. This allows changed observations to be reconsidered without certifying the second comparison must match; supplied digest validity and native witness premises remain with their owners.
|
|
27
31
|
* @evidence contracts/common.md#clear-and-simple-design One source reading is passed through both attempts and only the environment owner performs the refresh.
|
|
@@ -29,17 +33,20 @@ import { processPluginBuildEnvironment } from "./processPluginBuildEnvironment";
|
|
|
29
33
|
* @evidence contracts/common.md#meaningful-documentation The prose states source-digest provenance, environment invalidation and the reason a mismatching state gets a fresh comparison.
|
|
30
34
|
* @evidence contracts/portability.md#os-neutral-implementation Source reads and Go/environment witnesses use the same native boundary implementations as builds, rather than comparing OS names or guessed installation paths.
|
|
31
35
|
* @evidence contracts/performance.md#efficient-algorithms At most one source digest computation is requested and reused by both comparisons; supplied strings bypass it. Native enumeration/file bytes, full environment identity/witness checks, optional tool/SDK refresh and digest-string hashing contribute work; mismatch repeats environment resolution once rather than bounding total cost by comparison count.
|
|
32
|
-
* @evidence contracts/performance.md#reuse-equivalent-work A caller's proven source digest and the process owner's still-valid environment are reused; a mismatch forces the owning environment refresh before returning false.
|
|
36
|
+
* @evidence contracts/performance.md#reuse-equivalent-work A caller's proven source digest and the process owner's still-valid environment are reused; a mismatch forces the owning environment refresh before returning false. A named project lets the source and SDK readings come from its record store across processes.
|
|
33
37
|
*
|
|
34
38
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The proof owns no retained source buffer, process or watcher; its caller owns any saved source digest.
|
|
35
39
|
*/
|
|
36
40
|
export function pluginSourceStateHolds(
|
|
37
41
|
directory: string,
|
|
38
42
|
state: string,
|
|
39
|
-
options: { sourceDigest?: string } = {},
|
|
43
|
+
options: { projectRoot?: string; sourceDigest?: string } = {},
|
|
40
44
|
): boolean {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
const projectRoot = options.projectRoot;
|
|
46
|
+
const sourceDigest =
|
|
47
|
+
options.sourceDigest ?? provenPluginSourceDigest(directory, projectRoot);
|
|
48
|
+
if (pluginSourceState(directory, { projectRoot, sourceDigest }) === state)
|
|
49
|
+
return true;
|
|
50
|
+
processPluginBuildEnvironment(directory, true, projectRoot);
|
|
51
|
+
return pluginSourceState(directory, { projectRoot, sourceDigest }) === state;
|
|
45
52
|
}
|
|
@@ -21,20 +21,26 @@ import { PluginBuildEnvironmentReadings } from "./PluginBuildEnvironmentReadings
|
|
|
21
21
|
* possible metadata-restored change. The build itself never reads through here:
|
|
22
22
|
* it keys each binary on a fresh read.
|
|
23
23
|
*
|
|
24
|
+
* A caller that names its project lets a reading this process does not hold
|
|
25
|
+
* prove the SDK and executables from that project's plugin cache records
|
|
26
|
+
* (#1725) instead of reading the whole GOROOT in every new process.
|
|
27
|
+
*
|
|
24
28
|
* @param directory The directory a build runs `go` in.
|
|
25
29
|
* @param refresh Whether to read the environment again rather than reuse it.
|
|
30
|
+
* @param projectRoot Project whose plugin cache root holds the records.
|
|
26
31
|
* @evidence contracts/common.md#principled-implementation Variable serialization is sorted and length-delimited; reuse additionally requires unchanged pre-read metadata for every external dependency, including all contributing GOROOT directories and files, under the witness's documented metadata-distinguishability premise.
|
|
27
32
|
* @evidence contracts/common.md#clear-and-simple-design One process-local map owns environment memoization; fresh construction and witness validation remain separate operations shared with build verification.
|
|
28
33
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Refresh performs the real environment read rather than assuming that a stable compiler path or quiet source tree implies a stable toolchain.
|
|
29
34
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain why environment probing is retained, its variable/path invalidation, and why builds still read fresh; descriptive prose and tags are separated.
|
|
30
35
|
* @evidence contracts/portability.md#os-neutral-implementation Native environment and filesystem identity are delegated to the shared platform-aware probe and metadata witness rather than inferred from an OS name.
|
|
31
36
|
* @evidence contracts/performance.md#efficient-algorithms The delegated owner serializes/hashes variable names/values after text-sensitive sorting and validates witnessed native paths. A qualified hit avoids cold tool/content preparation but not native lookup/path/link/text costs. Misses/refresh delegate actual Go/toolchain/SDK readings; V/P counts alone do not bound all bytes or native work.
|
|
32
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Directory and all effective variables identify a candidate reading; the complete external-path witness must hold, otherwise the environment is read and its record replaced.
|
|
37
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Directory and all effective variables identify a candidate reading; the complete external-path witness must hold, otherwise the environment is read and its record replaced. A miss naming a project proves the SDK and executables from that project's record store across processes.
|
|
33
38
|
* @evidence contracts/performance.md#bound-retention-and-release-resources PluginBuildEnvironmentReadings owns the process-shared map and asynchronous worker/request lifetimes, not this forwarding call. Existing keys are replaced, but distinct directory/variable combinations retain historical witnesses without an eviction bound; consumers and the owner govern that retention.
|
|
34
39
|
*/
|
|
35
40
|
export function processPluginBuildEnvironment(
|
|
36
41
|
directory: string,
|
|
37
42
|
refresh = false,
|
|
43
|
+
projectRoot?: string,
|
|
38
44
|
): string {
|
|
39
|
-
return PluginBuildEnvironmentReadings.read(directory, refresh);
|
|
45
|
+
return PluginBuildEnvironmentReadings.read(directory, refresh, projectRoot);
|
|
40
46
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `pluginSourceDigest` of a plugin source directory, proven from the
|
|
5
|
+
* records of `projectRoot`'s plugin cache when the caller names its project.
|
|
6
|
+
*
|
|
7
|
+
* WARNING (#1725): a consumer that proves a reported plugin-source state in a
|
|
8
|
+
* new process, such as a bundler worker, a capability host or the language
|
|
9
|
+
* server, read every source byte again because its own memo starts empty. The
|
|
10
|
+
* load that reported the state already recorded this digest
|
|
11
|
+
* (`PluginContentIdentities`), so a process that names the same project reuses
|
|
12
|
+
* it while the files' metadata still holds. Without a project, or when no store
|
|
13
|
+
* can be opened, the bytes are read as before.
|
|
14
|
+
*
|
|
15
|
+
* @param directory The source directory.
|
|
16
|
+
* @param projectRoot Project whose plugin cache root holds the records.
|
|
17
|
+
* @param env Environment selecting that cache root; this process's by default.
|
|
18
|
+
* @returns The digest, as lowercase hex.
|
|
19
|
+
* @throws When a listed source file cannot be read, as `pluginSourceDigest`
|
|
20
|
+
* does.
|
|
21
|
+
* @evidence contracts/common.md#principled-implementation The record store applies its separable-signature rule to exactly the files the digest reads; every unproved premise falls back to the content reader.
|
|
22
|
+
* @evidence contracts/common.md#clear-and-simple-design Store opening and the population proof stay with PluginContentIdentities; this entry only names the project for consumers outside the loader.
|
|
23
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts No path, timestamp or process memo stands for content; the store refuses a root inside the source.
|
|
24
|
+
* @evidence contracts/common.md#meaningful-documentation The paragraphs state the regression, the reuse premise and the fallback.
|
|
25
|
+
* @evidence contracts/portability.md#os-neutral-implementation Cache-root selection and device-keyed references are delegated to the shared owners.
|
|
26
|
+
* @evidence contracts/performance.md#efficient-algorithms A proven digest stats the selected files; an unproven one also reads their bytes once. Opening the store writes two reference probes.
|
|
27
|
+
* @evidence contracts/performance.md#reuse-equivalent-work The digest the load recorded is reused across processes under the same project's store.
|
|
28
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The store lives for this call; records belong to the cache root's single-file pruning.
|
|
29
|
+
*/
|
|
30
|
+
export function provenPluginSourceDigest(
|
|
31
|
+
directory: string,
|
|
32
|
+
projectRoot?: string,
|
|
33
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
34
|
+
): string {
|
|
35
|
+
return PluginContentIdentities.sourceDirectory(
|
|
36
|
+
projectRoot === undefined
|
|
37
|
+
? undefined
|
|
38
|
+
: PluginContentIdentities.open({
|
|
39
|
+
projectRoot,
|
|
40
|
+
env,
|
|
41
|
+
sources: [directory],
|
|
42
|
+
}),
|
|
43
|
+
directory,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -8,8 +8,9 @@ import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
|
|
|
8
8
|
/**
|
|
9
9
|
* Opportunistically bound a part of the cache root whose entries are single
|
|
10
10
|
* files: the descriptor evaluations under `descriptors/`, the capability
|
|
11
|
-
* answers under `capabilities/`,
|
|
12
|
-
* `
|
|
11
|
+
* answers under `capabilities/`, the plugin content identities under
|
|
12
|
+
* `identities/`, the plugin-load answers under `answers/`, and the lowered
|
|
13
|
+
* orphan sources under `ttsx-orphan/`.
|
|
13
14
|
*
|
|
14
15
|
* The policy is the plugin cache's (`CachePrunePolicy`). An entry's last use is
|
|
15
16
|
* its modification time: a write sets it, and every hit records a use by
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The version of this ttsc package, read from its own manifest once per
|
|
6
|
+
* process, or `"0.0.0"` when the manifest cannot be read or names none.
|
|
7
|
+
*
|
|
8
|
+
* WARNING (#1726): a reader that only needs ttsc's own version must take it
|
|
9
|
+
* here, never from `pluginBuildVersions`, which also resolves the project's
|
|
10
|
+
* `typescript` package. A plugin descriptor evaluator opens a record store
|
|
11
|
+
* while its module resolution recorder runs, and on a runtime whose
|
|
12
|
+
* `require.resolve` consults resolve hooks every such lookup became an input of
|
|
13
|
+
* the descriptor: absent `typescript` candidates below the cache root's
|
|
14
|
+
* ancestors, whose proofs the system temporary directory's metadata then
|
|
15
|
+
* withdrew. Reading a file here resolves no module.
|
|
16
|
+
*
|
|
17
|
+
* The host installation is assumed stable for the process lifetime; changing
|
|
18
|
+
* its manifest does not refresh the label.
|
|
19
|
+
*
|
|
20
|
+
* @evidence contracts/common.md#principled-implementation The label is this package's manifest version, the same value plugin keys and record identities have always named; an unreadable manifest yields the documented default instead of a guess.
|
|
21
|
+
* @evidence contracts/common.md#clear-and-simple-design One reader owns the host label; `pluginBuildVersions` composes it with the project's compiler version.
|
|
22
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The manifest beside the compiled module is read directly; no module resolution or fixture version stands in for it.
|
|
23
|
+
* @evidence contracts/common.md#meaningful-documentation The paragraphs state the value, the regression that motivates a resolution-free reader and the process-stable premise.
|
|
24
|
+
* @evidence contracts/portability.md#os-neutral-implementation The manifest path is joined with Node path APIs relative to this module, without separator or case assumptions.
|
|
25
|
+
* @evidence contracts/performance.md#efficient-algorithms The first call reads and parses one small manifest; later calls return the retained label.
|
|
26
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Every caller in the process shares one reading of an installation assumed stable for that process.
|
|
27
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources One label string is retained for the process lifetime; the synchronous read leaves no descriptor open.
|
|
28
|
+
*/
|
|
29
|
+
export function ttscPackageVersion(): string {
|
|
30
|
+
if (version !== undefined) return version;
|
|
31
|
+
try {
|
|
32
|
+
const manifest = JSON.parse(
|
|
33
|
+
fs.readFileSync(
|
|
34
|
+
path.resolve(__dirname, "..", "..", "..", "..", "package.json"),
|
|
35
|
+
"utf8",
|
|
36
|
+
),
|
|
37
|
+
) as { version?: string };
|
|
38
|
+
version = manifest.version ?? "0.0.0";
|
|
39
|
+
} catch {
|
|
40
|
+
version = "0.0.0";
|
|
41
|
+
}
|
|
42
|
+
return version;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let version: string | undefined;
|
|
@@ -8,8 +8,8 @@ import { CapabilityResolutionFormat } from "./CapabilityResolutionFormat";
|
|
|
8
8
|
import type { ITtscCapabilityPluginSource } from "./ITtscCapabilityPluginSource";
|
|
9
9
|
import type { ITtscCapabilityResolutionEntry } from "./ITtscCapabilityResolutionEntry";
|
|
10
10
|
import type { ITtscCapabilityResolutionPlugin } from "./ITtscCapabilityResolutionPlugin";
|
|
11
|
+
import { PluginContentIdentities } from "./source/PluginContentIdentities";
|
|
11
12
|
import { SourceBuildCacheLayout } from "./source/SourceBuildCacheLayout";
|
|
12
|
-
import { pluginSourceDigest } from "./source/pluginSourceDigest";
|
|
13
13
|
import { pluginSourceFilesSignature } from "./source/pluginSourceFilesSignature";
|
|
14
14
|
import { pluginSourceState } from "./source/pluginSourceState";
|
|
15
15
|
|
|
@@ -71,6 +71,12 @@ export function writeCapabilityResolution(
|
|
|
71
71
|
|
|
72
72
|
/** Pre-load authority key; a moved evaluation authority refuses the write. */
|
|
73
73
|
expectedAuthority?: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Record store of the project's plugin cache, which proves the runtime and
|
|
77
|
+
* the plugin sources and SDK from metadata instead of their bytes (#1725).
|
|
78
|
+
*/
|
|
79
|
+
identities?: PluginContentIdentities.Store;
|
|
74
80
|
},
|
|
75
81
|
answer: {
|
|
76
82
|
/**
|
|
@@ -180,8 +186,8 @@ export function writeCapabilityResolution(
|
|
|
180
186
|
return null;
|
|
181
187
|
}
|
|
182
188
|
}
|
|
183
|
-
const evidence =
|
|
184
|
-
|
|
189
|
+
const evidence = PluginContentIdentities.sourceEvidence(
|
|
190
|
+
PluginContentIdentities.mintReference(path.dirname(file)),
|
|
185
191
|
);
|
|
186
192
|
const entry: ITtscCapabilityResolutionEntry = {
|
|
187
193
|
hostInputHashes,
|
|
@@ -191,7 +197,13 @@ export function writeCapabilityResolution(
|
|
|
191
197
|
pluginSources: Object.fromEntries(
|
|
192
198
|
Object.entries(answer.pluginSources).map(([directory, state]) => [
|
|
193
199
|
directory,
|
|
194
|
-
recordPluginSource(
|
|
200
|
+
recordPluginSource(
|
|
201
|
+
directory,
|
|
202
|
+
state,
|
|
203
|
+
evidence,
|
|
204
|
+
path.resolve(options.cwd),
|
|
205
|
+
options.identities,
|
|
206
|
+
),
|
|
195
207
|
]),
|
|
196
208
|
),
|
|
197
209
|
plugins: answer.plugins.map((plugin) => ({
|
|
@@ -239,23 +251,32 @@ export function writeCapabilityResolution(
|
|
|
239
251
|
* when both signatures agree, every stamp was separable from a reference minted
|
|
240
252
|
* before the read, and the digest gives the very state the load reported, so
|
|
241
253
|
* the entry never vouches for sources the binary was not built from. Anything
|
|
242
|
-
* else records the state alone, which a read proves in full.
|
|
254
|
+
* else records the state alone, which a read proves in full. The digest comes
|
|
255
|
+
* from the project's record store when its metadata still holds there, which
|
|
256
|
+
* places it inside the same two signatures, so a new process does not read
|
|
257
|
+
* every source byte again (#1725).
|
|
243
258
|
*/
|
|
244
259
|
function recordPluginSource(
|
|
245
260
|
directory: string,
|
|
246
261
|
state: string,
|
|
247
|
-
evidence: ReturnType<typeof
|
|
262
|
+
evidence: ReturnType<typeof PluginContentIdentities.sourceEvidence>,
|
|
263
|
+
projectRoot: string,
|
|
264
|
+
identities: PluginContentIdentities.Store | undefined,
|
|
248
265
|
): ITtscCapabilityPluginSource {
|
|
249
266
|
try {
|
|
250
267
|
const before = pluginSourceFilesSignature(directory, evidence);
|
|
251
|
-
const digest =
|
|
268
|
+
const digest = PluginContentIdentities.sourceDirectory(
|
|
269
|
+
identities,
|
|
270
|
+
directory,
|
|
271
|
+
);
|
|
252
272
|
const after = pluginSourceFilesSignature(directory, evidence);
|
|
253
273
|
if (
|
|
254
274
|
before !== undefined &&
|
|
255
275
|
after !== undefined &&
|
|
256
276
|
before.signature === after.signature &&
|
|
257
277
|
after.separable &&
|
|
258
|
-
pluginSourceState(directory, { sourceDigest: digest }) ===
|
|
278
|
+
pluginSourceState(directory, { projectRoot, sourceDigest: digest }) ===
|
|
279
|
+
state
|
|
259
280
|
) {
|
|
260
281
|
return { digest, signature: after.signature, state };
|
|
261
282
|
}
|
|
@@ -15,6 +15,7 @@ import { CapabilityResolutionFormat } from "./internal/CapabilityResolutionForma
|
|
|
15
15
|
import type { ITtscCapabilityResolutionEntry } from "./internal/ITtscCapabilityResolutionEntry";
|
|
16
16
|
import { loadProjectPlugins } from "./internal/load/loadProjectPlugins";
|
|
17
17
|
import { readCapabilityResolution } from "./internal/readCapabilityResolution";
|
|
18
|
+
import { PluginContentIdentities } from "./internal/source/PluginContentIdentities";
|
|
18
19
|
import { writeCapabilityResolution } from "./internal/writeCapabilityResolution";
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -102,12 +103,26 @@ export function resolveCapabilityPluginResolution(options: {
|
|
|
102
103
|
// the same cwd. NUL cannot occur in a filename, so this discovery key cannot
|
|
103
104
|
// collide with an explicitly supplied path.
|
|
104
105
|
const tsconfig = options.tsconfig ?? `${String.fromCharCode(0)}discovered`;
|
|
105
|
-
|
|
106
|
+
// WARNING (#1725): every lookup proved the runtime by streaming it, and a hit
|
|
107
|
+
// proved the plugin sources and the whole GOROOT by their bytes, in each
|
|
108
|
+
// process. One store of the project's plugin cache serves every proof of
|
|
109
|
+
// this resolution, opened only once the runtime may be cached at all.
|
|
110
|
+
const identities = capabilityRecordStore(cwd);
|
|
111
|
+
const runtimeProved = capabilityRuntimeAuthorityComplete(cwd, identities);
|
|
106
112
|
const authority = runtimeProved
|
|
107
|
-
? CapabilityResolutionFormat.resolutionFile({
|
|
113
|
+
? CapabilityResolutionFormat.resolutionFile({
|
|
114
|
+
cwd,
|
|
115
|
+
tsconfig,
|
|
116
|
+
identities: identities(),
|
|
117
|
+
})
|
|
108
118
|
: null;
|
|
109
119
|
const cached = runtimeProved
|
|
110
|
-
? readCapabilityResolution({
|
|
120
|
+
? readCapabilityResolution({
|
|
121
|
+
cwd,
|
|
122
|
+
tsconfig,
|
|
123
|
+
version,
|
|
124
|
+
identities: identities(),
|
|
125
|
+
})
|
|
111
126
|
: null;
|
|
112
127
|
E2ETrace.capabilityResolution("lookup", {
|
|
113
128
|
cwd,
|
|
@@ -118,7 +133,14 @@ export function resolveCapabilityPluginResolution(options: {
|
|
|
118
133
|
cacheHit: cached !== null,
|
|
119
134
|
});
|
|
120
135
|
if (cached !== null)
|
|
121
|
-
return resolved(
|
|
136
|
+
return resolved(
|
|
137
|
+
cached,
|
|
138
|
+
options.capability,
|
|
139
|
+
cwd,
|
|
140
|
+
tsconfig,
|
|
141
|
+
authority,
|
|
142
|
+
identities,
|
|
143
|
+
);
|
|
122
144
|
|
|
123
145
|
const binary = resolveBinary();
|
|
124
146
|
if (binary === null || binary === undefined)
|
|
@@ -178,12 +200,21 @@ export function resolveCapabilityPluginResolution(options: {
|
|
|
178
200
|
runtimeProved &&
|
|
179
201
|
authority !== null &&
|
|
180
202
|
(authorityUnchanged =
|
|
181
|
-
CapabilityResolutionFormat.resolutionFile({
|
|
182
|
-
|
|
203
|
+
CapabilityResolutionFormat.resolutionFile({
|
|
204
|
+
cwd,
|
|
205
|
+
tsconfig,
|
|
206
|
+
identities: identities(),
|
|
207
|
+
}) === authority) &&
|
|
183
208
|
loaded.descriptorReadsDeclared &&
|
|
184
209
|
loaded.discoveryInputsComplete
|
|
185
210
|
? writeCapabilityResolution(
|
|
186
|
-
{
|
|
211
|
+
{
|
|
212
|
+
cwd,
|
|
213
|
+
tsconfig,
|
|
214
|
+
version,
|
|
215
|
+
expectedAuthority: authority,
|
|
216
|
+
identities: identities(),
|
|
217
|
+
},
|
|
187
218
|
answer,
|
|
188
219
|
)
|
|
189
220
|
: null;
|
|
@@ -213,10 +244,40 @@ export function resolveCapabilityPluginResolution(options: {
|
|
|
213
244
|
plugins: CapabilityPluginResult.select(answer, options.capability),
|
|
214
245
|
status: "resolved",
|
|
215
246
|
}
|
|
216
|
-
: resolved(
|
|
247
|
+
: resolved(
|
|
248
|
+
recorded,
|
|
249
|
+
options.capability,
|
|
250
|
+
cwd,
|
|
251
|
+
tsconfig,
|
|
252
|
+
authority,
|
|
253
|
+
identities,
|
|
254
|
+
);
|
|
217
255
|
});
|
|
218
256
|
}
|
|
219
257
|
|
|
258
|
+
/**
|
|
259
|
+
* The record store of `cwd`'s plugin cache, opened on first use and kept for
|
|
260
|
+
* the resolution and its later freshness queries, or `undefined` when none can
|
|
261
|
+
* be opened. A kept store's references only grow older, which can make fewer
|
|
262
|
+
* stamps separable but never more.
|
|
263
|
+
*/
|
|
264
|
+
function capabilityRecordStore(
|
|
265
|
+
cwd: string,
|
|
266
|
+
): () => PluginContentIdentities.Store | undefined {
|
|
267
|
+
let opened = false;
|
|
268
|
+
let store: PluginContentIdentities.Store | undefined;
|
|
269
|
+
return () => {
|
|
270
|
+
if (!opened) {
|
|
271
|
+
opened = true;
|
|
272
|
+
store = PluginContentIdentities.open({
|
|
273
|
+
projectRoot: cwd,
|
|
274
|
+
env: process.env,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return store;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
220
281
|
/**
|
|
221
282
|
* Whether the keyed executable is the actual sole descriptor runtime.
|
|
222
283
|
*
|
|
@@ -228,7 +289,10 @@ export function resolveCapabilityPluginResolution(options: {
|
|
|
228
289
|
* operation's existing decision and preserves short-circuited comparisons as
|
|
229
290
|
* unobserved, without repeating native queries.
|
|
230
291
|
*/
|
|
231
|
-
function capabilityRuntimeAuthorityComplete(
|
|
292
|
+
function capabilityRuntimeAuthorityComplete(
|
|
293
|
+
cwd: string,
|
|
294
|
+
identities: () => PluginContentIdentities.Store | undefined,
|
|
295
|
+
): boolean {
|
|
232
296
|
if (SidecarEnvironment.read(process.env, "NODE_OPTIONS")?.trim()) {
|
|
233
297
|
E2ETrace.capabilityResolution("runtime-authority", {
|
|
234
298
|
cwd,
|
|
@@ -254,6 +318,7 @@ function capabilityRuntimeAuthorityComplete(cwd: string): boolean {
|
|
|
254
318
|
runtime,
|
|
255
319
|
process.env,
|
|
256
320
|
cwd,
|
|
321
|
+
identities(),
|
|
257
322
|
);
|
|
258
323
|
let executableMatches: boolean | undefined;
|
|
259
324
|
const proved =
|
|
@@ -291,6 +356,7 @@ function resolved(
|
|
|
291
356
|
cwd: string,
|
|
292
357
|
tsconfig: string,
|
|
293
358
|
authority: string | null,
|
|
359
|
+
identities: () => PluginContentIdentities.Store | undefined,
|
|
294
360
|
): ITtscCapabilityPluginResolution {
|
|
295
361
|
const proof = JSON.stringify(entry);
|
|
296
362
|
return {
|
|
@@ -298,12 +364,20 @@ function resolved(
|
|
|
298
364
|
try {
|
|
299
365
|
if (
|
|
300
366
|
authority === null ||
|
|
301
|
-
!capabilityRuntimeAuthorityComplete(cwd) ||
|
|
302
|
-
CapabilityResolutionFormat.resolutionFile({
|
|
303
|
-
|
|
367
|
+
!capabilityRuntimeAuthorityComplete(cwd, identities) ||
|
|
368
|
+
CapabilityResolutionFormat.resolutionFile({
|
|
369
|
+
cwd,
|
|
370
|
+
tsconfig,
|
|
371
|
+
identities: identities(),
|
|
372
|
+
}) !== authority
|
|
304
373
|
)
|
|
305
374
|
return false;
|
|
306
|
-
const current = readCapabilityResolution({
|
|
375
|
+
const current = readCapabilityResolution({
|
|
376
|
+
cwd,
|
|
377
|
+
tsconfig,
|
|
378
|
+
version,
|
|
379
|
+
identities: identities(),
|
|
380
|
+
});
|
|
307
381
|
return current !== null && JSON.stringify(current) === proof;
|
|
308
382
|
} catch {
|
|
309
383
|
return false;
|