@hydranium/protocol 1.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/lib/abstract-logger.d.ts +66 -0
- package/lib/abstract-logger.d.ts.map +1 -0
- package/lib/abstract-logger.js +110 -0
- package/lib/abstract-logger.js.map +1 -0
- package/lib/browser-runtime.d.ts +124 -0
- package/lib/browser-runtime.d.ts.map +1 -0
- package/lib/browser-runtime.js +11 -0
- package/lib/browser-runtime.js.map +1 -0
- package/lib/client/data-events.d.ts +49 -0
- package/lib/client/data-events.d.ts.map +1 -0
- package/lib/client/data-events.js +60 -0
- package/lib/client/data-events.js.map +1 -0
- package/lib/client/data-port.d.ts +95 -0
- package/lib/client/data-port.d.ts.map +1 -0
- package/lib/client/data-port.js +11 -0
- package/lib/client/data-port.js.map +1 -0
- package/lib/client/data-session.d.ts +129 -0
- package/lib/client/data-session.d.ts.map +1 -0
- package/lib/client/data-session.js +179 -0
- package/lib/client/data-session.js.map +1 -0
- package/lib/client/index.d.ts +39 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/index.js +55 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client/message-relay.d.ts +111 -0
- package/lib/client/message-relay.d.ts.map +1 -0
- package/lib/client/message-relay.js +139 -0
- package/lib/client/message-relay.js.map +1 -0
- package/lib/client/post-message-transport.d.ts +77 -0
- package/lib/client/post-message-transport.d.ts.map +1 -0
- package/lib/client/post-message-transport.js +95 -0
- package/lib/client/post-message-transport.js.map +1 -0
- package/lib/clock.d.ts +111 -0
- package/lib/clock.d.ts.map +1 -0
- package/lib/clock.js +74 -0
- package/lib/clock.js.map +1 -0
- package/lib/data/data-protocol-methods.d.ts +39 -0
- package/lib/data/data-protocol-methods.d.ts.map +1 -0
- package/lib/data/data-protocol-methods.js +61 -0
- package/lib/data/data-protocol-methods.js.map +1 -0
- package/lib/data/data-server-protocol.d.ts +264 -0
- package/lib/data/data-server-protocol.d.ts.map +1 -0
- package/lib/data/data-server-protocol.js +11 -0
- package/lib/data/data-server-protocol.js.map +1 -0
- package/lib/data/diagnostics.d.ts +97 -0
- package/lib/data/diagnostics.d.ts.map +1 -0
- package/lib/data/diagnostics.js +25 -0
- package/lib/data/diagnostics.js.map +1 -0
- package/lib/data/events.d.ts +107 -0
- package/lib/data/events.d.ts.map +1 -0
- package/lib/data/events.js +11 -0
- package/lib/data/events.js.map +1 -0
- package/lib/data/index.d.ts +15 -0
- package/lib/data/index.d.ts.map +1 -0
- package/lib/data/index.js +38 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/methods.d.ts +57 -0
- package/lib/data/methods.d.ts.map +1 -0
- package/lib/data/methods.js +60 -0
- package/lib/data/methods.js.map +1 -0
- package/lib/data/requests.d.ts +65 -0
- package/lib/data/requests.d.ts.map +1 -0
- package/lib/data/requests.js +11 -0
- package/lib/data/requests.js.map +1 -0
- package/lib/debouncer.d.ts +75 -0
- package/lib/debouncer.d.ts.map +1 -0
- package/lib/debouncer.js +91 -0
- package/lib/debouncer.js.map +1 -0
- package/lib/errors.d.ts +84 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +113 -0
- package/lib/errors.js.map +1 -0
- package/lib/host-diagnostics.d.ts +58 -0
- package/lib/host-diagnostics.d.ts.map +1 -0
- package/lib/host-diagnostics.js +18 -0
- package/lib/host-diagnostics.js.map +1 -0
- package/lib/index.d.ts +34 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/latency-collector.d.ts +126 -0
- package/lib/latency-collector.d.ts.map +1 -0
- package/lib/latency-collector.js +137 -0
- package/lib/latency-collector.js.map +1 -0
- package/lib/logger.d.ts +119 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +126 -0
- package/lib/logger.js.map +1 -0
- package/lib/model-server.d.ts +352 -0
- package/lib/model-server.d.ts.map +1 -0
- package/lib/model-server.js +160 -0
- package/lib/model-server.js.map +1 -0
- package/lib/model-service/args.d.ts +64 -0
- package/lib/model-service/args.d.ts.map +1 -0
- package/lib/model-service/args.js +11 -0
- package/lib/model-service/args.js.map +1 -0
- package/lib/model-service/index.d.ts +11 -0
- package/lib/model-service/index.d.ts.map +1 -0
- package/lib/model-service/index.js +31 -0
- package/lib/model-service/index.js.map +1 -0
- package/lib/model-service/reference-candidate.d.ts +56 -0
- package/lib/model-service/reference-candidate.d.ts.map +1 -0
- package/lib/model-service/reference-candidate.js +11 -0
- package/lib/model-service/reference-candidate.js.map +1 -0
- package/lib/noop-logger.d.ts +25 -0
- package/lib/noop-logger.d.ts.map +1 -0
- package/lib/noop-logger.js +36 -0
- package/lib/noop-logger.js.map +1 -0
- package/lib/observable-value.d.ts +72 -0
- package/lib/observable-value.d.ts.map +1 -0
- package/lib/observable-value.js +52 -0
- package/lib/observable-value.js.map +1 -0
- package/lib/patch-merge.d.ts +123 -0
- package/lib/patch-merge.d.ts.map +1 -0
- package/lib/patch-merge.js +104 -0
- package/lib/patch-merge.js.map +1 -0
- package/lib/profile-session.d.ts +87 -0
- package/lib/profile-session.d.ts.map +1 -0
- package/lib/profile-session.js +98 -0
- package/lib/profile-session.js.map +1 -0
- package/lib/profiling.d.ts +83 -0
- package/lib/profiling.d.ts.map +1 -0
- package/lib/profiling.js +20 -0
- package/lib/profiling.js.map +1 -0
- package/lib/project.d.ts +121 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +34 -0
- package/lib/project.js.map +1 -0
- package/lib/rpc/bind-rpc-methods.d.ts +100 -0
- package/lib/rpc/bind-rpc-methods.d.ts.map +1 -0
- package/lib/rpc/bind-rpc-methods.js +110 -0
- package/lib/rpc/bind-rpc-methods.js.map +1 -0
- package/lib/rpc/create-rpc-proxy.d.ts +188 -0
- package/lib/rpc/create-rpc-proxy.d.ts.map +1 -0
- package/lib/rpc/create-rpc-proxy.js +158 -0
- package/lib/rpc/create-rpc-proxy.js.map +1 -0
- package/lib/rpc/index.d.ts +12 -0
- package/lib/rpc/index.d.ts.map +1 -0
- package/lib/rpc/index.js +31 -0
- package/lib/rpc/index.js.map +1 -0
- package/lib/rpc/wire-prefix.d.ts +37 -0
- package/lib/rpc/wire-prefix.d.ts.map +1 -0
- package/lib/rpc/wire-prefix.js +49 -0
- package/lib/rpc/wire-prefix.js.map +1 -0
- package/lib/testing/data-doubles.d.ts +121 -0
- package/lib/testing/data-doubles.d.ts.map +1 -0
- package/lib/testing/data-doubles.js +102 -0
- package/lib/testing/data-doubles.js.map +1 -0
- package/lib/testing/fake-clock.d.ts +35 -0
- package/lib/testing/fake-clock.d.ts.map +1 -0
- package/lib/testing/fake-clock.js +86 -0
- package/lib/testing/fake-clock.js.map +1 -0
- package/lib/testing/harness.d.ts +24 -0
- package/lib/testing/harness.d.ts.map +1 -0
- package/lib/testing/harness.js +11 -0
- package/lib/testing/harness.js.map +1 -0
- package/lib/testing/index.d.ts +13 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +41 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/node/duplex-connection.d.ts +25 -0
- package/lib/testing/node/duplex-connection.d.ts.map +1 -0
- package/lib/testing/node/duplex-connection.js +34 -0
- package/lib/testing/node/duplex-connection.js.map +1 -0
- package/lib/testing/node/duplex-stream.d.ts +32 -0
- package/lib/testing/node/duplex-stream.d.ts.map +1 -0
- package/lib/testing/node/duplex-stream.js +35 -0
- package/lib/testing/node/duplex-stream.js.map +1 -0
- package/lib/testing/node/index.d.ts +11 -0
- package/lib/testing/node/index.d.ts.map +1 -0
- package/lib/testing/node/index.js +37 -0
- package/lib/testing/node/index.js.map +1 -0
- package/lib/testing/wait-for.d.ts +69 -0
- package/lib/testing/wait-for.d.ts.map +1 -0
- package/lib/testing/wait-for.js +67 -0
- package/lib/testing/wait-for.js.map +1 -0
- package/lib/tracer.d.ts +115 -0
- package/lib/tracer.d.ts.map +1 -0
- package/lib/tracer.js +205 -0
- package/lib/tracer.js.map +1 -0
- package/lib/transfer-diagnostic.d.ts +73 -0
- package/lib/transfer-diagnostic.d.ts.map +1 -0
- package/lib/transfer-diagnostic.js +45 -0
- package/lib/transfer-diagnostic.js.map +1 -0
- package/lib/transfer-document.d.ts +73 -0
- package/lib/transfer-document.d.ts.map +1 -0
- package/lib/transfer-document.js +46 -0
- package/lib/transfer-document.js.map +1 -0
- package/lib/transfer-element.d.ts +39 -0
- package/lib/transfer-element.d.ts.map +1 -0
- package/lib/transfer-element.js +11 -0
- package/lib/transfer-element.js.map +1 -0
- package/lib/uri.d.ts +62 -0
- package/lib/uri.d.ts.map +1 -0
- package/lib/uri.js +28 -0
- package/lib/uri.js.map +1 -0
- package/lib/util.d.ts +147 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +211 -0
- package/lib/util.js.map +1 -0
- package/package.json +100 -0
- package/src/abstract-logger.ts +120 -0
- package/src/browser-runtime.ts +135 -0
- package/src/client/data-events.ts +71 -0
- package/src/client/data-port.ts +99 -0
- package/src/client/data-session.ts +209 -0
- package/src/client/index.ts +40 -0
- package/src/client/message-relay.ts +216 -0
- package/src/client/post-message-transport.ts +167 -0
- package/src/clock.ts +173 -0
- package/src/data/data-protocol-methods.ts +79 -0
- package/src/data/data-server-protocol.ts +296 -0
- package/src/data/diagnostics.ts +116 -0
- package/src/data/events.ts +127 -0
- package/src/data/index.ts +23 -0
- package/src/data/methods.ts +59 -0
- package/src/data/requests.ts +70 -0
- package/src/debouncer.ts +118 -0
- package/src/errors.ts +127 -0
- package/src/host-diagnostics.ts +63 -0
- package/src/index.ts +41 -0
- package/src/latency-collector.ts +231 -0
- package/src/logger.ts +172 -0
- package/src/model-server.ts +479 -0
- package/src/model-service/args.ts +67 -0
- package/src/model-service/index.ts +16 -0
- package/src/model-service/reference-candidate.ts +58 -0
- package/src/noop-logger.ts +36 -0
- package/src/observable-value.ts +92 -0
- package/src/patch-merge.ts +165 -0
- package/src/profile-session.ts +164 -0
- package/src/profiling.ts +113 -0
- package/src/project.ts +125 -0
- package/src/rpc/README.md +163 -0
- package/src/rpc/bind-rpc-methods.ts +180 -0
- package/src/rpc/create-rpc-proxy.ts +292 -0
- package/src/rpc/index.ts +16 -0
- package/src/rpc/wire-prefix.ts +48 -0
- package/src/testing/data-doubles.ts +178 -0
- package/src/testing/fake-clock.ts +106 -0
- package/src/testing/harness.ts +24 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/node/duplex-connection.ts +50 -0
- package/src/testing/node/duplex-stream.ts +51 -0
- package/src/testing/node/index.ts +22 -0
- package/src/testing/wait-for.ts +94 -0
- package/src/tracer.ts +277 -0
- package/src/transfer-diagnostic.ts +92 -0
- package/src/transfer-document.ts +99 -0
- package/src/transfer-element.ts +43 -0
- package/src/uri.ts +61 -0
- package/src/util.ts +250 -0
package/src/clock.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { Disposable } from './util';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Measures how long something takes. Born running at {@link Clock.stopwatch};
|
|
14
|
+
* there is no `start` — you create it at the moment timing begins.
|
|
15
|
+
*
|
|
16
|
+
* Backed by a monotonic source (`performance.now`), so it is sub-millisecond
|
|
17
|
+
* precise and immune to wall-clock jumps (NTP/DST adjustments can make a
|
|
18
|
+
* `Date.now` delta go backwards; a stopwatch cannot). Use this for durations
|
|
19
|
+
* and {@link Clock.now} for "what time is it" (timestamps, TTLs).
|
|
20
|
+
*
|
|
21
|
+
* It holds no scheduled resource and is therefore NOT a {@link Disposable} —
|
|
22
|
+
* the disposable thing is a {@link Clock.setTimer}. Reads are lazy, so a fake
|
|
23
|
+
* clock just answers the current virtual time.
|
|
24
|
+
*/
|
|
25
|
+
export interface Stopwatch {
|
|
26
|
+
/** Total elapsed since creation; live while running, frozen after {@link stop}. */
|
|
27
|
+
readonly elapsedMs: number;
|
|
28
|
+
/**
|
|
29
|
+
* Split: the elapsed since the previous `lap` (or since creation for the
|
|
30
|
+
* first call), starting a new split. Non-destructive — the running total
|
|
31
|
+
* ({@link elapsedMs}) is unaffected, so `lap` is not a restart.
|
|
32
|
+
*/
|
|
33
|
+
lap(): number;
|
|
34
|
+
/**
|
|
35
|
+
* Freeze and return the grand total (the sum of every lap plus the tail
|
|
36
|
+
* since the last lap). Idempotent: further calls return the same value and
|
|
37
|
+
* {@link elapsedMs} stops advancing.
|
|
38
|
+
*/
|
|
39
|
+
stop(): number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Injectable time, so logic gated on time (debounce windows, slow-update
|
|
44
|
+
* warnings, request timeouts, the self-save TTL) is deterministically
|
|
45
|
+
* testable instead of waiting on real wall-clock. The {@link SystemClock}
|
|
46
|
+
* default delegates to the platform; tests bind `makeFakeClock`.
|
|
47
|
+
*
|
|
48
|
+
* Two distinct readings on purpose: {@link now} is wall-clock (for timestamps
|
|
49
|
+
* and TTLs that compare against a file's mtime), while {@link stopwatch}
|
|
50
|
+
* measures durations off a monotonic source. They are different clocks; do not
|
|
51
|
+
* subtract two {@link now} readings to measure a duration.
|
|
52
|
+
*
|
|
53
|
+
* A cross-head contract, so it carries no platform or DI-framework coupling.
|
|
54
|
+
*
|
|
55
|
+
* Note: the runtime health monitors (event-loop, memory) intentionally keep
|
|
56
|
+
* the real wall-clock rather than taking a `Clock` — faking the time they
|
|
57
|
+
* measure would invert their meaning.
|
|
58
|
+
*/
|
|
59
|
+
export interface Clock {
|
|
60
|
+
/** Current wall-clock time in milliseconds (the `Date.now` reading). */
|
|
61
|
+
now(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Run `callback` after `ms` have elapsed; the returned {@link Disposable}
|
|
64
|
+
* cancels it if disposed before it fires. This is the testable
|
|
65
|
+
* `setTimeout` — debounce, deferred slow-warn, request timeouts.
|
|
66
|
+
*/
|
|
67
|
+
setTimer(callback: () => void, ms: number): Disposable;
|
|
68
|
+
/** Start measuring a duration. See {@link Stopwatch}. */
|
|
69
|
+
stopwatch(): Stopwatch;
|
|
70
|
+
/**
|
|
71
|
+
* Run `callback` and return its result together with how long it took.
|
|
72
|
+
* Sugar over {@link stopwatch} — pure measurement, no logging and no
|
|
73
|
+
* threshold gating (unlike `Tracer.time`). Use in
|
|
74
|
+
* measurement pipelines that consume `elapsedMs` directly. Async callbacks
|
|
75
|
+
* return a Promise of the same shape, timed across the full settle.
|
|
76
|
+
*/
|
|
77
|
+
measure<T>(callback: () => Promise<T>): Promise<Timed<T>>;
|
|
78
|
+
measure<T>(callback: () => T): Timed<T>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The result of {@link Clock.measure}: a callback's return value paired with
|
|
83
|
+
* its elapsed wall-time in milliseconds. A plain value object — no methods, no
|
|
84
|
+
* logging — deliberately distinct from `@theia/core`'s `Measurement` (a
|
|
85
|
+
* stateful self-logging handle), which would collide in a Theia process.
|
|
86
|
+
*/
|
|
87
|
+
export interface Timed<T> {
|
|
88
|
+
/**
|
|
89
|
+
* The callback's return value, unchanged. For an async callback this is the
|
|
90
|
+
* settled value rather than the promise — and a callback that REJECTS
|
|
91
|
+
* produces no `Timed` at all, because the rejection propagates, so a failed
|
|
92
|
+
* run cannot be timed through this shape.
|
|
93
|
+
*/
|
|
94
|
+
result: T;
|
|
95
|
+
/**
|
|
96
|
+
* Milliseconds off the monotonic source, so it is comparable with
|
|
97
|
+
* {@link Stopwatch.elapsedMs} and NOT with a difference of two
|
|
98
|
+
* {@link Clock.now} readings. For an async callback it spans the full settle,
|
|
99
|
+
* not just the synchronous prefix.
|
|
100
|
+
*/
|
|
101
|
+
elapsedMs: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class SystemStopwatch implements Stopwatch {
|
|
105
|
+
protected readonly start = performance.now();
|
|
106
|
+
protected lastLap = this.start;
|
|
107
|
+
protected stopped: number | undefined;
|
|
108
|
+
|
|
109
|
+
get elapsedMs(): number {
|
|
110
|
+
return (this.stopped ?? performance.now()) - this.start;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
lap(): number {
|
|
114
|
+
const at = this.stopped ?? performance.now();
|
|
115
|
+
const split = at - this.lastLap;
|
|
116
|
+
this.lastLap = at;
|
|
117
|
+
return split;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
stop(): number {
|
|
121
|
+
if (this.stopped === undefined) {
|
|
122
|
+
this.stopped = performance.now();
|
|
123
|
+
}
|
|
124
|
+
return this.stopped - this.start;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Detach a timer from the event loop so a single pending timer never keeps a
|
|
130
|
+
* Node process alive — a CLI run or test worker can exit cleanly while deferred
|
|
131
|
+
* work (debounce, slow-warn, idle CST eviction) is still scheduled; the
|
|
132
|
+
* persistent server connection keeps the loop alive in production regardless.
|
|
133
|
+
*
|
|
134
|
+
* `unref` is Node-only. {@link SystemClock} also runs in the browser, where
|
|
135
|
+
* `setTimeout` returns a bare numeric handle with no `unref` — the optional
|
|
136
|
+
* call is a no-op there, and declaring the parameter's `unref` optional accepts
|
|
137
|
+
* a Node `NodeJS.Timeout` structurally without a cast.
|
|
138
|
+
*/
|
|
139
|
+
function unrefTimer(handle: { unref?: () => void }): void {
|
|
140
|
+
handle.unref?.();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Default {@link Clock}: delegates to the platform's `Date.now`,
|
|
145
|
+
* `performance.now`, and `setTimeout`/`clearTimeout`. Bound everywhere in
|
|
146
|
+
* production; swapped for `makeFakeClock` in tests.
|
|
147
|
+
*/
|
|
148
|
+
export class SystemClock implements Clock {
|
|
149
|
+
now(): number {
|
|
150
|
+
return Date.now();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setTimer(callback: () => void, ms: number): Disposable {
|
|
154
|
+
const handle = setTimeout(callback, ms);
|
|
155
|
+
unrefTimer(handle);
|
|
156
|
+
return Disposable.create(() => clearTimeout(handle));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
stopwatch(): Stopwatch {
|
|
160
|
+
return new SystemStopwatch();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
measure<T>(callback: () => Promise<T>): Promise<Timed<T>>;
|
|
164
|
+
measure<T>(callback: () => T): Timed<T>;
|
|
165
|
+
measure<T>(callback: () => T | Promise<T>): Timed<T> | Promise<Timed<T>> {
|
|
166
|
+
const stopwatch = this.stopwatch();
|
|
167
|
+
const result = callback();
|
|
168
|
+
if (result instanceof Promise) {
|
|
169
|
+
return (result as Promise<T>).then(value => ({ result: value, elapsedMs: stopwatch.elapsedMs }));
|
|
170
|
+
}
|
|
171
|
+
return { result: result as T, elapsedMs: stopwatch.elapsedMs };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import type { TransferElement } from '../transfer-element';
|
|
11
|
+
import type {
|
|
12
|
+
DataClientProtocol,
|
|
13
|
+
DataServerProtocol,
|
|
14
|
+
DocumentClientProtocol,
|
|
15
|
+
DocumentServerProtocol,
|
|
16
|
+
ProjectClientProtocol,
|
|
17
|
+
ProjectServerProtocol,
|
|
18
|
+
ReferenceServerProtocol
|
|
19
|
+
} from './data-server-protocol';
|
|
20
|
+
|
|
21
|
+
/** Request-method names on {@link DocumentServerProtocol}. */
|
|
22
|
+
export const DOCUMENT_SERVER_PROTOCOL_METHODS = [
|
|
23
|
+
'openModelDocument',
|
|
24
|
+
'closeModelDocument',
|
|
25
|
+
'getModelDocument',
|
|
26
|
+
'updateModelDocument',
|
|
27
|
+
'saveModelDocument',
|
|
28
|
+
'watchModelDocument',
|
|
29
|
+
'unwatchModelDocument',
|
|
30
|
+
'waitForReady'
|
|
31
|
+
] as const satisfies ReadonlyArray<keyof DocumentServerProtocol<TransferElement> & string>;
|
|
32
|
+
|
|
33
|
+
/** Request-method names on {@link ProjectServerProtocol}. */
|
|
34
|
+
export const PROJECT_SERVER_PROTOCOL_METHODS = ['getProjects', 'getProjectForUri'] as const satisfies ReadonlyArray<
|
|
35
|
+
keyof ProjectServerProtocol & string
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Request-method names on the composed {@link DataServerProtocol} — the
|
|
40
|
+
* document fragment followed by the project fragment. The `as const satisfies`
|
|
41
|
+
* constraint makes the array typed against the interface — stale names (typos,
|
|
42
|
+
* renamed methods) fail to typecheck.
|
|
43
|
+
*/
|
|
44
|
+
export const DATA_SERVER_PROTOCOL_METHODS = [
|
|
45
|
+
...DOCUMENT_SERVER_PROTOCOL_METHODS,
|
|
46
|
+
...PROJECT_SERVER_PROTOCOL_METHODS
|
|
47
|
+
] as const satisfies ReadonlyArray<keyof DataServerProtocol<TransferElement> & string>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Request-method names on {@link ReferenceServerProtocol}. NOT part of
|
|
51
|
+
* {@link DATA_SERVER_PROTOCOL_METHODS} — adopters that compose the reference
|
|
52
|
+
* fragment register these alongside (e.g. via a `DataServer` subclass's
|
|
53
|
+
* `additionalMethods`).
|
|
54
|
+
*/
|
|
55
|
+
export const REFERENCE_SERVER_PROTOCOL_METHODS = [
|
|
56
|
+
'findReferenceCandidates',
|
|
57
|
+
'resolveReference',
|
|
58
|
+
'findNextName'
|
|
59
|
+
] as const satisfies ReadonlyArray<keyof ReferenceServerProtocol<TransferElement> & string>;
|
|
60
|
+
|
|
61
|
+
/** Notification-method names on {@link DocumentClientProtocol}. */
|
|
62
|
+
export const DOCUMENT_CLIENT_PROTOCOL_METHODS = ['onDocumentUpdated', 'onDocumentSaved'] as const satisfies ReadonlyArray<
|
|
63
|
+
keyof DocumentClientProtocol<TransferElement> & string
|
|
64
|
+
>;
|
|
65
|
+
|
|
66
|
+
/** Notification-method names on {@link ProjectClientProtocol}. */
|
|
67
|
+
export const PROJECT_CLIENT_PROTOCOL_METHODS = ['onProjectsChanged'] as const satisfies ReadonlyArray<keyof ProjectClientProtocol & string>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Notification-method names on the composed {@link DataClientProtocol}.
|
|
71
|
+
* Passed as the `localMethods` allowlist to `createRpcProxy` (frontend) /
|
|
72
|
+
* `bindRpcMethods` (backend) when binding inbound `on*` notifications — the
|
|
73
|
+
* data head's connection routes `<methodNamespace>onDocumentUpdated` etc. to
|
|
74
|
+
* the bound client target.
|
|
75
|
+
*/
|
|
76
|
+
export const DATA_CLIENT_PROTOCOL_METHODS = [
|
|
77
|
+
...DOCUMENT_CLIENT_PROTOCOL_METHODS,
|
|
78
|
+
...PROJECT_CLIENT_PROTOCOL_METHODS
|
|
79
|
+
] as const satisfies ReadonlyArray<keyof DataClientProtocol<TransferElement> & string>;
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import type { TransferDiagnostic } from '../transfer-diagnostic';
|
|
11
|
+
import type { TransferElement } from '../transfer-element';
|
|
12
|
+
import type { Project } from '../project';
|
|
13
|
+
import type { TransferDocument } from '../transfer-document';
|
|
14
|
+
import type { CloseModelArgs, FindNextNameArgs, OpenModelArgs, ReferenceContext, ReferenceRequest } from '../model-server';
|
|
15
|
+
import type { ReferenceCandidate, ReferenceTarget } from '../model-service/reference-candidate';
|
|
16
|
+
import type { ProjectsChangedEvent, TransferDocumentSavedEvent, TransferDocumentUpdatedEvent } from './events';
|
|
17
|
+
import type {
|
|
18
|
+
GetModelDocumentArgs,
|
|
19
|
+
GetProjectForUriArgs,
|
|
20
|
+
TransferSaveDocumentArgs,
|
|
21
|
+
WatchModelDocumentArgs,
|
|
22
|
+
TransferUpdateDocumentArgs
|
|
23
|
+
} from './requests';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Document-scoped slice of the data-server wire contract: the per-document
|
|
27
|
+
* lifecycle (get / update / save), the per-`(uri, clientId)` watch
|
|
28
|
+
* pair, and the global `waitForReady` startup gate that callers await before
|
|
29
|
+
* the first document request.
|
|
30
|
+
*
|
|
31
|
+
* Generic over the transfer root type (`TTransfer`) and the diagnostic
|
|
32
|
+
* shape (`TDiagnostic`). `TTransfer extends TransferElement` constrains the
|
|
33
|
+
* root to the wire data shape (no `$container` cycles, references as
|
|
34
|
+
* strings — see `transfer-element.ts`) — the type-system bridge between the
|
|
35
|
+
* data-server's protocol surface and the transfer-model overlay each adopter
|
|
36
|
+
* declares.
|
|
37
|
+
*
|
|
38
|
+
* Composed into {@link DataServerProtocol} alongside {@link ProjectServerProtocol};
|
|
39
|
+
* a pure single-document consumer can compose only this fragment.
|
|
40
|
+
*/
|
|
41
|
+
export interface DocumentServerProtocol<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic = TransferDiagnostic> {
|
|
42
|
+
/**
|
|
43
|
+
* Open a document for an editor session and return its current state.
|
|
44
|
+
* Registers `(uri, clientId)` with the multi-client document manager
|
|
45
|
+
* (so concurrent editors share one built document) and returns the
|
|
46
|
+
* document at the server's configured target phase — the same shape
|
|
47
|
+
* {@link getModelDocument} returns, except that `version` is taken from
|
|
48
|
+
* the text-document store so the caller's first `baseVersion` write
|
|
49
|
+
* cannot self-conflict. Idempotent in registration terms: opening an
|
|
50
|
+
* already-open document refreshes the client registration. Note it does
|
|
51
|
+
* NOT avoid a rebuild — a second client attaching triggers
|
|
52
|
+
* `refreshContent`, which fires a change event, so mounting a form on a
|
|
53
|
+
* document already open in a text editor rebuilds it.
|
|
54
|
+
*
|
|
55
|
+
* The default `DataServer` impl delegates to `ModelService.open` then
|
|
56
|
+
* reads the built state. Pair with {@link watchModelDocument} to receive
|
|
57
|
+
* subsequent build-phase events (open returns a one-shot snapshot;
|
|
58
|
+
* later validation diagnostics arrive on the watch channel).
|
|
59
|
+
*/
|
|
60
|
+
openModelDocument(args: OpenModelArgs): Promise<TransferDocument<TTransfer, TDiagnostic>>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Close an editor session for `(uri, clientId)`. Counterpart to
|
|
64
|
+
* {@link openModelDocument}; the underlying document stays built until
|
|
65
|
+
* every registered client has closed. The default `DataServer` impl
|
|
66
|
+
* delegates to `ModelService.close` and ALSO releases any watch for the
|
|
67
|
+
* same `(uri, clientId)` (closing a session frees its own watch — a
|
|
68
|
+
* forgotten {@link unwatchModelDocument} would otherwise leak dispatch;
|
|
69
|
+
* the implicit unwatch is idempotent). `watchModelDocument` is NOT
|
|
70
|
+
* coupled the other way: opening does not force a watch, so a snapshot
|
|
71
|
+
* reader can open without streaming.
|
|
72
|
+
*/
|
|
73
|
+
closeModelDocument(args: CloseModelArgs): Promise<void>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the current state of a document. The server-side build is brought
|
|
77
|
+
* up to date before the response returns — callers don't need to await
|
|
78
|
+
* a separate "ready" gate per request, only the global
|
|
79
|
+
* {@link waitForReady} gate at startup.
|
|
80
|
+
*/
|
|
81
|
+
getModelDocument(args: GetModelDocumentArgs): Promise<TransferDocument<TTransfer, TDiagnostic>>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Update a document's content. The response carries the latest built
|
|
85
|
+
* state including diagnostics; callers observe convergence via that
|
|
86
|
+
* response.
|
|
87
|
+
*/
|
|
88
|
+
updateModelDocument(args: TransferUpdateDocumentArgs<TTransfer>): Promise<TransferDocument<TTransfer, TDiagnostic>>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Persist a document to disk. The response is the post-save document
|
|
92
|
+
* state (matches the file on disk). Save semantics depend on the
|
|
93
|
+
* filesystem-provider wiring; rejection paths surface as awaited
|
|
94
|
+
* promise rejections.
|
|
95
|
+
*/
|
|
96
|
+
saveModelDocument(args: TransferSaveDocumentArgs<TTransfer>): Promise<TransferDocument<TTransfer, TDiagnostic>>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Start watching `(uri, clientId)`. The server starts dispatching
|
|
100
|
+
* {@link DocumentClientProtocol.onDocumentUpdated} on the paired wire
|
|
101
|
+
* for every build-phase event on `uri`.
|
|
102
|
+
*
|
|
103
|
+
* Returns `Promise<void>` — the bidirectional pattern means the event
|
|
104
|
+
* channel is the client interface, not a returned handle. Callers fan
|
|
105
|
+
* out a single inbound `onDocumentUpdated` to multiple local listeners
|
|
106
|
+
* with their own `Emitter<T>` (matching Theia's pattern). Pair every
|
|
107
|
+
* `watchModelDocument` with {@link unwatchModelDocument} when no longer
|
|
108
|
+
* needed; the server holds the watch record until the matching unwatch
|
|
109
|
+
* lands.
|
|
110
|
+
*/
|
|
111
|
+
watchModelDocument(args: WatchModelDocumentArgs): Promise<void>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Stop watching `(uri, clientId)` previously started by
|
|
115
|
+
* {@link watchModelDocument}. Idempotent — unwatching twice is a no-op.
|
|
116
|
+
*/
|
|
117
|
+
unwatchModelDocument(args: WatchModelDocumentArgs): Promise<void>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve once the data-server is ready to serve requests. The default
|
|
121
|
+
* implementation in `DataServer` returns `Promise.resolve()`; adopters
|
|
122
|
+
* that need to warm-load services (workspace indexing, custom service
|
|
123
|
+
* initialisation) override this hook on their subclass.
|
|
124
|
+
*
|
|
125
|
+
* Callers SHOULD await `waitForReady` once at startup before the first
|
|
126
|
+
* `getModelDocument` / subscription call — Langium's document builder
|
|
127
|
+
* may otherwise miss documents added during the initial workspace
|
|
128
|
+
* walk.
|
|
129
|
+
*/
|
|
130
|
+
waitForReady(): Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Project-scoped slice of the data-server wire contract: enumerate the
|
|
135
|
+
* workspace's projects and resolve the project owning a URI.
|
|
136
|
+
*
|
|
137
|
+
* Generic over the project shape (`TProject`); most adopters use the
|
|
138
|
+
* default {@link Project}. Composed into {@link DataServerProtocol}
|
|
139
|
+
* alongside {@link DocumentServerProtocol}.
|
|
140
|
+
*/
|
|
141
|
+
export interface ProjectServerProtocol<TProject extends Project = Project> {
|
|
142
|
+
/**
|
|
143
|
+
* List projects exposed by the workspace. Returns an empty array when
|
|
144
|
+
* no project tier is wired (e.g. `SingleProjectManager` default with no
|
|
145
|
+
* descriptor discovery configured).
|
|
146
|
+
*/
|
|
147
|
+
getProjects(): Promise<readonly TProject[]>;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Look up the project owning the given URI. Returns `undefined` when
|
|
151
|
+
* the URI does not belong to any registered project, or when no
|
|
152
|
+
* project tier is wired (the `SingleProjectManager` default returns
|
|
153
|
+
* its synthetic workspace project for every URI, so that case never
|
|
154
|
+
* resolves to `undefined`).
|
|
155
|
+
*
|
|
156
|
+
* Membership semantics are decided by the server's `ProjectManager`
|
|
157
|
+
* implementation — the default in `AbstractProjectManager` is closest-
|
|
158
|
+
* ancestor descriptor folder; adopters override for explicit URI
|
|
159
|
+
* listings, glob patterns, manifest-declared file lists, etc.
|
|
160
|
+
*/
|
|
161
|
+
getProjectForUri(args: GetProjectForUriArgs): Promise<TProject | undefined>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Typed RPC contract for the data-server protocol head — the server-exposed
|
|
166
|
+
* surface clients call. Composed from the role-explicit fragments
|
|
167
|
+
* {@link DocumentServerProtocol} (per-document lifecycle + subscriptions +
|
|
168
|
+
* the readiness gate) and {@link ProjectServerProtocol} (project tier).
|
|
169
|
+
* Implementations live in `@hydranium/data-server`; typed client proxies are
|
|
170
|
+
* produced by `createRpcProxy` (in this package's `./rpc` subpath). The
|
|
171
|
+
* wire-level method names are `DATA_SERVER_WIRE_PREFIX + methodName` for
|
|
172
|
+
* every method in `DATA_SERVER_PROTOCOL_METHODS` (request methods)
|
|
173
|
+
* and `DATA_CLIENT_PROTOCOL_METHODS` (notification methods).
|
|
174
|
+
*
|
|
175
|
+
* Most adopters parameterise only `TTransfer`; `TDiagnostic` defaults to
|
|
176
|
+
* {@link TransferDiagnostic} and `TProject` to {@link Project}.
|
|
177
|
+
*
|
|
178
|
+
* **Bidirectional pattern.** `DataServerProtocol` is the SERVER-EXPOSED
|
|
179
|
+
* surface — methods the client calls. The dual surface
|
|
180
|
+
* {@link DataClientProtocol} contains methods the server calls (delivered
|
|
181
|
+
* as JSON-RPC notifications). The pair composes via `createRpcProxy`
|
|
182
|
+
* (`localTarget`/`localMethods`) so server and client are typed end-to-end
|
|
183
|
+
* with no method-list drift.
|
|
184
|
+
*
|
|
185
|
+
* **Architectural placement.** The SOLE typed surface for the data-server
|
|
186
|
+
* peer. `lsp-server` and `glsp-server` define their own peer-equivalent
|
|
187
|
+
* surfaces and DO NOT extend this one; all three heads sit at the same tier,
|
|
188
|
+
* coordinating through shared services in `@hydranium/core`.
|
|
189
|
+
*/
|
|
190
|
+
export interface DataServerProtocol<
|
|
191
|
+
TTransfer extends TransferElement,
|
|
192
|
+
TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
|
|
193
|
+
TProject extends Project = Project
|
|
194
|
+
>
|
|
195
|
+
extends DocumentServerProtocol<TTransfer, TDiagnostic>, ProjectServerProtocol<TProject> {}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Cross-reference / naming slice of the server surface — scope-aware
|
|
199
|
+
* queries that resolve against the language's reference services. NOT part
|
|
200
|
+
* of the {@link DataServerProtocol} composition: a pure data consumer
|
|
201
|
+
* doesn't need it, so adopters compose it onto their connection explicitly.
|
|
202
|
+
* The default `DataServer` impls delegate to
|
|
203
|
+
* the per-language `references` services, resolved from each request's
|
|
204
|
+
* source/URI (an `ElementSource` with no URI resolves to the sole
|
|
205
|
+
* registered language; a multi-language workspace overrides).
|
|
206
|
+
*
|
|
207
|
+
* Generic over the transfer root type only — the resolution result
|
|
208
|
+
* ({@link ReferenceTarget}) carries the resolved node's encoded subtree;
|
|
209
|
+
* candidates and names are diagnostic-free.
|
|
210
|
+
*/
|
|
211
|
+
export interface ReferenceServerProtocol<TTransfer extends TransferElement> {
|
|
212
|
+
/**
|
|
213
|
+
* List the reference candidates reachable for the property named in `ctx`
|
|
214
|
+
* from its (possibly synthetic) source. Backed by the language's
|
|
215
|
+
* `ReferenceCandidateProvider`.
|
|
216
|
+
*/
|
|
217
|
+
findReferenceCandidates(ctx: ReferenceContext): Promise<ReferenceCandidate[]>;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Resolve a concrete reference request to its target, returning the
|
|
221
|
+
* target's document URI, display fields, and the resolved node's encoded
|
|
222
|
+
* transfer subtree. Resolves via the language's reference services;
|
|
223
|
+
* `undefined` when the reference does not resolve.
|
|
224
|
+
*/
|
|
225
|
+
resolveReference(ref: ReferenceRequest): Promise<ReferenceTarget<TTransfer> | undefined>;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Compute the next free name for a new element of `args.type` based on
|
|
229
|
+
* `args.proposal`, unique within the `args.tier` scope (default
|
|
230
|
+
* `'project'`). Backed by the language's `NameProvider`.
|
|
231
|
+
*/
|
|
232
|
+
findNextName(args: FindNextNameArgs): Promise<string>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Document-scoped slice of the client surface — notifications the server
|
|
237
|
+
* delivers about subscribed documents. The dual of
|
|
238
|
+
* {@link DocumentServerProtocol}.
|
|
239
|
+
*
|
|
240
|
+
* Notification methods are conventionally `on*`-prefixed so the generic
|
|
241
|
+
* `createRpcProxy` helper routes them as `sendNotification` calls.
|
|
242
|
+
* Multi-listener fan-out is the consumer's responsibility — wrap a local
|
|
243
|
+
* implementation with an `Emitter<T>` to fan a single inbound
|
|
244
|
+
* `onDocumentUpdated` call out to multiple local subscribers.
|
|
245
|
+
*/
|
|
246
|
+
export interface DocumentClientProtocol<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic = TransferDiagnostic> {
|
|
247
|
+
/**
|
|
248
|
+
* Delivered when a subscribed document reached one of the data-server's
|
|
249
|
+
* configured build phases. Subscribers filter incoming events by URI
|
|
250
|
+
* if they hold multiple subscriptions on the same wire.
|
|
251
|
+
*/
|
|
252
|
+
onDocumentUpdated(event: TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>): void;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Delivered when a subscribed document was persisted to disk via
|
|
256
|
+
* {@link DocumentServerProtocol.saveModelDocument}. The persistence event
|
|
257
|
+
* is delivered on a separate channel from the build-phase update
|
|
258
|
+
* stream — subscribers tracking "saved" status (editor dirty-flag
|
|
259
|
+
* indicators, external sync drivers) listen here instead of filtering
|
|
260
|
+
* `onDocumentUpdated` for `reason: 'saved'` (the build-phase path
|
|
261
|
+
* never emits `'saved'` because saves take a synchronous RPC-response
|
|
262
|
+
* codepath, not a subscription codepath).
|
|
263
|
+
*/
|
|
264
|
+
onDocumentSaved(event: TransferDocumentSavedEvent<TTransfer, TDiagnostic>): void;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Project-scoped slice of the client surface — the project-registry change
|
|
269
|
+
* notification. The dual of {@link ProjectServerProtocol}.
|
|
270
|
+
*/
|
|
271
|
+
export interface ProjectClientProtocol<TProject extends Project = Project> {
|
|
272
|
+
/**
|
|
273
|
+
* Delivered when a project was added, updated, or removed from the
|
|
274
|
+
* registry. The data-server subscribes to its `ProjectManager`'s
|
|
275
|
+
* change channel and fans the internal `ProjectChangeEvent` (which
|
|
276
|
+
* carries arrays of added/updated/removed ids per registry diff) out
|
|
277
|
+
* to one wire notification per affected project — `'removed'` events
|
|
278
|
+
* carry the pre-removal snapshot so clients with no local cache can
|
|
279
|
+
* still read the project's metadata.
|
|
280
|
+
*/
|
|
281
|
+
onProjectsChanged(event: ProjectsChangedEvent<TProject>): void;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Client-side surface — methods the data-server invokes on the client as
|
|
286
|
+
* JSON-RPC notifications. The bidirectional dual of {@link DataServerProtocol},
|
|
287
|
+
* composed from {@link DocumentClientProtocol} and {@link ProjectClientProtocol}.
|
|
288
|
+
* A `createRpcProxy` over the connection exposes the server surface outbound and
|
|
289
|
+
* binds an implementation of this interface inbound (`localTarget`).
|
|
290
|
+
*/
|
|
291
|
+
export interface DataClientProtocol<
|
|
292
|
+
TTransfer extends TransferElement,
|
|
293
|
+
TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
|
|
294
|
+
TProject extends Project = Project
|
|
295
|
+
>
|
|
296
|
+
extends DocumentClientProtocol<TTransfer, TDiagnostic>, ProjectClientProtocol<TProject> {}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optional diagnostics slice of the data-server wire contract: capture memory /
|
|
12
|
+
* state snapshots of the running server process for OOM debugging (notably in a
|
|
13
|
+
* cloud pod). Kept OUT of `DataServerProtocol` so the core document /
|
|
14
|
+
* project contract stays lean, but the framework `DataServer` registers these
|
|
15
|
+
* BY DEFAULT (so every head gets them with no wiring); a head that does not want
|
|
16
|
+
* them drops the names via `DataServerOptions.excludedMethods`.
|
|
17
|
+
*
|
|
18
|
+
* Returns are plain formatted strings — the caller (a command, a log sink, the
|
|
19
|
+
* pod's stdout) decides how to surface them. The wire names are
|
|
20
|
+
* `DATA_SERVER_WIRE_PREFIX + methodName`, same as the core methods.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { LatencyReport } from '../latency-collector.js';
|
|
24
|
+
import type { ProfileCaptureOptions } from '../profiling.js';
|
|
25
|
+
|
|
26
|
+
/** Arguments for {@link DataServerDiagnosticsProtocol.dumpServerState}. */
|
|
27
|
+
export interface DumpServerStateArgs {
|
|
28
|
+
/** Optional label folded into the snapshot heading (e.g. an ISO timestamp). */
|
|
29
|
+
label?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Arguments for {@link DataServerDiagnosticsProtocol.startProfiling} — the capture
|
|
34
|
+
* dimensions to sample over the window (all off unless named). The wire args are
|
|
35
|
+
* exactly the neutral {@link ProfileCaptureOptions}.
|
|
36
|
+
*/
|
|
37
|
+
export type StartProfilingArgs = ProfileCaptureOptions;
|
|
38
|
+
|
|
39
|
+
/** Arguments for {@link DataServerDiagnosticsProtocol.stopProfiling}. */
|
|
40
|
+
export interface StopProfilingArgs {
|
|
41
|
+
/** Optional label folded into the profile filenames + report heading. */
|
|
42
|
+
label?: string;
|
|
43
|
+
/** Directory to write profile artefacts into; defaults to the OS temp dir when absent or not present on disk. */
|
|
44
|
+
directory?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Arguments for {@link DataServerDiagnosticsProtocol.writeHeapSnapshot}. */
|
|
48
|
+
export interface WriteServerHeapSnapshotArgs {
|
|
49
|
+
/** Optional label folded into the snapshot filename. */
|
|
50
|
+
label?: string;
|
|
51
|
+
/** Directory to write into; defaults to the OS temp dir when absent or not present on disk. */
|
|
52
|
+
directory?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Memory / state diagnostics the running server process exposes. Implemented by
|
|
57
|
+
* the framework `DataServer`; computed in the data-server process (the heavy
|
|
58
|
+
* model store), so the snapshots reflect the process that actually holds the
|
|
59
|
+
* workspace AST/CST — the one that OOMs.
|
|
60
|
+
*/
|
|
61
|
+
export interface DataServerDiagnosticsProtocol {
|
|
62
|
+
/**
|
|
63
|
+
* Capture a server-process state snapshot — heap, rss, V8 stats, event-loop
|
|
64
|
+
* utilisation, CPU usage, and document counts — and return it formatted.
|
|
65
|
+
*/
|
|
66
|
+
dumpServerState(args: DumpServerStateArgs): Promise<string>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Write a V8 heap snapshot of the server process to disk (full GC first;
|
|
70
|
+
* briefly pauses the process) and return the absolute file path. Open it in
|
|
71
|
+
* Chrome DevTools > Memory > Load for retained-size analysis.
|
|
72
|
+
*/
|
|
73
|
+
writeHeapSnapshot(args: WriteServerHeapSnapshotArgs): Promise<string>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Capture a pod/container memory snapshot — cgroup v2/v1 current/peak/limit
|
|
77
|
+
* plus a per-process RSS tree, the figure the Kubernetes OOM-killer watches.
|
|
78
|
+
* Degrades to a "no cgroup controller" note outside a container.
|
|
79
|
+
*/
|
|
80
|
+
dumpPodMemory(): Promise<string>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Begin a windowed sampled-profile capture (CPU / allocation / GC / event-loop
|
|
84
|
+
* delay, per the named dimensions) of the server process. Sampling does NOT
|
|
85
|
+
* pause the process. Rejects if a capture is already active — one inspector
|
|
86
|
+
* session at a time. Pair with {@link stopProfiling}.
|
|
87
|
+
*/
|
|
88
|
+
startProfiling(args: StartProfilingArgs): Promise<void>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Stop the active capture, write the requested profile artefacts, and return
|
|
92
|
+
* the formatted profile report. Rejects if no capture is active.
|
|
93
|
+
*/
|
|
94
|
+
stopProfiling(args: StopProfilingArgs): Promise<string>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Return the per-method RPC/LSP latency + throughput collected so far (count,
|
|
98
|
+
* p50/p99/max, total per method). Empty when the head was not started with a
|
|
99
|
+
* latency collector. Read-only — does not reset the window.
|
|
100
|
+
*/
|
|
101
|
+
getLatency(): Promise<LatencyReport>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Request-method names on {@link DataServerDiagnosticsProtocol}. The framework
|
|
106
|
+
* `DataServer` appends these to its registered method set by default; the
|
|
107
|
+
* `as const satisfies` constraint keeps the array in lockstep with the interface.
|
|
108
|
+
*/
|
|
109
|
+
export const DATA_SERVER_DIAGNOSTICS_METHODS = [
|
|
110
|
+
'dumpServerState',
|
|
111
|
+
'writeHeapSnapshot',
|
|
112
|
+
'dumpPodMemory',
|
|
113
|
+
'startProfiling',
|
|
114
|
+
'stopProfiling',
|
|
115
|
+
'getLatency'
|
|
116
|
+
] as const satisfies ReadonlyArray<keyof DataServerDiagnosticsProtocol & string>;
|