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
|
@@ -8,20 +8,21 @@ import { OwnedSynchronousProcess } from "../../../internal/OwnedSynchronousProce
|
|
|
8
8
|
import { SourceNativeRetirement } from "../../../internal/SourceNativeRetirement";
|
|
9
9
|
import { createCanonicalTempDirectory } from "../../../internal/createCanonicalTempDirectory";
|
|
10
10
|
import { runHoldingLock } from "../../../internal/runHoldingLock";
|
|
11
|
-
import { NativeSourcePackages } from "./NativeSourcePackages";
|
|
12
|
-
import { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
13
11
|
import { GoSourceInputs } from "./GoSourceInputs";
|
|
14
12
|
import { GoToolResolution } from "./GoToolResolution";
|
|
15
13
|
import type { IPluginModuleReplaceDirectory } from "./IPluginModuleReplaceDirectory";
|
|
16
14
|
import type { ITtscBuildContributor } from "./ITtscBuildContributor";
|
|
17
15
|
import type { ITtscSourceBuildCachePaths } from "./ITtscSourceBuildCachePaths";
|
|
16
|
+
import { NativeSourcePackages } from "./NativeSourcePackages";
|
|
18
17
|
import { PluginBinaryUse } from "./PluginBinaryUse";
|
|
19
18
|
import { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
|
|
20
19
|
import type { PluginBuildLockLease } from "./PluginBuildLockLease";
|
|
21
20
|
import { PluginBuildLockProtocol } from "./PluginBuildLockProtocol";
|
|
21
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
22
22
|
import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
|
|
23
23
|
import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
|
|
24
24
|
import { SourcePluginAdmission } from "./SourcePluginAdmission";
|
|
25
|
+
import { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
25
26
|
import { acquirePluginBuildLock } from "./acquirePluginBuildLock";
|
|
26
27
|
import { computeCacheKey } from "./computeCacheKey";
|
|
27
28
|
import { copiesPluginSourceEntry } from "./copiesPluginSourceEntry";
|
|
@@ -44,12 +45,12 @@ import { withGoBuildCacheLease } from "./withGoBuildCacheLease";
|
|
|
44
45
|
* Build one Go source plugin into a cached executable.
|
|
45
46
|
*
|
|
46
47
|
* `opts.env` is the effective environment for this build; the caller merges
|
|
47
|
-
* inherited and constructor layers using native environment-name identity so
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
48
|
+
* inherited and constructor layers using native environment-name identity so a
|
|
49
|
+
* programmatic `TtscCompiler` instance can pin its own Go toolchain
|
|
50
|
+
* (`TTSC_GO_BINARY`), Go build cache (`TTSC_GO_CACHE_DIR`), and Go build
|
|
51
|
+
* variables (`GOFLAGS`, `CGO_*`, …) without mutating the shared `process.env`.
|
|
52
|
+
* CLI callers omit it and inherit `process.env`, so ambient behavior is
|
|
53
|
+
* unchanged.
|
|
53
54
|
*
|
|
54
55
|
* The cache key includes source and toolchain readings. Before publication the
|
|
55
56
|
* scratch inputs are compared with their recorded digests and the external
|
|
@@ -65,16 +66,16 @@ import { withGoBuildCacheLease } from "./withGoBuildCacheLease";
|
|
|
65
66
|
* Optional loader-required package ownership enters the key. Its cold producer
|
|
66
67
|
* admits the entry and linked sources through Go metadata in the actual
|
|
67
68
|
* materialized workspace before compilation; legacy cache artifacts have a
|
|
68
|
-
* different key. Existing binary hits trust the cache producer and key rather
|
|
69
|
-
* executable bytes. Default caches are managed locally, while
|
|
70
|
-
* retain caller-managed pruning policy. Every returned cache key
|
|
71
|
-
* reader token retained by this process until exit; registration
|
|
72
|
-
* builder/collector lease, and other consumers register independent
|
|
73
|
-
* Failure to establish ownership propagates. Private opt-in tracing
|
|
74
|
-
* mandatory key-creation and pre-build witness gates and adds one
|
|
75
|
-
* observation after Go returns. That diagnostic read adds metadata
|
|
76
|
-
* publication permission. A discarded epoch is separately recorded
|
|
77
|
-
* bounded build owner starts again.
|
|
69
|
+
* different key. Existing binary hits trust the cache producer and key rather
|
|
70
|
+
* than rehashing executable bytes. Default caches are managed locally, while
|
|
71
|
+
* explicit roots retain caller-managed pruning policy. Every returned cache key
|
|
72
|
+
* registers a reader token retained by this process until exit; registration
|
|
73
|
+
* shares the builder/collector lease, and other consumers register independent
|
|
74
|
+
* readers. Failure to establish ownership propagates. Private opt-in tracing
|
|
75
|
+
* records the mandatory key-creation and pre-build witness gates and adds one
|
|
76
|
+
* diagnostic observation after Go returns. That diagnostic read adds metadata
|
|
77
|
+
* work, not publication permission. A discarded epoch is separately recorded
|
|
78
|
+
* when the bounded build owner starts again.
|
|
78
79
|
*
|
|
79
80
|
* Explicit worker scopes check cancellation between build phases, copied source
|
|
80
81
|
* entries and native commands, including reader admission and final
|
|
@@ -88,8 +89,8 @@ import { withGoBuildCacheLease } from "./withGoBuildCacheLease";
|
|
|
88
89
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts A changed witness is never overwritten or accepted. A fresh epoch takes a new toolchain reading and performs the actual build or qualified adoption; source, native and cleanup failures stay terminal. No selected SDK tool is warmed by a special command, and no foreign environment or memo is rebased. Shared Go objects retain their owner's toolID/actionID contract; same-version custom tool semantics beyond that contract are not newly guaranteed.
|
|
89
90
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain effective environment, recorded-input comparisons and their trust/observation limits, reader registration and managed versus explicit roots; option-map comments state their reading provenance with blank member separation.
|
|
90
91
|
* @evidence contracts/portability.md#os-neutral-implementation Node path/physical-cache/temp APIs preserve native identities; executable resolution and Windows command handling are isolated owners, and the binary filename explicitly follows its executable platform.
|
|
91
|
-
* @evidence contracts/performance.md#efficient-algorithms At most three epochs redo native toolchain observation, key construction, materialization and guarded admission;
|
|
92
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Existing binaries and concurrent builders share the version/platform/source/environment key with reader admission before return, assuming trustworthy cache producers and supplied digest maps. Fixed trimpath compilation removes disposable snapshot paths from Go object identities. Shared load readings and sequential source/toolchain comparisons reject observed changes; metadata reuse and unobserved concurrent mutation remain the underlying witnesses' limits.
|
|
92
|
+
* @evidence contracts/performance.md#efficient-algorithms At most three epochs redo native toolchain observation, key construction, materialization and guarded admission; the first can key on the load's witnessed same-directory reading instead of observing the toolchain again, and later ones always observe it. Each can run one Go build, preceded by one batched package metadata command when the loader requires ownership admission. The shared compiler reader can itself make three version observations, hence at most nine such cold observations for a persistently moving compiler and no build when its key witness fails. Full-file bytes, entries, path/sort text, contributors, external trees and witness populations drive work. Scoped cancellation is sampled between phases/entries/commands and does not bound native calls or delegated hashing. First source digest readings are reused across epochs, while each scratch is independently checked. Existing lock contention has its separate wait budget and native Go compiler work remains delegated.
|
|
93
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Existing binaries and concurrent builders share the version/platform/source/environment key with reader admission before return, assuming trustworthy cache producers and supplied digest maps. The key's content digests come from the record store when their separable metadata still matches, so a new process does not read unchanged sources, SDK or compiler bytes to find an existing binary (#1722). Fixed trimpath compilation removes disposable snapshot paths from Go object identities. Shared load readings and sequential source/toolchain comparisons reject observed changes; metadata reuse and unobserved concurrent mutation remain the underlying witnesses' limits.
|
|
93
94
|
* @evidence contracts/performance.md#bound-retention-and-release-resources Scratch directories and build/cache leases have finally-based cleanup, including cancellation; cancelled work admits no new post-build prune pass. Scratch removal or lease cleanup can fail, and selected synchronous child settlement does not join arbitrary descendants. Repeated commands can repeat external tool effects; only ttsc-owned failed outputs are discarded, and Go-owned object entries retain their existing lifetime. Pending binary cleanup is best-effort. Reader tokens and their process map grow with distinct physical keys until process exit. Managed pruning attempts age/LRU reclamation while protecting live, unknown and selected entries, so it is not a hard disk bound; explicit roots remain caller-managed.
|
|
94
95
|
*/
|
|
95
96
|
export function buildSourcePlugin(opts: {
|
|
@@ -101,7 +102,10 @@ export function buildSourcePlugin(opts: {
|
|
|
101
102
|
env?: NodeJS.ProcessEnv;
|
|
102
103
|
|
|
103
104
|
/** Loader-required ownership of the entry and named linked packages. */
|
|
104
|
-
packageOwnership?: readonly {
|
|
105
|
+
packageOwnership?: readonly {
|
|
106
|
+
entry: string;
|
|
107
|
+
kind: "executable" | "linked";
|
|
108
|
+
}[];
|
|
105
109
|
|
|
106
110
|
/**
|
|
107
111
|
* Digests of the environment each build directory is keyed on, shared by
|
|
@@ -110,7 +114,21 @@ export function buildSourcePlugin(opts: {
|
|
|
110
114
|
*/
|
|
111
115
|
environmentDigests?: Map<string, string>;
|
|
112
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Witnesses of the readings in `environmentDigests` (`computeCacheKey`), so
|
|
119
|
+
* the first epoch keys on a reading package selection already took. A
|
|
120
|
+
* discarded epoch reads the toolchain again instead.
|
|
121
|
+
*/
|
|
122
|
+
environmentWitnesses?: Map<string, PluginBuildEnvironmentWitness.Record>;
|
|
123
|
+
|
|
113
124
|
filesystem?: Partial<SourceBuildFilesystemOperations>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Record store the loader opened for this load, so its key reuses content
|
|
128
|
+
* identities across processes (#1722); a standalone build opens its own.
|
|
129
|
+
*/
|
|
130
|
+
identities?: PluginContentIdentities.Store;
|
|
131
|
+
|
|
114
132
|
label?: string;
|
|
115
133
|
overlayDirs?: readonly string[];
|
|
116
134
|
quiet?: boolean;
|
|
@@ -123,6 +141,9 @@ export function buildSourcePlugin(opts: {
|
|
|
123
141
|
*/
|
|
124
142
|
sourceDigests?: Map<string, string>;
|
|
125
143
|
|
|
144
|
+
/** Pinned-tool manifest readers shared only by this loader request. */
|
|
145
|
+
goModReaders?: Map<string, SourcePluginWorkspace.GoModReader>;
|
|
146
|
+
|
|
126
147
|
ttscVersion: string;
|
|
127
148
|
tsgoVersion: string;
|
|
128
149
|
}): string {
|
|
@@ -134,9 +155,17 @@ export function buildSourcePlugin(opts: {
|
|
|
134
155
|
for (let attempt = 0; ; attempt += 1) {
|
|
135
156
|
OwnedSynchronousProcess.checkpoint();
|
|
136
157
|
const environmentDigests = new Map(opts.environmentDigests);
|
|
158
|
+
// Only the first epoch may key on a reading taken before it; a discarded
|
|
159
|
+
// epoch means the toolchain moved, so later ones read it again.
|
|
160
|
+
const environmentWitnesses =
|
|
161
|
+
opts.environmentWitnesses === undefined
|
|
162
|
+
? undefined
|
|
163
|
+
: new Map(attempt === 0 ? opts.environmentWitnesses : []);
|
|
137
164
|
try {
|
|
138
165
|
const binary = buildSourcePluginAttempt(
|
|
139
|
-
|
|
166
|
+
attempt === 0
|
|
167
|
+
? { ...opts, environmentWitnesses }
|
|
168
|
+
: { ...opts, environmentWitnesses, goModReaders: undefined },
|
|
140
169
|
env,
|
|
141
170
|
sourceDigests,
|
|
142
171
|
environmentDigests,
|
|
@@ -146,6 +175,8 @@ export function buildSourcePlugin(opts: {
|
|
|
146
175
|
opts.sourceDigests?.set(directory, digest);
|
|
147
176
|
for (const [directory, digest] of environmentDigests)
|
|
148
177
|
opts.environmentDigests?.set(directory, digest);
|
|
178
|
+
for (const [directory, witness] of environmentWitnesses ?? [])
|
|
179
|
+
opts.environmentWitnesses?.set(directory, witness);
|
|
149
180
|
return binary;
|
|
150
181
|
} catch (error) {
|
|
151
182
|
if (
|
|
@@ -174,7 +205,9 @@ function buildSourcePluginAttempt(
|
|
|
174
205
|
): string {
|
|
175
206
|
OwnedSynchronousProcess.checkpoint();
|
|
176
207
|
const { dir, entry, source } = resolveSourceBuildTarget(opts);
|
|
177
|
-
const overlayDirs = [
|
|
208
|
+
const overlayDirs = [
|
|
209
|
+
...(opts.overlayDirs ?? SourcePluginWorkspace.findTtscOverlayDirs()),
|
|
210
|
+
].sort();
|
|
178
211
|
const contributors = opts.contributors ?? [];
|
|
179
212
|
const compiler = resolveGoCompiler(env);
|
|
180
213
|
const goBinary = GoToolResolution.resolveGoToolForBuild(
|
|
@@ -184,8 +217,27 @@ function buildSourcePluginAttempt(
|
|
|
184
217
|
);
|
|
185
218
|
ensureExecutableGoToolchain(goBinary, compiler.bundled);
|
|
186
219
|
OwnedSynchronousProcess.checkpoint();
|
|
220
|
+
const identities =
|
|
221
|
+
opts.identities ??
|
|
222
|
+
PluginContentIdentities.open({
|
|
223
|
+
projectRoot: opts.baseDir,
|
|
224
|
+
cacheDir: opts.cacheDir,
|
|
225
|
+
env,
|
|
226
|
+
sources: [
|
|
227
|
+
dir,
|
|
228
|
+
...overlayDirs,
|
|
229
|
+
...contributors.map((contributor) => contributor.source),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
187
232
|
// The digest of every directory the key covers, as the key read it, which
|
|
188
233
|
// the build proves against what it compiled.
|
|
234
|
+
const goModReader = SourcePluginWorkspace.createGoModReader(
|
|
235
|
+
goBinary,
|
|
236
|
+
opts.pluginName,
|
|
237
|
+
env,
|
|
238
|
+
opts.goModReaders,
|
|
239
|
+
identities,
|
|
240
|
+
);
|
|
189
241
|
const environmentWitness: PluginBuildEnvironmentWitness.Record = new Map();
|
|
190
242
|
const key = computeCacheKey({
|
|
191
243
|
contributors,
|
|
@@ -194,10 +246,13 @@ function buildSourcePluginAttempt(
|
|
|
194
246
|
entry,
|
|
195
247
|
env,
|
|
196
248
|
environmentWitness,
|
|
249
|
+
goModReader,
|
|
197
250
|
filesystem: opts.filesystem,
|
|
198
251
|
goBinary,
|
|
252
|
+
identities,
|
|
199
253
|
overlayDirs,
|
|
200
254
|
environmentDigests,
|
|
255
|
+
environmentWitnesses: opts.environmentWitnesses,
|
|
201
256
|
sourceDigests,
|
|
202
257
|
ttscVersion: opts.ttscVersion,
|
|
203
258
|
tsgoVersion: opts.tsgoVersion,
|
|
@@ -317,6 +372,7 @@ function buildSourcePluginAttempt(
|
|
|
317
372
|
env,
|
|
318
373
|
environmentWitness,
|
|
319
374
|
goBinary,
|
|
375
|
+
goModReader,
|
|
320
376
|
normalizeGoToolPermissions: compiler.bundled,
|
|
321
377
|
key,
|
|
322
378
|
keyedDigests: sourceDigests,
|
|
@@ -394,7 +450,10 @@ function compileSourcePlugin(opts: {
|
|
|
394
450
|
binaryPath: string;
|
|
395
451
|
cacheDir: string;
|
|
396
452
|
contributors: readonly ITtscBuildContributor[];
|
|
397
|
-
packageOwnership?: readonly {
|
|
453
|
+
packageOwnership?: readonly {
|
|
454
|
+
entry: string;
|
|
455
|
+
kind: "executable" | "linked";
|
|
456
|
+
}[];
|
|
398
457
|
dir: string;
|
|
399
458
|
entry: string;
|
|
400
459
|
env: NodeJS.ProcessEnv;
|
|
@@ -406,6 +465,7 @@ function compileSourcePlugin(opts: {
|
|
|
406
465
|
goBuildCacheRoot: string;
|
|
407
466
|
manageGoBuildCache: boolean;
|
|
408
467
|
normalizeGoToolPermissions: boolean;
|
|
468
|
+
goModReader: SourcePluginWorkspace.GoModReader;
|
|
409
469
|
key: string;
|
|
410
470
|
|
|
411
471
|
/** The digest of every directory the key covers, as the key read it. */
|
|
@@ -461,6 +521,7 @@ function compileSourcePlugin(opts: {
|
|
|
461
521
|
opts.dir,
|
|
462
522
|
opts.env,
|
|
463
523
|
opts.goBinary,
|
|
524
|
+
opts.goModReader,
|
|
464
525
|
);
|
|
465
526
|
OwnedSynchronousProcess.checkpoint();
|
|
466
527
|
// Every source the build would otherwise read in place, an overlay and
|
|
@@ -487,11 +548,7 @@ function compileSourcePlugin(opts: {
|
|
|
487
548
|
opts.goBinary,
|
|
488
549
|
opts.env,
|
|
489
550
|
);
|
|
490
|
-
const goModReader =
|
|
491
|
-
opts.goBinary,
|
|
492
|
-
opts.pluginName,
|
|
493
|
-
opts.env,
|
|
494
|
-
);
|
|
551
|
+
const goModReader = opts.goModReader;
|
|
495
552
|
OwnedSynchronousProcess.checkpoint();
|
|
496
553
|
if (opts.contributors.length > 0) {
|
|
497
554
|
mergeContributors({
|
|
@@ -543,12 +600,20 @@ function compileSourcePlugin(opts: {
|
|
|
543
600
|
const observations = NativeSourcePackages.read({
|
|
544
601
|
cwd: scratchDir,
|
|
545
602
|
entries: opts.packageOwnership.map((input) => input.entry),
|
|
546
|
-
env: GoSourceInputs.goBuildEnv(
|
|
603
|
+
env: GoSourceInputs.goBuildEnv(
|
|
604
|
+
opts.goBinary,
|
|
605
|
+
goBuildCacheRoot,
|
|
606
|
+
opts.env,
|
|
607
|
+
),
|
|
547
608
|
goBinary: opts.goBinary,
|
|
548
609
|
pluginName: opts.pluginName,
|
|
549
610
|
});
|
|
550
611
|
opts.packageOwnership.forEach((input, index) => {
|
|
551
|
-
NativeSourcePackages.kind(
|
|
612
|
+
NativeSourcePackages.kind(
|
|
613
|
+
observations[index]!,
|
|
614
|
+
`${opts.pluginName} ${input.entry}`,
|
|
615
|
+
input.kind,
|
|
616
|
+
);
|
|
552
617
|
});
|
|
553
618
|
}
|
|
554
619
|
runGoBuild(
|
|
@@ -629,10 +694,14 @@ function compileSourcePlugin(opts: {
|
|
|
629
694
|
taskFailure = { error };
|
|
630
695
|
throw error;
|
|
631
696
|
} finally {
|
|
632
|
-
SourceNativeRetirement.releaseResource(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
697
|
+
SourceNativeRetirement.releaseResource(
|
|
698
|
+
scratchDir,
|
|
699
|
+
() => {
|
|
700
|
+
fs.rmSync(scratchDir, { recursive: true, force: true });
|
|
701
|
+
SourceNativeRetirement.forget(opts.cacheDir);
|
|
702
|
+
},
|
|
703
|
+
taskFailure,
|
|
704
|
+
);
|
|
636
705
|
}
|
|
637
706
|
}
|
|
638
707
|
|
|
@@ -1009,7 +1078,6 @@ function resolveSourceBuildTarget(opts: {
|
|
|
1009
1078
|
return { dir: moduleRoot, entry, source };
|
|
1010
1079
|
}
|
|
1011
1080
|
|
|
1012
|
-
|
|
1013
1081
|
/**
|
|
1014
1082
|
* The path, below a build's scratch directory, of the tree holding its copies
|
|
1015
1083
|
* of the sources outside the module.
|
|
@@ -5,10 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { GoToolResolution } from "./GoToolResolution";
|
|
6
6
|
import type { ITtscBuildContributor } from "./ITtscBuildContributor";
|
|
7
7
|
import type { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
|
|
8
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
8
9
|
import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
|
|
10
|
+
import type { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
9
11
|
import { hashPluginBuildEnvironment } from "./hashPluginBuildEnvironment";
|
|
10
12
|
import { pluginModuleReplaceDirectories } from "./pluginModuleReplaceDirectories";
|
|
11
|
-
import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Compute a 32-character lowercase key from a plugin build's SHA-256 digest.
|
|
@@ -20,8 +21,8 @@ import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
|
20
21
|
* source files. An optional required package-ownership population also frames
|
|
21
22
|
* the key, separating binaries admitted by the matching cold producer from
|
|
22
23
|
* legacy artifacts; requests without that authority retain their existing key.
|
|
23
|
-
* Contributors are sorted by name; unique names remove input
|
|
24
|
-
*
|
|
24
|
+
* Contributors are sorted by name; unique names remove input order dependence,
|
|
25
|
+
* while equal-name rows retain the sort's input order.
|
|
25
26
|
*
|
|
26
27
|
* Each source directory enters the key as its digest (`pluginSourceDigest`),
|
|
27
28
|
* which the transform envelope reports, with the environment below, as the
|
|
@@ -29,10 +30,12 @@ import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
|
29
30
|
* values are trusted producer readings, not revalidated content observations.
|
|
30
31
|
* `sourceDigests` carries the digests one load already took: every build of the
|
|
31
32
|
* load keys on one reading of each directory, and the load reports those
|
|
32
|
-
* readings. The environment enters
|
|
33
|
-
* rule that reported state takes it from too,
|
|
34
|
-
* the digest of each build directory's, so the
|
|
35
|
-
* `go env` run.
|
|
33
|
+
* readings. The environment enters as the digest of
|
|
34
|
+
* `hashPluginBuildEnvironment`, the rule that reported state takes it from too,
|
|
35
|
+
* and `environmentDigests` carries the digest of each build directory's, so the
|
|
36
|
+
* load reports it without a second `go env` run. With `environmentWitnesses`, a
|
|
37
|
+
* reading the load already took for the same directory, such as package
|
|
38
|
+
* selection's, keys the build too, and its witness joins the build's.
|
|
36
39
|
*
|
|
37
40
|
* The `ttsc cache` CLI and plugin build pipeline share this key computation.
|
|
38
41
|
*
|
|
@@ -42,7 +45,7 @@ import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
|
42
45
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain input coverage and the provenance of reported digests; documented optional output maps have blank separation between members.
|
|
43
46
|
* @evidence contracts/portability.md#os-neutral-implementation Native roots are resolved with Node path APIs and executable resolution; platform/architecture intentionally distinguish incompatible binary artifacts.
|
|
44
47
|
* @evidence contracts/performance.md#efficient-algorithms Source digest misses enumerate/sort paths and read individual files in full, not bounded streaming chunks. Overlay/contributor ordering compares path/name text; replacement discovery can read a manifest/run Go and native containment queries. Toolchain/env hashing delegates native metadata/probes/full bytes and memo checks without an unmeasured dominant-cost ranking; JSON framing processes all version/entry/label/digest text.
|
|
45
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe; it
|
|
48
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. A supplied record store lets a new process reuse a directory, SDK or executable digest only while its separable metadata signature matches the recorded one (#1722), so unchanged inputs are not re-read per process. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe. A same-directory reading of the same load, with its witness, replaces a second environment probe; the witness then joins this build's, which the build compares after it ran, and a non-default byte adapter never shares. Reuse inherits the selected population and metadata/producer premises.
|
|
46
49
|
*
|
|
47
50
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Output maps belong to the enclosing load; this operation retains no handles or process-wide records itself.
|
|
48
51
|
*/
|
|
@@ -53,7 +56,10 @@ export function computeCacheKey(inputs: {
|
|
|
53
56
|
env?: NodeJS.ProcessEnv;
|
|
54
57
|
|
|
55
58
|
/** Required package ownership, admitted by the matching cold producer. */
|
|
56
|
-
packageOwnership?: readonly {
|
|
59
|
+
packageOwnership?: readonly {
|
|
60
|
+
entry: string;
|
|
61
|
+
kind: "executable" | "linked";
|
|
62
|
+
}[];
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
65
|
* Digests of the environment each build directory is keyed on
|
|
@@ -62,6 +68,14 @@ export function computeCacheKey(inputs: {
|
|
|
62
68
|
*/
|
|
63
69
|
environmentDigests?: Map<string, string>;
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Witness of each reading in `environmentDigests` this load took, by
|
|
73
|
+
* directory. A digest with its witness stands for this call's own reading:
|
|
74
|
+
* the key takes it instead of running Go again and copies the witness into
|
|
75
|
+
* `environmentWitness`. Filled with this call's reading when it takes one.
|
|
76
|
+
*/
|
|
77
|
+
environmentWitnesses?: Map<string, PluginBuildEnvironmentWitness.Record>;
|
|
78
|
+
|
|
65
79
|
/**
|
|
66
80
|
* Receives selected native toolchain dependencies from the environment reader
|
|
67
81
|
* for later comparison under its metadata-distinguishability premise; it is
|
|
@@ -72,6 +86,15 @@ export function computeCacheKey(inputs: {
|
|
|
72
86
|
/** Byte adapter for delegated SDK identity; plugin source digests use fs. */
|
|
73
87
|
filesystem?: Partial<SourceBuildFilesystemOperations>;
|
|
74
88
|
goBinary?: string;
|
|
89
|
+
goModReader?: SourcePluginWorkspace.GoModReader;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Record store that proves unchanged sources, overlays, contributors, GOROOT
|
|
93
|
+
* and executables from metadata in a new process (#1722). Without it every
|
|
94
|
+
* process reads their bytes.
|
|
95
|
+
*/
|
|
96
|
+
identities?: PluginContentIdentities.Store;
|
|
97
|
+
|
|
75
98
|
overlayDirs?: readonly string[];
|
|
76
99
|
|
|
77
100
|
/**
|
|
@@ -102,69 +125,117 @@ export function computeCacheKey(inputs: {
|
|
|
102
125
|
hash.update(JSON.stringify(["platform", process.platform, process.arch]));
|
|
103
126
|
hash.update(JSON.stringify(["entry", inputs.entry]));
|
|
104
127
|
if (inputs.packageOwnership !== undefined)
|
|
105
|
-
hash.update(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
128
|
+
hash.update(
|
|
129
|
+
JSON.stringify([
|
|
130
|
+
"package-ownership",
|
|
131
|
+
1,
|
|
132
|
+
[...inputs.packageOwnership].sort((left, right) =>
|
|
133
|
+
left.entry < right.entry
|
|
134
|
+
? -1
|
|
135
|
+
: left.entry > right.entry
|
|
136
|
+
? 1
|
|
137
|
+
: left.kind < right.kind
|
|
138
|
+
? -1
|
|
139
|
+
: left.kind > right.kind
|
|
140
|
+
? 1
|
|
141
|
+
: 0,
|
|
142
|
+
),
|
|
143
|
+
]),
|
|
144
|
+
);
|
|
110
145
|
// Private materialization changes native package working directories. Keep
|
|
111
146
|
// binaries produced by the former absolute-ancestry layout out of this
|
|
112
147
|
// layout's cache admissions, including callers with no SDK overlays.
|
|
113
148
|
hash.update(JSON.stringify(["external-source-layout", 1]));
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
hash.update(data);
|
|
121
|
-
environment.update(data);
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
goBinary,
|
|
125
|
-
inputs.dir,
|
|
126
|
-
env,
|
|
127
|
-
filesystem,
|
|
128
|
-
inputs.environmentWitness,
|
|
129
|
-
);
|
|
130
|
-
inputs.environmentDigests?.set(
|
|
131
|
-
path.resolve(inputs.dir),
|
|
132
|
-
environment.digest("hex"),
|
|
149
|
+
const overlays = [...(inputs.overlayDirs ?? [])].sort();
|
|
150
|
+
// Hash contributors in sorted-by-name order so two consumers with the
|
|
151
|
+
// same logical set produce the same key regardless of declaration order
|
|
152
|
+
// in the host's plugin descriptor.
|
|
153
|
+
const sortedContributors = [...(inputs.contributors ?? [])].sort((a, b) =>
|
|
154
|
+
a.name === b.name ? 0 : a.name < b.name ? -1 : 1,
|
|
133
155
|
);
|
|
134
|
-
|
|
156
|
+
// A store whose root lies inside a directory this key hashes would write
|
|
157
|
+
// records into that source, so such a store proves nothing here. The build
|
|
158
|
+
// rejects that cache placement (`SourcePluginAdmission`) once every source
|
|
159
|
+
// is known; replacement targets narrow the store below, before their own
|
|
160
|
+
// digests, since only Go's reading of the manifest names them.
|
|
161
|
+
let identities = PluginContentIdentities.outside(inputs.identities, [
|
|
162
|
+
inputs.dir,
|
|
163
|
+
...overlays,
|
|
164
|
+
...sortedContributors.map((contributor) => contributor.source),
|
|
165
|
+
]);
|
|
166
|
+
// The key frames the environment digest pluginBuildEnvironment reports for
|
|
167
|
+
// this directory. WARNING (#1721): package selection reads that environment
|
|
168
|
+
// first in the same load; reading it again here doubled `go env` and
|
|
169
|
+
// `go version` per load. A reading taken through another byte adapter is
|
|
170
|
+
// not this one's, so only the default adapter shares it.
|
|
171
|
+
const environmentDirectory = path.resolve(inputs.dir);
|
|
172
|
+
const sharedDigest =
|
|
173
|
+
inputs.filesystem === undefined
|
|
174
|
+
? inputs.environmentDigests?.get(environmentDirectory)
|
|
175
|
+
: undefined;
|
|
176
|
+
const sharedWitness =
|
|
177
|
+
sharedDigest === undefined
|
|
178
|
+
? undefined
|
|
179
|
+
: inputs.environmentWitnesses?.get(environmentDirectory);
|
|
180
|
+
let environment: string;
|
|
181
|
+
if (sharedDigest !== undefined && sharedWitness !== undefined) {
|
|
182
|
+
// The build compares this witness after it ran, exactly as it would its
|
|
183
|
+
// own reading's, so a toolchain change since the reading still refuses.
|
|
184
|
+
for (const [file, state] of sharedWitness)
|
|
185
|
+
if (!inputs.environmentWitness?.has(file))
|
|
186
|
+
inputs.environmentWitness?.set(file, state);
|
|
187
|
+
environment = sharedDigest;
|
|
188
|
+
} else {
|
|
189
|
+
const witness =
|
|
190
|
+
inputs.environmentWitness ??
|
|
191
|
+
(inputs.environmentWitnesses === undefined ? undefined : new Map());
|
|
192
|
+
const reading = crypto.createHash("sha256");
|
|
193
|
+
hashPluginBuildEnvironment(
|
|
194
|
+
reading,
|
|
195
|
+
goBinary,
|
|
196
|
+
inputs.dir,
|
|
197
|
+
env,
|
|
198
|
+
filesystem,
|
|
199
|
+
witness,
|
|
200
|
+
identities,
|
|
201
|
+
);
|
|
202
|
+
environment = reading.digest("hex");
|
|
203
|
+
inputs.environmentDigests?.set(environmentDirectory, environment);
|
|
204
|
+
if (inputs.filesystem === undefined && witness !== undefined)
|
|
205
|
+
inputs.environmentWitnesses?.set(environmentDirectory, new Map(witness));
|
|
206
|
+
}
|
|
207
|
+
hash.update(JSON.stringify(["environment", environment]));
|
|
208
|
+
const directory = (label: string, root: string): void =>
|
|
209
|
+
hashSourceDirectory(hash, label, root, inputs.sourceDigests, identities);
|
|
210
|
+
directory("plugin", inputs.dir);
|
|
135
211
|
// Local replacement targets outside the module supply separately keyed
|
|
136
212
|
// sources. buildSourcePlugin snapshots and proves their copies before
|
|
137
|
-
// redirecting the Go replacement directives to the scratch tree.
|
|
138
|
-
|
|
213
|
+
// redirecting the Go replacement directives to the scratch tree. Go reads the
|
|
214
|
+
// manifest after the plugin's digest, so an edit made meanwhile lands between
|
|
215
|
+
// that digest and the build's copy, which `requireKeyedSource` refuses.
|
|
216
|
+
const replacements = pluginModuleReplaceDirectories(
|
|
139
217
|
inputs.dir,
|
|
140
218
|
env,
|
|
141
219
|
goBinary,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
220
|
+
inputs.goModReader,
|
|
221
|
+
);
|
|
222
|
+
identities = PluginContentIdentities.outside(
|
|
223
|
+
identities,
|
|
224
|
+
replacements.map((replacement) => replacement.directory),
|
|
225
|
+
);
|
|
226
|
+
for (const replacement of replacements) {
|
|
227
|
+
directory(
|
|
145
228
|
`replace:${replacement.modulePath}${
|
|
146
229
|
replacement.version === undefined ? "" : `@${replacement.version}`
|
|
147
230
|
}`,
|
|
148
231
|
replacement.directory,
|
|
149
|
-
inputs.sourceDigests,
|
|
150
232
|
);
|
|
151
233
|
}
|
|
152
|
-
for (const [index, dir] of
|
|
153
|
-
|
|
234
|
+
for (const [index, dir] of overlays.entries()) {
|
|
235
|
+
directory(`overlay:${index}`, dir);
|
|
154
236
|
}
|
|
155
|
-
// Hash contributors in sorted-by-name order so two consumers with the
|
|
156
|
-
// same logical set produce the same key regardless of declaration order
|
|
157
|
-
// in the host's plugin descriptor.
|
|
158
|
-
const sortedContributors = [...(inputs.contributors ?? [])].sort((a, b) =>
|
|
159
|
-
a.name === b.name ? 0 : a.name < b.name ? -1 : 1,
|
|
160
|
-
);
|
|
161
237
|
for (const contributor of sortedContributors) {
|
|
162
|
-
|
|
163
|
-
hash,
|
|
164
|
-
`contributor:${contributor.name}`,
|
|
165
|
-
contributor.source,
|
|
166
|
-
inputs.sourceDigests,
|
|
167
|
-
);
|
|
238
|
+
directory(`contributor:${contributor.name}`, contributor.source);
|
|
168
239
|
}
|
|
169
240
|
return hash.digest("hex").slice(0, 32);
|
|
170
241
|
}
|
|
@@ -179,11 +250,14 @@ function hashSourceDirectory(
|
|
|
179
250
|
label: string,
|
|
180
251
|
root: string,
|
|
181
252
|
digests: Map<string, string> | undefined,
|
|
253
|
+
identities: PluginContentIdentities.Store | undefined,
|
|
182
254
|
): void {
|
|
183
255
|
const directory = path.resolve(root);
|
|
184
256
|
let digest = digests?.get(directory);
|
|
185
257
|
if (digest === undefined) {
|
|
186
|
-
|
|
258
|
+
// WARNING (#1722): the load-scoped map above ends with the process. Only the
|
|
259
|
+
// record store spares a new process from reading every source byte again.
|
|
260
|
+
digest = PluginContentIdentities.sourceDirectory(identities, directory);
|
|
187
261
|
digests?.set(directory, digest);
|
|
188
262
|
}
|
|
189
263
|
hash.update(JSON.stringify(["dir", label, digest]));
|