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.
Files changed (155) hide show
  1. package/cmd/platform/main.go +2 -2
  2. package/internal/sourceprocess/contain_windows.go +10 -13
  3. package/internal/sourceprocess/started_windows.go +41 -0
  4. package/internal/sourceprocess/windows_started_publication_distinguishes_pending_from_refusal_test.go +76 -0
  5. package/internal/watchhelper/run.go +30 -20
  6. package/internal/watchhelper/run_other.go +4 -4
  7. package/internal/watchhelper/run_windows.go +471 -0
  8. package/internal/watchhelper/watchhelper.go +8 -2
  9. package/internal/watchhelper/windows_notifications_preserve_mutations_without_access_noise_test.go +506 -0
  10. package/lib/compiler/internal/build/BuildExecution.d.ts +4 -2
  11. package/lib/compiler/internal/build/BuildExecution.js +40 -12
  12. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  13. package/lib/compiler/internal/startResidentTransform.js +5 -4
  14. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  15. package/lib/compiler/internal/transformProjectInMemory.js +9 -7
  16. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  17. package/lib/internal/javascriptRuntimeCapabilities.d.ts +13 -4
  18. package/lib/internal/javascriptRuntimeCapabilities.js +56 -6
  19. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  20. package/lib/internal/nativeBinary.d.ts +3 -3
  21. package/lib/internal/nativeBinary.js +3 -3
  22. package/lib/internal/pluginSource.d.ts +1 -0
  23. package/lib/internal/pluginSource.js +1 -0
  24. package/lib/internal/pluginSource.js.map +1 -1
  25. package/lib/internal/resolveNodeBinary.d.ts +6 -1
  26. package/lib/internal/resolveNodeBinary.js +6 -2
  27. package/lib/internal/resolveNodeBinary.js.map +1 -1
  28. package/lib/internal/runtimeExecutableIdentity.d.ts +22 -13
  29. package/lib/internal/runtimeExecutableIdentity.js +192 -102
  30. package/lib/internal/runtimeExecutableIdentity.js.map +1 -1
  31. package/lib/launcher/internal/runtime/installRuntimeHooks.js +58 -4
  32. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  33. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.d.ts +5 -1
  34. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js +7 -2
  35. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js.map +1 -1
  36. package/lib/launcher/internal/ttscserver/runTtscserver.js +8 -1
  37. package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
  38. package/lib/plugin/internal/CapabilityResolutionFormat.d.ts +21 -62
  39. package/lib/plugin/internal/CapabilityResolutionFormat.js +15 -106
  40. package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
  41. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +6 -0
  42. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +4 -4
  43. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  44. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +24 -14
  45. package/lib/plugin/internal/load/loadProjectPlugins.js +141 -56
  46. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  47. package/lib/plugin/internal/readCapabilityResolution.d.ts +6 -0
  48. package/lib/plugin/internal/readCapabilityResolution.js +8 -5
  49. package/lib/plugin/internal/readCapabilityResolution.js.map +1 -1
  50. package/lib/plugin/internal/source/GoEnvironmentReading.d.ts +48 -0
  51. package/lib/plugin/internal/source/GoEnvironmentReading.js +156 -0
  52. package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -0
  53. package/lib/plugin/internal/source/NativeSourcePackages.d.ts +51 -13
  54. package/lib/plugin/internal/source/NativeSourcePackages.js +182 -27
  55. package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
  56. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +42 -3
  57. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +68 -7
  58. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  59. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.d.ts +5 -3
  60. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.js +14 -8
  61. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.js.map +1 -1
  62. package/lib/plugin/internal/source/PluginContentIdentities.d.ts +265 -0
  63. package/lib/plugin/internal/source/PluginContentIdentities.js +452 -0
  64. package/lib/plugin/internal/source/PluginContentIdentities.js.map +1 -0
  65. package/lib/plugin/internal/source/PluginLoadAnswers.d.ts +63 -0
  66. package/lib/plugin/internal/source/PluginLoadAnswers.js +128 -0
  67. package/lib/plugin/internal/source/PluginLoadAnswers.js.map +1 -0
  68. package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +10 -0
  69. package/lib/plugin/internal/source/SourceBuildCacheLayout.js +12 -0
  70. package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
  71. package/lib/plugin/internal/source/SourcePluginWorkspace.d.ts +53 -19
  72. package/lib/plugin/internal/source/SourcePluginWorkspace.js +111 -22
  73. package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
  74. package/lib/plugin/internal/source/buildSourcePlugin.d.ts +34 -18
  75. package/lib/plugin/internal/source/buildSourcePlugin.js +52 -24
  76. package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
  77. package/lib/plugin/internal/source/computeCacheKey.d.ts +25 -7
  78. package/lib/plugin/internal/source/computeCacheKey.js +85 -34
  79. package/lib/plugin/internal/source/computeCacheKey.js.map +1 -1
  80. package/lib/plugin/internal/source/hashPluginBuildEnvironment.d.ts +7 -3
  81. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js +75 -102
  82. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js.map +1 -1
  83. package/lib/plugin/internal/source/pluginBuildEnvironment.d.ts +8 -3
  84. package/lib/plugin/internal/source/pluginBuildEnvironment.js +8 -4
  85. package/lib/plugin/internal/source/pluginBuildEnvironment.js.map +1 -1
  86. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js +12 -2
  87. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js.map +1 -1
  88. package/lib/plugin/internal/source/pluginBuildVersions.d.ts +2 -2
  89. package/lib/plugin/internal/source/pluginBuildVersions.js +4 -17
  90. package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
  91. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +6 -5
  92. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +10 -17
  93. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
  94. package/lib/plugin/internal/source/pluginSourceState.d.ts +6 -2
  95. package/lib/plugin/internal/source/pluginSourceState.js +20 -8
  96. package/lib/plugin/internal/source/pluginSourceState.js.map +1 -1
  97. package/lib/plugin/internal/source/pluginSourceStateHolds.d.ts +7 -2
  98. package/lib/plugin/internal/source/pluginSourceStateHolds.js +12 -7
  99. package/lib/plugin/internal/source/pluginSourceStateHolds.js.map +1 -1
  100. package/lib/plugin/internal/source/processPluginBuildEnvironment.d.ts +7 -2
  101. package/lib/plugin/internal/source/processPluginBuildEnvironment.js +8 -3
  102. package/lib/plugin/internal/source/processPluginBuildEnvironment.js.map +1 -1
  103. package/lib/plugin/internal/source/provenPluginSourceDigest.d.ts +28 -0
  104. package/lib/plugin/internal/source/provenPluginSourceDigest.js +41 -0
  105. package/lib/plugin/internal/source/provenPluginSourceDigest.js.map +1 -0
  106. package/lib/plugin/internal/source/pruneCacheFileRoot.d.ts +3 -2
  107. package/lib/plugin/internal/source/pruneCacheFileRoot.js +3 -2
  108. package/lib/plugin/internal/source/pruneCacheFileRoot.js.map +1 -1
  109. package/lib/plugin/internal/source/ttscPackageVersion.d.ts +26 -0
  110. package/lib/plugin/internal/source/ttscPackageVersion.js +47 -0
  111. package/lib/plugin/internal/source/ttscPackageVersion.js.map +1 -0
  112. package/lib/plugin/internal/writeCapabilityResolution.d.ts +6 -0
  113. package/lib/plugin/internal/writeCapabilityResolution.js +11 -7
  114. package/lib/plugin/internal/writeCapabilityResolution.js.map +1 -1
  115. package/lib/plugin/resolveCapabilityPlugins.js +67 -15
  116. package/lib/plugin/resolveCapabilityPlugins.js.map +1 -1
  117. package/package.json +8 -8
  118. package/src/compiler/internal/build/BuildExecution.ts +46 -13
  119. package/src/compiler/internal/startResidentTransform.ts +12 -3
  120. package/src/compiler/internal/transformProjectInMemory.ts +10 -2
  121. package/src/internal/javascriptRuntimeCapabilities.ts +71 -5
  122. package/src/internal/nativeBinary.ts +3 -3
  123. package/src/internal/pluginSource.ts +1 -0
  124. package/src/internal/resolveNodeBinary.ts +12 -1
  125. package/src/internal/runtimeExecutableIdentity.ts +227 -141
  126. package/src/launcher/internal/runtime/installRuntimeHooks.ts +68 -3
  127. package/src/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.ts +11 -2
  128. package/src/launcher/internal/ttscserver/runTtscserver.ts +18 -1
  129. package/src/plugin/internal/CapabilityResolutionFormat.ts +23 -107
  130. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
  131. package/src/plugin/internal/load/loadProjectPlugins.ts +421 -211
  132. package/src/plugin/internal/readCapabilityResolution.ts +17 -6
  133. package/src/plugin/internal/source/GoEnvironmentReading.ts +178 -0
  134. package/src/plugin/internal/source/NativeSourcePackages.ts +365 -48
  135. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +83 -5
  136. package/src/plugin/internal/source/PluginBuildEnvironmentWitness.ts +18 -8
  137. package/src/plugin/internal/source/PluginContentIdentities.ts +563 -0
  138. package/src/plugin/internal/source/PluginLoadAnswers.ts +151 -0
  139. package/src/plugin/internal/source/SourceBuildCacheLayout.ts +14 -0
  140. package/src/plugin/internal/source/SourcePluginWorkspace.ts +148 -25
  141. package/src/plugin/internal/source/buildSourcePlugin.ts +104 -36
  142. package/src/plugin/internal/source/computeCacheKey.ts +129 -55
  143. package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +152 -105
  144. package/src/plugin/internal/source/pluginBuildEnvironment.ts +9 -2
  145. package/src/plugin/internal/source/pluginBuildEnvironmentWorker.ts +18 -2
  146. package/src/plugin/internal/source/pluginBuildVersions.ts +5 -26
  147. package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +13 -25
  148. package/src/plugin/internal/source/pluginSourceState.ts +28 -12
  149. package/src/plugin/internal/source/pluginSourceStateHolds.ts +15 -8
  150. package/src/plugin/internal/source/processPluginBuildEnvironment.ts +8 -2
  151. package/src/plugin/internal/source/provenPluginSourceDigest.ts +45 -0
  152. package/src/plugin/internal/source/pruneCacheFileRoot.ts +3 -2
  153. package/src/plugin/internal/source/ttscPackageVersion.ts +45 -0
  154. package/src/plugin/internal/writeCapabilityResolution.ts +29 -8
  155. package/src/plugin/resolveCapabilityPlugins.ts +87 -13
