@mrclrchtr/supi-code-intelligence 4.10.0 → 6.0.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/README.md +4 -3
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +111 -5
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +22 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +245 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +171 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +413 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +690 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +97 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +448 -298
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +70 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +231 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +529 -119
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +12 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +42 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +17 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +350 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +586 -96
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +42 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +92 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +165 -118
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +17 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +221 -77
- package/src/analysis/health/file-scope.ts +184 -0
- package/src/analysis/health/recovery.ts +14 -3
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +34 -9
- package/src/analysis/refactor/apply.ts +45 -1
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- package/src/analysis/relations/callees.ts +9 -1
- package/src/analysis/relations/types.ts +5 -2
- package/src/analysis/search/ast-scan-timing.ts +9 -2
- package/src/analysis/search/ast-scan.ts +5 -2
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +54 -33
- package/src/analysis/search/pattern.ts +18 -3
- package/src/analysis/target/anchored.ts +20 -11
- package/src/analysis/target/file.ts +18 -11
- package/src/analysis/target/identity.ts +19 -3
- package/src/analysis/target/symbol.ts +36 -15
- package/src/api.ts +4 -0
- package/src/config.ts +35 -1
- package/src/diagnostics/evidence.ts +51 -0
- package/src/extension.ts +57 -14
- package/src/overview/overview-data.ts +9 -2
- package/src/overview/overview.ts +49 -30
- package/src/overview/types.ts +2 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-types.ts +2 -2
- package/src/session/find-workflow.ts +20 -9
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +175 -0
- package/src/session/health-types.ts +43 -15
- package/src/session/health-workflow.ts +69 -54
- package/src/session/input/workflows.ts +2 -2
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +12 -6
- package/src/session/orientation/collect.ts +12 -3
- package/src/session/orientation/context-facts.ts +3 -1
- package/src/session/orientation/context-sections.ts +20 -7
- package/src/session/orientation/gather.ts +17 -11
- package/src/session/orientation-types.ts +4 -1
- package/src/session/orientation-workflow.ts +23 -10
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +61 -15
- package/src/session/session.ts +29 -2
- package/src/session/target-workflow.ts +54 -23
- package/src/session/workflow-control.ts +7 -3
- package/src/substrate/lsp/lifecycle.ts +26 -46
- package/src/substrate/lsp/maintenance.ts +284 -28
- package/src/substrate/lsp/recovery.ts +11 -5
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/{find → code_find}/execute.ts +2 -16
- package/src/tool/code_find/guidance.ts +8 -0
- package/src/tool/{find → code_find}/render.ts +21 -4
- package/src/tool/{result/find.ts → code_find/result.ts} +78 -9
- package/src/tool/code_find/spec.ts +37 -0
- package/src/tool/code_find/tui.ts +41 -0
- package/src/tool/code_graph/execute.ts +24 -0
- package/src/tool/code_graph/guidance.ts +6 -0
- package/src/tool/{graph → code_graph}/markdown.ts +1 -1
- package/src/tool/{result/graph.ts → code_graph/result.ts} +111 -3
- package/src/tool/code_graph/spec.ts +39 -0
- package/src/tool/code_graph/tui.ts +117 -0
- package/src/tool/{health → code_health}/execute.ts +2 -13
- package/src/tool/code_health/guidance.ts +8 -0
- package/src/tool/{health → code_health}/markdown.ts +119 -27
- package/src/tool/code_health/refresh-status.ts +105 -0
- package/src/tool/{result/health.ts → code_health/result.ts} +80 -3
- package/src/tool/code_health/spec.ts +41 -0
- package/src/tool/{health → code_health}/tui.ts +95 -98
- package/src/tool/{inspect → code_inspect}/execute.ts +2 -16
- package/src/tool/code_inspect/guidance.ts +6 -0
- package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
- package/src/tool/{result/inspect.ts → code_inspect/result.ts} +143 -2
- package/src/tool/code_inspect/spec.ts +25 -0
- package/src/tool/code_inspect/tui.ts +44 -0
- package/src/tool/code_orientation/execute.ts +25 -0
- package/src/tool/code_orientation/guidance.ts +9 -0
- package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
- package/src/tool/code_orientation/result.ts +248 -0
- package/src/tool/code_orientation/spec.ts +25 -0
- package/src/tool/{orientation → code_orientation}/tui.ts +51 -35
- package/src/tool/code_refactor_apply/execute.ts +17 -0
- package/src/tool/code_refactor_apply/guidance.ts +5 -0
- package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +8 -11
- package/src/tool/code_refactor_apply/spec.ts +26 -0
- package/src/tool/code_refactor_apply/tui.ts +73 -0
- package/src/tool/code_refactor_plan/execute.ts +27 -0
- package/src/tool/code_refactor_plan/guidance.ts +8 -0
- package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +13 -22
- package/src/tool/code_refactor_plan/spec.ts +25 -0
- package/src/tool/code_refactor_plan/tui.ts +54 -0
- package/src/tool/code_resolve/execute.ts +19 -0
- package/src/tool/code_resolve/guidance.ts +8 -0
- package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
- package/src/tool/{result/resolve.ts → code_resolve/result.ts} +79 -29
- package/src/tool/code_resolve/spec.ts +25 -0
- package/src/tool/code_resolve/tui.ts +60 -0
- package/src/tool/guidance.ts +71 -52
- package/src/tool/infra/truncate.ts +40 -0
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
- package/src/tool/register.ts +105 -88
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/refactor.ts +37 -8
- package/src/tool/result/types.ts +20 -0
- package/src/tool/schemas.ts +26 -157
- package/src/tool/specs.ts +40 -126
- package/src/types/execution.ts +18 -5
- package/src/types/index.ts +8 -1
- package/src/ui/status-command.ts +5 -3
- package/src/ui/status-overlay.ts +4 -0
- package/src/ui/tui/common.ts +108 -10
- package/src/ui/tui/display.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -351
- package/src/tool/find/tui.ts +0 -39
- package/src/tool/graph/execute.ts +0 -62
- package/src/tool/graph/tui.ts +0 -104
- package/src/tool/inspect/tui.ts +0 -36
- package/src/tool/orientation/execute.ts +0 -62
- package/src/tool/refactor-apply/tui.ts +0 -55
- package/src/tool/refactor-plan/tui.ts +0 -41
- package/src/tool/resolve/execute.ts +0 -36
- package/src/tool/resolve/tui.ts +0 -42
- package/src/tool/result/orientation.ts +0 -132
- /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
- /package/src/tool/{find → code_find}/markdown.ts +0 -0
- /package/src/tool/{find → code_find}/modes.ts +0 -0
- /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Package-owned deterministic test Worker. Production never selects this entry.
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
|
|
4
|
+
const version = 1;
|
|
5
|
+
parentPort.postMessage({ kind: "ready", version, generation: workerData.generation });
|
|
6
|
+
parentPort.on("message", (message) => {
|
|
7
|
+
if (message.kind !== "request") return;
|
|
8
|
+
const started = Date.now();
|
|
9
|
+
while (Date.now() - started < 120) {
|
|
10
|
+
// Block only this test Worker thread.
|
|
11
|
+
}
|
|
12
|
+
const payload = Buffer.from(JSON.stringify({ kind: "success", data: [] }), "utf8");
|
|
13
|
+
parentPort.postMessage({
|
|
14
|
+
kind: "chunk",
|
|
15
|
+
version,
|
|
16
|
+
generation: workerData.generation,
|
|
17
|
+
requestId: message.requestId,
|
|
18
|
+
sequence: 0,
|
|
19
|
+
final: true,
|
|
20
|
+
encodedBytes: payload.byteLength,
|
|
21
|
+
payload,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
parentPort.on("message", (message) => {
|
|
26
|
+
if (message.kind !== "chunk-ack") return;
|
|
27
|
+
parentPort.postMessage({
|
|
28
|
+
kind: "terminal",
|
|
29
|
+
version,
|
|
30
|
+
generation: workerData.generation,
|
|
31
|
+
requestId: message.requestId,
|
|
32
|
+
outcome: "completed",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
|
|
4
|
+
if (!parentPort) throw new Error("Structural Worker requires a parent port");
|
|
5
|
+
|
|
6
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
7
|
+
const worker = await jiti.import("./worker-main.ts");
|
|
8
|
+
await worker.runStructuralWorker(parentPort, workerData);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Validate fixed positive safe-integer cache limits. */
|
|
2
|
+
export function validatePositiveLimits(limits: object): void {
|
|
3
|
+
for (const [name, value] of Object.entries(limits)) {
|
|
4
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
5
|
+
throw new Error(`${name} must be a positive safe integer`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Promote one Map entry to the most-recent position. */
|
|
11
|
+
export function touchEntry<K, V>(entries: Map<K, V>, key: K, value: V): void {
|
|
12
|
+
entries.delete(key);
|
|
13
|
+
entries.set(key, value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Delete one WASM resource without interrupting remaining cleanup. */
|
|
17
|
+
export function safeDelete(resource: { delete(): void }): void {
|
|
18
|
+
try {
|
|
19
|
+
resource.delete();
|
|
20
|
+
} catch {
|
|
21
|
+
// Continue deterministic cleanup of the remaining owned resources.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
3
|
+
import type { Query, Tree } from "web-tree-sitter";
|
|
4
|
+
import type { GrammarId } from "../types.ts";
|
|
5
|
+
import { safeDelete, touchEntry, validatePositiveLimits } from "./parsed-file-store-helpers.ts";
|
|
6
|
+
import {
|
|
7
|
+
isStructuralRequestInterruption,
|
|
8
|
+
type StructuralRequestControl,
|
|
9
|
+
throwIfStructuralRequestInterrupted,
|
|
10
|
+
} from "./request-control.ts";
|
|
11
|
+
|
|
12
|
+
/** Fixed internal limits for session-owned parsed files and compiled queries. */
|
|
13
|
+
export interface ParsedFileStoreLimits {
|
|
14
|
+
readonly maxFileEntries: number;
|
|
15
|
+
/** Maximum retained source size measured as UTF-8 bytes. */
|
|
16
|
+
readonly maxSourceBytes: number;
|
|
17
|
+
readonly maxQueryEntries: number;
|
|
18
|
+
/** Maximum retained query-text size measured as UTF-8 bytes. */
|
|
19
|
+
readonly maxQueryBytes: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Default bounded-cache limits. These values are not user configuration. */
|
|
23
|
+
export const DEFAULT_PARSED_FILE_STORE_LIMITS: ParsedFileStoreLimits = Object.freeze({
|
|
24
|
+
maxFileEntries: 128,
|
|
25
|
+
maxSourceBytes: 32 * 1024 * 1024,
|
|
26
|
+
maxQueryEntries: 128,
|
|
27
|
+
maxQueryBytes: 512 * 1024,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** Sanitized cache facts attached to structural timing observations. */
|
|
31
|
+
export interface StructuralCacheObservation {
|
|
32
|
+
readonly state: "hit" | "miss" | "replacement";
|
|
33
|
+
readonly retained: boolean;
|
|
34
|
+
readonly evictionCount: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ParsedFileOperations {
|
|
38
|
+
readonly realpath: (filePath: string) => Promise<string>;
|
|
39
|
+
readonly readFile: (
|
|
40
|
+
filePath: string,
|
|
41
|
+
options?: { readonly signal?: AbortSignal },
|
|
42
|
+
) => Promise<string>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ParsedFileStoreOptions {
|
|
46
|
+
readonly limits?: ParsedFileStoreLimits;
|
|
47
|
+
readonly operations?: ParsedFileOperations;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface AcquireParsedFileInput {
|
|
51
|
+
readonly resolvedPath: string;
|
|
52
|
+
readonly grammarId: GrammarId;
|
|
53
|
+
readonly parse: (source: string) => Tree | Promise<Tree>;
|
|
54
|
+
readonly control?: StructuralRequestControl;
|
|
55
|
+
readonly onPhase?: (phase: "file-read" | "content-hash") => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** One caller-owned parsed tree and its source facts. */
|
|
59
|
+
export interface OwnedParsedFile {
|
|
60
|
+
readonly tree: Tree;
|
|
61
|
+
readonly source: string;
|
|
62
|
+
readonly resolvedPath: string;
|
|
63
|
+
readonly grammarId: GrammarId;
|
|
64
|
+
readonly cache: StructuralCacheObservation;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CachedParsedFile {
|
|
68
|
+
readonly contentHash: string;
|
|
69
|
+
readonly source: string;
|
|
70
|
+
readonly sourceBytes: number;
|
|
71
|
+
readonly tree: Tree;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface CachedQuery {
|
|
75
|
+
readonly query: Query;
|
|
76
|
+
readonly queryBytes: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface QueryExecution<T> {
|
|
80
|
+
readonly control?: StructuralRequestControl;
|
|
81
|
+
readonly compile: () => Query;
|
|
82
|
+
readonly execute: (query: Pick<Query, "matches">, cache: StructuralCacheObservation) => T;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** File-access failure from canonicalization or an asynchronous source read. */
|
|
86
|
+
export class ParsedFileReadError extends Error {
|
|
87
|
+
constructor(message: string, options: ErrorOptions) {
|
|
88
|
+
super(message, options);
|
|
89
|
+
this.name = "ParsedFileReadError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Own bounded canonical trees and compiled queries for one Tree-sitter runtime.
|
|
95
|
+
*
|
|
96
|
+
* Parsed-file freshness uses canonical path, grammar, and SHA-256 source hash.
|
|
97
|
+
* Cached trees stay private. Each caller receives a shallow owned copy that it
|
|
98
|
+
* must delete. Source and query UTF-8 byte counts are memory-related proxies
|
|
99
|
+
* because web-tree-sitter does not report WASM resource sizes.
|
|
100
|
+
*/
|
|
101
|
+
export class ParsedFileStore {
|
|
102
|
+
readonly #limits: ParsedFileStoreLimits;
|
|
103
|
+
readonly #operations: ParsedFileOperations;
|
|
104
|
+
readonly #files = new Map<string, CachedParsedFile>();
|
|
105
|
+
readonly #queries = new Map<string, CachedQuery>();
|
|
106
|
+
#sourceBytes = 0;
|
|
107
|
+
#queryBytes = 0;
|
|
108
|
+
#disposed = false;
|
|
109
|
+
|
|
110
|
+
constructor(options: ParsedFileStoreOptions = {}) {
|
|
111
|
+
this.#limits = options.limits ?? DEFAULT_PARSED_FILE_STORE_LIMITS;
|
|
112
|
+
validatePositiveLimits(this.#limits);
|
|
113
|
+
this.#operations = options.operations ?? {
|
|
114
|
+
realpath,
|
|
115
|
+
readFile: (filePath, options) => readFile(filePath, { encoding: "utf-8", ...options }),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Read one file asynchronously and return a caller-owned current tree. */
|
|
120
|
+
async acquireParsedFile(input: AcquireParsedFileInput): Promise<OwnedParsedFile> {
|
|
121
|
+
this.#assertActive();
|
|
122
|
+
throwIfStructuralRequestInterrupted(input.control);
|
|
123
|
+
const canonicalPath = await this.#canonicalize(input.resolvedPath, input.control);
|
|
124
|
+
this.#assertActive();
|
|
125
|
+
throwIfStructuralRequestInterrupted(input.control);
|
|
126
|
+
const source = await this.#readSource(canonicalPath, input.control);
|
|
127
|
+
this.#assertActive();
|
|
128
|
+
throwIfStructuralRequestInterrupted(input.control);
|
|
129
|
+
notifyPhase(input.onPhase, "file-read");
|
|
130
|
+
|
|
131
|
+
const contentHash = createHash("sha256").update(source).digest("hex");
|
|
132
|
+
notifyPhase(input.onPhase, "content-hash");
|
|
133
|
+
throwIfStructuralRequestInterrupted(input.control);
|
|
134
|
+
const key = fileKey(canonicalPath, input.grammarId);
|
|
135
|
+
const existing = this.#files.get(key);
|
|
136
|
+
|
|
137
|
+
if (existing?.contentHash === contentHash) {
|
|
138
|
+
return this.#copyCacheHit(key, existing, canonicalPath, input.grammarId);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let parsedTree: Tree | undefined;
|
|
142
|
+
try {
|
|
143
|
+
parsedTree = await input.parse(source);
|
|
144
|
+
this.#assertActive();
|
|
145
|
+
throwIfStructuralRequestInterrupted(input.control);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (parsedTree) safeDelete(parsedTree);
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const current = this.#files.get(key);
|
|
152
|
+
if (current?.contentHash === contentHash) {
|
|
153
|
+
safeDelete(parsedTree);
|
|
154
|
+
parsedTree = undefined;
|
|
155
|
+
return this.#copyCacheHit(key, current, canonicalPath, input.grammarId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const state = current ? "replacement" : "miss";
|
|
159
|
+
const sourceBytes = Buffer.byteLength(source, "utf-8");
|
|
160
|
+
if (sourceBytes > this.#limits.maxSourceBytes) {
|
|
161
|
+
if (current) this.#removeFile(key, current);
|
|
162
|
+
return {
|
|
163
|
+
tree: parsedTree,
|
|
164
|
+
source,
|
|
165
|
+
resolvedPath: canonicalPath,
|
|
166
|
+
grammarId: input.grammarId,
|
|
167
|
+
cache: { state, retained: false, evictionCount: 0 },
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (current) this.#removeFile(key, current);
|
|
172
|
+
const cached = { contentHash, source, sourceBytes, tree: parsedTree };
|
|
173
|
+
this.#files.set(key, cached);
|
|
174
|
+
this.#sourceBytes += sourceBytes;
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const evictionCount = this.#evictFiles();
|
|
178
|
+
return this.#ownedCopy(cached, canonicalPath, input.grammarId, {
|
|
179
|
+
state,
|
|
180
|
+
retained: true,
|
|
181
|
+
evictionCount,
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (this.#files.get(key) === cached) this.#removeFile(key, cached);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Execute a callback with a compiled query owned by this store.
|
|
191
|
+
*
|
|
192
|
+
* The callback receives no `delete()` method. An oversized transient query is
|
|
193
|
+
* deleted after the callback. Retained queries are deleted on eviction or
|
|
194
|
+
* disposal.
|
|
195
|
+
*/
|
|
196
|
+
withQuery<T>(
|
|
197
|
+
grammarId: GrammarId,
|
|
198
|
+
queryText: string,
|
|
199
|
+
compileOrExecution: (() => Query) | QueryExecution<T>,
|
|
200
|
+
legacyExecute?: (query: Pick<Query, "matches">, cache: StructuralCacheObservation) => T,
|
|
201
|
+
): { readonly data: T; readonly cache: StructuralCacheObservation } {
|
|
202
|
+
const execution = normalizeQueryExecution(compileOrExecution, legacyExecute);
|
|
203
|
+
this.#assertActive();
|
|
204
|
+
throwIfStructuralRequestInterrupted(execution.control);
|
|
205
|
+
const key = queryKey(grammarId, queryText);
|
|
206
|
+
const existing = this.#queries.get(key);
|
|
207
|
+
if (existing) {
|
|
208
|
+
touchEntry(this.#queries, key, existing);
|
|
209
|
+
const cache = { state: "hit", retained: true, evictionCount: 0 } as const;
|
|
210
|
+
return { data: execution.execute(existing.query, cache), cache };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const query = execution.compile();
|
|
214
|
+
try {
|
|
215
|
+
throwIfStructuralRequestInterrupted(execution.control);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
safeDelete(query);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
const queryBytes = Buffer.byteLength(queryText, "utf-8");
|
|
221
|
+
if (queryBytes > this.#limits.maxQueryBytes) {
|
|
222
|
+
const cache = { state: "miss", retained: false, evictionCount: 0 } as const;
|
|
223
|
+
try {
|
|
224
|
+
return { data: execution.execute(query, cache), cache };
|
|
225
|
+
} finally {
|
|
226
|
+
safeDelete(query);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const cached = { query, queryBytes };
|
|
231
|
+
try {
|
|
232
|
+
this.#queries.set(key, cached);
|
|
233
|
+
this.#queryBytes += queryBytes;
|
|
234
|
+
const evictionCount = this.#evictQueries();
|
|
235
|
+
const cache = { state: "miss", retained: true, evictionCount } as const;
|
|
236
|
+
return { data: execution.execute(query, cache), cache };
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (this.#queries.get(key) === cached) this.#removeQuery(key, cached);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Delete all canonical trees and compiled queries owned by this store. */
|
|
244
|
+
dispose(): void {
|
|
245
|
+
if (this.#disposed) return;
|
|
246
|
+
this.#disposed = true;
|
|
247
|
+
for (const entry of this.#files.values()) safeDelete(entry.tree);
|
|
248
|
+
for (const entry of this.#queries.values()) safeDelete(entry.query);
|
|
249
|
+
this.#files.clear();
|
|
250
|
+
this.#queries.clear();
|
|
251
|
+
this.#sourceBytes = 0;
|
|
252
|
+
this.#queryBytes = 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#copyCacheHit(
|
|
256
|
+
key: string,
|
|
257
|
+
cached: CachedParsedFile,
|
|
258
|
+
canonicalPath: string,
|
|
259
|
+
grammarId: GrammarId,
|
|
260
|
+
): OwnedParsedFile {
|
|
261
|
+
touchEntry(this.#files, key, cached);
|
|
262
|
+
try {
|
|
263
|
+
return this.#ownedCopy(cached, canonicalPath, grammarId, {
|
|
264
|
+
state: "hit",
|
|
265
|
+
retained: true,
|
|
266
|
+
evictionCount: 0,
|
|
267
|
+
});
|
|
268
|
+
} catch (error) {
|
|
269
|
+
this.#removeFile(key, cached);
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
#ownedCopy(
|
|
275
|
+
cached: CachedParsedFile,
|
|
276
|
+
canonicalPath: string,
|
|
277
|
+
grammarId: GrammarId,
|
|
278
|
+
cache: StructuralCacheObservation,
|
|
279
|
+
): OwnedParsedFile {
|
|
280
|
+
return {
|
|
281
|
+
tree: cached.tree.copy(),
|
|
282
|
+
source: cached.source,
|
|
283
|
+
resolvedPath: canonicalPath,
|
|
284
|
+
grammarId,
|
|
285
|
+
cache,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async #canonicalize(
|
|
290
|
+
filePath: string,
|
|
291
|
+
control: StructuralRequestControl | undefined,
|
|
292
|
+
): Promise<string> {
|
|
293
|
+
try {
|
|
294
|
+
const canonicalPath = await this.#operations.realpath(filePath);
|
|
295
|
+
throwIfStructuralRequestInterrupted(control);
|
|
296
|
+
return canonicalPath;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (isStructuralRequestInterruption(error, control)) {
|
|
299
|
+
throwIfStructuralRequestInterrupted(control);
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
throw new ParsedFileReadError(fileReadMessage(error), { cause: error });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async #readSource(
|
|
307
|
+
filePath: string,
|
|
308
|
+
control: StructuralRequestControl | undefined,
|
|
309
|
+
): Promise<string> {
|
|
310
|
+
try {
|
|
311
|
+
const source = await this.#operations.readFile(filePath, { signal: control?.signal });
|
|
312
|
+
throwIfStructuralRequestInterrupted(control);
|
|
313
|
+
return source;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (isStructuralRequestInterruption(error, control)) {
|
|
316
|
+
throwIfStructuralRequestInterrupted(control);
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
throw new ParsedFileReadError(fileReadMessage(error), { cause: error });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
#evictFiles(): number {
|
|
324
|
+
let evictionCount = 0;
|
|
325
|
+
while (
|
|
326
|
+
this.#files.size > this.#limits.maxFileEntries ||
|
|
327
|
+
this.#sourceBytes > this.#limits.maxSourceBytes
|
|
328
|
+
) {
|
|
329
|
+
const oldest = this.#files.entries().next().value;
|
|
330
|
+
if (!oldest) break;
|
|
331
|
+
this.#removeFile(oldest[0], oldest[1]);
|
|
332
|
+
evictionCount++;
|
|
333
|
+
}
|
|
334
|
+
return evictionCount;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#evictQueries(): number {
|
|
338
|
+
let evictionCount = 0;
|
|
339
|
+
while (
|
|
340
|
+
this.#queries.size > this.#limits.maxQueryEntries ||
|
|
341
|
+
this.#queryBytes > this.#limits.maxQueryBytes
|
|
342
|
+
) {
|
|
343
|
+
const oldest = this.#queries.entries().next().value;
|
|
344
|
+
if (!oldest) break;
|
|
345
|
+
this.#removeQuery(oldest[0], oldest[1]);
|
|
346
|
+
evictionCount++;
|
|
347
|
+
}
|
|
348
|
+
return evictionCount;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
#removeFile(key: string, entry: CachedParsedFile): void {
|
|
352
|
+
if (!this.#files.delete(key)) return;
|
|
353
|
+
this.#sourceBytes -= entry.sourceBytes;
|
|
354
|
+
safeDelete(entry.tree);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
#removeQuery(key: string, entry: CachedQuery): void {
|
|
358
|
+
if (!this.#queries.delete(key)) return;
|
|
359
|
+
this.#queryBytes -= entry.queryBytes;
|
|
360
|
+
safeDelete(entry.query);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#assertActive(): void {
|
|
364
|
+
if (this.#disposed) throw new Error("Parsed-file store has been disposed");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function normalizeQueryExecution<T>(
|
|
369
|
+
compileOrExecution: (() => Query) | QueryExecution<T>,
|
|
370
|
+
legacyExecute:
|
|
371
|
+
| ((query: Pick<Query, "matches">, cache: StructuralCacheObservation) => T)
|
|
372
|
+
| undefined,
|
|
373
|
+
): QueryExecution<T> {
|
|
374
|
+
if (typeof compileOrExecution !== "function") return compileOrExecution;
|
|
375
|
+
if (!legacyExecute) throw new Error("Query execution callback is required");
|
|
376
|
+
return { compile: compileOrExecution, execute: legacyExecute };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function fileReadMessage(error: unknown): string {
|
|
380
|
+
return error instanceof Error ? error.message : "File could not be read";
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function fileKey(canonicalPath: string, grammarId: GrammarId): string {
|
|
384
|
+
return `${grammarId}\0${canonicalPath}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function queryKey(grammarId: GrammarId, queryText: string): string {
|
|
388
|
+
return `${grammarId}\0${queryText}`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function notifyPhase(
|
|
392
|
+
observer: AcquireParsedFileInput["onPhase"],
|
|
393
|
+
phase: "file-read" | "content-hash",
|
|
394
|
+
): void {
|
|
395
|
+
try {
|
|
396
|
+
observer?.(phase);
|
|
397
|
+
} catch {
|
|
398
|
+
// Instrumentation must not alter parsed-file behavior.
|
|
399
|
+
}
|
|
400
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
isCodeRequestDeadlineError,
|
|
4
|
+
isCodeRequestInterrupted,
|
|
5
|
+
throwIfCodeRequestInterrupted,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
7
|
+
|
|
8
|
+
/** Worker-local request control with one shared atomic cancellation slot. */
|
|
9
|
+
export interface StructuralRequestControl extends CodeRequestControl {
|
|
10
|
+
readonly cancellationFlag?: Int32Array;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Test whether caller, deadline, or the parent-side atomic flag stopped work. */
|
|
14
|
+
export function isStructuralRequestInterrupted(
|
|
15
|
+
control: StructuralRequestControl | undefined,
|
|
16
|
+
): boolean {
|
|
17
|
+
return atomicCancellationRequested(control) || isCodeRequestInterrupted(control);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Throw for caller, deadline, or parent-side atomic interruption. */
|
|
21
|
+
export function throwIfStructuralRequestInterrupted(
|
|
22
|
+
control: StructuralRequestControl | undefined,
|
|
23
|
+
): void {
|
|
24
|
+
throwIfCodeRequestInterrupted(control);
|
|
25
|
+
if (atomicCancellationRequested(control)) throw structuralCancellationError();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Identify one error as a Worker-local request interruption. */
|
|
29
|
+
export function isStructuralRequestInterruption(
|
|
30
|
+
error: unknown,
|
|
31
|
+
control: StructuralRequestControl | undefined,
|
|
32
|
+
): boolean {
|
|
33
|
+
return (
|
|
34
|
+
isCodeRequestDeadlineError(error) ||
|
|
35
|
+
control?.signal?.aborted === true ||
|
|
36
|
+
atomicCancellationRequested(control)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function atomicCancellationRequested(control: StructuralRequestControl | undefined): boolean {
|
|
41
|
+
return control?.cancellationFlag ? Atomics.load(control.cancellationFlag, 0) !== 0 : false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function structuralCancellationError(): Error {
|
|
45
|
+
const error = new Error("Structural request cancelled");
|
|
46
|
+
error.name = "AbortError";
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Parser } from "web-tree-sitter";
|
|
2
|
+
import type { GrammarId } from "../types.ts";
|
|
3
|
+
|
|
4
|
+
interface ParserEntryLike {
|
|
5
|
+
readonly parser: Parser;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Reset an interrupted parser, or discard it when reset cannot restore safe reuse. */
|
|
9
|
+
export function resetInterruptedParser<T extends ParserEntryLike>(
|
|
10
|
+
parsers: Map<GrammarId, T>,
|
|
11
|
+
grammarId: GrammarId,
|
|
12
|
+
entry: T,
|
|
13
|
+
): void {
|
|
14
|
+
try {
|
|
15
|
+
entry.parser.reset();
|
|
16
|
+
} catch {
|
|
17
|
+
if (parsers.get(grammarId) === entry) parsers.delete(grammarId);
|
|
18
|
+
try {
|
|
19
|
+
entry.parser.delete();
|
|
20
|
+
} catch {
|
|
21
|
+
// The failed parser is already removed from reuse.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { QueryMatch } from "web-tree-sitter";
|
|
2
|
+
import { nodeToRange } from "../coordinates.ts";
|
|
3
|
+
import type { QueryCapture, TreeSitterResult } from "../types.ts";
|
|
4
|
+
|
|
5
|
+
/** Validate one bounded Tree-sitter query before compilation. */
|
|
6
|
+
export function validateQueryString(queryString: string): TreeSitterResult<QueryCapture[]> | null {
|
|
7
|
+
if (!queryString || queryString.trim().length === 0) {
|
|
8
|
+
return { kind: "validation-error", message: "query is required and must be non-empty" };
|
|
9
|
+
}
|
|
10
|
+
if (queryString.length > MAX_QUERY_LENGTH) {
|
|
11
|
+
return {
|
|
12
|
+
kind: "validation-error",
|
|
13
|
+
message: `query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Convert provider query matches to source-backed captures. */
|
|
20
|
+
export function collectQueryCaptures(matches: QueryMatch[], source: string): QueryCapture[] {
|
|
21
|
+
const captures: QueryCapture[] = [];
|
|
22
|
+
for (const match of matches) {
|
|
23
|
+
for (const { name, node } of match.captures) {
|
|
24
|
+
captures.push({
|
|
25
|
+
name,
|
|
26
|
+
nodeType: node.type,
|
|
27
|
+
range: nodeToRange(node, source),
|
|
28
|
+
text: node.text,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return captures;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Format one runtime failure without exposing more than its first cause. */
|
|
36
|
+
export function formatRuntimeError(error: unknown, fallback = "Operation failed"): string {
|
|
37
|
+
if (!(error instanceof Error)) return String(error || fallback);
|
|
38
|
+
if (error.cause instanceof Error) return `${error.message}: ${error.cause.message}`;
|
|
39
|
+
return error.message || fallback;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Bound query text to reduce pathological query compilation. */
|
|
43
|
+
const MAX_QUERY_LENGTH = 10_000;
|