@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.
Files changed (203) hide show
  1. package/README.md +2 -1
  2. package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  3. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  6. package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  8. package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  9. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  10. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  11. package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  12. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  13. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  14. package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -5
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  20. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  21. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  22. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  24. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  26. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  27. package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
  28. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -0
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +114 -0
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
  33. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -0
  34. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
  35. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +491 -0
  36. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
  37. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +71 -11
  38. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +365 -297
  40. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -0
  42. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +467 -115
  44. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
  45. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
  46. package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
  47. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
  50. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +13 -4
  51. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
  52. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
  53. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -96
  54. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
  55. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
  56. package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
  57. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
  58. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
  59. package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
  60. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
  61. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +40 -0
  62. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
  63. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
  64. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
  65. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +17 -7
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  73. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  74. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  75. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  76. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  77. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  78. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
  80. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
  81. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
  82. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
  83. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
  84. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
  85. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
  86. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
  87. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
  88. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
  89. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
  90. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
  91. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
  92. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -3
  96. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
  97. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
  98. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
  99. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
  100. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
  101. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
  102. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
  103. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
  104. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
  105. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
  106. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
  107. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
  108. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
  109. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
  110. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
  111. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
  112. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
  113. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
  114. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
  115. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
  116. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
  117. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
  118. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
  119. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
  120. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
  121. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
  122. package/package.json +5 -5
  123. package/src/analysis/health/diagnostics.ts +236 -24
  124. package/src/analysis/health/recovery.ts +6 -2
  125. package/src/analysis/provider.ts +58 -0
  126. package/src/analysis/readiness.ts +30 -9
  127. package/src/analysis/refactor/apply.ts +45 -1
  128. package/src/analysis/refactor/mutation-authority.ts +143 -0
  129. package/src/analysis/relations/callees.ts +9 -1
  130. package/src/analysis/relations/types.ts +5 -2
  131. package/src/analysis/search/ast-scan-timing.ts +9 -2
  132. package/src/analysis/search/ast-scan.ts +1 -1
  133. package/src/analysis/search/deadline.ts +2 -2
  134. package/src/analysis/search/pattern-analysis.ts +46 -32
  135. package/src/analysis/search/pattern.ts +12 -2
  136. package/src/analysis/target/anchored.ts +20 -11
  137. package/src/analysis/target/file.ts +18 -11
  138. package/src/analysis/target/identity.ts +19 -3
  139. package/src/analysis/target/symbol.ts +36 -15
  140. package/src/api.ts +4 -0
  141. package/src/diagnostics/evidence.ts +51 -0
  142. package/src/session/capability-adapter.ts +12 -3
  143. package/src/session/find-workflow.ts +17 -7
  144. package/src/session/graph/collect.ts +18 -2
  145. package/src/session/graph-workflow.ts +19 -12
  146. package/src/session/health-refresh.ts +148 -0
  147. package/src/session/health-types.ts +37 -15
  148. package/src/session/health-workflow.ts +37 -39
  149. package/src/session/inspect/collect.ts +20 -3
  150. package/src/session/inspect-workflow.ts +10 -5
  151. package/src/session/orientation/collect.ts +10 -1
  152. package/src/session/orientation/context-facts.ts +3 -1
  153. package/src/session/orientation/context-sections.ts +20 -7
  154. package/src/session/orientation/gather.ts +17 -11
  155. package/src/session/orientation-types.ts +2 -1
  156. package/src/session/orientation-workflow.ts +15 -8
  157. package/src/session/refactor-plans.ts +2 -0
  158. package/src/session/refactor-workflow.ts +59 -14
  159. package/src/session/session.ts +4 -1
  160. package/src/session/target-workflow.ts +54 -23
  161. package/src/session/workflow-control.ts +7 -3
  162. package/src/substrate/lsp/lifecycle.ts +26 -46
  163. package/src/substrate/lsp/maintenance.ts +199 -25
  164. package/src/substrate/lsp/state.ts +2 -2
  165. package/src/substrate/workspace-provider-host.ts +25 -3
  166. package/src/tool/find/execute.ts +7 -1
  167. package/src/tool/find/render.ts +20 -3
  168. package/src/tool/find/tui.ts +23 -21
  169. package/src/tool/graph/execute.ts +23 -3
  170. package/src/tool/graph/tui.ts +59 -46
  171. package/src/tool/health/execute.ts +7 -2
  172. package/src/tool/health/markdown.ts +56 -14
  173. package/src/tool/health/refresh-status.ts +105 -0
  174. package/src/tool/health/tui.ts +95 -98
  175. package/src/tool/infra/workflow-control.ts +8 -0
  176. package/src/tool/inspect/execute.ts +7 -1
  177. package/src/tool/inspect/tui.ts +26 -18
  178. package/src/tool/orientation/execute.ts +21 -3
  179. package/src/tool/orientation/tui.ts +51 -35
  180. package/src/tool/refactor-apply/execute.ts +3 -0
  181. package/src/tool/refactor-apply/tui.ts +43 -25
  182. package/src/tool/refactor-plan/execute.ts +4 -0
  183. package/src/tool/refactor-plan/tui.ts +33 -20
  184. package/src/tool/register.ts +127 -22
  185. package/src/tool/resolve/execute.ts +18 -1
  186. package/src/tool/resolve/tui.ts +42 -24
  187. package/src/tool/result/display.ts +107 -0
  188. package/src/tool/result/errors.ts +20 -17
  189. package/src/tool/result/find.ts +52 -8
  190. package/src/tool/result/graph.ts +44 -1
  191. package/src/tool/result/health.ts +53 -1
  192. package/src/tool/result/inspect.ts +113 -1
  193. package/src/tool/result/orientation.ts +66 -14
  194. package/src/tool/result/refactor.ts +36 -4
  195. package/src/tool/result/resolve.ts +44 -7
  196. package/src/tool/result/types.ts +20 -0
  197. package/src/types/execution.ts +18 -5
  198. package/src/types/index.ts +8 -1
  199. package/src/ui/status-command.ts +5 -3
  200. package/src/ui/status-overlay.ts +4 -0
  201. package/src/ui/tui/common.ts +108 -10
  202. package/src/ui/tui/display.ts +48 -0
  203. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -351
