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
package/internal/watchhelper/windows_notifications_preserve_mutations_without_access_noise_test.go
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
//go:build windows
|
|
2
|
+
|
|
3
|
+
package watchhelper
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"bufio"
|
|
7
|
+
"bytes"
|
|
8
|
+
"encoding/binary"
|
|
9
|
+
"encoding/json"
|
|
10
|
+
"io"
|
|
11
|
+
"os"
|
|
12
|
+
"path/filepath"
|
|
13
|
+
"runtime"
|
|
14
|
+
"strings"
|
|
15
|
+
"testing"
|
|
16
|
+
"time"
|
|
17
|
+
|
|
18
|
+
"golang.org/x/sys/windows"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// TestWindowsNotificationsPreserveMutationsWithoutAccessNoise verifies the
|
|
22
|
+
// actual native notification boundary without a runtime or compiler child.
|
|
23
|
+
// A delivered marker establishes a positive stream frontier; elapsed quiet is
|
|
24
|
+
// never used to certify that access or earlier writes were filtered correctly.
|
|
25
|
+
//
|
|
26
|
+
// 1. Force actual access-time updates and reads, then observe a native marker.
|
|
27
|
+
// 2. Preserve A-B-A writes, recursion, Unicode rename/removal and separate owners.
|
|
28
|
+
// 3. Refuse missing/file roots, classify literal malformed/lost batches as gaps,
|
|
29
|
+
// and stream ready/drain, subscriber joins/removal and EOF retirement.
|
|
30
|
+
//
|
|
31
|
+
// @evidence contracts/testing.md#behavioral-verification The actual Windows broker acquires handles, receives native notifications and emits decoded wire results; assertions distinguish access noise, real changes, recursion, independent registrations, failed acquisition, loss and actual streaming ready/drain/join/remove/EOF retirement.
|
|
32
|
+
// @evidence contracts/testing.md#independent-expectations Authored file names, native access timestamps and literal malformed notification bytes establish expectations independently of the implementation's mask or decoded results.
|
|
33
|
+
// @evidence contracts/testing.md#distinguishing-cases Real access-only updates contrast with content-preserving A-B-A writes; shallow and recursive registrations contrast on one native tree; live and removed owners, pre-join and shared mutations across actual request/reply frontiers, valid Unicode names and malformed/truncated batches distinguish coverage and loss.
|
|
34
|
+
// @evidence contracts/testing.md#execution-ownership This direct Go unit invokes the owning native broker and run in process using temporary files and memory streams; it installs no package and launches no compiler, runtime or helper child.
|
|
35
|
+
func TestWindowsNotificationsPreserveMutationsWithoutAccessNoise(t *testing.T) {
|
|
36
|
+
t.Run("access and ABA", func(t *testing.T) {
|
|
37
|
+
root := t.TempDir()
|
|
38
|
+
source := filepath.Join(root, "source.ts")
|
|
39
|
+
original := []byte("export const value = 1;\n")
|
|
40
|
+
if err := os.WriteFile(source, original, 0600); err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
h, output := newWindowsTestBroker(t)
|
|
44
|
+
if err := h.request(windowsBrokerRequest{Op: "add", ID: 1, AllEvents: true, Locations: []windowsWatchLocation{{Directory: root}}}); err != nil {
|
|
45
|
+
t.Fatal(err)
|
|
46
|
+
}
|
|
47
|
+
// SetFileTime with nil last-write changes access alone, unlike utimes,
|
|
48
|
+
// which can round or write modification time and legitimately notify.
|
|
49
|
+
native, _ := windows.UTF16PtrFromString(source)
|
|
50
|
+
handle, err := windows.CreateFile(native, windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, nil, windows.OPEN_EXISTING, 0, 0)
|
|
51
|
+
if err != nil {
|
|
52
|
+
t.Fatal(err)
|
|
53
|
+
}
|
|
54
|
+
access := windows.NsecToFiletime(time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC).UnixNano())
|
|
55
|
+
err = windows.SetFileTime(handle, nil, &access, nil)
|
|
56
|
+
var observed windows.ByHandleFileInformation
|
|
57
|
+
if err == nil {
|
|
58
|
+
err = windows.GetFileInformationByHandle(handle, &observed)
|
|
59
|
+
}
|
|
60
|
+
_ = windows.CloseHandle(handle)
|
|
61
|
+
if err != nil {
|
|
62
|
+
t.Fatal(err)
|
|
63
|
+
}
|
|
64
|
+
if observed.LastAccessTime != access {
|
|
65
|
+
t.Fatalf("native access update was not established: %+v", observed.LastAccessTime)
|
|
66
|
+
}
|
|
67
|
+
if _, err := os.ReadFile(source); err != nil {
|
|
68
|
+
t.Fatal(err)
|
|
69
|
+
}
|
|
70
|
+
rows := windowsTestFrontier(t, h, output, root, "after-access")
|
|
71
|
+
for _, row := range rows {
|
|
72
|
+
if row["filename"] == "source.ts" {
|
|
73
|
+
t.Fatalf("access-only update/read became mutation: %+v", row)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if err := os.WriteFile(source, []byte("export const value = 2;\n"), 0600); err != nil {
|
|
77
|
+
t.Fatal(err)
|
|
78
|
+
}
|
|
79
|
+
if err := os.WriteFile(source, original, 0600); err != nil {
|
|
80
|
+
t.Fatal(err)
|
|
81
|
+
}
|
|
82
|
+
rows = windowsTestFrontier(t, h, output, root, "after-ABA")
|
|
83
|
+
found := false
|
|
84
|
+
for _, row := range rows {
|
|
85
|
+
if row["filename"] == "source.ts" && row["eventType"] == "change" {
|
|
86
|
+
found = true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if !found {
|
|
90
|
+
t.Fatalf("actual A-B-A writes disappeared: %+v", rows)
|
|
91
|
+
}
|
|
92
|
+
current, err := os.ReadFile(source)
|
|
93
|
+
if err != nil || !bytes.Equal(current, original) {
|
|
94
|
+
t.Fatalf("authored source restoration: %q, %v", current, err)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
t.Run("live native directory loss", func(t *testing.T) {
|
|
98
|
+
parent := t.TempDir()
|
|
99
|
+
root := filepath.Join(parent, "watched")
|
|
100
|
+
if err := os.Mkdir(root, 0700); err != nil {
|
|
101
|
+
t.Fatal(err)
|
|
102
|
+
}
|
|
103
|
+
h, output := newWindowsTestBroker(t)
|
|
104
|
+
if err := h.request(windowsBrokerRequest{Op: "add", ID: 1, AllEvents: true, Locations: []windowsWatchLocation{{Directory: root}}}); err != nil {
|
|
105
|
+
t.Fatal(err)
|
|
106
|
+
}
|
|
107
|
+
if err := os.Remove(root); err != nil {
|
|
108
|
+
t.Fatal(err)
|
|
109
|
+
}
|
|
110
|
+
until := time.Now().Add(10 * time.Second)
|
|
111
|
+
for {
|
|
112
|
+
if err := h.drain(); err != nil {
|
|
113
|
+
t.Fatal(err)
|
|
114
|
+
}
|
|
115
|
+
_ = h.out.Flush()
|
|
116
|
+
rows := decodeWindowsTestRows(t, output)
|
|
117
|
+
failed := false
|
|
118
|
+
for _, row := range rows {
|
|
119
|
+
failed = failed || row["failed"] == true
|
|
120
|
+
}
|
|
121
|
+
if failed {
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
if time.Now().After(until) {
|
|
125
|
+
t.Fatalf("deleted native directory retained silent authority: %+v", rows)
|
|
126
|
+
}
|
|
127
|
+
time.Sleep(time.Millisecond)
|
|
128
|
+
}
|
|
129
|
+
if len(h.locations) != 0 {
|
|
130
|
+
t.Fatal("failed native watch remained live")
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
t.Run("sharing recursion and retirement", func(t *testing.T) {
|
|
134
|
+
root := t.TempDir()
|
|
135
|
+
child := filepath.Join(root, "child")
|
|
136
|
+
if err := os.Mkdir(child, 0700); err != nil {
|
|
137
|
+
t.Fatal(err)
|
|
138
|
+
}
|
|
139
|
+
h, output := newWindowsTestBroker(t)
|
|
140
|
+
for _, id := range []int64{1, 2, 3} {
|
|
141
|
+
if err := h.request(windowsBrokerRequest{Op: "add", ID: id, AllEvents: true, Locations: []windowsWatchLocation{{Directory: root, Recursive: id == 3}}}); err != nil {
|
|
142
|
+
t.Fatal(err)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if len(h.locations) != 2 {
|
|
146
|
+
t.Fatalf("shallow owners must share while recursion remains distinct: %d", len(h.locations))
|
|
147
|
+
}
|
|
148
|
+
deep := filepath.Join(child, "한글.ts")
|
|
149
|
+
if err := os.WriteFile(deep, []byte("x"), 0600); err != nil {
|
|
150
|
+
t.Fatal(err)
|
|
151
|
+
}
|
|
152
|
+
rows := windowsTestFrontier(t, h, output, root, "after-deep")
|
|
153
|
+
found := false
|
|
154
|
+
for _, row := range rows {
|
|
155
|
+
if row["filename"] != filepath.Join("child", "한글.ts") {
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
if row["id"] != float64(3) {
|
|
159
|
+
t.Fatalf("shallow owner heard deep entry: %+v", row)
|
|
160
|
+
}
|
|
161
|
+
found = true
|
|
162
|
+
}
|
|
163
|
+
if !found {
|
|
164
|
+
t.Fatalf("recursive Unicode creation missing: %+v", rows)
|
|
165
|
+
}
|
|
166
|
+
h.remove(1)
|
|
167
|
+
file := filepath.Join(root, "rename-before.ts")
|
|
168
|
+
renamed := filepath.Join(root, "rename-after.ts")
|
|
169
|
+
if err := os.WriteFile(file, []byte("x"), 0600); err != nil {
|
|
170
|
+
t.Fatal(err)
|
|
171
|
+
}
|
|
172
|
+
if err := os.Rename(file, renamed); err != nil {
|
|
173
|
+
t.Fatal(err)
|
|
174
|
+
}
|
|
175
|
+
if err := os.Remove(renamed); err != nil {
|
|
176
|
+
t.Fatal(err)
|
|
177
|
+
}
|
|
178
|
+
rows = windowsTestFrontier(t, h, output, root, "after-retirement")
|
|
179
|
+
old, next := false, false
|
|
180
|
+
for _, row := range rows {
|
|
181
|
+
if row["id"] == float64(1) {
|
|
182
|
+
t.Fatalf("retired owner heard later events: %+v", row)
|
|
183
|
+
}
|
|
184
|
+
if row["id"] != float64(2) || row["eventType"] != "rename" {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
old = old || row["filename"] == "rename-before.ts"
|
|
188
|
+
next = next || row["filename"] == "rename-after.ts"
|
|
189
|
+
}
|
|
190
|
+
if !old || !next {
|
|
191
|
+
t.Fatalf("remaining shared owner lost structural events: %+v", rows)
|
|
192
|
+
}
|
|
193
|
+
h.remove(2)
|
|
194
|
+
h.remove(3)
|
|
195
|
+
if len(h.locations) != 0 || len(h.registrations) != 0 {
|
|
196
|
+
t.Fatalf("last owner did not retire native registry")
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
t.Run("streaming ready drain sharing and EOF", func(t *testing.T) {
|
|
200
|
+
root := t.TempDir()
|
|
201
|
+
input, requests := io.Pipe()
|
|
202
|
+
responses, output := io.Pipe()
|
|
203
|
+
rows := make(chan map[string]any, 256)
|
|
204
|
+
scanResult := make(chan error, 1)
|
|
205
|
+
finished := make(chan int, 1)
|
|
206
|
+
stopScan := make(chan struct{})
|
|
207
|
+
go func() {
|
|
208
|
+
code := Run(input, output, io.Discard)
|
|
209
|
+
_ = input.Close()
|
|
210
|
+
_ = output.Close()
|
|
211
|
+
finished <- code
|
|
212
|
+
}()
|
|
213
|
+
go func() {
|
|
214
|
+
defer close(rows)
|
|
215
|
+
scanner := bufio.NewScanner(responses)
|
|
216
|
+
for scanner.Scan() {
|
|
217
|
+
var row map[string]any
|
|
218
|
+
if err := json.Unmarshal(scanner.Bytes(), &row); err != nil {
|
|
219
|
+
scanResult <- err
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
select {
|
|
223
|
+
case rows <- row:
|
|
224
|
+
case <-stopScan:
|
|
225
|
+
scanResult <- nil
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
scanResult <- scanner.Err()
|
|
230
|
+
}()
|
|
231
|
+
joined := false
|
|
232
|
+
scannerJoined := false
|
|
233
|
+
t.Cleanup(func() {
|
|
234
|
+
_ = requests.Close()
|
|
235
|
+
// Caller-owned pipe closure unblocks native output and the scanner on
|
|
236
|
+
// failure. Join both owned goroutines before temporary cleanup.
|
|
237
|
+
if !joined {
|
|
238
|
+
_ = responses.Close()
|
|
239
|
+
select {
|
|
240
|
+
case <-finished:
|
|
241
|
+
case <-time.After(10 * time.Second):
|
|
242
|
+
t.Error("streaming native owner did not retire after caller closure")
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
close(stopScan)
|
|
246
|
+
_ = responses.Close()
|
|
247
|
+
if !scannerJoined {
|
|
248
|
+
select {
|
|
249
|
+
case <-scanResult:
|
|
250
|
+
case <-time.After(10 * time.Second):
|
|
251
|
+
t.Error("protocol scanner did not retire after caller closure")
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
encoder := json.NewEncoder(requests)
|
|
256
|
+
send := func(request windowsBrokerRequest) {
|
|
257
|
+
t.Helper()
|
|
258
|
+
if err := encoder.Encode(request); err != nil {
|
|
259
|
+
t.Fatal(err)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
observed := []map[string]any{}
|
|
263
|
+
until := func(predicate func(map[string]any) bool) {
|
|
264
|
+
t.Helper()
|
|
265
|
+
deadline := time.NewTimer(10 * time.Second)
|
|
266
|
+
defer deadline.Stop()
|
|
267
|
+
for {
|
|
268
|
+
select {
|
|
269
|
+
case row, open := <-rows:
|
|
270
|
+
if !open {
|
|
271
|
+
t.Fatal("protocol output ended before its positive frontier")
|
|
272
|
+
}
|
|
273
|
+
observed = append(observed, row)
|
|
274
|
+
if row["failed"] == true || row["gap"] == true {
|
|
275
|
+
t.Fatalf("unexpected native coverage loss: %+v", row)
|
|
276
|
+
}
|
|
277
|
+
if predicate(row) {
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
case <-deadline.C:
|
|
281
|
+
t.Fatalf("streaming native frontier stalled: %+v", observed)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
ready := func(id int64) {
|
|
286
|
+
t.Helper()
|
|
287
|
+
send(windowsBrokerRequest{Op: "add", ID: id, AllEvents: true, Locations: []windowsWatchLocation{{Directory: root}}})
|
|
288
|
+
until(func(row map[string]any) bool { return row["id"] == float64(id) && row["ready"] == true })
|
|
289
|
+
}
|
|
290
|
+
drain := func(id int64) {
|
|
291
|
+
t.Helper()
|
|
292
|
+
send(windowsBrokerRequest{Op: "drain", ID: id})
|
|
293
|
+
until(func(row map[string]any) bool { return row["id"] == float64(id) && row["drained"] == true })
|
|
294
|
+
}
|
|
295
|
+
write := func(name string) {
|
|
296
|
+
t.Helper()
|
|
297
|
+
if err := os.WriteFile(filepath.Join(root, name), []byte("actual native mutation"), 0600); err != nil {
|
|
298
|
+
t.Fatal(err)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
ready(1)
|
|
302
|
+
write("before-join.ts")
|
|
303
|
+
until(func(row map[string]any) bool { return row["id"] == float64(1) && row["filename"] == "before-join.ts" })
|
|
304
|
+
drain(100)
|
|
305
|
+
ready(2)
|
|
306
|
+
write("shared.ts")
|
|
307
|
+
seen := map[float64]bool{}
|
|
308
|
+
until(func(row map[string]any) bool {
|
|
309
|
+
if row["filename"] == "shared.ts" {
|
|
310
|
+
id, ok := row["id"].(float64)
|
|
311
|
+
if ok {
|
|
312
|
+
seen[id] = true
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return seen[1] && seen[2]
|
|
316
|
+
})
|
|
317
|
+
drain(101)
|
|
318
|
+
for _, row := range observed {
|
|
319
|
+
if row["id"] == float64(2) && row["filename"] == "before-join.ts" {
|
|
320
|
+
t.Fatalf("new owner received a mutation before its observed join frontier: %+v", row)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
send(windowsBrokerRequest{Op: "remove", ID: 1})
|
|
324
|
+
drain(102)
|
|
325
|
+
observed = nil
|
|
326
|
+
write("after-remove.ts")
|
|
327
|
+
until(func(row map[string]any) bool { return row["id"] == float64(2) && row["filename"] == "after-remove.ts" })
|
|
328
|
+
drain(103)
|
|
329
|
+
for _, row := range observed {
|
|
330
|
+
if row["id"] == float64(1) {
|
|
331
|
+
t.Fatalf("removed owner received a later native row: %+v", row)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if err := requests.Close(); err != nil {
|
|
335
|
+
t.Fatal(err)
|
|
336
|
+
}
|
|
337
|
+
select {
|
|
338
|
+
case code := <-finished:
|
|
339
|
+
joined = true
|
|
340
|
+
if code != 0 {
|
|
341
|
+
t.Fatalf("actual armed protocol did not retire successfully: %d", code)
|
|
342
|
+
}
|
|
343
|
+
case <-time.After(10 * time.Second):
|
|
344
|
+
t.Fatal("EOF did not retire the actual native loop")
|
|
345
|
+
}
|
|
346
|
+
err := <-scanResult
|
|
347
|
+
scannerJoined = true
|
|
348
|
+
if err != nil {
|
|
349
|
+
t.Fatalf("actual protocol framing failed: %v", err)
|
|
350
|
+
}
|
|
351
|
+
// A directory handle with no sharing cannot coexist with the helper's
|
|
352
|
+
// retired FILE_LIST_DIRECTORY handle. This checks actual native closure.
|
|
353
|
+
native, err := windows.UTF16PtrFromString(root)
|
|
354
|
+
if err != nil {
|
|
355
|
+
t.Fatal(err)
|
|
356
|
+
}
|
|
357
|
+
exclusive, err := windows.CreateFile(native, windows.FILE_LIST_DIRECTORY, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
|
358
|
+
if err != nil {
|
|
359
|
+
t.Fatalf("EOF retained a native directory owner: %v", err)
|
|
360
|
+
}
|
|
361
|
+
if err := windows.CloseHandle(exclusive); err != nil {
|
|
362
|
+
t.Fatal(err)
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
t.Run("failure loss and EOF", func(t *testing.T) {
|
|
367
|
+
root := t.TempDir()
|
|
368
|
+
file := filepath.Join(root, "file")
|
|
369
|
+
if err := os.WriteFile(file, []byte("x"), 0600); err != nil {
|
|
370
|
+
t.Fatal(err)
|
|
371
|
+
}
|
|
372
|
+
h, output := newWindowsTestBroker(t)
|
|
373
|
+
for index, location := range []string{filepath.Join(root, "missing"), file} {
|
|
374
|
+
if err := h.request(windowsBrokerRequest{Op: "add", ID: int64(index + 1), Locations: []windowsWatchLocation{{Directory: root}, {Directory: location}}}); err != nil {
|
|
375
|
+
t.Fatal(err)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
_ = h.out.Flush()
|
|
379
|
+
rows := decodeWindowsTestRows(t, output)
|
|
380
|
+
if len(rows) != 2 || rows[0]["failed"] != true || rows[1]["failed"] != true || len(h.locations) != 0 {
|
|
381
|
+
t.Fatalf("failed partial acquisition retained success: %+v", rows)
|
|
382
|
+
}
|
|
383
|
+
for _, buffer := range [][]byte{nil, {0}, make([]byte, 12), windowsTestRecord(6, "x"), windowsTestRecord(3, "x")[:13]} {
|
|
384
|
+
if _, complete := decodeWindowsNotifications(buffer); complete {
|
|
385
|
+
t.Fatalf("malformed/lost native bytes became complete: %v", buffer)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
valid := windowsTestRecord(3, "한글.ts")
|
|
389
|
+
decoded, complete := decodeWindowsNotifications(valid)
|
|
390
|
+
if !complete || len(decoded) != 1 || decoded[0].name != "한글.ts" || decoded[0].kind != "change" {
|
|
391
|
+
t.Fatalf("valid native record: %+v, %v", decoded, complete)
|
|
392
|
+
}
|
|
393
|
+
binary.LittleEndian.PutUint32(valid, 4)
|
|
394
|
+
if _, complete := decodeWindowsNotifications(valid); complete {
|
|
395
|
+
t.Fatal("overlapping native next offset accepted")
|
|
396
|
+
}
|
|
397
|
+
invalidName := windowsTestRecord(3, "x")
|
|
398
|
+
binary.LittleEndian.PutUint16(invalidName[12:], 0xd800)
|
|
399
|
+
if _, complete := decodeWindowsNotifications(invalidName); complete {
|
|
400
|
+
t.Fatal("unrepresentable native identity accepted")
|
|
401
|
+
}
|
|
402
|
+
request, _ := json.Marshal(windowsBrokerRequest{Op: "add", ID: 7, AllEvents: true, Locations: []windowsWatchLocation{{Directory: root}}})
|
|
403
|
+
var protocol bytes.Buffer
|
|
404
|
+
if code := run(strings.NewReader(string(request)+"\n"), &protocol, io.Discard); code != 0 {
|
|
405
|
+
t.Fatalf("EOF did not retire an armed native watch: %d", code)
|
|
406
|
+
}
|
|
407
|
+
if !strings.Contains(protocol.String(), `"ready":true`) {
|
|
408
|
+
t.Fatalf("actual native ready missing: %s", protocol.String())
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// newWindowsTestBroker opens a real completion port and retires every pending
|
|
414
|
+
// overlapped read before the test can release its buffer memory.
|
|
415
|
+
func newWindowsTestBroker(t *testing.T) (*windowsBroker, *bytes.Buffer) {
|
|
416
|
+
t.Helper()
|
|
417
|
+
runtime.LockOSThread()
|
|
418
|
+
t.Cleanup(runtime.UnlockOSThread)
|
|
419
|
+
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 1)
|
|
420
|
+
if err != nil {
|
|
421
|
+
t.Fatal(err)
|
|
422
|
+
}
|
|
423
|
+
output := &bytes.Buffer{}
|
|
424
|
+
out := bufio.NewWriter(output)
|
|
425
|
+
h := &windowsBroker{port: port, nextKey: 1, watches: map[uintptr]*windowsDirectoryWatch{}, locations: map[windowsWatchLocation]uintptr{}, registrations: map[int64]map[uintptr]struct{}{}, out: out, encoder: json.NewEncoder(out)}
|
|
426
|
+
t.Cleanup(func() {
|
|
427
|
+
for key, watch := range h.watches {
|
|
428
|
+
h.retire(key, watch)
|
|
429
|
+
}
|
|
430
|
+
for len(h.watches) != 0 {
|
|
431
|
+
if _, err := h.completion(windows.INFINITE); err != nil {
|
|
432
|
+
t.Errorf("native cleanup: %v", err)
|
|
433
|
+
break
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
_ = windows.CloseHandle(port)
|
|
437
|
+
})
|
|
438
|
+
return h, output
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// windowsTestFrontier waits for an actual marker from every live registration,
|
|
442
|
+
// collecting all earlier native rows. Its deadline only refuses stalled delivery.
|
|
443
|
+
func windowsTestFrontier(t *testing.T, h *windowsBroker, output *bytes.Buffer, root, marker string) []map[string]any {
|
|
444
|
+
t.Helper()
|
|
445
|
+
if err := os.WriteFile(filepath.Join(root, marker), []byte("frontier"), 0600); err != nil {
|
|
446
|
+
t.Fatal(err)
|
|
447
|
+
}
|
|
448
|
+
rows := []map[string]any{}
|
|
449
|
+
seen := map[int64]bool{}
|
|
450
|
+
until := time.Now().Add(10 * time.Second)
|
|
451
|
+
for {
|
|
452
|
+
if err := h.drain(); err != nil {
|
|
453
|
+
t.Fatal(err)
|
|
454
|
+
}
|
|
455
|
+
if err := h.out.Flush(); err != nil {
|
|
456
|
+
t.Fatal(err)
|
|
457
|
+
}
|
|
458
|
+
batch := decodeWindowsTestRows(t, output)
|
|
459
|
+
rows = append(rows, batch...)
|
|
460
|
+
for _, row := range batch {
|
|
461
|
+
if row["filename"] == marker {
|
|
462
|
+
seen[int64(row["id"].(float64))] = true
|
|
463
|
+
}
|
|
464
|
+
if row["failed"] == true || row["gap"] == true {
|
|
465
|
+
t.Fatalf("unexpected lost coverage: %+v", row)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if len(seen) == len(h.registrations) {
|
|
469
|
+
return rows
|
|
470
|
+
}
|
|
471
|
+
if time.Now().After(until) {
|
|
472
|
+
t.Fatalf("native frontier stalled: %+v", rows)
|
|
473
|
+
}
|
|
474
|
+
time.Sleep(time.Millisecond)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// decodeWindowsTestRows reads actual encoded broker output and resets only the
|
|
479
|
+
// test's observation buffer, never backend state.
|
|
480
|
+
func decodeWindowsTestRows(t *testing.T, output *bytes.Buffer) []map[string]any {
|
|
481
|
+
t.Helper()
|
|
482
|
+
var rows []map[string]any
|
|
483
|
+
decoder := json.NewDecoder(bytes.NewReader(output.Bytes()))
|
|
484
|
+
for decoder.More() {
|
|
485
|
+
var row map[string]any
|
|
486
|
+
if err := decoder.Decode(&row); err != nil {
|
|
487
|
+
t.Fatal(err)
|
|
488
|
+
}
|
|
489
|
+
rows = append(rows, row)
|
|
490
|
+
}
|
|
491
|
+
output.Reset()
|
|
492
|
+
return rows
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// windowsTestRecord authors one literal FILE_NOTIFY_INFORMATION layout.
|
|
496
|
+
func windowsTestRecord(action uint32, name string) []byte {
|
|
497
|
+
units, _ := windows.UTF16FromString(name)
|
|
498
|
+
units = units[:len(units)-1]
|
|
499
|
+
buffer := make([]byte, 12+len(units)*2)
|
|
500
|
+
binary.LittleEndian.PutUint32(buffer[4:], action)
|
|
501
|
+
binary.LittleEndian.PutUint32(buffer[8:], uint32(len(units)*2))
|
|
502
|
+
for i, unit := range units {
|
|
503
|
+
binary.LittleEndian.PutUint16(buffer[12+i*2:], unit)
|
|
504
|
+
}
|
|
505
|
+
return buffer
|
|
506
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PluginContentIdentities } from "../../../plugin/internal/source/PluginContentIdentities";
|
|
1
2
|
import type { ITtscLoadedNativePlugin } from "../../../structures/internal/ITtscLoadedNativePlugin";
|
|
2
3
|
import type { ITtscParsedProjectConfig } from "../../../structures/internal/ITtscParsedProjectConfig";
|
|
3
4
|
import type { ITtscProjectInputSnapshot } from "../../../structures/internal/ITtscProjectInputSnapshot";
|
|
@@ -143,7 +144,7 @@ export declare namespace BuildExecution {
|
|
|
143
144
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The caught resolution failure is legitimate only for commands not requiring a project; ordinary compilation failures do not enter this bypass.
|
|
144
145
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain project-independent commands, existing-project behavior and null applicability; the catch comment states its deliberate broad premise.
|
|
145
146
|
* @evidence contracts/portability.md#os-neutral-implementation Invocation cwd is resolved natively and spawnNative receives executable/argv separately with platform-aware environment merging.
|
|
146
|
-
* @evidence contracts/performance.md#efficient-algorithms Flag selection scans forwarded argument/name text before config IO. An applicable lane includes native cwd/config resolution, binary
|
|
147
|
+
* @evidence contracts/performance.md#efficient-algorithms Flag selection scans forwarded argument/name text before config IO. An applicable lane includes native cwd/config resolution, binary lookup, argv/env composition without runtime capability work (#1726), complete captured output and diagnostic normalization; one requested command does not bound its native duration or output bytes.
|
|
147
148
|
*
|
|
148
149
|
* @evidenceExclude contracts/performance.md#reuse-equivalent-work A project probe and effectful terminal invocation establish no retained or shared cross-request result.
|
|
149
150
|
*
|
|
@@ -225,7 +226,7 @@ export declare namespace BuildExecution {
|
|
|
225
226
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Supported compiler options express the check contract without intercepting writes or replacing compiler APIs; spawn errors remain errors.
|
|
226
227
|
* @evidence contracts/common.md#meaningful-documentation Native prose explains the no-emit use and load-bearing argument precedence.
|
|
227
228
|
* @evidence contracts/portability.md#os-neutral-implementation spawnNative uses the selected executable, separate argv and project cwd; environment composition handles native variable aliases and output text decoding is explicit UTF-8.
|
|
228
|
-
* @evidence contracts/performance.md#efficient-algorithms One argv composition includes delegated option-presence classification, threading/isolation text and argument copies. Environment composition
|
|
229
|
+
* @evidence contracts/performance.md#efficient-algorithms One argv composition includes delegated option-presence classification, threading/isolation text and argument copies. Environment composition only layers variables; the compiler reads no Node selection, so no runtime capability probe precedes the launch (#1726). Complete capture/decoding and diagnostic normalization follow. Work/storage depend on argument/env/path/report bytes and native child work, without an asserted measured cost ranking.
|
|
229
230
|
*
|
|
230
231
|
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This executes the requested compiler pass; equivalence with other phases must be established by orchestration rather than caching a result here.
|
|
231
232
|
*
|
|
@@ -332,6 +333,7 @@ export declare namespace BuildExecution {
|
|
|
332
333
|
projectGuard: readonly string[];
|
|
333
334
|
};
|
|
334
335
|
cwd: string;
|
|
336
|
+
identities?: PluginContentIdentities.Store | undefined;
|
|
335
337
|
nativePlugins: ITtscLoadedNativePlugin[];
|
|
336
338
|
pluginConfigDir: string | undefined;
|
|
337
339
|
pluginSetupFailure: TtscBuildResult | undefined;
|
|
@@ -10,6 +10,7 @@ const createCanonicalTempDirectory_1 = require("../../../internal/createCanonica
|
|
|
10
10
|
const resolveNodeBinary_1 = require("../../../internal/resolveNodeBinary");
|
|
11
11
|
const hasProjectPluginEntries_1 = require("../../../plugin/internal/load/hasProjectPluginEntries");
|
|
12
12
|
const loadProjectPlugins_1 = require("../../../plugin/internal/load/loadProjectPlugins");
|
|
13
|
+
const PluginContentIdentities_1 = require("../../../plugin/internal/source/PluginContentIdentities");
|
|
13
14
|
const outputText_1 = require("../outputText");
|
|
14
15
|
const readProjectConfig_1 = require("../project/readProjectConfig");
|
|
15
16
|
const CompilerProjectSelection_1 = require("../project/CompilerProjectSelection");
|
|
@@ -68,14 +69,28 @@ var BuildExecution;
|
|
|
68
69
|
* Merge extra environment variables over `process.env`, always injecting
|
|
69
70
|
* `TTSC_NODE_BINARY` selected by the runtime capability owner. Explicit
|
|
70
71
|
* compatible overrides can select a different Node executable; this is not
|
|
71
|
-
* necessarily the current process binary.
|
|
72
|
+
* necessarily the current process binary. An invocation that loaded plugins
|
|
73
|
+
* passes its record store, so the runtime is proven from metadata instead of
|
|
74
|
+
* streamed again before every native spawn (#1723).
|
|
72
75
|
*/
|
|
73
|
-
function mergeEnv(extra, cwd
|
|
76
|
+
function mergeEnv(extra, cwd, identities, ...later) {
|
|
74
77
|
const env = SidecarEnvironment_1.SidecarEnvironment.merge(process.env, extra, ...later);
|
|
75
|
-
const node = (0, resolveNodeBinary_1.resolveNodeBinary)(env, cwd);
|
|
78
|
+
const node = (0, resolveNodeBinary_1.resolveNodeBinary)(env, cwd, identities);
|
|
76
79
|
SidecarEnvironment_1.SidecarEnvironment.write(env, "TTSC_NODE_BINARY", node);
|
|
77
80
|
return env;
|
|
78
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Merge extra environment variables over `process.env` for a spawn of the
|
|
84
|
+
* selected TypeScript-Go compiler itself.
|
|
85
|
+
*
|
|
86
|
+
* WARNING (#1726): the compiler never reads `TTSC_NODE_BINARY`, which only
|
|
87
|
+
* plugin sidecars consume (`mergeEnv`). Selecting it streamed the 85 MB Node
|
|
88
|
+
* executable twice and probed it on every launch, plugins or not, about 0.2 s
|
|
89
|
+
* of a 0.5 s build without plugins.
|
|
90
|
+
*/
|
|
91
|
+
function compilerEnv(extra) {
|
|
92
|
+
return SidecarEnvironment_1.SidecarEnvironment.merge(process.env, extra);
|
|
93
|
+
}
|
|
79
94
|
/**
|
|
80
95
|
* Build the environment for a native plugin spawn. Injects `TTSC_TSGO_BINARY`
|
|
81
96
|
* and `TTSC_TTSX_BINARY` alongside the base env from `mergeEnv`, plus
|
|
@@ -107,7 +122,7 @@ var BuildExecution;
|
|
|
107
122
|
: { TTSC_PLUGIN_CONFIG_DIR: execution.pluginConfigDir }),
|
|
108
123
|
TTSC_TTSX_BINARY: process.env.TTSC_TTSX_BINARY ??
|
|
109
124
|
node_path_1.default.join(__dirname, "..", "..", "..", "launcher", "ttsx.js"),
|
|
110
|
-
}, execution.projectRoot, extra,
|
|
125
|
+
}, execution.projectRoot, execution.identities, extra,
|
|
111
126
|
// The invocation's resolved compiler owns the final environment layer.
|
|
112
127
|
{ TTSC_TSGO_BINARY: execution.tsgo.binary });
|
|
113
128
|
return composeNativePluginEnv(env, extra, execution, SidecarEnvironment_1.SidecarEnvironment.read(env, "TTSC_NODE_BINARY"), plugin, tsgoArgs);
|
|
@@ -353,7 +368,7 @@ var BuildExecution;
|
|
|
353
368
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The caught resolution failure is legitimate only for commands not requiring a project; ordinary compilation failures do not enter this bypass.
|
|
354
369
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain project-independent commands, existing-project behavior and null applicability; the catch comment states its deliberate broad premise.
|
|
355
370
|
* @evidence contracts/portability.md#os-neutral-implementation Invocation cwd is resolved natively and spawnNative receives executable/argv separately with platform-aware environment merging.
|
|
356
|
-
* @evidence contracts/performance.md#efficient-algorithms Flag selection scans forwarded argument/name text before config IO. An applicable lane includes native cwd/config resolution, binary
|
|
371
|
+
* @evidence contracts/performance.md#efficient-algorithms Flag selection scans forwarded argument/name text before config IO. An applicable lane includes native cwd/config resolution, binary lookup, argv/env composition without runtime capability work (#1726), complete captured output and diagnostic normalization; one requested command does not bound its native duration or output bytes.
|
|
357
372
|
*
|
|
358
373
|
* @evidenceExclude contracts/performance.md#reuse-equivalent-work A project probe and effectful terminal invocation establish no retained or shared cross-request result.
|
|
359
374
|
*
|
|
@@ -382,7 +397,7 @@ var BuildExecution;
|
|
|
382
397
|
const tsgo = (0, resolveTsgo_1.resolveTsgo)({ ...options, cwd });
|
|
383
398
|
const res = (0, spawnNative_1.spawnNative)(tsgo.binary, [...(options.passthrough ?? [])], {
|
|
384
399
|
cwd,
|
|
385
|
-
env:
|
|
400
|
+
env: compilerEnv(options.env),
|
|
386
401
|
encoding: "utf8",
|
|
387
402
|
});
|
|
388
403
|
if (res.error) {
|
|
@@ -612,7 +627,7 @@ var BuildExecution;
|
|
|
612
627
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Supported compiler options express the check contract without intercepting writes or replacing compiler APIs; spawn errors remain errors.
|
|
613
628
|
* @evidence contracts/common.md#meaningful-documentation Native prose explains the no-emit use and load-bearing argument precedence.
|
|
614
629
|
* @evidence contracts/portability.md#os-neutral-implementation spawnNative uses the selected executable, separate argv and project cwd; environment composition handles native variable aliases and output text decoding is explicit UTF-8.
|
|
615
|
-
* @evidence contracts/performance.md#efficient-algorithms One argv composition includes delegated option-presence classification, threading/isolation text and argument copies. Environment composition
|
|
630
|
+
* @evidence contracts/performance.md#efficient-algorithms One argv composition includes delegated option-presence classification, threading/isolation text and argument copies. Environment composition only layers variables; the compiler reads no Node selection, so no runtime capability probe precedes the launch (#1726). Complete capture/decoding and diagnostic normalization follow. Work/storage depend on argument/env/path/report bytes and native child work, without an asserted measured cost ranking.
|
|
616
631
|
*
|
|
617
632
|
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This executes the requested compiler pass; equivalence with other phases must be established by orchestration rather than caching a result here.
|
|
618
633
|
*
|
|
@@ -631,7 +646,7 @@ var BuildExecution;
|
|
|
631
646
|
...CompilerProjectSelection_1.CompilerProjectSelection.readGuard(execution.compilerSelection, options.passthrough),
|
|
632
647
|
], {
|
|
633
648
|
cwd: execution.compilerSelection.compilerArgsCwd,
|
|
634
|
-
env:
|
|
649
|
+
env: compilerEnv(options.env),
|
|
635
650
|
encoding: "utf8",
|
|
636
651
|
});
|
|
637
652
|
if (res.error) {
|
|
@@ -655,7 +670,7 @@ var BuildExecution;
|
|
|
655
670
|
* its single emitting run; unsupported or unstable relations stay unknown.
|
|
656
671
|
*/
|
|
657
672
|
function runTsgoBuild(execution, options, args) {
|
|
658
|
-
const env =
|
|
673
|
+
const env = compilerEnv(options.env);
|
|
659
674
|
const userOptions = (0, readEffectiveCompilerOptions_1.readEffectiveCompilerOptions)(execution.project, options.passthrough, execution.tsgo.binary, env, execution.compilerSelection.compilerArgsCwd);
|
|
660
675
|
const userListedEmitted = userOptions?.("listEmittedFiles") === true;
|
|
661
676
|
const run = (commandArgs) => {
|
|
@@ -901,16 +916,28 @@ var BuildExecution;
|
|
|
901
916
|
const tsgo = (0, resolveTsgo_1.resolveTsgo)({ ...options, cwd: projectRoot });
|
|
902
917
|
let pluginSetupFailure;
|
|
903
918
|
let nativePlugins = [];
|
|
919
|
+
// Opened only for an invocation that loads plugins, so a project without
|
|
920
|
+
// any never gains a cache directory; the load and every later native spawn
|
|
921
|
+
// of this invocation prove their unchanged inputs through it (#1722, #1723).
|
|
922
|
+
let identities;
|
|
904
923
|
try {
|
|
905
924
|
if (compilerSelection.inspectionError === undefined &&
|
|
906
925
|
(0, hasProjectPluginEntries_1.hasProjectPluginEntries)(project, options.plugins)) {
|
|
926
|
+
const cacheDir = options.cacheDir ??
|
|
927
|
+
SidecarEnvironment_1.SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR");
|
|
928
|
+
const env = (0, inheritedSidecarEnv_1.inheritedSidecarEnv)(options.env, options.binary);
|
|
929
|
+
identities = PluginContentIdentities_1.PluginContentIdentities.open({
|
|
930
|
+
projectRoot,
|
|
931
|
+
cacheDir,
|
|
932
|
+
env,
|
|
933
|
+
});
|
|
907
934
|
nativePlugins = (0, loadProjectPlugins_1.loadProjectPlugins)({
|
|
908
935
|
binary: (0, resolveBinary_1.resolveBinary)(options) ?? "",
|
|
909
|
-
cacheDir
|
|
910
|
-
SidecarEnvironment_1.SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
|
|
936
|
+
cacheDir,
|
|
911
937
|
cwd,
|
|
912
938
|
entries: options.plugins,
|
|
913
|
-
env
|
|
939
|
+
env,
|
|
940
|
+
identities,
|
|
914
941
|
onWatchInputs: options.onWatchInputs,
|
|
915
942
|
pluginConfigDir: options.pluginConfigDir,
|
|
916
943
|
projectRoot,
|
|
@@ -932,6 +959,7 @@ var BuildExecution;
|
|
|
932
959
|
return {
|
|
933
960
|
compilerSelection,
|
|
934
961
|
cwd,
|
|
962
|
+
...(identities === undefined ? {} : { identities }),
|
|
935
963
|
nativePlugins,
|
|
936
964
|
pluginConfigDir: (0, resolvePluginConfigDir_1.resolvePluginConfigDir)({
|
|
937
965
|
cwd,
|