@@ -8,9 +8,9 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
8
8
  const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
10
  const GoToolResolution_1 = require("./GoToolResolution");
11
+ const PluginContentIdentities_1 = require("./PluginContentIdentities");
11
12
  const hashPluginBuildEnvironment_1 = require("./hashPluginBuildEnvironment");
12
13
  const pluginModuleReplaceDirectories_1 = require("./pluginModuleReplaceDirectories");
13
- const pluginSourceDigest_1 = require("./pluginSourceDigest");
14
14
  /**
15
15
  * Compute a 32-character lowercase key from a plugin build's SHA-256 digest.
16
16
  *
@@ -21,8 +21,8 @@ const pluginSourceDigest_1 = require("./pluginSourceDigest");
21
21
  * source files. An optional required package-ownership population also frames
22
22
  * the key, separating binaries admitted by the matching cold producer from
23
23
  * legacy artifacts; requests without that authority retain their existing key.
24
- * Contributors are sorted by name; unique names remove input
25
- * order dependence, while equal-name rows retain the sort's input order.
24
+ * Contributors are sorted by name; unique names remove input order dependence,
25
+ * while equal-name rows retain the sort's input order.
26
26
  *
27
27
  * Each source directory enters the key as its digest (`pluginSourceDigest`),
28
28
  * which the transform envelope reports, with the environment below, as the
@@ -30,10 +30,12 @@ const pluginSourceDigest_1 = require("./pluginSourceDigest");
30
30
  * values are trusted producer readings, not revalidated content observations.
31
31
  * `sourceDigests` carries the digests one load already took: every build of the
32
32
  * load keys on one reading of each directory, and the load reports those
33
- * readings. The environment enters through `hashPluginBuildEnvironment`, the
34
- * rule that reported state takes it from too, and `environmentDigests` carries
35
- * the digest of each build directory's, so the load reports it without a second
36
- * `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.
37
39
  *
38
40
  * The `ttsc cache` CLI and plugin build pipeline share this key computation.
39
41
  *
@@ -43,7 +45,7 @@ const pluginSourceDigest_1 = require("./pluginSourceDigest");
43
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.
44
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.
45
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.
46
- * @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 does not skip environment hashing. Reuse inherits the selected population and metadata/producer premises.
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.
47
49
  *
48
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.
49
51
  */