@@ -0,0 +1,206 @@
1
+ import {
2
+ type CodeRequestControl,
3
+ CodeRequestDeadlineError,
4
+ throwIfCodeRequestInterrupted,
5
+ } from "@mrclrchtr/supi-code-runtime/api";
6
+ import type {
7
+ DiagnosticPushWaitOutcome,
8
+ DiagnosticSettleResult,
9
+ } from "./client-diagnostic-timing.ts";
10
+
11
+ type DiagnosticWaiter = (outcome: "published" | "released") => void;
12
+
13
+ /** One cancellable wait for diagnostic state change. */
14
+ export interface DiagnosticStateWait {
15
+ readonly promise: Promise<void>;
16
+ cancel(): void;
17
+ }
18
+
19
+ interface DiagnosticSettleOptions {
20
+ readonly syncStart: number;
21
+ readonly maxWaitMs: number;
22
+ readonly quietMs: number;
23
+ readonly settleEpoch: number;
24
+ readonly isComplete: () => boolean;
25
+ readonly latestReceived: () => number;
26
+ }
27
+
28
+ /** Own diagnostic waiters and their timers for one LSP client. */
29
+ export class DiagnosticWaitRegistry {
30
+ readonly #pushWaiters = new Map<string, DiagnosticWaiter[]>();
31
+ readonly #stateWaiters = new Set<() => void>();
32
+ readonly #settleWaiters = new Set<() => void>();
33
+ #settleEpoch = 0;
34
+
35
+ get settleEpoch(): number {
36
+ return this.#settleEpoch;
37
+ }
38
+
39
+ /** Wait for a diagnostic publication or a lifecycle release for one URI. */
40
+ waitForPush(
41
+ uri: string,
42
+ timeoutMs: number,
43
+ control?: CodeRequestControl,
44
+ ): Promise<DiagnosticPushWaitOutcome> {
45
+ const deadlineRemaining =
46
+ control?.deadline === undefined ? undefined : control.deadline - Date.now();
47
+ const bindMs =
48
+ deadlineRemaining === undefined ? timeoutMs : Math.min(timeoutMs, deadlineRemaining);
49
+ if (control?.signal?.aborted) {
50
+ return Promise.reject(control.signal.reason ?? new Error("Diagnostic push wait cancelled"));
51
+ }
52
+ if (bindMs <= 0) {
53
+ if (deadlineRemaining !== undefined && deadlineRemaining < timeoutMs) {
54
+ return Promise.reject(new CodeRequestDeadlineError());
55
+ }
56
+ return Promise.resolve("timed-out");
57
+ }
58
+
59
+ return new Promise<DiagnosticPushWaitOutcome>((resolve, reject) => {
60
+ const cleanup = () => {
61
+ clearTimeout(timer);
62
+ if (abortHandler) control?.signal?.removeEventListener("abort", abortHandler);
63
+ };
64
+ const waiter: DiagnosticWaiter = (outcome) => {
65
+ cleanup();
66
+ this.#removePushWaiter(uri, waiter);
67
+ resolve(outcome);
68
+ };
69
+ const timer = setTimeout(() => {
70
+ this.#removePushWaiter(uri, waiter);
71
+ cleanup();
72
+ if (deadlineRemaining !== undefined && deadlineRemaining < timeoutMs) {
73
+ reject(new CodeRequestDeadlineError());
74
+ } else {
75
+ resolve("timed-out");
76
+ }
77
+ }, bindMs);
78
+ let abortHandler: (() => void) | undefined;
79
+ abortHandler = () => {
80
+ cleanup();
81
+ this.#removePushWaiter(uri, waiter);
82
+ reject(control?.signal?.reason ?? new Error("Diagnostic push wait cancelled"));
83
+ };
84
+ if (control?.signal?.aborted) abortHandler();
85
+ else control?.signal?.addEventListener("abort", abortHandler, { once: true });
86
+ const waiters = this.#pushWaiters.get(uri) ?? [];
87
+ waiters.push(waiter);
88
+ this.#pushWaiters.set(uri, waiters);
89
+ });
90
+ }
91
+
92
+ /** Wait until any diagnostic or lifecycle state changes. */
93
+ waitForChange(): DiagnosticStateWait {
94
+ let finish = () => {};
95
+ const promise = new Promise<void>((resolve) => {
96
+ finish = () => {
97
+ this.#stateWaiters.delete(finish);
98
+ resolve();
99
+ };
100
+ this.#stateWaiters.add(finish);
101
+ });
102
+ return { promise, cancel: finish };
103
+ }
104
+
105
+ /** Release pending push waiters for one URI. */
106
+ releaseFile(uri: string, outcome: "published" | "released" = "released"): void {
107
+ const waiters = this.#pushWaiters.get(uri);
108
+ if (waiters) {
109
+ this.#pushWaiters.delete(uri);
110
+ for (const waiter of waiters) waiter(outcome);
111
+ }
112
+ this.#notifyStateWaiters();
113
+ }
114
+
115
+ /** Release all pending push waiters. */
116
+ releaseAll(): void {
117
+ for (const uri of Array.from(this.#pushWaiters.keys())) this.releaseFile(uri);
118
+ }
119
+
120
+ /** Cancel the active settle generation and release its timers. */
121
+ cancelSettle(): void {
122
+ this.#settleEpoch++;
123
+ this.notifySettle();
124
+ }
125
+
126
+ /** Notify settle operations that diagnostic state changed. */
127
+ notifySettle(): void {
128
+ for (const release of Array.from(this.#settleWaiters)) release();
129
+ this.#notifyStateWaiters();
130
+ }
131
+
132
+ /** Wait for fresh diagnostic state to become quiet or reach its deadline. */
133
+ async waitForSettle(
134
+ options: DiagnosticSettleOptions,
135
+ control?: CodeRequestControl,
136
+ ): Promise<DiagnosticSettleResult> {
137
+ const { syncStart, maxWaitMs, quietMs, settleEpoch, isComplete, latestReceived } = options;
138
+ const deadlineRemaining =
139
+ control?.deadline === undefined ? undefined : control.deadline - Date.now();
140
+ const deadline =
141
+ deadlineRemaining === undefined
142
+ ? syncStart + maxWaitMs
143
+ : Math.min(syncStart + maxWaitMs, control?.deadline ?? syncStart + maxWaitMs);
144
+ while (Date.now() < deadline) {
145
+ throwIfCodeRequestInterrupted(control);
146
+ if (this.#settleEpoch !== settleEpoch) {
147
+ return { outcome: "released", freshness: "not-observed" };
148
+ }
149
+ const observedAt = latestReceived();
150
+ const elapsed = Date.now() - observedAt;
151
+ const complete = isComplete();
152
+ if (observedAt > 0 && complete && elapsed >= quietMs) {
153
+ return { outcome: "quiet", freshness: "observed" };
154
+ }
155
+ const waitMs =
156
+ observedAt > 0 && complete
157
+ ? Math.min(quietMs - elapsed, deadline - Date.now())
158
+ : deadline - Date.now();
159
+ await this.#waitForStateChange(waitMs, control);
160
+ }
161
+ // A bound absolute deadline makes the settle outcome a deadline
162
+ // interruption instead of an ordinary relative timeout.
163
+ throwIfCodeRequestInterrupted(control);
164
+ return {
165
+ outcome: "timed-out",
166
+ freshness: latestReceived() > 0 ? "observed" : "not-observed",
167
+ };
168
+ }
169
+
170
+ /** Wait until diagnostic state changes or the timeout expires. */
171
+ #waitForStateChange(timeoutMs: number, control?: CodeRequestControl): Promise<void> {
172
+ if (timeoutMs <= 0) return Promise.resolve();
173
+ if (control?.signal?.aborted) {
174
+ return Promise.reject(control.signal.reason ?? new Error("Settle wait cancelled"));
175
+ }
176
+ return new Promise((resolve, reject) => {
177
+ let abortHandler: (() => void) | undefined;
178
+ const finish = () => {
179
+ clearTimeout(timer);
180
+ if (abortHandler) control?.signal?.removeEventListener("abort", abortHandler);
181
+ this.#settleWaiters.delete(finish);
182
+ resolve();
183
+ };
184
+ const timer = setTimeout(finish, timeoutMs);
185
+ abortHandler = () => {
186
+ clearTimeout(timer);
187
+ this.#settleWaiters.delete(finish);
188
+ reject(control?.signal?.reason ?? new Error("Settle wait cancelled"));
189
+ };
190
+ control?.signal?.addEventListener("abort", abortHandler, { once: true });
191
+ this.#settleWaiters.add(finish);
192
+ });
193
+ }
194
+
195
+ #notifyStateWaiters(): void {
196
+ for (const release of Array.from(this.#stateWaiters)) release();
197
+ }
198
+
199
+ #removePushWaiter(uri: string, waiter: DiagnosticWaiter): void {
200
+ const waiters = this.#pushWaiters.get(uri);
201
+ if (!waiters) return;
202
+ const next = waiters.filter((entry) => entry !== waiter);
203
+ if (next.length > 0) this.#pushWaiters.set(uri, next);
204
+ else this.#pushWaiters.delete(uri);
205
+ }
206
+ }