ttsc 0.23.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.go +264 -48
- package/cmd/ttscgraph/serve_answers_a_project_that_implements_its_own_ambient_declaration_test.go +69 -0
- package/cmd/ttscgraph/serve_early_error_encode_failure_is_observable_test.go +40 -0
- package/cmd/ttscgraph/serve_graph_snapshot_version_matches_typescript_client_test.go +32 -0
- package/cmd/ttscgraph/serve_phase_trace_accounts_for_load_failure_test.go +45 -0
- package/cmd/ttscgraph/serve_phase_trace_is_opt_in_and_payload_free_test.go +52 -0
- package/cmd/ttscgraph/serve_protocol_negotiates_incremental_shards_test.go +44 -0
- package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +2 -2
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +46 -20
- package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +9 -9
- package/cmd/ttscgraph/serve_shards.go +1390 -0
- package/cmd/ttscgraph/serve_shards_abandoned_generation_converges_on_retry_test.go +92 -0
- package/cmd/ttscgraph/serve_shards_delete_removed_source_test.go +57 -0
- package/cmd/ttscgraph/serve_shards_external_endpoints_test.go +53 -0
- package/cmd/ttscgraph/serve_shards_keep_source_distributed_dependencies_at_the_boundary_test.go +108 -0
- package/cmd/ttscgraph/serve_shards_match_full_dump_oracle_test.go +88 -0
- package/cmd/ttscgraph/serve_shards_merge_pending_and_new_edits_test.go +50 -0
- package/cmd/ttscgraph/serve_shards_prune_unreferenced_external_endpoints_test.go +65 -0
- package/cmd/ttscgraph/serve_shards_publish_created_source_test.go +92 -0
- package/cmd/ttscgraph/serve_shards_rebuild_declaration_edit_test.go +43 -0
- package/cmd/ttscgraph/serve_shards_refresh_cross_file_implementation_test.go +63 -0
- package/cmd/ttscgraph/serve_shards_refresh_dependent_diagnostics_test.go +80 -0
- package/cmd/ttscgraph/serve_shards_rename_moves_source_identity_test.go +95 -0
- package/cmd/ttscgraph/serve_shards_retry_preserves_committed_generation_test.go +48 -0
- package/cmd/ttscgraph/serve_shards_reuse_unaffected_sources_test.go +121 -0
- package/driver/declaration_shape.go +85 -0
- package/driver/program.go +39 -10
- 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/build.go +62 -10
- package/internal/graph/build_files_reuses_committed_cross_file_endpoints_test.go +87 -0
- package/internal/graph/build_keeps_source_distributed_dependencies_at_the_external_boundary_test.go +74 -0
- package/internal/graph/dump.go +84 -43
- package/internal/graph/dump_path_mapper_caches_alias_resolution_test.go +29 -0
- package/internal/graph/dump_paths.go +134 -11
- package/internal/graph/dump_paths_canonicalize_windows_short_root_test.go +39 -0
- package/internal/graph/dump_rejects_provenance_only_path_errors_test.go +32 -0
- package/internal/graph/dump_serializes_the_full_graph_test.go +2 -2
- package/internal/graph/dump_symlink_project_publishes_matching_base_test.go +82 -0
- package/internal/graph/edges.go +73 -17
- package/internal/graph/exports.go +9 -2
- package/internal/graph/graph.go +47 -7
- package/internal/graph/graph_kind_contracts_match_their_producers_test.go +121 -0
- package/internal/graph/node_modifiers_emit_union_strings_test.go +1 -1
- package/internal/graph/provenance.go +11 -1
- package/internal/graph/resolve.go +1 -4
- package/internal/graph/source.go +23 -0
- package/internal/graph/wire_node_ids_share_alias_resolution_test.go +29 -0
- 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.go
CHANGED
|
@@ -13,6 +13,7 @@ import (
|
|
|
13
13
|
"slices"
|
|
14
14
|
"sort"
|
|
15
15
|
"strings"
|
|
16
|
+
"time"
|
|
16
17
|
|
|
17
18
|
shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
|
|
18
19
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
@@ -45,7 +46,9 @@ const (
|
|
|
45
46
|
serveModeUnchanged = "unchanged"
|
|
46
47
|
// serveModeIncremental is edits applied onto the reused resident program.
|
|
47
48
|
serveModeIncremental = "incremental"
|
|
48
|
-
// serveModeRebuild is edits applied but
|
|
49
|
+
// serveModeRebuild is edits applied but graph projection requires a complete
|
|
50
|
+
// rebuild, either because the program could not be reused or because a
|
|
51
|
+
// declaration-file boundary has no authored source shard of its own.
|
|
49
52
|
serveModeRebuild = "rebuild"
|
|
50
53
|
// serveModeError is a request that produced no snapshot. It is a transport
|
|
51
54
|
// mode, not a computation mode: it exists so mode is never absent, because a
|
|
@@ -71,10 +74,15 @@ var serveCapabilities = fullSnapshotCapabilities
|
|
|
71
74
|
|
|
72
75
|
type serveRequest struct {
|
|
73
76
|
ID int `json:"id"`
|
|
77
|
+
// GraphSnapshotVersion opts into the incremental shard protocol. Omitted
|
|
78
|
+
// requests retain the schema-v6 full-dump response for existing
|
|
79
|
+
// @ttsc/graph clients.
|
|
80
|
+
GraphSnapshotVersion int `json:"graphSnapshotVersion,omitempty"`
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
type serveResponse struct {
|
|
77
|
-
Dump
|
|
84
|
+
Dump *graph.Dump `json:"dump,omitempty"`
|
|
85
|
+
Snapshot *serveGraphSnapshot `json:"snapshot,omitempty"`
|
|
78
86
|
// Error is set when the request produced no snapshot; Mode is then
|
|
79
87
|
// serveModeError.
|
|
80
88
|
Error string `json:"error,omitempty"`
|
|
@@ -90,6 +98,8 @@ type serveResponse struct {
|
|
|
90
98
|
Changed bool `json:"changed"`
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
const graphPhaseTraceEnvironment = "SAMCHON_GRAPH_TTSC_PHASE_TRACE"
|
|
102
|
+
|
|
93
103
|
// newServeResponse stamps the fields every response owes the client, so no exit
|
|
94
104
|
// from the serve loop can forget one.
|
|
95
105
|
func newServeResponse(id int) serveResponse {
|
|
@@ -124,13 +134,17 @@ type graphSession struct {
|
|
|
124
134
|
// generation, captured from the same parse that produced configHashes and
|
|
125
135
|
// rootFiles so the published evidence and the invalidation state can never
|
|
126
136
|
// describe different loads.
|
|
127
|
-
configDigests
|
|
128
|
-
roots
|
|
129
|
-
initialized
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
configDigests []graph.FileDigest
|
|
138
|
+
roots []graph.RootFile
|
|
139
|
+
initialized bool
|
|
140
|
+
graphStore *serveGraphStore
|
|
141
|
+
requestProtocol int
|
|
142
|
+
requestProtocolSelected bool
|
|
143
|
+
// pending remembers a generation whose state was captured but whose selected
|
|
144
|
+
// projection failed. Until an input change lets the session rebuild, an
|
|
145
|
+
// unchanged request retries the same full or partial invalidation instead of
|
|
146
|
+
// falsely confirming the older client graph.
|
|
147
|
+
pending *graphChange
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
func newGraphSession(cwd, tsconfig string) (*graphSession, error) {
|
|
@@ -149,106 +163,187 @@ func (s *graphSession) Close() error {
|
|
|
149
163
|
}
|
|
150
164
|
|
|
151
165
|
func (s *graphSession) Snapshot() (*graph.Dump, string, bool, error) {
|
|
166
|
+
change, err := s.nextChange(false)
|
|
167
|
+
if err != nil {
|
|
168
|
+
return nil, "", false, err
|
|
169
|
+
}
|
|
170
|
+
if change == nil {
|
|
171
|
+
return nil, serveModeUnchanged, false, nil
|
|
172
|
+
}
|
|
173
|
+
dump, err := s.buildDump()
|
|
174
|
+
if err != nil {
|
|
175
|
+
s.pending = change
|
|
176
|
+
return nil, "", false, err
|
|
177
|
+
}
|
|
178
|
+
s.pending = nil
|
|
179
|
+
return &dump, change.mode, true, nil
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
type graphChange struct {
|
|
183
|
+
mode string
|
|
184
|
+
files []string
|
|
185
|
+
publicFiles []string
|
|
186
|
+
full bool
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// nextChange advances only the resident compiler and its captured input state.
|
|
190
|
+
// Projection is deliberately outside this method so the legacy full-dump and
|
|
191
|
+
// incremental shard protocols share exactly one invalidation decision without
|
|
192
|
+
// forcing either representation onto the other.
|
|
193
|
+
func (s *graphSession) nextChange(trackPublicShape bool) (*graphChange, error) {
|
|
152
194
|
if !s.initialized {
|
|
153
195
|
// The captured compiler state is initialized even when its first dump is
|
|
154
196
|
// not publishable. Mark the attempt before building so a later request can
|
|
155
197
|
// observe a config/source edit that repairs the path error instead of
|
|
156
198
|
// retrying the stale Program forever.
|
|
157
199
|
s.initialized = true
|
|
158
|
-
|
|
159
|
-
if err != nil {
|
|
160
|
-
s.pendingDumpMode = serveModeInitial
|
|
161
|
-
return nil, "", false, err
|
|
162
|
-
}
|
|
163
|
-
s.pendingDumpMode = ""
|
|
164
|
-
return &dump, serveModeInitial, true, nil
|
|
200
|
+
return &graphChange{mode: serveModeInitial, full: true}, nil
|
|
165
201
|
}
|
|
166
202
|
|
|
167
203
|
configChanged, err := hashesChanged(s.configHashes)
|
|
168
204
|
if err != nil {
|
|
169
|
-
return nil,
|
|
205
|
+
return nil, err
|
|
170
206
|
}
|
|
171
207
|
if configChanged {
|
|
172
208
|
if err := s.reload(); err != nil {
|
|
173
|
-
return nil,
|
|
209
|
+
return nil, err
|
|
174
210
|
}
|
|
175
|
-
return
|
|
211
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
176
212
|
}
|
|
177
213
|
|
|
178
214
|
if diskStatesChanged(s.auxStates) {
|
|
179
215
|
if err := s.reload(); err != nil {
|
|
180
|
-
return nil,
|
|
216
|
+
return nil, err
|
|
181
217
|
}
|
|
182
|
-
return
|
|
218
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
183
219
|
}
|
|
184
220
|
|
|
185
221
|
roots, err := projectRootFiles(s.compiler.Program(), true)
|
|
186
222
|
if err != nil {
|
|
187
|
-
return nil,
|
|
223
|
+
return nil, err
|
|
188
224
|
}
|
|
189
225
|
if !slices.Equal(s.rootFiles, roots) {
|
|
190
226
|
if err := s.reload(); err != nil {
|
|
191
|
-
return nil,
|
|
227
|
+
return nil, err
|
|
192
228
|
}
|
|
193
|
-
return
|
|
229
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
194
230
|
}
|
|
195
231
|
|
|
196
232
|
changed, deleted, err := changedSources(s.sourceHashes)
|
|
197
233
|
if err != nil {
|
|
198
|
-
return nil,
|
|
234
|
+
return nil, err
|
|
199
235
|
}
|
|
200
236
|
if deleted {
|
|
201
237
|
if err := s.reload(); err != nil {
|
|
202
|
-
return nil,
|
|
238
|
+
return nil, err
|
|
203
239
|
}
|
|
204
|
-
return
|
|
240
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
205
241
|
}
|
|
206
242
|
if len(changed) == 0 {
|
|
207
|
-
if s.
|
|
208
|
-
return s.
|
|
243
|
+
if s.pending != nil {
|
|
244
|
+
return s.pending, nil
|
|
209
245
|
}
|
|
210
|
-
return nil,
|
|
246
|
+
return nil, nil
|
|
211
247
|
}
|
|
212
248
|
if s.compiler.Program().HasLinkedProgramPlugins() {
|
|
213
249
|
if err := s.reload(); err != nil {
|
|
214
|
-
return nil,
|
|
250
|
+
return nil, err
|
|
215
251
|
}
|
|
216
|
-
return
|
|
252
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
217
253
|
}
|
|
218
254
|
|
|
219
255
|
mode := serveModeIncremental
|
|
256
|
+
full := false
|
|
220
257
|
paths := make([]string, 0, len(changed))
|
|
221
258
|
for path := range changed {
|
|
222
259
|
paths = append(paths, path)
|
|
223
260
|
}
|
|
224
261
|
sort.Strings(paths)
|
|
262
|
+
oldShapes := make(map[string]string, len(paths))
|
|
263
|
+
for _, path := range paths {
|
|
264
|
+
source := s.compiler.Program().SourceFile(path)
|
|
265
|
+
if source == nil {
|
|
266
|
+
if err := s.reload(); err != nil {
|
|
267
|
+
return nil, err
|
|
268
|
+
}
|
|
269
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
270
|
+
}
|
|
271
|
+
if source.IsDeclarationFile {
|
|
272
|
+
mode = serveModeRebuild
|
|
273
|
+
full = true
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
if trackPublicShape {
|
|
277
|
+
shape, err := s.compiler.Program().DeclarationShapeDigest(source)
|
|
278
|
+
if err != nil {
|
|
279
|
+
return nil, err
|
|
280
|
+
}
|
|
281
|
+
oldShapes[path] = shape
|
|
282
|
+
}
|
|
283
|
+
}
|
|
225
284
|
for _, path := range paths {
|
|
226
285
|
if reused := s.compiler.Apply(path, changed[path]); !reused {
|
|
227
286
|
mode = serveModeRebuild
|
|
287
|
+
full = true
|
|
228
288
|
}
|
|
229
289
|
current, exists := s.compiler.SourceText(path)
|
|
230
290
|
expected := driver.ApplySourcePreambleToFile(path, changed[path], s.compiler.Program().SourcePreamble)
|
|
231
291
|
if !exists || current != expected {
|
|
232
292
|
if err := s.reload(); err != nil {
|
|
233
|
-
return nil,
|
|
293
|
+
return nil, err
|
|
234
294
|
}
|
|
235
|
-
return
|
|
295
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
296
|
+
}
|
|
297
|
+
source := s.compiler.Program().SourceFile(path)
|
|
298
|
+
if source == nil {
|
|
299
|
+
if err := s.reload(); err != nil {
|
|
300
|
+
return nil, err
|
|
301
|
+
}
|
|
302
|
+
return &graphChange{mode: serveModeReload, full: true}, nil
|
|
303
|
+
}
|
|
304
|
+
if source.IsDeclarationFile {
|
|
305
|
+
mode = serveModeRebuild
|
|
306
|
+
full = true
|
|
236
307
|
}
|
|
237
308
|
}
|
|
238
309
|
if err := s.captureState(); err != nil {
|
|
239
|
-
|
|
310
|
+
// The compiler already accepted the edit. Preserve a full retry marker so
|
|
311
|
+
// a transient hashing/input failure cannot make the next request compare
|
|
312
|
+
// public shape against the uncommitted compiler generation and publish an
|
|
313
|
+
// incomplete closure.
|
|
314
|
+
s.pending = &graphChange{mode: serveModeRebuild, full: true}
|
|
315
|
+
return nil, err
|
|
240
316
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
317
|
+
publicFiles := []string{}
|
|
318
|
+
if trackPublicShape && !full {
|
|
319
|
+
for _, path := range paths {
|
|
320
|
+
source := s.compiler.Program().SourceFile(path)
|
|
321
|
+
if source == nil {
|
|
322
|
+
return &graphChange{mode: serveModeRebuild, full: true}, nil
|
|
323
|
+
}
|
|
324
|
+
shape, err := s.compiler.Program().DeclarationShapeDigest(source)
|
|
325
|
+
if err != nil {
|
|
326
|
+
mode = serveModeRebuild
|
|
327
|
+
full = true
|
|
328
|
+
publicFiles = nil
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
if shape != oldShapes[path] {
|
|
332
|
+
publicFiles = append(publicFiles, path)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if s.pending != nil {
|
|
337
|
+
if s.pending.full {
|
|
338
|
+
mode = s.pending.mode
|
|
339
|
+
full = true
|
|
340
|
+
paths = nil
|
|
341
|
+
} else if !full {
|
|
342
|
+
paths = compactSortedStrings(append(paths, s.pending.files...))
|
|
343
|
+
publicFiles = compactSortedStrings(append(publicFiles, s.pending.publicFiles...))
|
|
344
|
+
}
|
|
249
345
|
}
|
|
250
|
-
|
|
251
|
-
return &dump, mode, true, nil
|
|
346
|
+
return &graphChange{mode: mode, files: paths, publicFiles: publicFiles, full: full}, nil
|
|
252
347
|
}
|
|
253
348
|
|
|
254
349
|
func (s *graphSession) reload() error {
|
|
@@ -750,26 +845,86 @@ func serveSnapshots(input io.Reader, output io.Writer, cwd, tsconfig string) int
|
|
|
750
845
|
fmt.Fprintf(stderr, "ttscgraph: unaddressable serve request: %v\n", err)
|
|
751
846
|
return 1
|
|
752
847
|
}
|
|
848
|
+
requestStarted := time.Now()
|
|
849
|
+
if request.GraphSnapshotVersion != 0 && request.GraphSnapshotVersion != graphSnapshotProtocolVersion {
|
|
850
|
+
if err := encodeServeResponseWithTrace(encoder, errorResponse(
|
|
851
|
+
request.ID,
|
|
852
|
+
fmt.Sprintf(
|
|
853
|
+
"ttscgraph: unsupported graph snapshot protocol v%d (supported: v%d)",
|
|
854
|
+
request.GraphSnapshotVersion,
|
|
855
|
+
graphSnapshotProtocolVersion,
|
|
856
|
+
),
|
|
857
|
+
), requestStarted, 0, 0, 0); err != nil {
|
|
858
|
+
fmt.Fprintf(stderr, "ttscgraph: write serve response: %v\n", err)
|
|
859
|
+
return 1
|
|
860
|
+
}
|
|
861
|
+
continue
|
|
862
|
+
}
|
|
863
|
+
var loadDuration time.Duration
|
|
753
864
|
if session == nil {
|
|
865
|
+
loadStarted := time.Now()
|
|
754
866
|
created, err := newGraphSession(cwd, tsconfig)
|
|
867
|
+
loadDuration = time.Since(loadStarted)
|
|
755
868
|
if err != nil {
|
|
756
|
-
|
|
869
|
+
if err := encodeServeResponseWithTrace(
|
|
870
|
+
encoder,
|
|
871
|
+
errorResponse(request.ID, err.Error()),
|
|
872
|
+
requestStarted,
|
|
873
|
+
loadDuration,
|
|
874
|
+
0,
|
|
875
|
+
0,
|
|
876
|
+
); err != nil {
|
|
877
|
+
fmt.Fprintf(stderr, "ttscgraph: write serve response: %v\n", err)
|
|
878
|
+
return 1
|
|
879
|
+
}
|
|
757
880
|
continue
|
|
758
881
|
}
|
|
759
882
|
session = created
|
|
760
883
|
}
|
|
761
|
-
|
|
884
|
+
if session.requestProtocolSelected && session.requestProtocol != request.GraphSnapshotVersion {
|
|
885
|
+
if err := encodeServeResponseWithTrace(encoder, errorResponse(
|
|
886
|
+
request.ID,
|
|
887
|
+
"ttscgraph: graphSnapshotVersion cannot change within one resident session",
|
|
888
|
+
), requestStarted, loadDuration, 0, 0); err != nil {
|
|
889
|
+
fmt.Fprintf(stderr, "ttscgraph: write serve response: %v\n", err)
|
|
890
|
+
return 1
|
|
891
|
+
}
|
|
892
|
+
continue
|
|
893
|
+
}
|
|
894
|
+
session.requestProtocol = request.GraphSnapshotVersion
|
|
895
|
+
session.requestProtocolSelected = true
|
|
896
|
+
var dump *graph.Dump
|
|
897
|
+
var snapshot *serveGraphSnapshot
|
|
898
|
+
var mode string
|
|
899
|
+
var changed bool
|
|
900
|
+
var semanticDuration time.Duration
|
|
901
|
+
var exportDuration time.Duration
|
|
902
|
+
var err error
|
|
903
|
+
if request.GraphSnapshotVersion == graphSnapshotProtocolVersion {
|
|
904
|
+
snapshot, mode, changed, semanticDuration, exportDuration, err = session.snapshotShardsWithTiming()
|
|
905
|
+
} else {
|
|
906
|
+
dump, mode, changed, err = session.Snapshot()
|
|
907
|
+
}
|
|
762
908
|
response := newServeResponse(request.ID)
|
|
763
909
|
response.Dump = dump
|
|
910
|
+
response.Snapshot = snapshot
|
|
764
911
|
response.Mode = mode
|
|
765
912
|
response.Changed = changed
|
|
766
913
|
if err != nil {
|
|
767
914
|
response.Error = err.Error()
|
|
768
915
|
response.Dump = nil
|
|
916
|
+
response.Snapshot = nil
|
|
769
917
|
response.Mode = serveModeError
|
|
770
918
|
response.Changed = false
|
|
771
919
|
}
|
|
772
|
-
if err :=
|
|
920
|
+
if err := encodeServeResponseWithTrace(
|
|
921
|
+
encoder,
|
|
922
|
+
response,
|
|
923
|
+
requestStarted,
|
|
924
|
+
loadDuration,
|
|
925
|
+
semanticDuration,
|
|
926
|
+
exportDuration,
|
|
927
|
+
); err != nil {
|
|
773
928
|
fmt.Fprintf(stderr, "ttscgraph: write serve response: %v\n", err)
|
|
774
929
|
return 1
|
|
775
930
|
}
|
|
@@ -780,3 +935,64 @@ func serveSnapshots(input io.Reader, output io.Writer, cwd, tsconfig string) int
|
|
|
780
935
|
}
|
|
781
936
|
return 0
|
|
782
937
|
}
|
|
938
|
+
|
|
939
|
+
// encodeServeResponseWithTrace accounts for every addressed response, including
|
|
940
|
+
// failures that occur before a graph session exists.
|
|
941
|
+
func encodeServeResponseWithTrace(
|
|
942
|
+
encoder *json.Encoder,
|
|
943
|
+
response serveResponse,
|
|
944
|
+
requestStarted time.Time,
|
|
945
|
+
loadDuration time.Duration,
|
|
946
|
+
semanticDuration time.Duration,
|
|
947
|
+
exportDuration time.Duration,
|
|
948
|
+
) error {
|
|
949
|
+
encodeStarted := time.Now()
|
|
950
|
+
if err := encoder.Encode(response); err != nil {
|
|
951
|
+
return err
|
|
952
|
+
}
|
|
953
|
+
writeServePhaseTrace(
|
|
954
|
+
response.ID,
|
|
955
|
+
response.Mode,
|
|
956
|
+
loadDuration,
|
|
957
|
+
semanticDuration,
|
|
958
|
+
exportDuration,
|
|
959
|
+
time.Since(encodeStarted),
|
|
960
|
+
time.Since(requestStarted),
|
|
961
|
+
)
|
|
962
|
+
return nil
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// writeServePhaseTrace emits opt-in timings without source paths or payloads.
|
|
966
|
+
func writeServePhaseTrace(
|
|
967
|
+
request int,
|
|
968
|
+
mode string,
|
|
969
|
+
load time.Duration,
|
|
970
|
+
semantic time.Duration,
|
|
971
|
+
export time.Duration,
|
|
972
|
+
encode time.Duration,
|
|
973
|
+
total time.Duration,
|
|
974
|
+
) {
|
|
975
|
+
if os.Getenv(graphPhaseTraceEnvironment) != "1" {
|
|
976
|
+
return
|
|
977
|
+
}
|
|
978
|
+
phases := []struct {
|
|
979
|
+
name string
|
|
980
|
+
duration time.Duration
|
|
981
|
+
}{
|
|
982
|
+
{name: "native-load", duration: load},
|
|
983
|
+
{name: "semantic-refresh", duration: semantic},
|
|
984
|
+
{name: "shard-export", duration: export},
|
|
985
|
+
{name: "encode", duration: encode},
|
|
986
|
+
{name: "producer-total", duration: total},
|
|
987
|
+
}
|
|
988
|
+
for _, phase := range phases {
|
|
989
|
+
fmt.Fprintf(
|
|
990
|
+
stderr,
|
|
991
|
+
"@samchon/graph: ttscgraph-phase owner=producer request=%d mode=%s phase=%s durationMs=%.3f\n",
|
|
992
|
+
request,
|
|
993
|
+
mode,
|
|
994
|
+
phase.name,
|
|
995
|
+
float64(phase.duration)/float64(time.Millisecond),
|
|
996
|
+
)
|
|
997
|
+
}
|
|
998
|
+
}
|