@@ -61,51 +63,100 @@ function computeCacheKey(inputs) {
61
63
  hash.update(JSON.stringify(["platform", process.platform, process.arch]));
62
64
  hash.update(JSON.stringify(["entry", inputs.entry]));
63
65
  if (inputs.packageOwnership !== undefined)
64
- hash.update(JSON.stringify(["package-ownership", 1,
65
- [...inputs.packageOwnership].sort((left, right) => left.entry < right.entry ? -1 : left.entry > right.entry ? 1 :
66
- left.kind < right.kind ? -1 : left.kind > right.kind ? 1 : 0),
66
+ hash.update(JSON.stringify([
67
+ "package-ownership",
68
+ 1,
69
+ [...inputs.packageOwnership].sort((left, right) => left.entry < right.entry
70
+ ? -1
71
+ : left.entry > right.entry
72
+ ? 1
73
+ : left.kind < right.kind
74
+ ? -1
75
+ : left.kind > right.kind
76
+ ? 1
77
+ : 0),
67
78
  ]));
68
79
  // Private materialization changes native package working directories. Keep
69
80
  // binaries produced by the former absolute-ancestry layout out of this
70
81
  // layout's cache admissions, including callers with no SDK overlays.
71
82
  hash.update(JSON.stringify(["external-source-layout", 1]));
72
- // The same framed values enter the key and the environment-only digest,
73
- // which pluginBuildEnvironment reads for this directory.
74
- const environment = node_crypto_1.default.createHash("sha256");
75
- (0, hashPluginBuildEnvironment_1.hashPluginBuildEnvironment)({
76
- update: (data) => {
77
- hash.update(data);
78
- environment.update(data);
79
- },
80
- }, goBinary, inputs.dir, env, filesystem, inputs.environmentWitness);
81
- inputs.environmentDigests?.set(node_path_1.default.resolve(inputs.dir), environment.digest("hex"));
82
- hashSourceDirectory(hash, "plugin", inputs.dir, inputs.sourceDigests);
83
- // Local replacement targets outside the module supply separately keyed
84
- // sources. buildSourcePlugin snapshots and proves their copies before
85
- // redirecting the Go replacement directives to the scratch tree.
86
- for (const replacement of (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(inputs.dir, env, goBinary)) {
87
- hashSourceDirectory(hash, `replace:${replacement.modulePath}${replacement.version === undefined ? "" : `@${replacement.version}`}`, replacement.directory, inputs.sourceDigests);
88
- }
89
- for (const [index, dir] of [...(inputs.overlayDirs ?? [])].sort().entries()) {
90
- hashSourceDirectory(hash, `overlay:${index}`, dir, inputs.sourceDigests);
91
- }
83
+ const overlays = [...(inputs.overlayDirs ?? [])].sort();
92
84
  // Hash contributors in sorted-by-name order so two consumers with the
93
85
  // same logical set produce the same key regardless of declaration order
94
86
  // in the host's plugin descriptor.
95
87
  const sortedContributors = [...(inputs.contributors ?? [])].sort((a, b) => a.name === b.name ? 0 : a.name < b.name ? -1 : 1);
88
+ // A store whose root lies inside a directory this key hashes would write
89
+ // records into that source, so such a store proves nothing here. The build
90
+ // rejects that cache placement (`SourcePluginAdmission`) once every source
91
+ // is known; replacement targets narrow the store below, before their own
92
+ // digests, since only Go's reading of the manifest names them.
93
+ let identities = PluginContentIdentities_1.PluginContentIdentities.outside(inputs.identities, [
94
+ inputs.dir,
95
+ ...overlays,
96
+ ...sortedContributors.map((contributor) => contributor.source),
97
+ ]);
98
+ // The key frames the environment digest pluginBuildEnvironment reports for
99
+ // this directory. WARNING (#1721): package selection reads that environment
100
+ // first in the same load; reading it again here doubled `go env` and
101
+ // `go version` per load. A reading taken through another byte adapter is
102
+ // not this one's, so only the default adapter shares it.
103
+ const environmentDirectory = node_path_1.default.resolve(inputs.dir);
104
+ const sharedDigest = inputs.filesystem === undefined
105
+ ? inputs.environmentDigests?.get(environmentDirectory)
106
+ : undefined;
107
+ const sharedWitness = sharedDigest === undefined
108
+ ? undefined
109
+ : inputs.environmentWitnesses?.get(environmentDirectory);
110
+ let environment;
111
+ if (sharedDigest !== undefined && sharedWitness !== undefined) {
112
+ // The build compares this witness after it ran, exactly as it would its
113
+ // own reading's, so a toolchain change since the reading still refuses.
114
+ for (const [file, state] of sharedWitness)
115
+ if (!inputs.environmentWitness?.has(file))
116
+ inputs.environmentWitness?.set(file, state);
117
+ environment = sharedDigest;
118
+ }
119
+ else {
120
+ const witness = inputs.environmentWitness ??
121
+ (inputs.environmentWitnesses === undefined ? undefined : new Map());
122
+ const reading = node_crypto_1.default.createHash("sha256");
123
+ (0, hashPluginBuildEnvironment_1.hashPluginBuildEnvironment)(reading, goBinary, inputs.dir, env, filesystem, witness, identities);
124
+ environment = reading.digest("hex");
125
+ inputs.environmentDigests?.set(environmentDirectory, environment);
126
+ if (inputs.filesystem === undefined && witness !== undefined)
127
+ inputs.environmentWitnesses?.set(environmentDirectory, new Map(witness));
128
+ }
129
+ hash.update(JSON.stringify(["environment", environment]));
130
+ const directory = (label, root) => hashSourceDirectory(hash, label, root, inputs.sourceDigests, identities);
131
+ directory("plugin", inputs.dir);
132
+ // Local replacement targets outside the module supply separately keyed
133
+ // sources. buildSourcePlugin snapshots and proves their copies before
134
+ // redirecting the Go replacement directives to the scratch tree. Go reads the
135
+ // manifest after the plugin's digest, so an edit made meanwhile lands between
136
+ // that digest and the build's copy, which `requireKeyedSource` refuses.
137
+ const replacements = (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(inputs.dir, env, goBinary, inputs.goModReader);
138
+ identities = PluginContentIdentities_1.PluginContentIdentities.outside(identities, replacements.map((replacement) => replacement.directory));
139
+ for (const replacement of replacements) {
140
+ directory(`replace:${replacement.modulePath}${replacement.version === undefined ? "" : `@${replacement.version}`}`, replacement.directory);
141
+ }
142
+ for (const [index, dir] of overlays.entries()) {
143
+ directory(`overlay:${index}`, dir);
144
+ }
96
145
  for (const contributor of sortedContributors) {
97
- hashSourceDirectory(hash, `contributor:${contributor.name}`, contributor.source, inputs.sourceDigests);
146
+ directory(`contributor:${contributor.name}`, contributor.source);
98
147
  }
99
148
  return hash.digest("hex").slice(0, 32);
100
149
  }
101
150
  const DEFAULT_SOURCE_BUILD_FILESYSTEM = Object.freeze({
102
151
  readFile: (location) => node_fs_1.default.readFileSync(location),
103
152
  });
104
- function hashSourceDirectory(hash, label, root, digests) {
153
+ function hashSourceDirectory(hash, label, root, digests, identities) {
105
154
  const directory = node_path_1.default.resolve(root);
106
155
  let digest = digests?.get(directory);
107
156
  if (digest === undefined) {
108
- digest = (0, pluginSourceDigest_1.pluginSourceDigest)(directory);
157
+ // WARNING (#1722): the load-scoped map above ends with the process. Only the
158
+ // record store spares a new process from reading every source byte again.
159
+ digest = PluginContentIdentities_1.PluginContentIdentities.sourceDirectory(identities, directory);
109
160
  digests?.set(directory, digest);
110
161
  }
111
162
  hash.update(JSON.stringify(["dir", label, digest]));
@@ -1 +1 @@
1
- {"version":3,"file":"computeCacheKey.js","sourceRoot":"","sources":["../../../../src/plugin/internal/source/computeCacheKey.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"computeCacheKey.js","sourceRoot":"","sources":["../../../../src/plugin/internal/source/computeCacheKey.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
2
+ import { PluginContentIdentities } from "./PluginContentIdentities";
2
3
  import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
3
4
  /**
4
5
  * Hash the environment a plugin build is keyed on into `hash`: the Go
@@ -33,15 +34,18 @@ import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOpe
33
34
  * variable carries: the Go tool, the Go environment file `go env -w` writes,
34
35
  * the executables the C toolchain commands name, and GOROOT. A consumer that
35
36
  * keeps the reading compares their metadata before reusing it.
37
+ * @param identities Record store that lets a new process prove the GOROOT
38
+ * corpus and executable bytes from their metadata (`PluginContentIdentities`,
39
+ * #1722); without it every process reads them.
36
40
  * @evidence contracts/common.md#principled-implementation Fixed artifact flags, observed compiler bytes/version, selected reported-or-fallback build values, named command executables and selected SDK files enter the same framed identity used by source-state reporting. Fallback values and metadata-qualified reuse are explicit premises, not complete observation of arbitrary toolchain inputs.
37
41
  * @evidence contracts/common.md#clear-and-simple-design Private helpers separate compiler identity, Go-reported settings, external variables and SDK content while sharing one hash sink and optional pre-read witness.
38
42
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Cache signatures include real identity/change metadata and effective invocation context; failed SDK witnessing refuses reuse rather than accepting a VERSION-only proxy.
39
43
  * @evidence contracts/common.md#meaningful-documentation Native documentation names the input classes and witness purpose; private comments explain context-sensitive compiler memoization and SDK exclusions without treating a passing check as proof.
40
44
  * @evidence contracts/portability.md#os-neutral-implementation Node path/stat/process APIs resolve native tool identities; platform-specific executable suffixes and environment lookup are isolated in GoToolResolution, and Go supplies its own effective build settings.
41
45
  * @evidence contracts/performance.md#efficient-algorithms SDK traversal sorts reached entry names and selected file paths, performs native stat/realpath/link queries and serializes topology/metadata/path text. Matching manifests share aggregate hashes; changes read full selected files via the caller adapter, retaining file buffers with population/topology data. Compiler misses capture at most three complete version/byte readings when native metadata moves during a reading; only a stable attempt enters the memo. Go env can run up to three times for its file witness, and command tokens can each perform executable searches/full reads. Environment sorting/framing and command substring parsing also process name/value/command bytes; E/D/F/B alone do not capture native lookup or text comparison costs.
42
- * @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. Both rely on native metadata distinguishability and sequential observation premises, not independently rehashed content on each hit. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
43
- * @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
46
+ * @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. With a record store, a new process reuses the SDK aggregate and executable digests only while their signatures match the recorded ones and every stamp is separable from a freshly minted reference (#1722); `go version` and `go env` stay live because they report toolchain selection no file metadata shows. All of this relies on native metadata distinguishability and sequential observation premises. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
47
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction; persisted records belong to the single-file collector. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
44
48
  */
45
49
  export declare function hashPluginBuildEnvironment(hash: {
46
50
  update(data: string): unknown;
47
- }, goBinary: string | undefined, directory: string, env: NodeJS.ProcessEnv, filesystem: SourceBuildFilesystemOperations, witness?: PluginBuildEnvironmentWitness.Record): void;
51
+ }, goBinary: string | undefined, directory: string, env: NodeJS.ProcessEnv, filesystem: SourceBuildFilesystemOperations, witness?: PluginBuildEnvironmentWitness.Record, identities?: PluginContentIdentities.Store): void;
@@ -7,9 +7,11 @@ exports.hashPluginBuildEnvironment = hashPluginBuildEnvironment;
7
7
  const node_crypto_1 = __importDefault(require("node:crypto"));
8
8
  const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const GoEnvironmentReading_1 = require("./GoEnvironmentReading");
10
11
  const GoSourceInputs_1 = require("./GoSourceInputs");
11
12
  const GoToolResolution_1 = require("./GoToolResolution");
12
13
  const PluginBuildEnvironmentWitness_1 = require("./PluginBuildEnvironmentWitness");
14
+ const PluginContentIdentities_1 = require("./PluginContentIdentities");
13
15
  const spawnGoTool_1 = require("./spawnGoTool");
14
16
  /**
15
17
  * Hash the environment a plugin build is keyed on into `hash`: the Go
@@ -44,26 +46,29 @@ const spawnGoTool_1 = require("./spawnGoTool");
44
46
  * variable carries: the Go tool, the Go environment file `go env -w` writes,
45
47
  * the executables the C toolchain commands name, and GOROOT. A consumer that
46
48
  * keeps the reading compares their metadata before reusing it.
49
+ * @param identities Record store that lets a new process prove the GOROOT
50
+ * corpus and executable bytes from their metadata (`PluginContentIdentities`,
51
+ * #1722); without it every process reads them.
47
52
  * @evidence contracts/common.md#principled-implementation Fixed artifact flags, observed compiler bytes/version, selected reported-or-fallback build values, named command executables and selected SDK files enter the same framed identity used by source-state reporting. Fallback values and metadata-qualified reuse are explicit premises, not complete observation of arbitrary toolchain inputs.
48
53
  * @evidence contracts/common.md#clear-and-simple-design Private helpers separate compiler identity, Go-reported settings, external variables and SDK content while sharing one hash sink and optional pre-read witness.
49
54
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Cache signatures include real identity/change metadata and effective invocation context; failed SDK witnessing refuses reuse rather than accepting a VERSION-only proxy.
50
55
  * @evidence contracts/common.md#meaningful-documentation Native documentation names the input classes and witness purpose; private comments explain context-sensitive compiler memoization and SDK exclusions without treating a passing check as proof.
51
56
  * @evidence contracts/portability.md#os-neutral-implementation Node path/stat/process APIs resolve native tool identities; platform-specific executable suffixes and environment lookup are isolated in GoToolResolution, and Go supplies its own effective build settings.
52
57
  * @evidence contracts/performance.md#efficient-algorithms SDK traversal sorts reached entry names and selected file paths, performs native stat/realpath/link queries and serializes topology/metadata/path text. Matching manifests share aggregate hashes; changes read full selected files via the caller adapter, retaining file buffers with population/topology data. Compiler misses capture at most three complete version/byte readings when native metadata moves during a reading; only a stable attempt enters the memo. Go env can run up to three times for its file witness, and command tokens can each perform executable searches/full reads. Environment sorting/framing and command substring parsing also process name/value/command bytes; E/D/F/B alone do not capture native lookup or text comparison costs.
53
- * @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. Both rely on native metadata distinguishability and sequential observation premises, not independently rehashed content on each hit. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
54
- * @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
58
+ * @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. With a record store, a new process reuses the SDK aggregate and executable digests only while their signatures match the recorded ones and every stamp is separable from a freshly minted reference (#1722); `go version` and `go env` stay live because they report toolchain selection no file metadata shows. All of this relies on native metadata distinguishability and sequential observation premises. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
59
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction; persisted records belong to the single-file collector. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
55
60
  */
56
- function hashPluginBuildEnvironment(hash, goBinary, directory, env, filesystem, witness) {
61
+ function hashPluginBuildEnvironment(hash, goBinary, directory, env, filesystem, witness, identities) {
57
62
  // Fixed artifact policy must invalidate binaries and reported source states
58
63
  // together, including binaries cached before this policy was introduced.
59
64
  hash.update(JSON.stringify(["source-build-flags", GoSourceInputs_1.GoSourceInputs.BUILD_FLAGS]));
60
65
  if (goBinary !== undefined) {
61
66
  hash.update(JSON.stringify([
62
67
  "go",
63
- resolveGoCompilerIdentity(goBinary, env, directory, witness),
68
+ resolveGoCompilerIdentity(goBinary, env, directory, witness, identities),
64
69
  ]));
65
70
  }
66
- hashGoBuildEnvironment(hash, goBinary, directory, env, filesystem, witness);
71
+ hashGoBuildEnvironment(hash, goBinary, directory, env, filesystem, witness, identities);
67
72
  hashExternalGoBuildEnvironment(hash, env);
68
73
  }
69
74
  // Go build environment values that can change the produced binary or decide
@@ -154,7 +159,7 @@ const EXTERNAL_GO_BUILD_ENV_KEYS = [
154
159
  // memo key includes that context so an environment-sensitive wrapper cannot
155
160
  // lend its version result to another compiler invocation.
156
161
  const goCompilerIdentityCache = new Map();
157
- function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cwd(), witness) {
162
+ function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cwd(), witness, identities) {
158
163
  const selected = GoToolResolution_1.GoToolResolution.resolveGoToolForBuild(goBinary, env, cwd, witness);
159
164
  const resolved = process.platform === "win32"
160
165
  ? resolveRealPath(selected)
@@ -182,7 +187,7 @@ function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cw
182
187
  const observed = new Map();
183
188
  PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(observed, resolved);
184
189
  const memoKey = goCompilerIdentityMemoKey(goBinary, resolved, compilerEnv, cwd);
185
- identity = computeGoCompilerIdentity(selected, resolved, compilerEnv, cwd);
190
+ identity = computeGoCompilerIdentity(selected, resolved, compilerEnv, cwd, identities);
186
191
  if (!PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.holds(observed))
187
192
  continue;
188
193
  for (const [file, signature] of observed)
@@ -224,10 +229,12 @@ function goCompilerIdentityMemoKey(goBinary, resolved, env, cwd) {
224
229
  return null;
225
230
  }
226
231
  }
227
- function computeGoCompilerIdentity(goBinary, resolved, env, cwd) {
232
+ function computeGoCompilerIdentity(goBinary, resolved, env, cwd, identities) {
228
233
  if (!node_fs_1.default.existsSync(resolved)) {
229
234
  return "missing";
230
235
  }
236
+ // `go version` stays a live probe: it answers which toolchain the effective
237
+ // environment selects (GOTOOLCHAIN, go.mod), which no file metadata shows.
231
238
  const version = (0, spawnGoTool_1.spawnGoTool)(goBinary, ["version"], {
232
239
  cwd,
233
240
  encoding: "utf8",
@@ -237,9 +244,16 @@ function computeGoCompilerIdentity(goBinary, resolved, env, cwd) {
237
244
  const versionText = version.error !== undefined
238
245
  ? (version.error.code ?? version.error.message)
239
246
  : `${version.status ?? 0}:${version.stdout}${version.stderr}`;
240
- const binaryHash = hashFile(resolved);
247
+ const binaryHash = hashExecutable(resolved, identities);
241
248
  return `sha256:${binaryHash}:${versionText}`;
242
249
  }
250
+ /**
251
+ * The content digest of an executable, proven from its metadata in a process
252
+ * that has the record store (#1722) and read in full otherwise.
253
+ */
254
+ function hashExecutable(file, identities) {
255
+ return PluginContentIdentities_1.PluginContentIdentities.digest(identities, "executable", file, (references) => PluginContentIdentities_1.PluginContentIdentities.fileObservation(references, file), () => hashFile(file));
256
+ }
243
257
  function resolveExecutableIdentityPath(binary, env = process.env, cwd = process.cwd(), witness) {
244
258
  const resolved = findExecutablePath(binary, env, cwd, witness);
245
259
  return resolved === null ? binary : resolveRealPath(resolved);
@@ -281,8 +295,8 @@ function hashFile(file) {
281
295
  hash.update(node_fs_1.default.readFileSync(file));
282
296
  return hash.digest("hex");
283
297
  }
284
- function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness) {
285
- const values = resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness);
298
+ function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness, identities) {
299
+ const values = resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness, identities);
286
300
  for (const key of GO_BUILD_ENV_KEYS) {
287
301
  const value = values.get(key);
288
302
  if (value !== undefined && value !== "") {
@@ -290,16 +304,16 @@ function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness) {
290
304
  }
291
305
  }
292
306
  }
293
- function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness) {
307
+ function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness, identities) {
294
308
  const values = new Map();
295
309
  if (goBinary !== undefined) {
296
- const parsed = readGoEnvironment(goBinary, cwd, env, witness);
310
+ const parsed = GoEnvironmentReading_1.GoEnvironmentReading.read(goBinary, cwd, env, GO_BUILD_ENV_KEYS, witness, identities);
297
311
  if (parsed !== undefined) {
298
312
  try {
299
313
  for (const key of GO_BUILD_ENV_KEYS) {
300
314
  const raw = parsed[key];
301
315
  if (typeof raw === "string" && raw !== "") {
302
- values.set(key, normalizeGoBuildEnvValue(key, raw, env, filesystem, witness));
316
+ values.set(key, normalizeGoBuildEnvValue(key, raw, env, filesystem, witness, identities));
303
317
  }
304
318
  }
305
319
  }
@@ -314,89 +328,22 @@ function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness) {
314
328
  continue;
315
329
  const value = env[key];
316
330
  if (value !== undefined && value !== "") {
317
- values.set(key, normalizeGoBuildEnvValue(key, value, env, filesystem, witness));
331
+ values.set(key, normalizeGoBuildEnvValue(key, value, env, filesystem, witness, identities));
318
332
  }
319
333
  }
320
334
  return values;
321
335
  }
322
- /**
323
- * The Go environment file each Go tool and environment was last seen to name,
324
- * which lets a reading witness that file before `go env` reads it.
325
- */
326
- const goEnvironmentFiles = new Map();
327
- /**
328
- * Run `go env -json` for the build keys and `GOENV`, the file `go env -w`
329
- * writes, witnessing that file before the run reads it.
330
- *
331
- * The file decides the reading without being part of it, so only its metadata
332
- * is kept, and it must be taken before the read: metadata taken after would
333
- * describe an edit that landed between the two, and a reading of the old
334
- * content would pass as proven. Which file `go env` reads is known only from
335
- * its answer, so the file this Go tool and environment named last is witnessed
336
- * first, and a reading that names another is taken again with that one
337
- * witnessed. A file that keeps moving between runs leaves the reading
338
- * unwitnessable, so a kept reading or a build keyed on it never holds.
339
- *
340
- * @returns The parsed answer, or `undefined` when `go env` failed.
341
- */
342
- function readGoEnvironment(goBinary, cwd, env, witness) {
343
- const memo = node_crypto_1.default.createHash("sha256").update(goBinary);
344
- for (const [key, value] of Object.entries(env).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
345
- if (value !== undefined)
346
- memo.update(`\0${key.length}:${key}${value.length}:${value}`);
347
- }
348
- const memoKey = memo.digest("hex");
349
- let named = goEnvironmentFiles.get(memoKey);
350
- for (let attempt = 0; attempt < 3; attempt += 1) {
351
- if (named !== undefined && named !== null)
352
- PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, named);
353
- const result = (0, spawnGoTool_1.spawnGoTool)(goBinary, ["env", "-json", ...GO_BUILD_ENV_KEYS, "GOENV"], {
354
- cwd,
355
- encoding: "utf8",
356
- env: GoSourceInputs_1.GoSourceInputs.goBuildEnv(goBinary, undefined, env),
357
- windowsHide: true,
358
- });
359
- if (result.error !== undefined || result.status !== 0)
360
- return undefined;
361
- let parsed;
362
- try {
363
- parsed = JSON.parse(result.stdout);
364
- }
365
- catch {
366
- return undefined;
367
- }
368
- const reported = typeof parsed.GOENV === "string" &&
369
- parsed.GOENV !== "" &&
370
- parsed.GOENV !== "off"
371
- ? parsed.GOENV
372
- : null;
373
- if (reported === named)
374
- return parsed;
375
- if (named !== undefined && named !== null)
376
- witness?.delete(named);
377
- named = reported;
378
- goEnvironmentFiles.set(memoKey, named);
379
- // No file to witness: the reading depends on none.
380
- if (named === null)
381
- return parsed;
382
- if (witness === undefined)
383
- return parsed;
384
- }
385
- if (named !== undefined && named !== null)
386
- PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.refuse(witness, named);
387
- return undefined;
388
- }
389
- function normalizeGoBuildEnvValue(key, value, env, filesystem, witness) {
336
+ function normalizeGoBuildEnvValue(key, value, env, filesystem, witness, identities) {
390
337
  if (key === "GOROOT") {
391
338
  // Root/version observations also cover a missing SDK. The complete SDK
392
339
  // manifest below witnesses contributing files and directories, including
393
340
  // nested content changes that do not move the root or VERSION metadata.
394
341
  PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, value);
395
342
  PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, node_path_1.default.join(value, "VERSION"));
396
- return resolveGoRootCacheIdentity(value, filesystem, witness);
343
+ return resolveGoRootCacheIdentity(value, filesystem, witness, identities);
397
344
  }
398
345
  if (GO_BUILD_COMMAND_ENV_KEYS.has(key)) {
399
- return `${value}\0${resolveCommandCacheIdentity(value, env, witness)}`;
346
+ return `${value}\0${resolveCommandCacheIdentity(value, env, witness, identities)}`;
400
347
  }
401
348
  return value;
402
349
  }
@@ -412,7 +359,7 @@ function normalizeGoBuildEnvValue(key, value, env, filesystem, witness) {
412
359
  * which the key carries beside this identity. A program a launcher finds by its
413
360
  * own means, not named in the command, is outside what the command can show.
414
361
  */
415
- function resolveCommandCacheIdentity(command, env, witness) {
362
+ function resolveCommandCacheIdentity(command, env, witness, identities) {
416
363
  const tokens = splitGoCommand(command);
417
364
  if (tokens === null) {
418
365
  return `command:unterminated:${command}`;
@@ -428,7 +375,7 @@ function resolveCommandCacheIdentity(command, env, witness) {
428
375
  }
429
376
  let identity;
430
377
  try {
431
- identity = `command:sha256:${hashFile(resolved)}`;
378
+ identity = `command:sha256:${hashExecutable(resolved, identities)}`;
432
379
  }
433
380
  catch {
434
381
  return `command:unreadable:${resolved}`;
@@ -439,7 +386,7 @@ function resolveCommandCacheIdentity(command, env, witness) {
439
386
  return;
440
387
  PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, operand);
441
388
  try {
442
- identity += `;${index + 1}:sha256:${hashFile(operand)}`;
389
+ identity += `;${index + 1}:sha256:${hashExecutable(operand, identities)}`;
443
390
  }
444
391
  catch {
445
392
  identity += `;${index + 1}:unreadable:${operand}`;
@@ -492,14 +439,21 @@ function hashExternalGoBuildEnvironment(hash, env) {
492
439
  // GOROOT is usually stable but its selected source/tool payloads contribute to
493
440
  // every plugin key. Retain only the final aggregate
494
441
  // identity, guarded by a fresh metadata/topology manifest on every call. A
495
- // changed or incomplete manifest falls through to a full content read.
442
+ // changed or incomplete manifest falls through to the record store, then to a
443
+ // full content read.
444
+ //
445
+ // WARNING (#1186, #1722): this map lives only as long as the process. #1186
446
+ // stopped at it, so every CLI launch, worker and bundler restart still read the
447
+ // whole SDK (3,723 files, 135 MB for the bundled toolchain). The record store
448
+ // below is what carries the identity across processes; never drop it in favor
449
+ // of this memo alone.
496
450
  const goRootIdentityCache = new Map();
497
- function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
451
+ function resolveGoRootCacheIdentity(goRoot, filesystem, witness, identities) {
498
452
  const resolved = resolveRealPath(goRoot);
499
453
  if (!node_fs_1.default.existsSync(resolved)) {
500
454
  return `missing:${goRoot}`;
501
455
  }
502
- const snapshot = collectGoRootIdentitySnapshot(resolved, witness);
456
+ const snapshot = collectGoRootIdentitySnapshot(resolved, witness, identities?.references);
503
457
  if (!snapshot.complete)
504
458
  PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.refuse(witness, resolved);
505
459
  if (snapshot.complete) {
@@ -508,15 +462,30 @@ function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
508
462
  return cached.identity;
509
463
  }
510
464
  }
511
- const hash = node_crypto_1.default.createHash("sha256");
512
- hash.update(JSON.stringify(snapshot.topology));
513
- for (const file of snapshot.files) {
514
- const relative = node_path_1.default.relative(resolved, file).split(node_path_1.default.sep).join("/");
515
- const contents = filesystem.readFile(file);
516
- hash.update(JSON.stringify([relative, contents.length]));
517
- hash.update(contents);
518
- }
519
- const identity = `sha256:${hash.digest("hex")}`;
465
+ // The first observation is the snapshot the witness just recorded; a second
466
+ // one, after the content read, brackets that read for publication.
467
+ let observed = 0;
468
+ const identity = PluginContentIdentities_1.PluginContentIdentities.digest(snapshot.complete ? identities : undefined, "goroot", resolved, (references) => {
469
+ const current = observed++ === 0
470
+ ? snapshot
471
+ : collectGoRootIdentitySnapshot(resolved, undefined, references);
472
+ return current.complete
473
+ ? { separable: current.separable, signature: current.signature }
474
+ : undefined;
475
+ }, () => {
476
+ const hash = node_crypto_1.default.createHash("sha256");
477
+ hash.update(JSON.stringify(snapshot.topology));
478
+ for (const file of snapshot.files) {
479
+ const relative = node_path_1.default
480
+ .relative(resolved, file)
481
+ .split(node_path_1.default.sep)
482
+ .join("/");
483
+ const contents = filesystem.readFile(file);
484
+ hash.update(JSON.stringify([relative, contents.length]));
485
+ hash.update(contents);
486
+ }
487
+ return `sha256:${hash.digest("hex")}`;
488
+ });
520
489
  if (snapshot.complete) {
521
490
  goRootIdentityCache.set(resolved, {
522
491
  identity,
@@ -525,9 +494,9 @@ function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
525
494
  }
526
495
  return identity;
527
496
  }
528
- function collectGoRootIdentitySnapshot(root, witness) {
497
+ function collectGoRootIdentitySnapshot(root, witness, references) {
529
498
  const out = [];
530
- const state = { complete: true };
499
+ const state = { complete: true, separable: references !== undefined };
531
500
  const topology = [];
532
501
  walkGoRootIdentity(root, out, topology, state, witness);
533
502
  out.sort();
@@ -542,6 +511,9 @@ function collectGoRootIdentitySnapshot(root, witness) {
542
511
  state.complete = false;
543
512
  continue;
544
513
  }
514
+ if (references === undefined ||
515
+ !PluginContentIdentities_1.PluginContentIdentities.separable(references, stats))
516
+ state.separable = false;
545
517
  signature.update([
546
518
  relative,
547
519
  stats.dev,
@@ -562,6 +534,7 @@ function collectGoRootIdentitySnapshot(root, witness) {
562
534
  files: out,
563
535
  topology,
564
536
  signature: signature.digest("hex"),
537
+ separable: state.separable,
565
538
  };
566
539
  }
567
540
  function walkGoRootIdentity(root, out, topology, state, witness) {