@mrclrchtr/supi-code-intelligence 4.9.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/debug-timing.ts +34 -21
- 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/debug-timing.ts +34 -21
- 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 +215 -0
- 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 +370 -266
- 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 +238 -11
- 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 +26 -6
- 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/debug-timing.ts +34 -21
- 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/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +7 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- 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 +59 -20
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- package/src/analysis/refactor/position.ts +47 -0
- package/src/analysis/refactor/safety.ts +18 -10
- 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 -4
- 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 -247
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodeQueryResult,
|
|
3
|
+
partialCodeQuery,
|
|
4
|
+
unavailableCodeQuery,
|
|
5
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
6
|
+
import type {
|
|
7
|
+
Diagnostic,
|
|
8
|
+
DocumentDiagnosticReport,
|
|
9
|
+
PublishDiagnosticsParams,
|
|
10
|
+
} from "../config/types.ts";
|
|
11
|
+
import type { DiagnosticStateWait } from "./client-diagnostic-waiters.ts";
|
|
12
|
+
|
|
13
|
+
/** Validate one untrusted LSP diagnostic publication before it enters the cache. */
|
|
14
|
+
export function isValidPublishDiagnosticsParams(value: unknown): value is PublishDiagnosticsParams {
|
|
15
|
+
if (!isRecord(value) || !isValidUri(value.uri)) return false;
|
|
16
|
+
if (value.version !== undefined && !isInteger(value.version)) return false;
|
|
17
|
+
return Array.isArray(value.diagnostics) && value.diagnostics.every(isValidDiagnostic);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Validate one untrusted LSP pull report before it enters the cache. */
|
|
21
|
+
export function isValidDocumentDiagnosticReport(
|
|
22
|
+
value: unknown,
|
|
23
|
+
allowRelatedDocuments = true,
|
|
24
|
+
): value is DocumentDiagnosticReport {
|
|
25
|
+
if (!isRecord(value)) return false;
|
|
26
|
+
// gopls v0.23.0 ships a non-conforming pull report with an empty `kind`
|
|
27
|
+
// discriminator and no resultId (upstream work in progress). Treat a
|
|
28
|
+
// missing/empty kind with an items array as a full report; every other
|
|
29
|
+
// kind value stays strict.
|
|
30
|
+
if (value.kind === "full" || value.kind === undefined || value.kind === "") {
|
|
31
|
+
if (!Array.isArray(value.items) || !value.items.every(isValidDiagnostic)) return false;
|
|
32
|
+
if (value.resultId !== undefined && typeof value.resultId !== "string") return false;
|
|
33
|
+
} else if (value.kind === "unchanged") {
|
|
34
|
+
if (typeof value.resultId !== "string") return false;
|
|
35
|
+
} else {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return allowRelatedDocuments
|
|
39
|
+
? isValidRelatedDocuments(value.relatedDocuments)
|
|
40
|
+
: value.relatedDocuments === undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isValidRelatedDocuments(value: unknown): boolean {
|
|
44
|
+
if (value === undefined) return true;
|
|
45
|
+
if (!isRecord(value)) return false;
|
|
46
|
+
return Object.entries(value).every(
|
|
47
|
+
([uri, report]) => isValidUri(uri) && isValidDocumentDiagnosticReport(report, false),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isValidDiagnostic(value: unknown): value is Diagnostic {
|
|
52
|
+
if (!isRecord(value) || !isValidRange(value.range) || !isValidDiagnosticMessage(value.message)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return (
|
|
56
|
+
isValidOptionalSeverity(value.severity) &&
|
|
57
|
+
isValidOptionalCode(value.code) &&
|
|
58
|
+
isValidOptionalCodeDescription(value.codeDescription, value.code) &&
|
|
59
|
+
isValidOptionalSource(value.source) &&
|
|
60
|
+
isValidOptionalTags(value.tags) &&
|
|
61
|
+
isValidOptionalRelatedInformation(value.relatedInformation)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isValidOptionalSeverity(value: unknown): boolean {
|
|
66
|
+
return value === undefined || isDiagnosticSeverity(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isValidOptionalCode(value: unknown): boolean {
|
|
70
|
+
return value === undefined || isInteger(value) || typeof value === "string";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isValidOptionalCodeDescription(value: unknown, code: unknown): boolean {
|
|
74
|
+
return value === undefined || (code !== undefined && isRecord(value) && isValidUri(value.href));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isValidOptionalSource(value: unknown): boolean {
|
|
78
|
+
return value === undefined || typeof value === "string";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isValidOptionalTags(value: unknown): boolean {
|
|
82
|
+
return (
|
|
83
|
+
value === undefined || (Array.isArray(value) && value.every((tag) => tag === 1 || tag === 2))
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isValidOptionalRelatedInformation(value: unknown): boolean {
|
|
88
|
+
return (
|
|
89
|
+
value === undefined ||
|
|
90
|
+
(Array.isArray(value) && value.every(isValidDiagnosticRelatedInformation))
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isValidDiagnosticMessage(value: unknown): boolean {
|
|
95
|
+
if (typeof value === "string") return true;
|
|
96
|
+
return (
|
|
97
|
+
isRecord(value) &&
|
|
98
|
+
(value.kind === "plaintext" || value.kind === "markdown") &&
|
|
99
|
+
typeof value.value === "string"
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isValidDiagnosticRelatedInformation(value: unknown): boolean {
|
|
104
|
+
return isRecord(value) && isValidLocation(value.location) && typeof value.message === "string";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isValidLocation(value: unknown): boolean {
|
|
108
|
+
return isRecord(value) && isValidUri(value.uri) && isValidRange(value.range);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isValidRange(value: unknown): value is {
|
|
112
|
+
start: { line: number; character: number };
|
|
113
|
+
end: { line: number; character: number };
|
|
114
|
+
} {
|
|
115
|
+
if (!isRecord(value) || !isValidPosition(value.start) || !isValidPosition(value.end)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return (
|
|
119
|
+
value.start.line < value.end.line ||
|
|
120
|
+
(value.start.line === value.end.line && value.start.character <= value.end.character)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isValidPosition(value: unknown): value is { line: number; character: number } {
|
|
125
|
+
return (
|
|
126
|
+
isRecord(value) &&
|
|
127
|
+
isInteger(value.line) &&
|
|
128
|
+
value.line >= 0 &&
|
|
129
|
+
value.line <= 2_147_483_647 &&
|
|
130
|
+
isInteger(value.character) &&
|
|
131
|
+
value.character >= 0 &&
|
|
132
|
+
value.character <= 2_147_483_647
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isValidUri(value: unknown): value is string {
|
|
137
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
138
|
+
try {
|
|
139
|
+
return new URL(value).protocol.length > 0;
|
|
140
|
+
} catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isDiagnosticSeverity(value: unknown): boolean {
|
|
146
|
+
return value === 1 || value === 2 || value === 3 || value === 4;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isInteger(value: unknown): value is number {
|
|
150
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
154
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Stored diagnostic data and the protocol evidence that established it. */
|
|
158
|
+
export interface DiagnosticCacheEntry {
|
|
159
|
+
diagnostics: Diagnostic[];
|
|
160
|
+
receivedAt: number;
|
|
161
|
+
source: "pull" | "push";
|
|
162
|
+
synchronizationId?: number;
|
|
163
|
+
evidenceRevision?: number;
|
|
164
|
+
version?: number;
|
|
165
|
+
resultId?: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** One document synchronization that needs current diagnostic evidence. */
|
|
169
|
+
export interface DiagnosticSynchronization {
|
|
170
|
+
readonly uri: string;
|
|
171
|
+
readonly synchronizationId: number;
|
|
172
|
+
readonly evidenceRevision?: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Monotonic document version state retained across close and reopen. */
|
|
176
|
+
export type DocumentVersionHistory = Map<string, number>;
|
|
177
|
+
|
|
178
|
+
/** Allocate the next document version for one URI. */
|
|
179
|
+
export function nextDocumentVersion(history: DocumentVersionHistory, uri: string): number {
|
|
180
|
+
const version = (history.get(uri) ?? 0) + 1;
|
|
181
|
+
history.set(uri, version);
|
|
182
|
+
return version;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Test whether a synchronization still names the open document state. */
|
|
186
|
+
export function isCurrentSynchronization(
|
|
187
|
+
openDocs: ReadonlyMap<string, { synchronizationId: number; evidenceRevision?: number }>,
|
|
188
|
+
synchronization: DiagnosticSynchronization,
|
|
189
|
+
): boolean {
|
|
190
|
+
const document = openDocs.get(synchronization.uri);
|
|
191
|
+
return Boolean(
|
|
192
|
+
document?.synchronizationId === synchronization.synchronizationId &&
|
|
193
|
+
(synchronization.evidenceRevision === undefined ||
|
|
194
|
+
document.evidenceRevision === synchronization.evidenceRevision),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Test whether a fresh push confirms the supplied synchronization. */
|
|
199
|
+
export function hasFreshPush(
|
|
200
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
201
|
+
synchronization: DiagnosticSynchronization,
|
|
202
|
+
currentEvidenceRevision?: number,
|
|
203
|
+
): boolean {
|
|
204
|
+
const entry = store.get(synchronization.uri);
|
|
205
|
+
return Boolean(
|
|
206
|
+
entry?.source === "push" &&
|
|
207
|
+
entry.synchronizationId === synchronization.synchronizationId &&
|
|
208
|
+
(synchronization.evidenceRevision === undefined ||
|
|
209
|
+
entry.evidenceRevision === synchronization.evidenceRevision) &&
|
|
210
|
+
(currentEvidenceRevision === undefined || entry.evidenceRevision === currentEvidenceRevision),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Test whether pull or push evidence confirms the supplied synchronization. */
|
|
215
|
+
export function hasFreshEvidence(
|
|
216
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
217
|
+
synchronization: DiagnosticSynchronization,
|
|
218
|
+
currentEvidenceRevision?: number,
|
|
219
|
+
): boolean {
|
|
220
|
+
const entry = store.get(synchronization.uri);
|
|
221
|
+
return Boolean(
|
|
222
|
+
entry &&
|
|
223
|
+
entry.synchronizationId === synchronization.synchronizationId &&
|
|
224
|
+
(synchronization.evidenceRevision === undefined ||
|
|
225
|
+
entry.evidenceRevision === synchronization.evidenceRevision) &&
|
|
226
|
+
(currentEvidenceRevision === undefined || entry.evidenceRevision === currentEvidenceRevision),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Return the latest cache update that confirms one of the synchronizations. */
|
|
231
|
+
export function latestFreshEvidenceReceivedAt(
|
|
232
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
233
|
+
synchronizations: DiagnosticSynchronization[],
|
|
234
|
+
currentEvidenceRevision?: number,
|
|
235
|
+
): number {
|
|
236
|
+
let latest = 0;
|
|
237
|
+
for (const synchronization of synchronizations) {
|
|
238
|
+
const entry = store.get(synchronization.uri);
|
|
239
|
+
if (hasFreshEvidence(store, synchronization, currentEvidenceRevision)) {
|
|
240
|
+
latest = Math.max(latest, entry?.receivedAt ?? 0);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return latest;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
interface PullRaceOptions {
|
|
247
|
+
readonly pull: Promise<boolean>;
|
|
248
|
+
readonly waitForChange: () => DiagnosticStateWait;
|
|
249
|
+
readonly freshPush: () => boolean;
|
|
250
|
+
readonly current: () => boolean;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Race a pull against fresh push evidence and lifecycle release. */
|
|
254
|
+
export async function raceDiagnosticPull(
|
|
255
|
+
options: PullRaceOptions,
|
|
256
|
+
): Promise<"pull" | "push" | "released"> {
|
|
257
|
+
const pull = options.pull.then((confirmed) =>
|
|
258
|
+
confirmed ? ("pull" as const) : ("released" as const),
|
|
259
|
+
);
|
|
260
|
+
while (options.current()) {
|
|
261
|
+
if (options.freshPush()) return "push";
|
|
262
|
+
const change = options.waitForChange();
|
|
263
|
+
let outcome: "changed" | "pull" | "released";
|
|
264
|
+
try {
|
|
265
|
+
outcome = await Promise.race([pull, change.promise.then(() => "changed" as const)]);
|
|
266
|
+
} finally {
|
|
267
|
+
change.cancel();
|
|
268
|
+
}
|
|
269
|
+
if (outcome !== "changed") return outcome;
|
|
270
|
+
}
|
|
271
|
+
return "released";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Return explicit partial or unavailable evidence after fresh collection fails. */
|
|
275
|
+
export function incompleteDiagnosticResult(
|
|
276
|
+
cachedDiagnostics: Diagnostic[] | null,
|
|
277
|
+
outcome: "released" | "timed-out",
|
|
278
|
+
): CodeQueryResult<Diagnostic[]> {
|
|
279
|
+
if (outcome === "released") {
|
|
280
|
+
return unavailableCodeQuery(
|
|
281
|
+
"Diagnostic collection ended before the current document synchronization was confirmed.",
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (cachedDiagnostics) {
|
|
285
|
+
return partialCodeQuery(
|
|
286
|
+
cachedDiagnostics,
|
|
287
|
+
"Fresh diagnostics were not confirmed for the current document synchronization; cached diagnostics are partial evidence.",
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return unavailableCodeQuery(
|
|
291
|
+
"Fresh diagnostics were not confirmed for the current document synchronization.",
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
interface ApplyPullReportOptions {
|
|
296
|
+
readonly store: Map<string, DiagnosticCacheEntry>;
|
|
297
|
+
readonly uri: string;
|
|
298
|
+
readonly report: DocumentDiagnosticReport;
|
|
299
|
+
readonly previous: DiagnosticCacheEntry | undefined;
|
|
300
|
+
readonly previousResultId: string | undefined;
|
|
301
|
+
readonly synchronizationId: number | undefined;
|
|
302
|
+
readonly evidenceRevision: number;
|
|
303
|
+
readonly isRelatedUriTracked: (uri: string) => boolean;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Apply one valid full or linked unchanged pull report. */
|
|
307
|
+
export function applyPullReport(options: ApplyPullReportOptions): boolean {
|
|
308
|
+
if (!isValidDocumentDiagnosticReport(options.report)) return false;
|
|
309
|
+
const {
|
|
310
|
+
store,
|
|
311
|
+
uri,
|
|
312
|
+
report,
|
|
313
|
+
previous,
|
|
314
|
+
previousResultId,
|
|
315
|
+
synchronizationId,
|
|
316
|
+
evidenceRevision,
|
|
317
|
+
isRelatedUriTracked,
|
|
318
|
+
} = options;
|
|
319
|
+
// A report validated as full (kind "full", "", or absent — gopls v0.23.0)
|
|
320
|
+
// stores its items; only an explicit "unchanged" reuses the previous entry.
|
|
321
|
+
if (report.kind !== "unchanged") {
|
|
322
|
+
if (!Array.isArray(report.items)) return false;
|
|
323
|
+
if (report.resultId !== undefined && typeof report.resultId !== "string") return false;
|
|
324
|
+
store.set(uri, {
|
|
325
|
+
diagnostics: report.items,
|
|
326
|
+
receivedAt: Date.now(),
|
|
327
|
+
source: "pull",
|
|
328
|
+
synchronizationId,
|
|
329
|
+
evidenceRevision,
|
|
330
|
+
resultId: report.resultId,
|
|
331
|
+
});
|
|
332
|
+
applyRelatedPullReports(store, report, evidenceRevision, isRelatedUriTracked);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
if (!previous || previousResultId === undefined || typeof report.resultId !== "string") {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
store.set(uri, {
|
|
339
|
+
...previous,
|
|
340
|
+
receivedAt: Date.now(),
|
|
341
|
+
source: "pull",
|
|
342
|
+
synchronizationId,
|
|
343
|
+
evidenceRevision,
|
|
344
|
+
resultId: report.resultId,
|
|
345
|
+
});
|
|
346
|
+
applyRelatedPullReports(store, report, evidenceRevision, isRelatedUriTracked);
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function applyRelatedPullReports(
|
|
351
|
+
store: Map<string, DiagnosticCacheEntry>,
|
|
352
|
+
report: DocumentDiagnosticReport,
|
|
353
|
+
evidenceRevision: number,
|
|
354
|
+
isRelatedUriTracked: (uri: string) => boolean,
|
|
355
|
+
): void {
|
|
356
|
+
for (const [relatedUri, relatedReport] of Object.entries(report.relatedDocuments ?? {})) {
|
|
357
|
+
if (!isValidDocumentDiagnosticReport(relatedReport, false)) continue;
|
|
358
|
+
// Skip explicit unchanged reports; full reports (kind "full", "", or
|
|
359
|
+
// absent) may enter the related-document store.
|
|
360
|
+
if (relatedReport.kind === "unchanged") continue;
|
|
361
|
+
if (isRelatedUriTracked(relatedUri)) continue;
|
|
362
|
+
const existing = store.get(relatedUri);
|
|
363
|
+
if (existing?.evidenceRevision === evidenceRevision) continue;
|
|
364
|
+
store.set(relatedUri, {
|
|
365
|
+
diagnostics: relatedReport.items,
|
|
366
|
+
receivedAt: Date.now(),
|
|
367
|
+
source: "pull",
|
|
368
|
+
evidenceRevision,
|
|
369
|
+
resultId: relatedReport.resultId,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DocumentDiagnosticReport } from "../config/types.ts";
|
|
2
|
+
import type { DiagnosticPullRequest } from "./client-diagnostic-request.ts";
|
|
3
|
+
|
|
4
|
+
/** Transport and capability operations required by diagnostic state. */
|
|
5
|
+
export interface ClientDiagnosticsHost {
|
|
6
|
+
/** Configured server name, for debug-telemetry identity. */
|
|
7
|
+
server: string;
|
|
8
|
+
/** Absolute workspace root, for debug-telemetry identity. */
|
|
9
|
+
cwd?: string;
|
|
10
|
+
isOperational(): boolean;
|
|
11
|
+
supportsPullDiagnostics(): boolean;
|
|
12
|
+
sendNotification(method: string, params: unknown): void;
|
|
13
|
+
pullDocumentDiagnostics(request: DiagnosticPullRequest): Promise<DocumentDiagnosticReport | null>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CodeRequestDeadlineError } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
+
import type { DocumentDiagnosticReport } from "../config/types.ts";
|
|
3
|
+
import { applyPullReport, type DiagnosticCacheEntry } from "./client-diagnostic-evidence.ts";
|
|
4
|
+
import type { DiagnosticPullRequest } from "./client-diagnostic-request.ts";
|
|
5
|
+
|
|
6
|
+
interface PullDiagnosticEvidenceOptions {
|
|
7
|
+
store: Map<string, DiagnosticCacheEntry>;
|
|
8
|
+
uri: string;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
synchronizationId?: number;
|
|
11
|
+
evidenceRevision: number;
|
|
12
|
+
currentRevision(): number;
|
|
13
|
+
isCurrentSynchronization(): boolean;
|
|
14
|
+
isRelatedUriTracked(uri: string): boolean;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
deadline?: number;
|
|
17
|
+
operationId?: string;
|
|
18
|
+
pull(request: DiagnosticPullRequest): Promise<DocumentDiagnosticReport | null>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Collect one pull report only if its document and workspace generations remain current. */
|
|
22
|
+
export async function pullDiagnosticEvidence(
|
|
23
|
+
options: PullDiagnosticEvidenceOptions,
|
|
24
|
+
): Promise<boolean> {
|
|
25
|
+
const previous = options.store.get(options.uri);
|
|
26
|
+
const previousResultId = previous?.resultId;
|
|
27
|
+
const report = await options.pull({
|
|
28
|
+
uri: options.uri,
|
|
29
|
+
previousResultId,
|
|
30
|
+
timeoutMs: options.timeoutMs,
|
|
31
|
+
signal: options.signal,
|
|
32
|
+
deadline: options.deadline,
|
|
33
|
+
operationId: options.operationId,
|
|
34
|
+
});
|
|
35
|
+
// Final gate: a report that lands after cancellation or deadline expiry
|
|
36
|
+
// must not be applied as evidence.
|
|
37
|
+
if (options.signal?.aborted) throw options.signal.reason;
|
|
38
|
+
if (options.deadline !== undefined && Date.now() >= options.deadline) {
|
|
39
|
+
throw new CodeRequestDeadlineError();
|
|
40
|
+
}
|
|
41
|
+
if (report === null || report === undefined) {
|
|
42
|
+
throw new Error("Invalid LSP diagnostic report.");
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
options.evidenceRevision !== options.currentRevision() ||
|
|
46
|
+
(options.synchronizationId !== undefined && !options.isCurrentSynchronization())
|
|
47
|
+
) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const applied = applyPullReport({
|
|
51
|
+
store: options.store,
|
|
52
|
+
uri: options.uri,
|
|
53
|
+
report,
|
|
54
|
+
previous,
|
|
55
|
+
previousResultId,
|
|
56
|
+
synchronizationId: options.synchronizationId,
|
|
57
|
+
evidenceRevision: options.evidenceRevision,
|
|
58
|
+
isRelatedUriTracked: options.isRelatedUriTracked,
|
|
59
|
+
});
|
|
60
|
+
if (!applied) throw new Error("Invalid LSP diagnostic report.");
|
|
61
|
+
return true;
|
|
62
|
+
}
|