@mrclrchtr/supi-code-intelligence 4.10.0 → 5.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 +2 -1
- 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 +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 -3
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -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 +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 -3
- 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 +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -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 +114 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -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 +491 -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 +71 -11
- 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 +365 -297
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -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 +467 -115
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
- 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 +13 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -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 +40 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
- 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 +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 -3
- 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 +236 -24
- package/src/analysis/health/recovery.ts +6 -2
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +30 -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 +1 -1
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +46 -32
- package/src/analysis/search/pattern.ts +12 -2
- 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/diagnostics/evidence.ts +51 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-workflow.ts +17 -7
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +148 -0
- package/src/session/health-types.ts +37 -15
- package/src/session/health-workflow.ts +37 -39
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +10 -5
- package/src/session/orientation/collect.ts +10 -1
- 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 +2 -1
- package/src/session/orientation-workflow.ts +15 -8
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +59 -14
- package/src/session/session.ts +4 -1
- 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 +199 -25
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/find/execute.ts +7 -1
- package/src/tool/find/render.ts +20 -3
- package/src/tool/find/tui.ts +23 -21
- package/src/tool/graph/execute.ts +23 -3
- package/src/tool/graph/tui.ts +59 -46
- package/src/tool/health/execute.ts +7 -2
- package/src/tool/health/markdown.ts +56 -14
- package/src/tool/health/refresh-status.ts +105 -0
- package/src/tool/health/tui.ts +95 -98
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/inspect/execute.ts +7 -1
- package/src/tool/inspect/tui.ts +26 -18
- package/src/tool/orientation/execute.ts +21 -3
- package/src/tool/orientation/tui.ts +51 -35
- package/src/tool/refactor-apply/execute.ts +3 -0
- package/src/tool/refactor-apply/tui.ts +43 -25
- package/src/tool/refactor-plan/execute.ts +4 -0
- package/src/tool/refactor-plan/tui.ts +33 -20
- package/src/tool/register.ts +127 -22
- package/src/tool/resolve/execute.ts +18 -1
- package/src/tool/resolve/tui.ts +42 -24
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/find.ts +52 -8
- package/src/tool/result/graph.ts +44 -1
- package/src/tool/result/health.ts +53 -1
- package/src/tool/result/inspect.ts +113 -1
- package/src/tool/result/orientation.ts +66 -14
- package/src/tool/result/refactor.ts +36 -4
- package/src/tool/result/resolve.ts +44 -7
- package/src/tool/result/types.ts +20 -0
- 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
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
getDefaultWorkspaceRuntime,
|
|
4
|
+
throwIfCodeRequestInterrupted,
|
|
5
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
6
|
import {
|
|
3
7
|
getWorkspaceLspRuntime,
|
|
4
8
|
type SemanticReadinessResult,
|
|
@@ -14,7 +18,10 @@ export async function ensureSemanticReadiness(
|
|
|
14
18
|
cwd: string,
|
|
15
19
|
scope: SemanticStartupScope,
|
|
16
20
|
timeoutMs: number = DEFAULT_SEMANTIC_STARTUP_TIMEOUT_MS,
|
|
21
|
+
control?: CodeRequestControl,
|
|
17
22
|
): Promise<SemanticReadinessResult> {
|
|
23
|
+
// A cancelled caller stops before any readiness work starts.
|
|
24
|
+
throwIfCodeRequestInterrupted(control);
|
|
18
25
|
const workspace = getDefaultWorkspaceRuntime().getWorkspace(cwd);
|
|
19
26
|
if (workspace.semantic.provider === null) {
|
|
20
27
|
return {
|
|
@@ -24,7 +31,7 @@ export async function ensureSemanticReadiness(
|
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
if (workspace.semantic.state.kind === "ready") {
|
|
27
|
-
return resolveReadySemanticState(cwd, scope, timeoutMs);
|
|
34
|
+
return resolveReadySemanticState(cwd, scope, timeoutMs, control);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
if (workspace.semantic.state.kind !== "pending") {
|
|
@@ -34,13 +41,15 @@ export async function ensureSemanticReadiness(
|
|
|
34
41
|
};
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
// The request deadline bounds the internal readiness budget when it is
|
|
45
|
+
// earlier than the relative startup timeout.
|
|
46
|
+
const deadline = Math.min(Date.now() + timeoutMs, control?.deadline ?? Infinity);
|
|
38
47
|
|
|
39
48
|
const remainingAfterService = deadline - Date.now();
|
|
40
49
|
if (remainingAfterService <= 0) {
|
|
41
50
|
return { kind: "timeout" };
|
|
42
51
|
}
|
|
43
|
-
const lspState = await resolveSemanticServiceState(cwd, remainingAfterService);
|
|
52
|
+
const lspState = await resolveSemanticServiceState(cwd, remainingAfterService, control);
|
|
44
53
|
if (lspState.kind === "pending") {
|
|
45
54
|
return { kind: "timeout" };
|
|
46
55
|
}
|
|
@@ -58,8 +67,13 @@ export async function ensureSemanticReadiness(
|
|
|
58
67
|
if (remainingAfterLsp <= 0) {
|
|
59
68
|
return { kind: "timeout" };
|
|
60
69
|
}
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
if (scope.kind === "workspace") {
|
|
71
|
+
return control
|
|
72
|
+
? lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs: remainingAfterLsp }, control)
|
|
73
|
+
: lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs: remainingAfterLsp });
|
|
74
|
+
}
|
|
75
|
+
return control
|
|
76
|
+
? lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs: remainingAfterLsp }, control)
|
|
63
77
|
: lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs: remainingAfterLsp });
|
|
64
78
|
}
|
|
65
79
|
|
|
@@ -67,11 +81,17 @@ function resolveReadySemanticState(
|
|
|
67
81
|
cwd: string,
|
|
68
82
|
scope: SemanticStartupScope,
|
|
69
83
|
timeoutMs: number,
|
|
84
|
+
control?: CodeRequestControl,
|
|
70
85
|
): Promise<SemanticReadinessResult> | SemanticReadinessResult {
|
|
71
86
|
const lspState = getWorkspaceLspRuntime(cwd);
|
|
72
87
|
if (lspState.kind === "ready") {
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
if (scope.kind === "workspace") {
|
|
89
|
+
return control
|
|
90
|
+
? lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs }, control)
|
|
91
|
+
: lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs });
|
|
92
|
+
}
|
|
93
|
+
return control
|
|
94
|
+
? lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs }, control)
|
|
75
95
|
: lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs });
|
|
76
96
|
}
|
|
77
97
|
return {
|
|
@@ -86,8 +106,9 @@ function resolveReadySemanticState(
|
|
|
86
106
|
async function resolveSemanticServiceState(
|
|
87
107
|
cwd: string,
|
|
88
108
|
timeoutMs: number,
|
|
109
|
+
control?: CodeRequestControl,
|
|
89
110
|
): Promise<WorkspaceLspRuntimeState> {
|
|
90
111
|
const initialState = getWorkspaceLspRuntime(cwd);
|
|
91
112
|
if (initialState.kind !== "pending") return initialState;
|
|
92
|
-
return waitForWorkspaceLspRuntime(cwd, timeoutMs);
|
|
113
|
+
return waitForWorkspaceLspRuntime(cwd, timeoutMs, control);
|
|
93
114
|
}
|
|
@@ -16,6 +16,7 @@ import { readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:
|
|
|
16
16
|
import { basename, dirname, join } from "node:path";
|
|
17
17
|
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
18
18
|
import type { FileEdit, WorkspaceEdit } from "@mrclrchtr/supi-code-runtime/api";
|
|
19
|
+
import { revalidateMutationAuthority } from "./mutation-authority.ts";
|
|
19
20
|
import { compareCodePositions, createLogicalLineIndex } from "./position.ts";
|
|
20
21
|
import { validateEditAgainstFiles } from "./safety.ts";
|
|
21
22
|
|
|
@@ -24,8 +25,12 @@ export type ApplyResult =
|
|
|
24
25
|
| { kind: "error"; reason: string };
|
|
25
26
|
|
|
26
27
|
export interface ApplyOptions {
|
|
28
|
+
/** Canonical roots authorized by the semantic provider that made the plan. */
|
|
29
|
+
authorizedMutationRoots: readonly string[];
|
|
27
30
|
/** Expected SHA-256 fingerprints per file, validated after queue acquisition. */
|
|
28
31
|
expectedFingerprints?: ReadonlyMap<string, string>;
|
|
32
|
+
/** Current LSP document version, or null when the document is not open. */
|
|
33
|
+
getOpenDocumentVersion?: (file: string) => number | null;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -49,16 +54,24 @@ export interface ApplyOptions {
|
|
|
49
54
|
*/
|
|
50
55
|
export async function applyWorkspaceEdit(
|
|
51
56
|
edit: WorkspaceEdit,
|
|
52
|
-
options: ApplyOptions
|
|
57
|
+
options: ApplyOptions,
|
|
53
58
|
): Promise<ApplyResult> {
|
|
54
59
|
const grouped = groupEditsByFile(edit.edits);
|
|
55
60
|
const files = [...grouped.keys()].sort();
|
|
56
61
|
return withAllMutationQueues(files, async () => {
|
|
62
|
+
const authority = revalidateMutationAuthority(files, options.authorizedMutationRoots);
|
|
63
|
+
if (authority.kind === "unavailable") {
|
|
64
|
+
return { kind: "error", reason: authority.reason };
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
if (options.expectedFingerprints) {
|
|
58
68
|
const freshness = validateFingerprints(files, options.expectedFingerprints);
|
|
59
69
|
if (!freshness.ok) return { kind: "error", reason: freshness.reason };
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
const documentState = validateDocumentPreconditions(edit, options.getOpenDocumentVersion);
|
|
73
|
+
if (!documentState.ok) return { kind: "error", reason: documentState.reason };
|
|
74
|
+
|
|
62
75
|
const validation = validateEditAgainstFiles(edit);
|
|
63
76
|
if (!validation.safe) {
|
|
64
77
|
return { kind: "error", reason: validation.reason };
|
|
@@ -76,6 +89,37 @@ export async function applyWorkspaceEdit(
|
|
|
76
89
|
});
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
function validateDocumentPreconditions(
|
|
93
|
+
edit: WorkspaceEdit,
|
|
94
|
+
getOpenDocumentVersion: ApplyOptions["getOpenDocumentVersion"],
|
|
95
|
+
): { ok: true } | { ok: false; reason: string } {
|
|
96
|
+
const preconditions = edit.documentPreconditions ?? [];
|
|
97
|
+
if (preconditions.length === 0) return { ok: true };
|
|
98
|
+
if (!getOpenDocumentVersion) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: "Stored document preconditions cannot be revalidated.",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const precondition of preconditions) {
|
|
106
|
+
const currentVersion = getOpenDocumentVersion(precondition.file);
|
|
107
|
+
if (precondition.kind === "open-document-version" && currentVersion !== precondition.version) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
reason: `File ${precondition.file} open document version has changed since the plan was generated.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (precondition.kind === "disk-content" && currentVersion !== null) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
reason: `File ${precondition.file} is now open and no longer uses disk content as its master.`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { ok: true };
|
|
121
|
+
}
|
|
122
|
+
|
|
79
123
|
function validateFingerprints(
|
|
80
124
|
files: string[],
|
|
81
125
|
expected: ReadonlyMap<string, string>,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { lstatSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
|
|
4
|
+
export type MutationAuthorityResult =
|
|
5
|
+
| { kind: "authorized"; canonicalRoots: string[] }
|
|
6
|
+
| { kind: "unavailable"; reason: string };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Establish canonical provider roots and verify that all mutation files are
|
|
10
|
+
* regular files inside at least one root.
|
|
11
|
+
*/
|
|
12
|
+
export function establishMutationAuthority(
|
|
13
|
+
files: readonly string[],
|
|
14
|
+
providerRoots: readonly string[],
|
|
15
|
+
): MutationAuthorityResult {
|
|
16
|
+
const roots = canonicalizeProviderRoots(providerRoots);
|
|
17
|
+
if (roots.kind === "unavailable") return roots;
|
|
18
|
+
|
|
19
|
+
const fileValidation = validateFiles(files, roots.canonicalRoots);
|
|
20
|
+
return fileValidation.kind === "unavailable" ? fileValidation : roots;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Revalidate stored canonical roots and mutation files immediately before mutation. */
|
|
24
|
+
export function revalidateMutationAuthority(
|
|
25
|
+
files: readonly string[],
|
|
26
|
+
canonicalRoots: readonly string[],
|
|
27
|
+
): MutationAuthorityResult {
|
|
28
|
+
if (canonicalRoots.length === 0) {
|
|
29
|
+
return { kind: "unavailable", reason: "Mutation plan has no authorized provider root." };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const root of canonicalRoots) {
|
|
33
|
+
if (!path.isAbsolute(root) || path.normalize(root) !== root) {
|
|
34
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" is not canonical.` };
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
if (!lstatSync(root).isDirectory() || realpathSync(root) !== root) {
|
|
38
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" has changed.` };
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" is unavailable.` };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return validateFiles(files, canonicalRoots);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function canonicalizeProviderRoots(providerRoots: readonly string[]): MutationAuthorityResult {
|
|
49
|
+
if (providerRoots.length === 0) {
|
|
50
|
+
return { kind: "unavailable", reason: "Semantic route did not authorize a mutation root." };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const canonicalRoots = new Set<string>();
|
|
54
|
+
for (const root of providerRoots) {
|
|
55
|
+
if (!path.isAbsolute(root)) {
|
|
56
|
+
return { kind: "unavailable", reason: `Mutation root "${root}" is not absolute.` };
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const canonicalRoot = realpathSync(root);
|
|
60
|
+
if (!statSync(canonicalRoot).isDirectory()) {
|
|
61
|
+
return {
|
|
62
|
+
kind: "unavailable",
|
|
63
|
+
reason: `Mutation root "${root}" is not a directory.`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
canonicalRoots.add(canonicalRoot);
|
|
67
|
+
} catch {
|
|
68
|
+
return {
|
|
69
|
+
kind: "unavailable",
|
|
70
|
+
reason: `Mutation root "${root}" cannot be resolved to a canonical directory.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { kind: "authorized", canonicalRoots: [...canonicalRoots].sort() };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateFiles(
|
|
79
|
+
files: readonly string[],
|
|
80
|
+
canonicalRoots: readonly string[],
|
|
81
|
+
): MutationAuthorityResult {
|
|
82
|
+
for (const file of new Set(files)) {
|
|
83
|
+
const validation = validateFile(file, canonicalRoots);
|
|
84
|
+
if (validation) return { kind: "unavailable", reason: validation };
|
|
85
|
+
}
|
|
86
|
+
return { kind: "authorized", canonicalRoots: [...canonicalRoots] };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function validateFile(file: string, canonicalRoots: readonly string[]): string | null {
|
|
90
|
+
if (!path.isAbsolute(file) || path.normalize(file) !== file) {
|
|
91
|
+
return `Mutation file "${file}" is not a normalized absolute path.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let canonicalParent: string;
|
|
95
|
+
try {
|
|
96
|
+
canonicalParent = realpathSync(path.dirname(file));
|
|
97
|
+
} catch {
|
|
98
|
+
return `Mutation file "${file}" does not have a resolvable canonical parent.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
if (!lstatSync(file).isFile()) {
|
|
103
|
+
return `Mutation file "${file}" is not a regular file.`;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
return `Mutation file "${file}" is not a readable regular file.`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let canonicalFile: string;
|
|
110
|
+
try {
|
|
111
|
+
canonicalFile = realpathSync(file);
|
|
112
|
+
} catch {
|
|
113
|
+
return `Mutation file "${file}" cannot be resolved to a canonical file.`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const expectedCanonicalFile = path.join(canonicalParent, path.basename(file));
|
|
117
|
+
if (path.resolve(canonicalFile) !== path.resolve(expectedCanonicalFile)) {
|
|
118
|
+
return `Mutation file "${file}" resolves through an unsupported file link.`;
|
|
119
|
+
}
|
|
120
|
+
if (!canonicalRoots.some((root) => isWithinOrEqual(root, canonicalFile))) {
|
|
121
|
+
return `Mutation file "${file}" is outside the authorized provider roots.`;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isWithinOrEqual(root: string, candidate: string): boolean {
|
|
127
|
+
return isMutationPathWithinRoot(root, candidate, path);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Check canonical path containment with the selected platform path rules. */
|
|
131
|
+
export function isMutationPathWithinRoot(
|
|
132
|
+
root: string,
|
|
133
|
+
candidate: string,
|
|
134
|
+
pathApi: Pick<typeof path, "isAbsolute" | "relative" | "sep">,
|
|
135
|
+
): boolean {
|
|
136
|
+
const difference = pathApi.relative(root, candidate);
|
|
137
|
+
return (
|
|
138
|
+
difference === "" ||
|
|
139
|
+
(difference !== ".." &&
|
|
140
|
+
!difference.startsWith(`..${pathApi.sep}`) &&
|
|
141
|
+
!pathApi.isAbsolute(difference))
|
|
142
|
+
);
|
|
143
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Structural callee lookup — finds direct calls in the enclosing scope at a target position.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
5
6
|
import type { CalleeEntry, CalleeScope, RelationsServiceDeps } from "./types.ts";
|
|
6
7
|
|
|
7
8
|
export interface CalleesResult {
|
|
@@ -26,12 +27,19 @@ export async function collectCallees(
|
|
|
26
27
|
deps: RelationsServiceDeps,
|
|
27
28
|
maxResults?: number,
|
|
28
29
|
depth: "direct" | "deep" = "direct",
|
|
30
|
+
control?: CodeRequestControl,
|
|
29
31
|
): Promise<CalleesResult> {
|
|
30
32
|
if (!deps.provider?.calleesAt) {
|
|
31
33
|
return unavailableCallees(targetName);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
const
|
|
36
|
+
const depthOrOptions = control ? { depth, control } : depth;
|
|
37
|
+
const result = await deps.provider.calleesAt(
|
|
38
|
+
targetFile,
|
|
39
|
+
targetLine,
|
|
40
|
+
targetCharacter,
|
|
41
|
+
depthOrOptions,
|
|
42
|
+
);
|
|
35
43
|
if (result.kind !== "success" || !result.data) {
|
|
36
44
|
return unavailableCallees(targetName);
|
|
37
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
|
|
1
|
+
import type { CodeRequestControl, SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Shared types for the relations analysis modules.
|
|
@@ -58,7 +58,10 @@ export interface RelationsServiceDeps {
|
|
|
58
58
|
file: string,
|
|
59
59
|
line: number,
|
|
60
60
|
character: number,
|
|
61
|
-
|
|
61
|
+
depthOrOptions?:
|
|
62
|
+
| "direct"
|
|
63
|
+
| "deep"
|
|
64
|
+
| { depth?: "direct" | "deep"; control?: CodeRequestControl },
|
|
62
65
|
) => Promise<{
|
|
63
66
|
kind: string;
|
|
64
67
|
data?: {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
1
2
|
import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
|
|
3
|
+
import { truncateIdentity } from "@mrclrchtr/supi-lsp/debug-telemetry";
|
|
2
4
|
import type { StructuralSearchOperation } from "@mrclrchtr/supi-tree-sitter/api";
|
|
3
5
|
import type { StructuredFileAnalysis, StructuredPatternParams } from "./pattern-analysis.ts";
|
|
4
6
|
|
|
@@ -25,8 +27,11 @@ export interface AstScanTimer {
|
|
|
25
27
|
record(input: AstScanTimingInput): void;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
/** Start one AST scan timer
|
|
29
|
-
export function startAstScanTimer(
|
|
30
|
+
/** Start one AST scan timer, recording the workspace root as event-level cwd. */
|
|
31
|
+
export function startAstScanTimer(
|
|
32
|
+
control: CodeRequestControl | undefined,
|
|
33
|
+
cwd: string,
|
|
34
|
+
): AstScanTimer {
|
|
30
35
|
const timer = startDebugTimer();
|
|
31
36
|
return {
|
|
32
37
|
enumerationCompleted() {
|
|
@@ -35,10 +40,12 @@ export function startAstScanTimer(): AstScanTimer {
|
|
|
35
40
|
record(input) {
|
|
36
41
|
timer.finish(
|
|
37
42
|
() => ({
|
|
43
|
+
operationId: control?.operationId,
|
|
38
44
|
source: "code-intelligence",
|
|
39
45
|
level: "debug",
|
|
40
46
|
category: "ast-scan.timing",
|
|
41
47
|
message: `AST ${input.context.params.kind} scan analyzed ${input.analysis.analyzedFileCount} files`,
|
|
48
|
+
cwd: truncateIdentity(cwd),
|
|
42
49
|
data: {
|
|
43
50
|
kind: input.context.params.kind,
|
|
44
51
|
operation: input.operation,
|
|
@@ -292,7 +292,7 @@ class AstFileEnumerator {
|
|
|
292
292
|
#checkControl(): boolean {
|
|
293
293
|
this.options.signal?.throwIfAborted();
|
|
294
294
|
if (this.#safetyLimited) return false;
|
|
295
|
-
if (this.#now()
|
|
295
|
+
if (this.#now() < this.options.deadline) return true;
|
|
296
296
|
this.#markTimeout();
|
|
297
297
|
return false;
|
|
298
298
|
}
|
|
@@ -24,7 +24,7 @@ export async function settleByDeadline<T>(
|
|
|
24
24
|
): Promise<DeadlineOutcome<T>> {
|
|
25
25
|
control.signal?.throwIfAborted();
|
|
26
26
|
const remainingMs = control.deadline - control.now();
|
|
27
|
-
if (remainingMs
|
|
27
|
+
if (remainingMs <= 0) return { kind: "timeout" };
|
|
28
28
|
|
|
29
29
|
return new Promise<DeadlineOutcome<T>>((resolve, reject) => {
|
|
30
30
|
let settled = false;
|
|
@@ -70,7 +70,7 @@ export async function settleByDeadline<T>(
|
|
|
70
70
|
.then(
|
|
71
71
|
(value) => {
|
|
72
72
|
complete(
|
|
73
|
-
control.now()
|
|
73
|
+
control.now() >= control.deadline ? { kind: "timeout" } : { kind: "completed", value },
|
|
74
74
|
);
|
|
75
75
|
},
|
|
76
76
|
(error: unknown) => fail(error),
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
type CodeResult,
|
|
4
|
+
isCodeRequestDeadlineError,
|
|
5
|
+
isCodeRequestInterruption,
|
|
6
|
+
type OutlineData,
|
|
7
|
+
type StructuralProvider as StructuralSubstrate,
|
|
5
8
|
} from "@mrclrchtr/supi-code-runtime/api";
|
|
6
9
|
import type { CodeFindAstKind } from "../../tool/find/ast-kinds.ts";
|
|
7
10
|
import type { AstScanLimitation } from "./ast-scan.ts";
|
|
8
11
|
import { callableExpressionForMatching } from "./call-name.ts";
|
|
9
|
-
import { settleByDeadline } from "./deadline.ts";
|
|
12
|
+
import { type DeadlineOutcome, settleByDeadline } from "./deadline.ts";
|
|
10
13
|
import { relativeDisplayPath } from "./paths.ts";
|
|
11
14
|
|
|
12
15
|
export interface StructuredPatternParams {
|
|
@@ -52,6 +55,7 @@ interface AnalyzeStructuredFilesOptions {
|
|
|
52
55
|
readonly deadline: number;
|
|
53
56
|
readonly now: () => number;
|
|
54
57
|
readonly signal?: AbortSignal;
|
|
58
|
+
readonly requestControl: CodeRequestControl;
|
|
55
59
|
readonly initialLimitations: readonly AstScanLimitation[];
|
|
56
60
|
}
|
|
57
61
|
|
|
@@ -67,34 +71,43 @@ export async function analyzeStructuredFiles(
|
|
|
67
71
|
|
|
68
72
|
for (const [index, absoluteFile] of options.files.entries()) {
|
|
69
73
|
options.signal?.throwIfAborted();
|
|
70
|
-
if (options.now()
|
|
74
|
+
if (options.now() >= options.deadline) {
|
|
71
75
|
addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
|
|
72
76
|
break;
|
|
73
77
|
}
|
|
74
78
|
const relativeFile = relativeDisplayPath(options.displayBase, absoluteFile);
|
|
75
79
|
const fileMatches: StructuredMatch[] = [];
|
|
76
80
|
const fileFailures: StructuredFailure[] = [];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
let outcome: DeadlineOutcome<void>;
|
|
82
|
+
try {
|
|
83
|
+
outcome = await settleByDeadline(
|
|
84
|
+
async () => {
|
|
85
|
+
try {
|
|
86
|
+
await collectMatchesForFile(
|
|
87
|
+
fileMatches,
|
|
88
|
+
fileFailures,
|
|
89
|
+
options.structural,
|
|
90
|
+
relativeFile,
|
|
91
|
+
options.params.kind,
|
|
92
|
+
matcher,
|
|
93
|
+
options.requestControl,
|
|
94
|
+
);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (isCodeRequestInterruption(error, options.requestControl)) throw error;
|
|
97
|
+
fileFailures.push({
|
|
98
|
+
file: relativeFile,
|
|
99
|
+
kind: "runtime-error",
|
|
100
|
+
reason: errorMessage(error),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{ deadline: options.deadline, now: options.now, signal: options.signal },
|
|
105
|
+
);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!isCodeRequestDeadlineError(error)) throw error;
|
|
108
|
+
addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
98
111
|
if (outcome.kind === "timeout") {
|
|
99
112
|
addAnalysisTimeout(limitations, options.files.slice(index), options.displayBase);
|
|
100
113
|
break;
|
|
@@ -137,13 +150,14 @@ async function collectMatchesForFile(
|
|
|
137
150
|
relFile: string,
|
|
138
151
|
kind: CodeFindAstKind,
|
|
139
152
|
matcher: (value: string) => boolean,
|
|
153
|
+
requestControl: CodeRequestControl,
|
|
140
154
|
): Promise<void> {
|
|
141
155
|
const recordFailure = (kind: StructuredFailureKind, reason: string) => {
|
|
142
156
|
failures.push({ file: relFile, kind, reason });
|
|
143
157
|
};
|
|
144
158
|
|
|
145
159
|
if (kind === "definition") {
|
|
146
|
-
const outline = await structural.outline(relFile);
|
|
160
|
+
const outline = await structural.outline(relFile, requestControl);
|
|
147
161
|
if (!handleStructuralResult(outline, recordFailure)) return;
|
|
148
162
|
for (const item of flattenOutlineItems(outline.data)) {
|
|
149
163
|
if (!matcher(item.name)) continue;
|
|
@@ -153,7 +167,7 @@ async function collectMatchesForFile(
|
|
|
153
167
|
}
|
|
154
168
|
|
|
155
169
|
if (kind === "export") {
|
|
156
|
-
const exportsResult = await structural.exports(relFile);
|
|
170
|
+
const exportsResult = await structural.exports(relFile, requestControl);
|
|
157
171
|
if (!handleStructuralResult(exportsResult, recordFailure)) return;
|
|
158
172
|
for (const item of exportsResult.data) {
|
|
159
173
|
if (!matcher(item.name)) continue;
|
|
@@ -163,7 +177,7 @@ async function collectMatchesForFile(
|
|
|
163
177
|
}
|
|
164
178
|
|
|
165
179
|
if (kind === "import") {
|
|
166
|
-
const importsResult = await structural.imports(relFile);
|
|
180
|
+
const importsResult = await structural.imports(relFile, requestControl);
|
|
167
181
|
if (!handleStructuralResult(importsResult, recordFailure)) return;
|
|
168
182
|
for (const item of importsResult.data) {
|
|
169
183
|
if (!matcher(item.moduleSpecifier)) continue;
|
|
@@ -178,7 +192,7 @@ async function collectMatchesForFile(
|
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
if (kind === "call") {
|
|
181
|
-
const callResult = await structural.callSites(relFile);
|
|
195
|
+
const callResult = await structural.callSites(relFile, requestControl);
|
|
182
196
|
if (!handleStructuralResult(callResult, recordFailure)) return;
|
|
183
197
|
for (const call of callResult.data) {
|
|
184
198
|
if (!matcher(callableExpressionForMatching(call.name))) continue;
|
|
@@ -187,7 +201,7 @@ async function collectMatchesForFile(
|
|
|
187
201
|
return;
|
|
188
202
|
}
|
|
189
203
|
|
|
190
|
-
const outline = await structural.outline(relFile);
|
|
204
|
+
const outline = await structural.outline(relFile, requestControl);
|
|
191
205
|
if (!handleStructuralResult(outline, recordFailure)) return;
|
|
192
206
|
for (const item of flattenOutlineItems(outline.data)) {
|
|
193
207
|
if (kind === "type" && !TYPE_KIND.test(item.kind.toLowerCase())) continue;
|
|
@@ -56,11 +56,14 @@ export type StructuredPatternOutcome =
|
|
|
56
56
|
|
|
57
57
|
/** Internal controls for deterministic deadline, filesystem, and cancellation tests. */
|
|
58
58
|
export interface StructuredPatternControl {
|
|
59
|
+
readonly operationId?: string;
|
|
59
60
|
readonly signal?: AbortSignal;
|
|
60
61
|
readonly now?: () => number;
|
|
61
62
|
readonly operations?: AstScanOperations;
|
|
62
63
|
readonly maxFiles?: number;
|
|
63
64
|
readonly timeoutMs?: number;
|
|
65
|
+
/** Optional caller deadline; the earlier caller/policy deadline wins. */
|
|
66
|
+
readonly deadline?: number;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
export interface StructuredPatternSearchOptions {
|
|
@@ -80,11 +83,12 @@ export interface StructuredPatternSearchOptions {
|
|
|
80
83
|
export async function getStructuredPatternMatches(
|
|
81
84
|
options: StructuredPatternSearchOptions,
|
|
82
85
|
): Promise<StructuredPatternOutcome> {
|
|
83
|
-
const scanTimer = startAstScanTimer();
|
|
86
|
+
const scanTimer = startAstScanTimer(options.control, options.cwd);
|
|
84
87
|
const now = options.control?.now ?? Date.now;
|
|
85
88
|
const maxFiles = options.control?.maxFiles ?? DEFAULT_AST_SCAN_MAX_FILES;
|
|
86
89
|
const timeoutMs = options.control?.timeoutMs ?? DEFAULT_AST_SCAN_TIMEOUT_MS;
|
|
87
|
-
const
|
|
90
|
+
const policyDeadline = now() + timeoutMs;
|
|
91
|
+
const deadline = Math.min(options.control?.deadline ?? Number.POSITIVE_INFINITY, policyDeadline);
|
|
88
92
|
const roots = Array.isArray(options.roots) ? [...options.roots] : [options.roots];
|
|
89
93
|
const operation = structuralOperationForKind(options.params.kind);
|
|
90
94
|
options.control?.signal?.throwIfAborted();
|
|
@@ -127,6 +131,11 @@ export async function getStructuredPatternMatches(
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
scanTimer.enumerationCompleted();
|
|
134
|
+
const requestControl = {
|
|
135
|
+
operationId: options.control?.operationId,
|
|
136
|
+
signal: options.control?.signal,
|
|
137
|
+
deadline,
|
|
138
|
+
};
|
|
130
139
|
const analysis = await analyzeStructuredFiles({
|
|
131
140
|
files: enumeration.files,
|
|
132
141
|
displayBase: enumeration.displayBase,
|
|
@@ -135,6 +144,7 @@ export async function getStructuredPatternMatches(
|
|
|
135
144
|
deadline,
|
|
136
145
|
now,
|
|
137
146
|
signal: options.control?.signal,
|
|
147
|
+
requestControl,
|
|
138
148
|
initialLimitations: enumeration.limitations,
|
|
139
149
|
});
|
|
140
150
|
const capabilityMismatches = analysis.failures.filter(
|