ttsc 0.31.1 → 0.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/lib/compiler/internal/build/BuildExecution.d.ts +4 -2
  2. package/lib/compiler/internal/build/BuildExecution.js +40 -12
  3. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  4. package/lib/compiler/internal/startResidentTransform.js +5 -4
  5. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  6. package/lib/compiler/internal/transformProjectInMemory.js +9 -7
  7. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  8. package/lib/internal/javascriptRuntimeCapabilities.d.ts +13 -4
  9. package/lib/internal/javascriptRuntimeCapabilities.js +56 -6
  10. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  11. package/lib/internal/pluginSource.d.ts +1 -0
  12. package/lib/internal/pluginSource.js +1 -0
  13. package/lib/internal/pluginSource.js.map +1 -1
  14. package/lib/internal/resolveNodeBinary.d.ts +6 -1
  15. package/lib/internal/resolveNodeBinary.js +6 -2
  16. package/lib/internal/resolveNodeBinary.js.map +1 -1
  17. package/lib/internal/runtimeExecutableIdentity.d.ts +22 -13
  18. package/lib/internal/runtimeExecutableIdentity.js +192 -102
  19. package/lib/internal/runtimeExecutableIdentity.js.map +1 -1
  20. package/lib/launcher/internal/runtime/installRuntimeHooks.js +58 -4
  21. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  22. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.d.ts +5 -1
  23. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js +7 -2
  24. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js.map +1 -1
  25. package/lib/launcher/internal/ttscserver/runTtscserver.js +8 -1
  26. package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
  27. package/lib/plugin/internal/CapabilityResolutionFormat.d.ts +21 -62
  28. package/lib/plugin/internal/CapabilityResolutionFormat.js +15 -106
  29. package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
  30. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +6 -0
  31. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +4 -4
  32. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  33. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +12 -1
  34. package/lib/plugin/internal/load/loadProjectPlugins.js +80 -20
  35. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  36. package/lib/plugin/internal/readCapabilityResolution.d.ts +6 -0
  37. package/lib/plugin/internal/readCapabilityResolution.js +8 -5
  38. package/lib/plugin/internal/readCapabilityResolution.js.map +1 -1
  39. package/lib/plugin/internal/source/GoEnvironmentReading.d.ts +9 -3
  40. package/lib/plugin/internal/source/GoEnvironmentReading.js +53 -7
  41. package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -1
  42. package/lib/plugin/internal/source/NativeSourcePackages.d.ts +44 -6
  43. package/lib/plugin/internal/source/NativeSourcePackages.js +150 -8
  44. package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
  45. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +21 -3
  46. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +32 -7
  47. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  48. package/lib/plugin/internal/source/PluginContentIdentities.d.ts +265 -0
  49. package/lib/plugin/internal/source/PluginContentIdentities.js +452 -0
  50. package/lib/plugin/internal/source/PluginContentIdentities.js.map +1 -0
  51. package/lib/plugin/internal/source/PluginLoadAnswers.d.ts +63 -0
  52. package/lib/plugin/internal/source/PluginLoadAnswers.js +128 -0
  53. package/lib/plugin/internal/source/PluginLoadAnswers.js.map +1 -0
  54. package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +10 -0
  55. package/lib/plugin/internal/source/SourceBuildCacheLayout.js +12 -0
  56. package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
  57. package/lib/plugin/internal/source/SourcePluginWorkspace.d.ts +5 -2
  58. package/lib/plugin/internal/source/SourcePluginWorkspace.js +5 -3
  59. package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
  60. package/lib/plugin/internal/source/buildSourcePlugin.d.ts +15 -2
  61. package/lib/plugin/internal/source/buildSourcePlugin.js +27 -4
  62. package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
  63. package/lib/plugin/internal/source/computeCacheKey.d.ts +21 -5
  64. package/lib/plugin/internal/source/computeCacheKey.js +71 -29
  65. package/lib/plugin/internal/source/computeCacheKey.js.map +1 -1
  66. package/lib/plugin/internal/source/hashPluginBuildEnvironment.d.ts +7 -3
  67. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js +74 -35
  68. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js.map +1 -1
  69. package/lib/plugin/internal/source/pluginBuildEnvironment.d.ts +8 -3
  70. package/lib/plugin/internal/source/pluginBuildEnvironment.js +8 -4
  71. package/lib/plugin/internal/source/pluginBuildEnvironment.js.map +1 -1
  72. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js +12 -2
  73. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js.map +1 -1
  74. package/lib/plugin/internal/source/pluginBuildVersions.d.ts +2 -2
  75. package/lib/plugin/internal/source/pluginBuildVersions.js +4 -17
  76. package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
  77. package/lib/plugin/internal/source/pluginSourceState.d.ts +6 -2
  78. package/lib/plugin/internal/source/pluginSourceState.js +20 -8
  79. package/lib/plugin/internal/source/pluginSourceState.js.map +1 -1
  80. package/lib/plugin/internal/source/pluginSourceStateHolds.d.ts +7 -2
  81. package/lib/plugin/internal/source/pluginSourceStateHolds.js +12 -7
  82. package/lib/plugin/internal/source/pluginSourceStateHolds.js.map +1 -1
  83. package/lib/plugin/internal/source/processPluginBuildEnvironment.d.ts +7 -2
  84. package/lib/plugin/internal/source/processPluginBuildEnvironment.js +8 -3
  85. package/lib/plugin/internal/source/processPluginBuildEnvironment.js.map +1 -1
  86. package/lib/plugin/internal/source/provenPluginSourceDigest.d.ts +28 -0
  87. package/lib/plugin/internal/source/provenPluginSourceDigest.js +41 -0
  88. package/lib/plugin/internal/source/provenPluginSourceDigest.js.map +1 -0
  89. package/lib/plugin/internal/source/pruneCacheFileRoot.d.ts +3 -2
  90. package/lib/plugin/internal/source/pruneCacheFileRoot.js +3 -2
  91. package/lib/plugin/internal/source/pruneCacheFileRoot.js.map +1 -1
  92. package/lib/plugin/internal/source/ttscPackageVersion.d.ts +26 -0
  93. package/lib/plugin/internal/source/ttscPackageVersion.js +47 -0
  94. package/lib/plugin/internal/source/ttscPackageVersion.js.map +1 -0
  95. package/lib/plugin/internal/writeCapabilityResolution.d.ts +6 -0
  96. package/lib/plugin/internal/writeCapabilityResolution.js +11 -7
  97. package/lib/plugin/internal/writeCapabilityResolution.js.map +1 -1
  98. package/lib/plugin/resolveCapabilityPlugins.js +67 -15
  99. package/lib/plugin/resolveCapabilityPlugins.js.map +1 -1
  100. package/package.json +8 -8
  101. package/src/compiler/internal/build/BuildExecution.ts +46 -13
  102. package/src/compiler/internal/startResidentTransform.ts +12 -3
  103. package/src/compiler/internal/transformProjectInMemory.ts +10 -2
  104. package/src/internal/javascriptRuntimeCapabilities.ts +71 -5
  105. package/src/internal/pluginSource.ts +1 -0
  106. package/src/internal/resolveNodeBinary.ts +12 -1
  107. package/src/internal/runtimeExecutableIdentity.ts +227 -141
  108. package/src/launcher/internal/runtime/installRuntimeHooks.ts +68 -3
  109. package/src/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.ts +11 -2
  110. package/src/launcher/internal/ttscserver/runTtscserver.ts +18 -1
  111. package/src/plugin/internal/CapabilityResolutionFormat.ts +23 -107
  112. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
  113. package/src/plugin/internal/load/loadProjectPlugins.ts +212 -94
  114. package/src/plugin/internal/readCapabilityResolution.ts +17 -6
  115. package/src/plugin/internal/source/GoEnvironmentReading.ts +74 -5
  116. package/src/plugin/internal/source/NativeSourcePackages.ts +262 -5
  117. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +41 -5
  118. package/src/plugin/internal/source/PluginContentIdentities.ts +563 -0
  119. package/src/plugin/internal/source/PluginLoadAnswers.ts +151 -0
  120. package/src/plugin/internal/source/SourceBuildCacheLayout.ts +14 -0
  121. package/src/plugin/internal/source/SourcePluginWorkspace.ts +6 -1
  122. package/src/plugin/internal/source/buildSourcePlugin.ts +43 -3
  123. package/src/plugin/internal/source/computeCacheKey.ts +103 -47
  124. package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +144 -29
  125. package/src/plugin/internal/source/pluginBuildEnvironment.ts +9 -2
  126. package/src/plugin/internal/source/pluginBuildEnvironmentWorker.ts +18 -2
  127. package/src/plugin/internal/source/pluginBuildVersions.ts +5 -26
  128. package/src/plugin/internal/source/pluginSourceState.ts +28 -12
  129. package/src/plugin/internal/source/pluginSourceStateHolds.ts +15 -8
  130. package/src/plugin/internal/source/processPluginBuildEnvironment.ts +8 -2
  131. package/src/plugin/internal/source/provenPluginSourceDigest.ts +45 -0
  132. package/src/plugin/internal/source/pruneCacheFileRoot.ts +3 -2
  133. package/src/plugin/internal/source/ttscPackageVersion.ts +45 -0
  134. package/src/plugin/internal/writeCapabilityResolution.ts +29 -8
  135. package/src/plugin/resolveCapabilityPlugins.ts +87 -13
