ttsc 0.24.0 → 0.25.0
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/LICENSE +21 -21
- package/README.md +66 -63
- package/cmd/ttscgraph/serve_answers_a_project_that_implements_its_own_ambient_declaration_test.go +69 -0
- package/go.sum +22 -22
- package/internal/graph/assigned_implementations_of_declaration_file_symbols_leave_no_edge_source_test.go +84 -0
- package/internal/graph/assigned_implementations_of_node_modules_symbols_leave_no_edge_source_test.go +85 -0
- package/internal/graph/edges.go +15 -1
- package/internal/lspserver/lsp_native_plugin_source.go +5 -6
- package/internal/lspserver/lsp_native_resident.go +17 -24
- package/lib/TtscService.d.ts +1 -6
- package/lib/TtscService.js +2 -2
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.d.ts +26 -0
- package/lib/compiler/internal/captureProcessOutput.js +92 -0
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -0
- package/lib/compiler/internal/compileProjectInMemory.js +4 -7
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/residentCheckProcess.d.ts +0 -2
- package/lib/compiler/internal/residentCheckProcess.js +1 -15
- package/lib/compiler/internal/residentCheckProcess.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +0 -6
- package/lib/compiler/internal/residentTransformProcess.js +0 -25
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +14 -6
- package/lib/compiler/internal/spawnNative.js +32 -11
- package/lib/compiler/internal/spawnNative.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +1 -5
- package/lib/compiler/internal/startResidentTransform.js +1 -3
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.d.ts +2 -1
- package/lib/launcher/internal/prepareExecution.js +269 -23
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/registerRuntimeHooks.js +6 -1
- package/lib/launcher/internal/registerRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +4 -9
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -55
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +25 -2
- package/lib/launcher/internal/runtimeHooks.js +344 -69
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.js +31 -3
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/descriptorProcessFailure.d.ts +23 -19
- package/lib/plugin/internal/descriptorProcessFailure.js +40 -51
- package/lib/plugin/internal/descriptorProcessFailure.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +12 -0
- package/lib/plugin/internal/loadProjectPlugins.js +77 -28
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/package.json +8 -8
- package/shim/ast/extra-shim.json +4 -4
- package/shim/ast/go.mod +14 -14
- package/shim/ast/go.sum +20 -20
- package/shim/astnav/go.mod +14 -14
- package/shim/astnav/go.sum +20 -20
- package/shim/bundled/extra-shim.json +1 -1
- package/shim/bundled/go.mod +5 -5
- package/shim/checker/extra-shim.json +31 -31
- package/shim/checker/go.mod +5 -5
- package/shim/compiler/extra-shim.json +4 -4
- package/shim/compiler/go.mod +14 -14
- package/shim/compiler/go.sum +22 -22
- package/shim/core/extra-shim.json +4 -4
- package/shim/core/go.mod +5 -5
- package/shim/diagnosticwriter/go.mod +5 -5
- package/shim/lsp/extra-shim.json +4 -4
- package/shim/lsp/go.mod +16 -16
- package/shim/lsp/go.sum +26 -26
- package/shim/parser/extra-shim.json +4 -4
- package/shim/parser/go.mod +5 -5
- package/shim/printer/go.mod +14 -14
- package/shim/printer/go.sum +22 -22
- package/shim/scanner/extra-shim.json +4 -4
- package/shim/scanner/go.mod +5 -5
- package/shim/transformers/go.mod +14 -14
- package/shim/transformers/go.sum +20 -20
- package/shim/tsoptions/extra-shim.json +4 -4
- package/shim/tsoptions/go.mod +5 -5
- package/shim/tspath/extra-shim.json +4 -4
- package/shim/tspath/go.mod +5 -5
- package/shim/vfs/cachedvfs/extra-shim.json +4 -4
- package/shim/vfs/cachedvfs/go.mod +5 -5
- package/shim/vfs/extra-shim.json +4 -4
- package/shim/vfs/go.mod +5 -5
- package/shim/vfs/osvfs/extra-shim.json +4 -4
- package/shim/vfs/osvfs/go.mod +5 -5
- package/src/TtscService.ts +8 -20
- package/src/compiler/internal/captureProcessOutput.ts +96 -0
- package/src/compiler/internal/compileProjectInMemory.ts +7 -7
- package/src/compiler/internal/residentCheckProcess.ts +1 -21
- package/src/compiler/internal/residentTransformProcess.ts +0 -42
- package/src/compiler/internal/spawnNative.ts +39 -18
- package/src/compiler/internal/startResidentTransform.ts +1 -12
- package/src/launcher/internal/prepareExecution.ts +297 -26
- package/src/launcher/internal/registerRuntimeHooks.ts +11 -2
- package/src/launcher/internal/runTtscserver.ts +4 -9
- package/src/launcher/internal/runTtsx.ts +2 -78
- package/src/launcher/internal/runtimeHooks.ts +385 -82
- package/src/plugin/internal/buildSourcePlugin.ts +36 -3
- package/src/plugin/internal/descriptorProcessFailure.ts +34 -65
- package/src/plugin/internal/loadProjectPlugins.ts +78 -39
- package/tools/gen_shims/go.mod +14 -14
- package/tools/gen_shims/go.sum +12 -12
- package/tools/shim_audit/go.mod +10 -10
- package/tools/shim_audit/go.sum +8 -8
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Jeongho Nam
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeongho Nam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,8 +8,9 @@ A `typescript-go` toolchain for compiler-powered plugins and type-safe execution
|
|
|
8
8
|
|
|
9
9
|
- **`ttsc`**: build, check, and transform.
|
|
10
10
|
- **`ttsx`**: execute TypeScript with type checking.
|
|
11
|
-
- [**`@ttsc/graph`**](https://github.com/samchon/ttsc/tree/master/packages/graph): MCP code graph that reduces agent token usage.
|
|
12
11
|
- [**`@ttsc/lint`**](https://github.com/samchon/ttsc/tree/master/packages/lint): lint violations as compiler errors.
|
|
12
|
+
- [**`@ttsc/evidence`**](https://github.com/samchon/ttsc/tree/master/packages/evidence): 100% requirement coverage, or the build fails.
|
|
13
|
+
- [**`@ttsc/graph`**](https://github.com/samchon/ttsc/tree/master/packages/graph): compiler knowledge graph that cuts agent tokens by 92%.
|
|
13
14
|
- **plugin support**: compiler-powered libraries, such as `typia`.
|
|
14
15
|
|
|
15
16
|
## Setup
|
|
@@ -35,94 +36,95 @@ That covers the CLI. The integrations each have a short guide:
|
|
|
35
36
|
- [`@ttsc/metro`](https://ttsc.dev/docs/setup/metro): React Native and Expo.
|
|
36
37
|
- [`@ttsc/vscode`](https://ttsc.dev/docs/setup/vscode): live editor diagnostics.
|
|
37
38
|
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
Your coding agent answers from the compiler, spending roughly 90% fewer tokens.
|
|
39
|
+
## Lint
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Lint and format inside the type-check you already run. 720+ rules across 21 families, plus a formatter that reproduces Prettier's output.
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
```ts
|
|
44
|
+
// src/index.ts
|
|
45
|
+
var x: number = 3;
|
|
46
|
+
let y: number = 4;
|
|
47
|
+
const z: string = 5;
|
|
48
|
+
```
|
|
45
49
|
|
|
46
50
|
```bash
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
$ npx ttsc --noEmit
|
|
52
|
+
src/index.ts:3:7 - error TS2322: Type 'number' is not assignable to type 'string'.
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"mcpServers": {
|
|
53
|
-
"ttsc-graph": {
|
|
54
|
-
"command": "npx",
|
|
55
|
-
"args": ["-y", "@ttsc/graph"]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
54
|
+
3 const z: string = 5;
|
|
55
|
+
~
|
|
60
56
|
|
|
61
|
-
|
|
57
|
+
src/index.ts:2:5 - error TS17397: [prefer-const] Use const instead of let.
|
|
62
58
|
|
|
63
|
-
|
|
59
|
+
2 let y: number = 4;
|
|
60
|
+
~~~~~~~~~~~~~
|
|
64
61
|
|
|
65
|
-
|
|
62
|
+
src/index.ts:1:1 - error TS11966: [no-var] Unexpected var, use let or const instead.
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
1 var x: number = 3;
|
|
65
|
+
~~~~~~~~~~~~~~~~~~
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
Found 3 errors in the same file, starting at: src/index.ts:3
|
|
68
|
+
```
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
Type errors and lint violations arrive in one stream, so the CI step that already runs `ttsc --noEmit` gates lint with no second job and no second parse. On vscode's 6,093 files the rules take 73 ms inside that check, where ESLint spends 66.7 s as its own command.
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
npm install -D @ttsc/lint
|
|
75
|
-
```
|
|
72
|
+
`npx ttsc fix` applies autofixes and formatting; `npx ttsc format` only formats. Rules and every `format` key are in the [Lint and Format guide](https://ttsc.dev/docs/lint).
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} satisfies ITtscLintConfig;
|
|
74
|
+
## Evidence Graph
|
|
75
|
+
|
|
76
|
+
Your spec becomes a compile error, so requirement coverage is 100% or the build does not pass. An agent can still lie, but it cannot lie by omission.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
/**
|
|
80
|
+
* @evidence docs/discount.md#coupon-stacking
|
|
81
|
+
* States the per-issuer stacking limit
|
|
82
|
+
* this section defines, in the buyer's words.
|
|
83
|
+
* @evidence POST:/orders/{orderId}/coupons
|
|
84
|
+
* Explains the rejection this endpoint returns
|
|
85
|
+
* for an over-stacked coupon set.
|
|
86
|
+
* @evidence {@link hooks.useCouponStacking} Renders the limit this hook resolves.
|
|
87
|
+
*/
|
|
88
|
+
export function CouponStackingNotice(props: IProps): JSX.Element;
|
|
93
89
|
```
|
|
94
90
|
|
|
95
|
-
|
|
91
|
+
`@evidence <target> <reason>` names one unit of the spec and why this declaration answers for it. A target is a document section, an API operation, a database schema model, or a TypeScript symbol as an inline link.
|
|
96
92
|
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
```bash
|
|
94
|
+
$ npx ttsc
|
|
95
|
+
error TS16411: [evidence/graph] Missing acknowledgement for 'docs/discount.md#coupon-stacking'
|
|
96
|
+
(Markdown H2 'Coupon Stacking' at docs/discount.md:3)
|
|
97
|
+
in Claim 1 reference 1 (markdown, symbols: h2, h3).
|
|
98
|
+
|
|
99
|
+
Use @evidence on a selected typescript host or @evidenceExclude on an eligible carrier.
|
|
100
|
+
|
|
101
|
+
Found 3 errors.
|
|
101
102
|
```
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
$ npx ttsc --noEmit
|
|
105
|
-
src/index.ts:1:1 - error TS11966: [no-var] Unexpected var, use let or const instead.
|
|
104
|
+
Without those tags, the build fails once per obligation, because one reference never covers another. An AI coding agent has to clear them to finish, and clearing them means citing each target and writing down why its code answers for it.
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
~~~
|
|
106
|
+

|
|
109
107
|
|
|
110
|
-
|
|
108
|
+
## Compiler Knowledge Graph
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
~~~~~
|
|
110
|
+
Your coding agent answers from the compiler instead of grepping and re-reading files.
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"ttsc-graph": {
|
|
116
|
+
"command": "npx",
|
|
117
|
+
"args": ["-y", "@ttsc/graph"]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
116
121
|
```
|
|
117
122
|
|
|
118
|
-
|
|
123
|
+
One typed MCP tool over a graph the type checker resolved: what calls what, what a change would touch, where to start reading. Answers carry names, signatures, edges, and spans, never file bodies, so a large repository cannot inflate the response.
|
|
119
124
|
|
|
120
|
-
|
|
121
|
-
npx ttsc fix # lint autofixes + format edits
|
|
122
|
-
npx ttsc format # format edits only, never changes behavior
|
|
123
|
-
```
|
|
125
|
+
Across 64 measured question and model pairs, the median answer costs 92% fewer tokens and 95% fewer tool calls than the same agent with no MCP. The design and the comparators are in [`@ttsc/graph`](https://github.com/samchon/ttsc/tree/master/packages/graph).
|
|
124
126
|
|
|
125
|
-
|
|
127
|
+

|
|
126
128
|
|
|
127
129
|
## Plugins
|
|
128
130
|
|
|
@@ -148,6 +150,7 @@ export const isStringArray = (() => {
|
|
|
148
150
|
Utility plugins shipped in this repository:
|
|
149
151
|
|
|
150
152
|
- [`@ttsc/banner`](https://github.com/samchon/ttsc/tree/master/packages/banner): adds `@packageDocumentation` JSDoc banners.
|
|
153
|
+
- [`@ttsc/evidence`](https://github.com/samchon/ttsc/tree/master/packages/evidence): turns a requirement into a compile error until code, tests, or docs acknowledge it by name.
|
|
151
154
|
- [`@ttsc/graph`](https://github.com/samchon/ttsc/tree/master/packages/graph): MCP server exposing a checker-resolved code graph to coding agents.
|
|
152
155
|
- [`@ttsc/lint`](https://github.com/samchon/ttsc/tree/master/packages/lint): lints and formats TypeScript source.
|
|
153
156
|
- [`@ttsc/paths`](https://github.com/samchon/ttsc/tree/master/packages/paths): rewrites source path aliases so JS and declaration emit receive relative imports.
|
package/cmd/ttscgraph/serve_answers_a_project_that_implements_its_own_ambient_declaration_test.go
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestServeAnswersAProjectThatImplementsItsOwnAmbientDeclaration verifies shard
|
|
12
|
+
// assembly succeeds for a project that declares an ambient global in its own
|
|
13
|
+
// `.d.ts` and assigns the implementation elsewhere.
|
|
14
|
+
//
|
|
15
|
+
// Shard partition indexes edge sources by non-external node, and external nodes
|
|
16
|
+
// live in a non-source shard that may own no edges. An edge leaving one is
|
|
17
|
+
// therefore not a wrong fact but an unassemblable snapshot: the session refused
|
|
18
|
+
// the whole transaction with "edge source node is absent from shard facts", so
|
|
19
|
+
// every request failed before it ran while `dump`, which applies no such check,
|
|
20
|
+
// still emitted the graph.
|
|
21
|
+
//
|
|
22
|
+
// 1. Serve a project whose `src/globals.d.ts` declares `var patched` and whose
|
|
23
|
+
// `src/index.ts` assigns an arrow function to it.
|
|
24
|
+
// 2. Request one negotiated shard snapshot.
|
|
25
|
+
// 3. Assert the response carries a complete transaction and no error.
|
|
26
|
+
func TestServeAnswersAProjectThatImplementsItsOwnAmbientDeclaration(t *testing.T) {
|
|
27
|
+
root := t.TempDir()
|
|
28
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
29
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true },
|
|
30
|
+
"include": ["src"]
|
|
31
|
+
}`)
|
|
32
|
+
writeGraphFile(t, filepath.Join(root, "src", "globals.d.ts"), `declare global {
|
|
33
|
+
var patched: (message: string) => void;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
`)
|
|
37
|
+
writeGraphFile(t, filepath.Join(root, "src", "index.ts"), `export function helper(): void {}
|
|
38
|
+
patched = (message: string): void => {
|
|
39
|
+
helper();
|
|
40
|
+
};
|
|
41
|
+
`)
|
|
42
|
+
|
|
43
|
+
input := strings.NewReader("{\"id\":1,\"graphSnapshotVersion\":1}\n")
|
|
44
|
+
var output bytes.Buffer
|
|
45
|
+
if code := serveSnapshots(input, &output, root, "tsconfig.json"); code != 0 {
|
|
46
|
+
t.Fatalf("serveSnapshots exited %d: %s", code, output.String())
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var initial serveResponse
|
|
50
|
+
if err := json.NewDecoder(&output).Decode(&initial); err != nil {
|
|
51
|
+
t.Fatal(err)
|
|
52
|
+
}
|
|
53
|
+
if initial.Error != "" {
|
|
54
|
+
t.Fatalf("shard assembly refused a project that implements its own ambient declaration: %s", initial.Error)
|
|
55
|
+
}
|
|
56
|
+
if initial.Mode != serveModeInitial || !initial.Changed || initial.Dump != nil || initial.Snapshot == nil {
|
|
57
|
+
t.Fatalf("negotiated initial response: %#v", initial)
|
|
58
|
+
}
|
|
59
|
+
if len(initial.Snapshot.Manifest) == 0 ||
|
|
60
|
+
len(initial.Snapshot.Upserts) != len(initial.Snapshot.Manifest) ||
|
|
61
|
+
len(initial.Snapshot.Deletes) != 0 {
|
|
62
|
+
t.Fatalf(
|
|
63
|
+
"initial transaction is not complete: manifest=%d upserts=%d deletes=%d",
|
|
64
|
+
len(initial.Snapshot.Manifest),
|
|
65
|
+
len(initial.Snapshot.Upserts),
|
|
66
|
+
len(initial.Snapshot.Deletes),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
package/go.sum
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
|
|
2
|
-
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
|
3
|
-
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
4
|
-
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
5
|
-
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
|
6
|
-
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
7
|
-
github.com/microsoft/typescript-go v0.0.0-20260429010842-56ab4af42157 h1:llOMPhKDiQ+UEJiAaQuuWaJOTg5V7tNNTEf9JPWoYSY=
|
|
8
|
-
github.com/microsoft/typescript-go v0.0.0-20260429010842-56ab4af42157/go.mod h1:m8YA0PMC7ti0GW0RI05D6fEcjQeu98XVS+FWF+VDW2k=
|
|
9
|
-
github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA=
|
|
10
|
-
github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0=
|
|
11
|
-
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
|
12
|
-
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
|
13
|
-
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
|
14
|
-
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
|
15
|
-
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
16
|
-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
17
|
-
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
|
18
|
-
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
19
|
-
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
|
20
|
-
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
|
21
|
-
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
|
22
|
-
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
|
1
|
+
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
|
|
2
|
+
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
|
3
|
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
4
|
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
5
|
+
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
|
6
|
+
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
7
|
+
github.com/microsoft/typescript-go v0.0.0-20260429010842-56ab4af42157 h1:llOMPhKDiQ+UEJiAaQuuWaJOTg5V7tNNTEf9JPWoYSY=
|
|
8
|
+
github.com/microsoft/typescript-go v0.0.0-20260429010842-56ab4af42157/go.mod h1:m8YA0PMC7ti0GW0RI05D6fEcjQeu98XVS+FWF+VDW2k=
|
|
9
|
+
github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA=
|
|
10
|
+
github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0=
|
|
11
|
+
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
|
12
|
+
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
|
13
|
+
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
|
14
|
+
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
|
15
|
+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|
16
|
+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
17
|
+
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
|
18
|
+
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
19
|
+
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
|
20
|
+
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
|
21
|
+
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
|
22
|
+
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// ambientGlobalFixtureTSConfig compiles a project that declares an ambient
|
|
11
|
+
// global in its own `.d.ts` and assigns the implementation in a sibling source.
|
|
12
|
+
// Both files are named because a global augmentation only reaches the program
|
|
13
|
+
// when its declaration file is an input.
|
|
14
|
+
const ambientGlobalFixtureTSConfig = `{
|
|
15
|
+
"compilerOptions": {
|
|
16
|
+
"target": "ES2022",
|
|
17
|
+
"module": "commonjs",
|
|
18
|
+
"strict": true,
|
|
19
|
+
"rootDir": "src",
|
|
20
|
+
"outDir": "dist"
|
|
21
|
+
},
|
|
22
|
+
"files": ["src/globals.d.ts", "src/main.ts"]
|
|
23
|
+
}
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
// TestAssignedImplementationsOfDeclarationFileSymbolsLeaveNoEdgeSource verifies
|
|
27
|
+
// that assigning a function to a symbol declared in a declaration file records
|
|
28
|
+
// no edge leaving the external boundary leaf that symbol enters the graph as.
|
|
29
|
+
//
|
|
30
|
+
// An external leaf has no outgoing side: the graph never walks a dependency's
|
|
31
|
+
// internals, and no shard may hold an edge whose source is one — an external
|
|
32
|
+
// node is owned by the non-source external shard, which is forbidden to own
|
|
33
|
+
// edges. Attributing the assigned body to it therefore did not merely misplace
|
|
34
|
+
// facts (with evidence offsets from the implementation read against the
|
|
35
|
+
// declaration file); it produced a snapshot `ttscgraph serve` could not
|
|
36
|
+
// assemble, so every request against such a project failed. The calls the body
|
|
37
|
+
// makes are not lost by refusing it: the module that runs the assignment owns
|
|
38
|
+
// them.
|
|
39
|
+
//
|
|
40
|
+
// 1. Compile a fixture whose `src/globals.d.ts` declares `var patched` in a
|
|
41
|
+
// global augmentation and whose `src/main.ts` assigns an arrow function to
|
|
42
|
+
// it that calls a local `helper`.
|
|
43
|
+
// 2. Build the graph.
|
|
44
|
+
// 3. Assert no edge leaves an external node, and that `main.ts`'s module node
|
|
45
|
+
// still owns the value-call edge to `helper`.
|
|
46
|
+
func TestAssignedImplementationsOfDeclarationFileSymbolsLeaveNoEdgeSource(t *testing.T) {
|
|
47
|
+
root := t.TempDir()
|
|
48
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), ambientGlobalFixtureTSConfig)
|
|
49
|
+
writeFile(t, filepath.Join(root, "src", "globals.d.ts"), `declare global {
|
|
50
|
+
var patched: (message: string) => void;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
53
|
+
`)
|
|
54
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `export function helper(): void {}
|
|
55
|
+
patched = (message: string): void => {
|
|
56
|
+
helper();
|
|
57
|
+
};
|
|
58
|
+
`)
|
|
59
|
+
|
|
60
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
61
|
+
if err != nil {
|
|
62
|
+
t.Fatal(err)
|
|
63
|
+
}
|
|
64
|
+
if len(diags) != 0 {
|
|
65
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
66
|
+
}
|
|
67
|
+
defer func() { _ = prog.Close() }()
|
|
68
|
+
|
|
69
|
+
graph := Build(prog)
|
|
70
|
+
mainPath := sourceFile(t, prog, "main.ts").FileName()
|
|
71
|
+
|
|
72
|
+
for _, edge := range graph.Edges {
|
|
73
|
+
node, ok := graph.Nodes[edge.From]
|
|
74
|
+
if ok && node.External {
|
|
75
|
+
t.Fatalf("edge leaves the external boundary: %s -> %s (%s), source declared in %s", edge.From, edge.To, edge.Kind, node.File)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module := moduleID(mainPath)
|
|
80
|
+
helper := nodeID(mainPath, "helper", NodeFunction)
|
|
81
|
+
if !hasEdge(graph, module, helper, EdgeValueCall) {
|
|
82
|
+
t.Fatalf("the assigned body's call to helper is not owned by the running module; edges: %v", graph.Edges)
|
|
83
|
+
}
|
|
84
|
+
}
|
package/internal/graph/assigned_implementations_of_node_modules_symbols_leave_no_edge_source_test.go
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// nodeModulesGlobalFixtureTSConfig compiles a project whose ambient global is
|
|
11
|
+
// declared by a dependency's raw `.ts` source. Both inputs are named because
|
|
12
|
+
// the declaring file is reached by neither an import nor a glob, and no
|
|
13
|
+
// `rootDir` or `outDir` is set because a `node_modules` input under one would
|
|
14
|
+
// be an input outside the root.
|
|
15
|
+
const nodeModulesGlobalFixtureTSConfig = `{
|
|
16
|
+
"compilerOptions": {
|
|
17
|
+
"target": "ES2022",
|
|
18
|
+
"module": "commonjs",
|
|
19
|
+
"strict": true
|
|
20
|
+
},
|
|
21
|
+
"files": ["node_modules/dep/globals.ts", "src/main.ts"]
|
|
22
|
+
}
|
|
23
|
+
`
|
|
24
|
+
|
|
25
|
+
// TestAssignedImplementationsOfNodeModulesSymbolsLeaveNoEdgeSource verifies the
|
|
26
|
+
// other half of the external boundary: a symbol declared under `node_modules`
|
|
27
|
+
// gains no outgoing edge from an assignment either.
|
|
28
|
+
//
|
|
29
|
+
// `IsWorkspaceSourceFile` refuses a file for two disjoint reasons, and only one
|
|
30
|
+
// of them is being a declaration file. A dependency that ships raw TypeScript
|
|
31
|
+
// is not a declaration file and still owns no facts in this graph, so an
|
|
32
|
+
// assignment to something it declares must be refused by the same rule. Without
|
|
33
|
+
// this case the fix is pinned on one branch of the classifier and a change to
|
|
34
|
+
// the other would pass.
|
|
35
|
+
//
|
|
36
|
+
// 1. Compile a fixture whose `node_modules/dep/globals.ts` declares `var
|
|
37
|
+
// patched` in a global augmentation and whose `src/main.ts` assigns an arrow
|
|
38
|
+
// function to it that calls a local `helper`.
|
|
39
|
+
// 2. Build the graph.
|
|
40
|
+
// 3. Assert no edge leaves an external node, and that `main.ts`'s module node
|
|
41
|
+
// still owns the value-call edge to `helper`.
|
|
42
|
+
func TestAssignedImplementationsOfNodeModulesSymbolsLeaveNoEdgeSource(t *testing.T) {
|
|
43
|
+
root := t.TempDir()
|
|
44
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), nodeModulesGlobalFixtureTSConfig)
|
|
45
|
+
writeFile(t, filepath.Join(root, "node_modules", "dep", "package.json"), `{
|
|
46
|
+
"name": "dep",
|
|
47
|
+
"version": "1.0.0"
|
|
48
|
+
}
|
|
49
|
+
`)
|
|
50
|
+
writeFile(t, filepath.Join(root, "node_modules", "dep", "globals.ts"), `declare global {
|
|
51
|
+
var patched: (message: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
54
|
+
`)
|
|
55
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `export function helper(): void {}
|
|
56
|
+
patched = (message: string): void => {
|
|
57
|
+
helper();
|
|
58
|
+
};
|
|
59
|
+
`)
|
|
60
|
+
|
|
61
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
62
|
+
if err != nil {
|
|
63
|
+
t.Fatal(err)
|
|
64
|
+
}
|
|
65
|
+
if len(diags) != 0 {
|
|
66
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
67
|
+
}
|
|
68
|
+
defer func() { _ = prog.Close() }()
|
|
69
|
+
|
|
70
|
+
graph := Build(prog)
|
|
71
|
+
mainPath := sourceFile(t, prog, "main.ts").FileName()
|
|
72
|
+
|
|
73
|
+
for _, edge := range graph.Edges {
|
|
74
|
+
node, ok := graph.Nodes[edge.From]
|
|
75
|
+
if ok && node.External {
|
|
76
|
+
t.Fatalf("edge leaves the external boundary: %s -> %s (%s), source declared in %s", edge.From, edge.To, edge.Kind, node.File)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module := moduleID(mainPath)
|
|
81
|
+
helper := nodeID(mainPath, "helper", NodeFunction)
|
|
82
|
+
if !hasEdge(graph, module, helper, EdgeValueCall) {
|
|
83
|
+
t.Fatalf("the assigned body's call to helper is not owned by the running module; edges: %v", graph.Edges)
|
|
84
|
+
}
|
|
85
|
+
}
|
package/internal/graph/edges.go
CHANGED
|
@@ -604,6 +604,20 @@ func (g *Graph) callsWithin(checker *shimchecker.Checker, from string, node *shi
|
|
|
604
604
|
})
|
|
605
605
|
}
|
|
606
606
|
|
|
607
|
+
// assignedFunctionTarget returns the graph node an `x.y = function` assignment
|
|
608
|
+
// implements, or "" when the assignment implements no node this graph owns.
|
|
609
|
+
//
|
|
610
|
+
// A declaration outside the workspace is not such a node. `globalThis.assert =
|
|
611
|
+
// …` against a `declare global { function assert(…) }`, or a patched member of
|
|
612
|
+
// a dependency's `@types`, resolves to a symbol whose only declaration is in a
|
|
613
|
+
// declaration file — which has no body, holds no facts, and enters the graph as
|
|
614
|
+
// an external boundary leaf. Re-attributing the assigned body to it moved the
|
|
615
|
+
// implementation's calls and type references onto a node that never ran them,
|
|
616
|
+
// under offsets read against the wrong file: `recordImplementation` already
|
|
617
|
+
// refuses an external node, so the evidence file stayed unset and the spans
|
|
618
|
+
// pointed at neither the declaration nor the implementation. And the edge
|
|
619
|
+
// itself was unassemblable, so every request against such a project failed at
|
|
620
|
+
// shard partition.
|
|
607
621
|
func (g *Graph) assignedFunctionTarget(checker *shimchecker.Checker, from string, node *shimast.Node) string {
|
|
608
622
|
binary := node.AsBinaryExpression()
|
|
609
623
|
if binary == nil ||
|
|
@@ -614,7 +628,7 @@ func (g *Graph) assignedFunctionTarget(checker *shimchecker.Checker, from string
|
|
|
614
628
|
return ""
|
|
615
629
|
}
|
|
616
630
|
target := g.resolve(checker, binary.Left)
|
|
617
|
-
if target == nil || target.Symbol == nil {
|
|
631
|
+
if target == nil || target.Symbol == nil || target.External {
|
|
618
632
|
return ""
|
|
619
633
|
}
|
|
620
634
|
to := g.ensureTargetNode(target)
|
|
@@ -11,10 +11,8 @@ import (
|
|
|
11
11
|
"strings"
|
|
12
12
|
"sync"
|
|
13
13
|
"sync/atomic"
|
|
14
|
-
"time"
|
|
15
14
|
)
|
|
16
15
|
|
|
17
|
-
const nativePluginCommandTimeout = 30 * time.Second
|
|
18
16
|
const nativePluginCommandStdoutLimit = 4 * 1024 * 1024
|
|
19
17
|
const nativePluginCommandStderrLimit = 1024 * 1024
|
|
20
18
|
|
|
@@ -825,7 +823,11 @@ func (s *NativePluginSource) runWithStdin(plugin NativeLSPPluginEntry, command s
|
|
|
825
823
|
if strings.TrimSpace(plugin.Binary) == "" {
|
|
826
824
|
return nil, fmt.Errorf("ttscserver: %s has no binary", pluginLabel(plugin))
|
|
827
825
|
}
|
|
828
|
-
|
|
826
|
+
// No deadline: the command is running the user's own rules, and how long
|
|
827
|
+
// that takes is not this process's call. The context stays so that a future
|
|
828
|
+
// early return cannot leave the child running — cancelling it kills the
|
|
829
|
+
// process rather than abandoning it.
|
|
830
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
829
831
|
defer cancel()
|
|
830
832
|
allArgs := []string{
|
|
831
833
|
command,
|
|
@@ -848,9 +850,6 @@ func (s *NativePluginSource) runWithStdin(plugin NativeLSPPluginEntry, command s
|
|
|
848
850
|
cmd.Stdout = &stdout
|
|
849
851
|
cmd.Stderr = &stderr
|
|
850
852
|
err := cmd.Run()
|
|
851
|
-
if ctx.Err() != nil {
|
|
852
|
-
return nil, fmt.Errorf("ttscserver: %s %s timed out", pluginLabel(plugin), command)
|
|
853
|
-
}
|
|
854
853
|
if err != nil {
|
|
855
854
|
msg := strings.TrimSpace(stderr.String())
|
|
856
855
|
if msg == "" {
|
|
@@ -9,15 +9,8 @@ import (
|
|
|
9
9
|
"os/exec"
|
|
10
10
|
"strings"
|
|
11
11
|
"sync"
|
|
12
|
-
"time"
|
|
13
12
|
)
|
|
14
13
|
|
|
15
|
-
// residentRequestTimeout bounds one resident verb round trip, mirroring the
|
|
16
|
-
// spawn-per-verb path's nativePluginCommandTimeout. A rule that hangs must not
|
|
17
|
-
// wedge the daemon's request mutex forever; on timeout the sidecar is killed and
|
|
18
|
-
// the caller falls back to a fresh spawn.
|
|
19
|
-
const residentRequestTimeout = nativePluginCommandTimeout
|
|
20
|
-
|
|
21
14
|
// The serve verbs below are routed to the resident daemon because they load a
|
|
22
15
|
// Program.
|
|
23
16
|
// lsp-command-ids / lsp-code-action-kinds run once at startup and never load one
|
|
@@ -121,8 +114,10 @@ func (s *NativePluginSource) serveRun(plugin NativeLSPPluginEntry, verb string,
|
|
|
121
114
|
}
|
|
122
115
|
|
|
123
116
|
// call sends one request to the daemon and reads its reply, serializing access
|
|
124
|
-
// to the single pipe. It spawns the child on first use or after a death
|
|
125
|
-
//
|
|
117
|
+
// to the single pipe. It spawns the child on first use or after a death. The
|
|
118
|
+
// read is unbounded — the rule is the user's own code — and the mutex is held
|
|
119
|
+
// only as long as the sidecar is alive, because its death closes stdout and
|
|
120
|
+
// ends the read.
|
|
126
121
|
func (sc *residentSidecar) call(s *NativePluginSource, plugin NativeLSPPluginEntry, req serveClientRequest) ([]byte, int, error) {
|
|
127
122
|
sc.mu.Lock()
|
|
128
123
|
defer sc.mu.Unlock()
|
|
@@ -162,23 +157,21 @@ func (sc *residentSidecar) call(s *NativePluginSource, plugin NativeLSPPluginEnt
|
|
|
162
157
|
raw, err := sc.stdout.ReadBytes('\n')
|
|
163
158
|
done <- readResult{line: raw, err: err}
|
|
164
159
|
}()
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
// No deadline: a rule that takes a long time is the user's own code running,
|
|
161
|
+
// and the read still ends the moment the sidecar dies, because closing its
|
|
162
|
+
// stdout surfaces here as a read error.
|
|
163
|
+
res := <-done
|
|
164
|
+
if res.err != nil {
|
|
167
165
|
sc.kill()
|
|
168
|
-
return nil, 0,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
var resp serveClientResponse
|
|
175
|
-
if err := json.Unmarshal(res.line, &resp); err != nil {
|
|
176
|
-
sc.kill()
|
|
177
|
-
return nil, 0, err
|
|
178
|
-
}
|
|
179
|
-
sc.everServed = true
|
|
180
|
-
return resp.Result, resp.Code, nil
|
|
166
|
+
return nil, 0, res.err
|
|
167
|
+
}
|
|
168
|
+
var resp serveClientResponse
|
|
169
|
+
if err := json.Unmarshal(res.line, &resp); err != nil {
|
|
170
|
+
sc.kill()
|
|
171
|
+
return nil, 0, err
|
|
181
172
|
}
|
|
173
|
+
sc.everServed = true
|
|
174
|
+
return resp.Result, resp.Code, nil
|
|
182
175
|
}
|
|
183
176
|
|
|
184
177
|
// spawn starts the resident child with the base project args and the lsp-serve
|