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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { E2ETrace } from "../../../internal/E2ETrace";
|
|
6
|
+
import type { PluginContentIdentities } from "./PluginContentIdentities";
|
|
7
|
+
import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
|
|
8
|
+
import { recordCacheFileUse } from "./recordCacheFileUse";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Answers a plugin load computed from inputs it could identify by content, kept
|
|
12
|
+
* in the plugin cache root under that identity so a later process with equal
|
|
13
|
+
* inputs reads the answer instead of computing it again.
|
|
14
|
+
*
|
|
15
|
+
* WARNING (#1721, #1723): the key is the whole proof. A caller must fold into
|
|
16
|
+
* `identity` every input that can change the answer, as content identities
|
|
17
|
+
* (`PluginContentIdentities`) or the authoritative live probe results, never a
|
|
18
|
+
* path, a timestamp or a value it merely expects to be stable. An answer is
|
|
19
|
+
* written only after the computation succeeded on inputs the caller proved
|
|
20
|
+
* unchanged around it; a failed or unproved computation is never recorded,
|
|
21
|
+
* since a later process could not tell it from a current one.
|
|
22
|
+
*
|
|
23
|
+
* Entries are single files published by rename, aged out by the single-file
|
|
24
|
+
* collector and removed by `ttsc clean`. Their identity includes the format tag
|
|
25
|
+
* and the ttsc version, so another release never reads them.
|
|
26
|
+
*
|
|
27
|
+
* @evidence contracts/common.md#principled-implementation The content-addressed key carries every caller-declared input identity; reads accept only a matching format, version and kind, and writes are refused for unproved computations by the caller contract.
|
|
28
|
+
* @evidence contracts/common.md#clear-and-simple-design One owner stores and loads opaque answers; callers own key composition, validation of the answer shape and the decision to record.
|
|
29
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts No answer is guessed; a missing, corrupt or foreign entry is a miss and the caller computes.
|
|
30
|
+
* @evidence contracts/common.md#meaningful-documentation Native paragraphs state the key contract that prevents stale answers, the recording rule and the storage lifetime.
|
|
31
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native path joins and same-directory rename publication; keys are hashed text, independent of path dialect.
|
|
32
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms Members state their own costs.
|
|
33
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Members define reuse through the caller's identity.
|
|
34
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Members state retention; the namespace holds no state.
|
|
35
|
+
*/
|
|
36
|
+
export namespace PluginLoadAnswers {
|
|
37
|
+
/**
|
|
38
|
+
* The answer recorded for `identity`, or `undefined` when none is.
|
|
39
|
+
*
|
|
40
|
+
* @param store The opened store, or `undefined` for no persistence.
|
|
41
|
+
* @param kind Answer family; families never share entries.
|
|
42
|
+
* @param identity Every input identity the answer depends on.
|
|
43
|
+
* @evidence contracts/common.md#principled-implementation An entry is returned only for the same format, version, kind and identity hash; the caller validates the value's shape.
|
|
44
|
+
* @evidence contracts/common.md#clear-and-simple-design One lookup returns an opaque value or undefined.
|
|
45
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Unreadable or foreign entries are misses.
|
|
46
|
+
* @evidence contracts/common.md#meaningful-documentation Parameters describe the identity contract.
|
|
47
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native path join and file read.
|
|
48
|
+
* @evidence contracts/performance.md#efficient-algorithms Hashes the serialized identity and reads one small file; a hit refreshes its use time.
|
|
49
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Processes share an answer only for an identical serialized identity under the same version.
|
|
50
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Nothing is retained in memory; the collector owns entry lifetime.
|
|
51
|
+
*/
|
|
52
|
+
export function read(
|
|
53
|
+
store: PluginContentIdentities.Store | undefined,
|
|
54
|
+
kind: string,
|
|
55
|
+
identity: unknown,
|
|
56
|
+
): unknown {
|
|
57
|
+
if (store === undefined) return undefined;
|
|
58
|
+
const file = answerFile(store, kind, identity);
|
|
59
|
+
try {
|
|
60
|
+
const entry = JSON.parse(
|
|
61
|
+
fs.readFileSync(file, "utf8"),
|
|
62
|
+
) as Partial<IEntry>;
|
|
63
|
+
if (
|
|
64
|
+
entry.format !== FORMAT ||
|
|
65
|
+
entry.version !== store.version ||
|
|
66
|
+
entry.kind !== kind ||
|
|
67
|
+
!Object.prototype.hasOwnProperty.call(entry, "value")
|
|
68
|
+
)
|
|
69
|
+
return undefined;
|
|
70
|
+
recordCacheFileUse(file);
|
|
71
|
+
E2ETrace.capabilityResolution("plugin-load-answer", {
|
|
72
|
+
kind,
|
|
73
|
+
outcome: "reused",
|
|
74
|
+
});
|
|
75
|
+
return entry.value;
|
|
76
|
+
} catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Record `value` as the answer for `identity`. Failures are ignored: the
|
|
83
|
+
* answer just computed stands, and the next process computes it again.
|
|
84
|
+
*
|
|
85
|
+
* @param store The opened store, or `undefined` for no persistence.
|
|
86
|
+
* @param kind Answer family.
|
|
87
|
+
* @param identity Every input identity the answer depends on.
|
|
88
|
+
* @param value JSON-serializable answer the caller proved for `identity`.
|
|
89
|
+
* @evidence contracts/common.md#principled-implementation Publication happens only when the caller decided the computation was proven; the entry records the same identity framing that read checks.
|
|
90
|
+
* @evidence contracts/common.md#clear-and-simple-design One atomic publication through the shared metadata writer.
|
|
91
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts A failed write never fails the load or fabricates an entry.
|
|
92
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states failure behavior.
|
|
93
|
+
* @evidence contracts/portability.md#os-neutral-implementation Same-directory staging and rename through the shared writer.
|
|
94
|
+
* @evidence contracts/performance.md#efficient-algorithms Serializes the identity and value once and writes one small file.
|
|
95
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Publication creates the reusable entry; read owns reuse.
|
|
96
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources One file per identity, removed by the single-file collector or `ttsc clean`; staging cleanup belongs to the shared writer.
|
|
97
|
+
*/
|
|
98
|
+
export function write(
|
|
99
|
+
store: PluginContentIdentities.Store | undefined,
|
|
100
|
+
kind: string,
|
|
101
|
+
identity: unknown,
|
|
102
|
+
value: unknown,
|
|
103
|
+
): void {
|
|
104
|
+
if (store === undefined) return;
|
|
105
|
+
const file = answerFile(store, kind, identity);
|
|
106
|
+
try {
|
|
107
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
108
|
+
SourceBuildCacheLayout.replaceCacheMetadataFile(
|
|
109
|
+
file,
|
|
110
|
+
JSON.stringify({
|
|
111
|
+
format: FORMAT,
|
|
112
|
+
version: store.version,
|
|
113
|
+
kind,
|
|
114
|
+
value,
|
|
115
|
+
} satisfies IEntry),
|
|
116
|
+
);
|
|
117
|
+
E2ETrace.capabilityResolution("plugin-load-answer", {
|
|
118
|
+
kind,
|
|
119
|
+
outcome: "recorded",
|
|
120
|
+
});
|
|
121
|
+
} catch {
|
|
122
|
+
// The answer just computed stands; only its reuse is lost.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Entry format tag; move it when the entry shape changes. */
|
|
127
|
+
const FORMAT = "ttsc-plugin-load-answer-v1";
|
|
128
|
+
|
|
129
|
+
interface IEntry {
|
|
130
|
+
format: string;
|
|
131
|
+
version: string;
|
|
132
|
+
kind: string;
|
|
133
|
+
value: unknown;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function answerFile(
|
|
137
|
+
store: PluginContentIdentities.Store,
|
|
138
|
+
kind: string,
|
|
139
|
+
identity: unknown,
|
|
140
|
+
): string {
|
|
141
|
+
const key = crypto
|
|
142
|
+
.createHash("sha256")
|
|
143
|
+
.update(JSON.stringify([FORMAT, store.version, kind, identity]))
|
|
144
|
+
.digest("hex");
|
|
145
|
+
return path.join(
|
|
146
|
+
store.root,
|
|
147
|
+
SourceBuildCacheLayout.ANSWER_CACHE_DIRNAME,
|
|
148
|
+
`${key}.json`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -53,6 +53,18 @@ export namespace SourceBuildCacheLayout {
|
|
|
53
53
|
/** Directory of the capability-resolution answers inside the cache root. */
|
|
54
54
|
export const CAPABILITY_CACHE_DIRNAME = "capabilities";
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Directory of the content identities a plugin load proves from metadata
|
|
58
|
+
* (`PluginContentIdentities`) inside the cache root.
|
|
59
|
+
*/
|
|
60
|
+
export const IDENTITY_CACHE_DIRNAME = "identities";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Directory of the plugin-load answers keyed by those identities, such as Go
|
|
64
|
+
* package selections and runtime capabilities (`PluginLoadAnswers`).
|
|
65
|
+
*/
|
|
66
|
+
export const ANSWER_CACHE_DIRNAME = "answers";
|
|
67
|
+
|
|
56
68
|
/** Directory of the ttsx runtime inside the cache root. */
|
|
57
69
|
export const RUNTIME_CACHE_DIRNAME = "ttsx";
|
|
58
70
|
|
|
@@ -72,6 +84,8 @@ export namespace SourceBuildCacheLayout {
|
|
|
72
84
|
export const CACHE_FILE_DIRNAMES: readonly string[] = [
|
|
73
85
|
DESCRIPTOR_CACHE_DIRNAME,
|
|
74
86
|
CAPABILITY_CACHE_DIRNAME,
|
|
87
|
+
IDENTITY_CACHE_DIRNAME,
|
|
88
|
+
ANSWER_CACHE_DIRNAME,
|
|
75
89
|
ORPHAN_CACHE_DIRNAME,
|
|
76
90
|
];
|
|
77
91
|
|
|
@@ -3,9 +3,12 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { OwnedSynchronousProcess } from "../../../internal/OwnedSynchronousProcess";
|
|
6
|
+
import { GoEnvironmentReading } from "./GoEnvironmentReading";
|
|
6
7
|
import { GoSourceInputs } from "./GoSourceInputs";
|
|
7
|
-
import {
|
|
8
|
+
import { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
|
|
9
|
+
import type { PluginContentIdentities } from "./PluginContentIdentities";
|
|
8
10
|
import { SourcePluginAdmission } from "./SourcePluginAdmission";
|
|
11
|
+
import { copiesPluginSourceEntry } from "./copiesPluginSourceEntry";
|
|
9
12
|
import { formatGoWorkPath } from "./formatGoWorkPath";
|
|
10
13
|
import { spawnGoTool } from "./spawnGoTool";
|
|
11
14
|
|
|
@@ -13,20 +16,23 @@ import { spawnGoTool } from "./spawnGoTool";
|
|
|
13
16
|
* Share generated Go workspace policy between package selection and building.
|
|
14
17
|
*
|
|
15
18
|
* Package selection must use the builder's overlays, generated workspace and
|
|
16
|
-
* selected tool rather than an ancestor go.work found in a source directory.
|
|
17
|
-
*
|
|
18
|
-
* and effective environment;
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* selected tool rather than an ancestor go.work found in a source directory. A
|
|
20
|
+
* load shares early replacement watch, package and build manifest records under
|
|
21
|
+
* its pinned tool and effective environment; identical current bytes in
|
|
22
|
+
* independently proven scratch inputs share their Go parse. A moved native tool
|
|
23
|
+
* clears that reader's syntax memo, and a toolchain retry starts a new reader
|
|
24
|
+
* scope. Each caller has a diagnostic view over the shared native reader state;
|
|
25
|
+
* one plugin cannot inherit another plugin's failure label (#1720). Go supplies
|
|
26
|
+
* the workspace's required Go and toolchain directives. These reads preserve
|
|
27
|
+
* the existing installation/version and sequential-stability limits.
|
|
22
28
|
*
|
|
23
29
|
* @evidence contracts/common.md#principled-implementation Shared manifest acquisition, managed replacement admission, overlay filtering and Go-owned workspace version selection keep metadata and compilation under the same workspace policy; callers own source materialization and effective environment.
|
|
24
30
|
* @evidence contracts/common.md#clear-and-simple-design One namespace owns the generated workspace and its module reader, overlay discovery and Go launch diagnostics; no second manifest parser or build-tag interpreter is introduced.
|
|
25
31
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Actual Go mod edit and work use commands interpret manifests; selected tool errors propagate and no static workspace version replaces Go's decision.
|
|
26
32
|
* @evidence contracts/common.md#meaningful-documentation Native prose explains shared policy and caller ownership; private helpers remain part of this operation's review surface.
|
|
27
33
|
* @evidence contracts/portability.md#os-neutral-implementation Native fs/path operations preserve path spelling and formatGoWorkPath owns Go syntax quoting; subprocesses use the existing native wrapper owner with hidden windows.
|
|
28
|
-
* @evidence contracts/performance.md#efficient-algorithms Workspace construction scans module and replacement populations; each
|
|
29
|
-
* @evidence contracts/performance.md#reuse-equivalent-work
|
|
34
|
+
* @evidence contracts/performance.md#efficient-algorithms Workspace construction scans module and replacement populations; each reader reads current manifest bytes and memoizes equivalent Go syntax within its pinned native tool/load. Overlay discovery recursively scans admitted directories and sorts paths; native Go output and JSON parsing scale with complete bytes.
|
|
35
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Metadata and materialized builds share Go parsing only for identical current manifest bytes under the same pinned tool/effective environment. Independent materialization/content proof remains with each caller; tool movement clears syntax reuse and retries start another scope.
|
|
30
36
|
* @evidence contracts/performance.md#bound-retention-and-release-resources Readers retain module records only for their enclosing load or materialized build; command capture cleanup belongs to spawnGoTool and scratch lifetime belongs to the caller. Native commands can mutate Go-owned state and their selected-child completion does not prove arbitrary descendants retired.
|
|
31
37
|
*/
|
|
32
38
|
export namespace SourcePluginWorkspace {
|
|
@@ -55,7 +61,8 @@ export namespace SourcePluginWorkspace {
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
/**
|
|
58
|
-
* Write the native builder's generated workspace and let Go select its
|
|
64
|
+
* Write the native builder's generated workspace and let Go select its
|
|
65
|
+
* version.
|
|
59
66
|
*
|
|
60
67
|
* @evidence contracts/common.md#principled-implementation Actual manifest records drive managed overlay filtering and replacement admission; Go work use supplies required directives.
|
|
61
68
|
* @evidence contracts/common.md#clear-and-simple-design The writer shares one reader across source/overlay queries and delegates manifest interpretation to Go.
|
|
@@ -173,6 +180,7 @@ export namespace SourcePluginWorkspace {
|
|
|
173
180
|
interface GoModInfo {
|
|
174
181
|
readonly modulePath: string | null;
|
|
175
182
|
readonly replacements: readonly GoModReplacement[];
|
|
183
|
+
readonly directives: NonNullable<GoModJson["Replace"]>;
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
/**
|
|
@@ -190,17 +198,19 @@ export namespace SourcePluginWorkspace {
|
|
|
190
198
|
export interface GoModReader {
|
|
191
199
|
/**
|
|
192
200
|
* Observe the module at this native directory within the reader context.
|
|
201
|
+
* The optional diagnostic owner overrides the creating caller's label only;
|
|
202
|
+
* it changes no selected-tool, environment or byte-content authority.
|
|
193
203
|
*
|
|
194
204
|
* @evidence contracts/common.md#principled-implementation The signature associates a native directory with the selected reader's Go-owned module and replacement observation.
|
|
195
|
-
* @evidence contracts/common.md#clear-and-simple-design
|
|
205
|
+
* @evidence contracts/common.md#clear-and-simple-design A directory and optional diagnostic label expose manifest policy without treating the label as native cache authority.
|
|
196
206
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The signature supplies no parser, alternate selection policy or stability certificate.
|
|
197
|
-
* @evidence contracts/common.md#meaningful-documentation Native prose states
|
|
207
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose states selected-context result ownership and the optional caller label, which changes failure attribution only.
|
|
198
208
|
* @evidence contracts/portability.md#os-neutral-implementation The directory is a native filesystem path; its resolution belongs to the implementing reader.
|
|
199
209
|
* @evidence contracts/performance.md#efficient-algorithms A method signature executes no algorithm; createGoModReader owns acquisition and parsing.
|
|
200
210
|
* @evidence contracts/performance.md#reuse-equivalent-work Implementations supply observations within their selected tool/environment scope; this signature establishes no cross-context memo.
|
|
201
211
|
* @evidence contracts/performance.md#bound-retention-and-release-resources The signature acquires no resource; record retention belongs to the reader's enclosing load or materialized build.
|
|
202
212
|
*/
|
|
203
|
-
read(dir: string): GoModInfo;
|
|
213
|
+
read(dir: string, diagnosticPlugin?: string): GoModInfo;
|
|
204
214
|
}
|
|
205
215
|
|
|
206
216
|
interface GoModJson {
|
|
@@ -224,36 +234,148 @@ export namespace SourcePluginWorkspace {
|
|
|
224
234
|
}
|
|
225
235
|
|
|
226
236
|
/**
|
|
227
|
-
* Read
|
|
237
|
+
* Read current manifest bytes and share identical Go parses within one pinned
|
|
238
|
+
* selected-tool/load context, including copied scratch modules. An optional
|
|
239
|
+
* registry shares native reader state from the first watch query onward; its
|
|
240
|
+
* owner must keep one fixed effective environment and discard it on retry.
|
|
241
|
+
* Caller views share that native state and syntax, while each default
|
|
242
|
+
* diagnostic label remains with its current caller. An optional record store
|
|
243
|
+
* only carries the Go environment-file discovery hint across processes; it
|
|
244
|
+
* never carries a parse.
|
|
228
245
|
*
|
|
229
|
-
* @evidence contracts/common.md#principled-implementation Actual go mod edit JSON determines module/replacement identities;
|
|
230
|
-
* @evidence contracts/common.md#clear-and-simple-design
|
|
246
|
+
* @evidence contracts/common.md#principled-implementation Actual go mod edit JSON determines module/replacement identities; current bytes, selected/dispatched-tool metadata and Go's environment-file witness qualify reuse under the existing metadata-distinguishability premise. Environment values are copied at reader creation. Missing go.mod returns the existing empty module observation, and moving tool/manifest inputs cannot publish a parsed record.
|
|
247
|
+
* @evidence contracts/common.md#clear-and-simple-design One factory acquires load-owned native reader state by selected tool; a thin caller view forwards its diagnostic label without duplicating the byte-content map. Current reads and post-parse byte/tool comparisons qualify insertion.
|
|
231
248
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts No custom Go manifest parser or global context-insensitive memo is introduced.
|
|
232
249
|
* @evidence contracts/common.md#meaningful-documentation Native prose states this operation's authority and delegated boundaries; fields retain separate native comments.
|
|
233
250
|
* @evidence contracts/portability.md#os-neutral-implementation Native path resolution and temporary cwd avoid deep Windows working-directory limits; the selected tool handles manifest syntax.
|
|
234
|
-
* @evidence contracts/performance.md#efficient-algorithms Each distinct
|
|
235
|
-
* @evidence contracts/performance.md#reuse-equivalent-work The caller may share this reader only within one load and the same pinned tool/effective environment;
|
|
236
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources The map
|
|
251
|
+
* @evidence contracts/performance.md#efficient-algorithms Reader acquisition performs a selected-tool registry lookup; every nonmissing read qualifies tool/environment metadata and reads current manifest bytes. A cold or stale witness first observes the selected launcher; an unavailable or nonfile launcher skips optional environment acquisition and permits only the actual uncached parse attempt. An observed file lends its same pre-read stat to the witness before acquiring current Go values and GOENV evidence through the common reader, without a GOROOT walk. Only complete native GOENV/GOROOT locations qualify syntax sharing; a dispatched tool is witnessed before parsing. Unavailable native environment acquisition disables syntax sharing while preserving actual parse success or failure. Each distinct byte string that earns stable successful syntax in that tool epoch runs Go mod edit and complete JSON parsing once, then rechecks bytes/tool metadata before caching; copied manifests share that parse. Failed queries remain uncached and report the current caller's diagnostic label.
|
|
252
|
+
* @evidence contracts/performance.md#reuse-equivalent-work The caller may share this reader only within one load and the same pinned tool/effective environment; parsed syntax is not cross-load or native containment authority. Caller views share native state while preserving failure attribution; diagnostic labels are not syntax cache inputs.
|
|
253
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The map retains distinct manifest bytes and parsed syntax for its caller's load/build lifetime; process/output capture cleanup remains delegated.
|
|
237
254
|
*/
|
|
238
255
|
export function createGoModReader(
|
|
239
256
|
goBinary: string,
|
|
240
257
|
pluginName: string,
|
|
241
258
|
env: NodeJS.ProcessEnv,
|
|
259
|
+
readers?: Map<string, GoModReader>,
|
|
260
|
+
identities?: PluginContentIdentities.Store,
|
|
242
261
|
): GoModReader {
|
|
262
|
+
const existing = readers?.get(goBinary);
|
|
263
|
+
if (existing !== undefined)
|
|
264
|
+
return {
|
|
265
|
+
read: (dir, diagnosticPlugin = pluginName) =>
|
|
266
|
+
existing.read(dir, diagnosticPlugin),
|
|
267
|
+
};
|
|
268
|
+
// WARNING (#1712): parse identical bytes once in this pinned tool/load
|
|
269
|
+
// context, including early replacement watch projection and copied scratch
|
|
270
|
+
// manifests. The optional registry belongs to one fixed effective environment
|
|
271
|
+
// and one load; it is never shared with a toolchain retry. Always read current bytes;
|
|
272
|
+
// a pathname or metadata-only memo misses manifest edits. Never promote
|
|
273
|
+
// this map to a process cache: tool/environment epochs change Go authority.
|
|
274
|
+
// Fixed variable strings do not freeze GOENV or a dispatched Go executable.
|
|
275
|
+
// Requalify their actual native evidence before every syntax hit; missing
|
|
276
|
+
// environment authority permits a fresh parse only, never positive reuse.
|
|
277
|
+
// WARNING (#1720): diagnostic names are per caller, not a syntax/cache input.
|
|
278
|
+
// Shared views must forward their current label to every failed native read.
|
|
243
279
|
const cache = new Map<string, GoModInfo>();
|
|
244
|
-
|
|
245
|
-
|
|
280
|
+
const environment = { ...env };
|
|
281
|
+
let toolWitness: PluginBuildEnvironmentWitness.Record | undefined;
|
|
282
|
+
const reader: GoModReader = {
|
|
283
|
+
read(dir, diagnosticPlugin = pluginName) {
|
|
246
284
|
OwnedSynchronousProcess.checkpoint();
|
|
247
285
|
const resolved = path.resolve(dir);
|
|
248
|
-
const
|
|
286
|
+
const manifest = path.join(resolved, "go.mod");
|
|
287
|
+
let bytes: Buffer;
|
|
288
|
+
try {
|
|
289
|
+
bytes = fs.readFileSync(manifest);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
|
292
|
+
return emptyGoModInfo();
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
if (
|
|
296
|
+
toolWitness === undefined ||
|
|
297
|
+
!PluginBuildEnvironmentWitness.holds(toolWitness)
|
|
298
|
+
) {
|
|
299
|
+
cache.clear();
|
|
300
|
+
const witness: PluginBuildEnvironmentWitness.Record = new Map();
|
|
301
|
+
let launcher: fs.BigIntStats | undefined;
|
|
302
|
+
try {
|
|
303
|
+
launcher = fs.statSync(goBinary, { bigint: true });
|
|
304
|
+
} catch {
|
|
305
|
+
// Unavailable metadata cannot qualify optional syntax reuse.
|
|
306
|
+
}
|
|
307
|
+
if (launcher?.isFile() === true)
|
|
308
|
+
PluginBuildEnvironmentWitness.add(witness, goBinary, launcher);
|
|
309
|
+
// GOENV can change toolchain selection without changing env strings.
|
|
310
|
+
// Share its existing native discovery owner; do not scan GOROOT here.
|
|
311
|
+
// Do not launch a missing/unobservable tool twice just to learn that
|
|
312
|
+
// optional cache qualification is unavailable (#1712). The actual
|
|
313
|
+
// mod-edit attempt below still owns its current failure or success.
|
|
314
|
+
const observed =
|
|
315
|
+
launcher?.isFile() === true
|
|
316
|
+
? GoEnvironmentReading.read(
|
|
317
|
+
goBinary,
|
|
318
|
+
os.tmpdir(),
|
|
319
|
+
environment,
|
|
320
|
+
["GOROOT"],
|
|
321
|
+
witness,
|
|
322
|
+
identities,
|
|
323
|
+
)
|
|
324
|
+
: undefined;
|
|
325
|
+
if (
|
|
326
|
+
typeof observed?.GOROOT === "string" &&
|
|
327
|
+
path.isAbsolute(observed.GOROOT)
|
|
328
|
+
)
|
|
329
|
+
PluginBuildEnvironmentWitness.add(
|
|
330
|
+
witness,
|
|
331
|
+
path.join(
|
|
332
|
+
observed.GOROOT,
|
|
333
|
+
"bin",
|
|
334
|
+
process.platform === "win32" ? "go.exe" : "go",
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
const complete =
|
|
338
|
+
typeof observed?.GOROOT === "string" &&
|
|
339
|
+
path.isAbsolute(observed.GOROOT) &&
|
|
340
|
+
typeof observed.GOENV === "string" &&
|
|
341
|
+
(observed.GOENV === "off" || path.isAbsolute(observed.GOENV));
|
|
342
|
+
toolWitness =
|
|
343
|
+
complete && PluginBuildEnvironmentWitness.holds(witness)
|
|
344
|
+
? witness
|
|
345
|
+
: undefined;
|
|
346
|
+
}
|
|
347
|
+
// UTF-8 decoding is lossy for malformed input. Only identical bytes
|
|
348
|
+
// can share Go syntax, even when Go serializes two inputs identically.
|
|
349
|
+
const byteKey = bytes.toString("base64");
|
|
350
|
+
const cached = cache.get(byteKey);
|
|
249
351
|
if (cached !== undefined) {
|
|
250
352
|
return cached;
|
|
251
353
|
}
|
|
252
|
-
const info = readGoModInfo(
|
|
253
|
-
|
|
354
|
+
const info = readGoModInfo(
|
|
355
|
+
resolved,
|
|
356
|
+
goBinary,
|
|
357
|
+
diagnosticPlugin,
|
|
358
|
+
environment,
|
|
359
|
+
);
|
|
360
|
+
if (!fs.readFileSync(manifest).equals(bytes))
|
|
361
|
+
throw new Error(
|
|
362
|
+
`ttsc: go.mod changed while Go was reading ${manifest}`,
|
|
363
|
+
);
|
|
364
|
+
if (
|
|
365
|
+
toolWitness !== undefined &&
|
|
366
|
+
!PluginBuildEnvironmentWitness.holds(toolWitness)
|
|
367
|
+
)
|
|
368
|
+
throw new Error(
|
|
369
|
+
`ttsc: Go tool or environment changed while reading ${manifest}`,
|
|
370
|
+
);
|
|
371
|
+
// An unavailable environment observation can still execute a fresh Go
|
|
372
|
+
// parse, but it cannot authorize sharing that result on another call.
|
|
373
|
+
if (toolWitness !== undefined) cache.set(byteKey, info);
|
|
254
374
|
return info;
|
|
255
375
|
},
|
|
256
376
|
};
|
|
377
|
+
readers?.set(goBinary, reader);
|
|
378
|
+
return reader;
|
|
257
379
|
}
|
|
258
380
|
|
|
259
381
|
function readGoModInfo(
|
|
@@ -313,6 +435,7 @@ export namespace SourcePluginWorkspace {
|
|
|
313
435
|
|
|
314
436
|
return {
|
|
315
437
|
modulePath: json.Module?.Path ?? null,
|
|
438
|
+
directives: json.Replace ?? [],
|
|
316
439
|
replacements: (json.Replace ?? [])
|
|
317
440
|
.map(jsonReplacementToGoModReplacement)
|
|
318
441
|
.filter((replacement) => replacement !== null),
|
|
@@ -322,6 +445,7 @@ export namespace SourcePluginWorkspace {
|
|
|
322
445
|
function emptyGoModInfo(): GoModInfo {
|
|
323
446
|
return {
|
|
324
447
|
modulePath: null,
|
|
448
|
+
directives: [],
|
|
325
449
|
replacements: [],
|
|
326
450
|
};
|
|
327
451
|
}
|
|
@@ -441,6 +565,5 @@ export namespace SourcePluginWorkspace {
|
|
|
441
565
|
}
|
|
442
566
|
}
|
|
443
567
|
|
|
444
|
-
|
|
445
568
|
const TTSC_GO_MODULE_PATH = "github.com/samchon/ttsc/packages/ttsc";
|
|
446
569
|
}
|