@@ -21,20 +21,26 @@ import { PluginBuildEnvironmentReadings } from "./PluginBuildEnvironmentReadings
21
21
  * possible metadata-restored change. The build itself never reads through here:
22
22
  * it keys each binary on a fresh read.
23
23
  *
24
+ * A caller that names its project lets a reading this process does not hold
25
+ * prove the SDK and executables from that project's plugin cache records
26
+ * (#1725) instead of reading the whole GOROOT in every new process.
27
+ *
24
28
  * @param directory The directory a build runs `go` in.
25
29
  * @param refresh Whether to read the environment again rather than reuse it.
30
+ * @param projectRoot Project whose plugin cache root holds the records.
26
31
  * @evidence contracts/common.md#principled-implementation Variable serialization is sorted and length-delimited; reuse additionally requires unchanged pre-read metadata for every external dependency, including all contributing GOROOT directories and files, under the witness's documented metadata-distinguishability premise.
27
32
  * @evidence contracts/common.md#clear-and-simple-design One process-local map owns environment memoization; fresh construction and witness validation remain separate operations shared with build verification.
28
33
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Refresh performs the real environment read rather than assuming that a stable compiler path or quiet source tree implies a stable toolchain.
29
34
  * @evidence contracts/common.md#meaningful-documentation Native paragraphs explain why environment probing is retained, its variable/path invalidation, and why builds still read fresh; descriptive prose and tags are separated.
30
35
  * @evidence contracts/portability.md#os-neutral-implementation Native environment and filesystem identity are delegated to the shared platform-aware probe and metadata witness rather than inferred from an OS name.
31
36
  * @evidence contracts/performance.md#efficient-algorithms The delegated owner serializes/hashes variable names/values after text-sensitive sorting and validates witnessed native paths. A qualified hit avoids cold tool/content preparation but not native lookup/path/link/text costs. Misses/refresh delegate actual Go/toolchain/SDK readings; V/P counts alone do not bound all bytes or native work.
32
- * @evidence contracts/performance.md#reuse-equivalent-work Directory and all effective variables identify a candidate reading; the complete external-path witness must hold, otherwise the environment is read and its record replaced.
37
+ * @evidence contracts/performance.md#reuse-equivalent-work Directory and all effective variables identify a candidate reading; the complete external-path witness must hold, otherwise the environment is read and its record replaced. A miss naming a project proves the SDK and executables from that project's record store across processes.
33
38
  * @evidence contracts/performance.md#bound-retention-and-release-resources PluginBuildEnvironmentReadings owns the process-shared map and asynchronous worker/request lifetimes, not this forwarding call. Existing keys are replaced, but distinct directory/variable combinations retain historical witnesses without an eviction bound; consumers and the owner govern that retention.
34
39
  */
35
40
  export function processPluginBuildEnvironment(
36
41
  directory: string,
37
42
  refresh = false,
43
+ projectRoot?: string,
38
44
  ): string {
39
- return PluginBuildEnvironmentReadings.read(directory, refresh);
45
+ return PluginBuildEnvironmentReadings.read(directory, refresh, projectRoot);
40
46
  }
@@ -0,0 +1,45 @@
1
+ import { PluginContentIdentities } from "./PluginContentIdentities";
2
+
3
+ /**
4
+ * The `pluginSourceDigest` of a plugin source directory, proven from the
5
+ * records of `projectRoot`'s plugin cache when the caller names its project.
6
+ *
7
+ * WARNING (#1725): a consumer that proves a reported plugin-source state in a
8
+ * new process, such as a bundler worker, a capability host or the language
9
+ * server, read every source byte again because its own memo starts empty. The
10
+ * load that reported the state already recorded this digest
11
+ * (`PluginContentIdentities`), so a process that names the same project reuses
12
+ * it while the files' metadata still holds. Without a project, or when no store
13
+ * can be opened, the bytes are read as before.
14
+ *
15
+ * @param directory The source directory.
16
+ * @param projectRoot Project whose plugin cache root holds the records.
17
+ * @param env Environment selecting that cache root; this process's by default.
18
+ * @returns The digest, as lowercase hex.
19
+ * @throws When a listed source file cannot be read, as `pluginSourceDigest`
20
+ * does.
21
+ * @evidence contracts/common.md#principled-implementation The record store applies its separable-signature rule to exactly the files the digest reads; every unproved premise falls back to the content reader.
22
+ * @evidence contracts/common.md#clear-and-simple-design Store opening and the population proof stay with PluginContentIdentities; this entry only names the project for consumers outside the loader.
23
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No path, timestamp or process memo stands for content; the store refuses a root inside the source.
24
+ * @evidence contracts/common.md#meaningful-documentation The paragraphs state the regression, the reuse premise and the fallback.
25
+ * @evidence contracts/portability.md#os-neutral-implementation Cache-root selection and device-keyed references are delegated to the shared owners.
26
+ * @evidence contracts/performance.md#efficient-algorithms A proven digest stats the selected files; an unproven one also reads their bytes once. Opening the store writes two reference probes.
27
+ * @evidence contracts/performance.md#reuse-equivalent-work The digest the load recorded is reused across processes under the same project's store.
28
+ * @evidence contracts/performance.md#bound-retention-and-release-resources The store lives for this call; records belong to the cache root's single-file pruning.
29
+ */
30
+ export function provenPluginSourceDigest(
31
+ directory: string,
32
+ projectRoot?: string,
33
+ env: NodeJS.ProcessEnv = process.env,
34
+ ): string {
35
+ return PluginContentIdentities.sourceDirectory(
36
+ projectRoot === undefined
37
+ ? undefined
38
+ : PluginContentIdentities.open({
39
+ projectRoot,
40
+ env,
41
+ sources: [directory],
42
+ }),
43
+ directory,
44
+ );
45
+ }
@@ -8,8 +8,9 @@ import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
8
8
  /**
9
9
  * Opportunistically bound a part of the cache root whose entries are single
10
10
  * files: the descriptor evaluations under `descriptors/`, the capability
11
- * answers under `capabilities/`, and the lowered orphan sources under
12
- * `ttsx-orphan/`.
11
+ * answers under `capabilities/`, the plugin content identities under
12
+ * `identities/`, the plugin-load answers under `answers/`, and the lowered
13
+ * orphan sources under `ttsx-orphan/`.
13
14
  *
14
15
  * The policy is the plugin cache's (`CachePrunePolicy`). An entry's last use is
15
16
  * its modification time: a write sets it, and every hit records a use by
@@ -0,0 +1,45 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * The version of this ttsc package, read from its own manifest once per
6
+ * process, or `"0.0.0"` when the manifest cannot be read or names none.
7
+ *
8
+ * WARNING (#1726): a reader that only needs ttsc's own version must take it
9
+ * here, never from `pluginBuildVersions`, which also resolves the project's
10
+ * `typescript` package. A plugin descriptor evaluator opens a record store
11
+ * while its module resolution recorder runs, and on a runtime whose
12
+ * `require.resolve` consults resolve hooks every such lookup became an input of
13
+ * the descriptor: absent `typescript` candidates below the cache root's
14
+ * ancestors, whose proofs the system temporary directory's metadata then
15
+ * withdrew. Reading a file here resolves no module.
16
+ *
17
+ * The host installation is assumed stable for the process lifetime; changing
18
+ * its manifest does not refresh the label.
19
+ *
20
+ * @evidence contracts/common.md#principled-implementation The label is this package's manifest version, the same value plugin keys and record identities have always named; an unreadable manifest yields the documented default instead of a guess.
21
+ * @evidence contracts/common.md#clear-and-simple-design One reader owns the host label; `pluginBuildVersions` composes it with the project's compiler version.
22
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The manifest beside the compiled module is read directly; no module resolution or fixture version stands in for it.
23
+ * @evidence contracts/common.md#meaningful-documentation The paragraphs state the value, the regression that motivates a resolution-free reader and the process-stable premise.
24
+ * @evidence contracts/portability.md#os-neutral-implementation The manifest path is joined with Node path APIs relative to this module, without separator or case assumptions.
25
+ * @evidence contracts/performance.md#efficient-algorithms The first call reads and parses one small manifest; later calls return the retained label.
26
+ * @evidence contracts/performance.md#reuse-equivalent-work Every caller in the process shares one reading of an installation assumed stable for that process.
27
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One label string is retained for the process lifetime; the synchronous read leaves no descriptor open.
28
+ */
29
+ export function ttscPackageVersion(): string {
30
+ if (version !== undefined) return version;
31
+ try {
32
+ const manifest = JSON.parse(
33
+ fs.readFileSync(
34
+ path.resolve(__dirname, "..", "..", "..", "..", "package.json"),
35
+ "utf8",
36
+ ),
37
+ ) as { version?: string };
38
+ version = manifest.version ?? "0.0.0";
39
+ } catch {
40
+ version = "0.0.0";
41
+ }
42
+ return version;
43
+ }
44
+
45
+ let version: string | undefined;
@@ -8,8 +8,8 @@ import { CapabilityResolutionFormat } from "./CapabilityResolutionFormat";
8
8
  import type { ITtscCapabilityPluginSource } from "./ITtscCapabilityPluginSource";
9
9
  import type { ITtscCapabilityResolutionEntry } from "./ITtscCapabilityResolutionEntry";
10
10
  import type { ITtscCapabilityResolutionPlugin } from "./ITtscCapabilityResolutionPlugin";
11
+ import { PluginContentIdentities } from "./source/PluginContentIdentities";
11
12
  import { SourceBuildCacheLayout } from "./source/SourceBuildCacheLayout";
12
- import { pluginSourceDigest } from "./source/pluginSourceDigest";
13
13
  import { pluginSourceFilesSignature } from "./source/pluginSourceFilesSignature";
14
14
  import { pluginSourceState } from "./source/pluginSourceState";
15
15
 
@@ -71,6 +71,12 @@ export function writeCapabilityResolution(
71
71
 
72
72
  /** Pre-load authority key; a moved evaluation authority refuses the write. */
73
73
  expectedAuthority?: string;
74
+
75
+ /**
76
+ * Record store of the project's plugin cache, which proves the runtime and
77
+ * the plugin sources and SDK from metadata instead of their bytes (#1725).
78
+ */
79
+ identities?: PluginContentIdentities.Store;
74
80
  },
75
81
  answer: {
76
82
  /**
@@ -180,8 +186,8 @@ export function writeCapabilityResolution(
180
186
  return null;
181
187
  }
182
188
  }
183
- const evidence = CapabilityResolutionFormat.sourceEvidence(
184
- CapabilityResolutionFormat.clockReference(file),
189
+ const evidence = PluginContentIdentities.sourceEvidence(
190
+ PluginContentIdentities.mintReference(path.dirname(file)),
185
191
  );
186
192
  const entry: ITtscCapabilityResolutionEntry = {
187
193
  hostInputHashes,
@@ -191,7 +197,13 @@ export function writeCapabilityResolution(
191
197
  pluginSources: Object.fromEntries(
192
198
  Object.entries(answer.pluginSources).map(([directory, state]) => [
193
199
  directory,
194
- recordPluginSource(directory, state, evidence),
200
+ recordPluginSource(
201
+ directory,
202
+ state,
203
+ evidence,
204
+ path.resolve(options.cwd),
205
+ options.identities,
206
+ ),
195
207
  ]),
196
208
  ),
197
209
  plugins: answer.plugins.map((plugin) => ({
@@ -239,23 +251,32 @@ export function writeCapabilityResolution(
239
251
  * when both signatures agree, every stamp was separable from a reference minted
240
252
  * before the read, and the digest gives the very state the load reported, so
241
253
  * the entry never vouches for sources the binary was not built from. Anything
242
- * else records the state alone, which a read proves in full.
254
+ * else records the state alone, which a read proves in full. The digest comes
255
+ * from the project's record store when its metadata still holds there, which
256
+ * places it inside the same two signatures, so a new process does not read
257
+ * every source byte again (#1725).
243
258
  */
244
259
  function recordPluginSource(
245
260
  directory: string,
246
261
  state: string,
247
- evidence: ReturnType<typeof CapabilityResolutionFormat.sourceEvidence>,
262
+ evidence: ReturnType<typeof PluginContentIdentities.sourceEvidence>,
263
+ projectRoot: string,
264
+ identities: PluginContentIdentities.Store | undefined,
248
265
  ): ITtscCapabilityPluginSource {
249
266
  try {
250
267
  const before = pluginSourceFilesSignature(directory, evidence);
251
- const digest = pluginSourceDigest(directory);
268
+ const digest = PluginContentIdentities.sourceDirectory(
269
+ identities,
270
+ directory,
271
+ );
252
272
  const after = pluginSourceFilesSignature(directory, evidence);
253
273
  if (
254
274
  before !== undefined &&
255
275
  after !== undefined &&
256
276
  before.signature === after.signature &&
257
277
  after.separable &&
258
- pluginSourceState(directory, { sourceDigest: digest }) === state
278
+ pluginSourceState(directory, { projectRoot, sourceDigest: digest }) ===
279
+ state
259
280
  ) {
260
281
  return { digest, signature: after.signature, state };
261
282
  }
@@ -15,6 +15,7 @@ import { CapabilityResolutionFormat } from "./internal/CapabilityResolutionForma
15
15
  import type { ITtscCapabilityResolutionEntry } from "./internal/ITtscCapabilityResolutionEntry";
16
16
  import { loadProjectPlugins } from "./internal/load/loadProjectPlugins";
17
17
  import { readCapabilityResolution } from "./internal/readCapabilityResolution";
18
+ import { PluginContentIdentities } from "./internal/source/PluginContentIdentities";
18
19
  import { writeCapabilityResolution } from "./internal/writeCapabilityResolution";
19
20
 
20
21
  /**
@@ -102,12 +103,26 @@ export function resolveCapabilityPluginResolution(options: {
102
103
  // the same cwd. NUL cannot occur in a filename, so this discovery key cannot
103
104
  // collide with an explicitly supplied path.
104
105
  const tsconfig = options.tsconfig ?? `${String.fromCharCode(0)}discovered`;
105
- const runtimeProved = capabilityRuntimeAuthorityComplete(cwd);
106
+ // WARNING (#1725): every lookup proved the runtime by streaming it, and a hit
107
+ // proved the plugin sources and the whole GOROOT by their bytes, in each
108
+ // process. One store of the project's plugin cache serves every proof of
109
+ // this resolution, opened only once the runtime may be cached at all.
110
+ const identities = capabilityRecordStore(cwd);
111
+ const runtimeProved = capabilityRuntimeAuthorityComplete(cwd, identities);
106
112
  const authority = runtimeProved
107
- ? CapabilityResolutionFormat.resolutionFile({ cwd, tsconfig })
113
+ ? CapabilityResolutionFormat.resolutionFile({
114
+ cwd,
115
+ tsconfig,
116
+ identities: identities(),
117
+ })
108
118
  : null;
109
119
  const cached = runtimeProved
110
- ? readCapabilityResolution({ cwd, tsconfig, version })
120
+ ? readCapabilityResolution({
121
+ cwd,
122
+ tsconfig,
123
+ version,
124
+ identities: identities(),
125
+ })
111
126
  : null;
112
127
  E2ETrace.capabilityResolution("lookup", {
113
128
  cwd,
@@ -118,7 +133,14 @@ export function resolveCapabilityPluginResolution(options: {
118
133
  cacheHit: cached !== null,
119
134
  });
120
135
  if (cached !== null)
121
- return resolved(cached, options.capability, cwd, tsconfig, authority);
136
+ return resolved(
137
+ cached,
138
+ options.capability,
139
+ cwd,
140
+ tsconfig,
141
+ authority,
142
+ identities,
143
+ );
122
144
 
123
145
  const binary = resolveBinary();
124
146
  if (binary === null || binary === undefined)
@@ -178,12 +200,21 @@ export function resolveCapabilityPluginResolution(options: {
178
200
  runtimeProved &&
179
201
  authority !== null &&
180
202
  (authorityUnchanged =
181
- CapabilityResolutionFormat.resolutionFile({ cwd, tsconfig }) ===
182
- authority) &&
203
+ CapabilityResolutionFormat.resolutionFile({
204
+ cwd,
205
+ tsconfig,
206
+ identities: identities(),
207
+ }) === authority) &&
183
208
  loaded.descriptorReadsDeclared &&
184
209
  loaded.discoveryInputsComplete
185
210
  ? writeCapabilityResolution(
186
- { cwd, tsconfig, version, expectedAuthority: authority },
211
+ {
212
+ cwd,
213
+ tsconfig,
214
+ version,
215
+ expectedAuthority: authority,
216
+ identities: identities(),
217
+ },
187
218
  answer,
188
219
  )
189
220
  : null;
@@ -213,10 +244,40 @@ export function resolveCapabilityPluginResolution(options: {
213
244
  plugins: CapabilityPluginResult.select(answer, options.capability),
214
245
  status: "resolved",
215
246
  }
216
- : resolved(recorded, options.capability, cwd, tsconfig, authority);
247
+ : resolved(
248
+ recorded,
249
+ options.capability,
250
+ cwd,
251
+ tsconfig,
252
+ authority,
253
+ identities,
254
+ );
217
255
  });
218
256
  }
219
257
 
258
+ /**
259
+ * The record store of `cwd`'s plugin cache, opened on first use and kept for
260
+ * the resolution and its later freshness queries, or `undefined` when none can
261
+ * be opened. A kept store's references only grow older, which can make fewer
262
+ * stamps separable but never more.
263
+ */
264
+ function capabilityRecordStore(
265
+ cwd: string,
266
+ ): () => PluginContentIdentities.Store | undefined {
267
+ let opened = false;
268
+ let store: PluginContentIdentities.Store | undefined;
269
+ return () => {
270
+ if (!opened) {
271
+ opened = true;
272
+ store = PluginContentIdentities.open({
273
+ projectRoot: cwd,
274
+ env: process.env,
275
+ });
276
+ }
277
+ return store;
278
+ };
279
+ }
280
+
220
281
  /**
221
282
  * Whether the keyed executable is the actual sole descriptor runtime.
222
283
  *
@@ -228,7 +289,10 @@ export function resolveCapabilityPluginResolution(options: {
228
289
  * operation's existing decision and preserves short-circuited comparisons as
229
290
  * unobserved, without repeating native queries.
230
291
  */
231
- function capabilityRuntimeAuthorityComplete(cwd: string): boolean {
292
+ function capabilityRuntimeAuthorityComplete(
293
+ cwd: string,
294
+ identities: () => PluginContentIdentities.Store | undefined,
295
+ ): boolean {
232
296
  if (SidecarEnvironment.read(process.env, "NODE_OPTIONS")?.trim()) {
233
297
  E2ETrace.capabilityResolution("runtime-authority", {
234
298
  cwd,
@@ -254,6 +318,7 @@ function capabilityRuntimeAuthorityComplete(cwd: string): boolean {
254
318
  runtime,
255
319
  process.env,
256
320
  cwd,
321
+ identities(),
257
322
  );
258
323
  let executableMatches: boolean | undefined;
259
324
  const proved =
@@ -291,6 +356,7 @@ function resolved(
291
356
  cwd: string,
292
357
  tsconfig: string,
293
358
  authority: string | null,
359
+ identities: () => PluginContentIdentities.Store | undefined,
294
360
  ): ITtscCapabilityPluginResolution {
295
361
  const proof = JSON.stringify(entry);
296
362
  return {
@@ -298,12 +364,20 @@ function resolved(
298
364
  try {
299
365
  if (
300
366
  authority === null ||
301
- !capabilityRuntimeAuthorityComplete(cwd) ||
302
- CapabilityResolutionFormat.resolutionFile({ cwd, tsconfig }) !==
303
- authority
367
+ !capabilityRuntimeAuthorityComplete(cwd, identities) ||
368
+ CapabilityResolutionFormat.resolutionFile({
369
+ cwd,
370
+ tsconfig,
371
+ identities: identities(),
372
+ }) !== authority
304
373
  )
305
374
  return false;
306
- const current = readCapabilityResolution({ cwd, tsconfig, version });
375
+ const current = readCapabilityResolution({
376
+ cwd,
377
+ tsconfig,
378
+ version,
379
+ identities: identities(),
380
+ });
307
381
  return current !== null && JSON.stringify(current) === proof;
308
382
  } catch {
309
383
  return false;