@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.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/lib/abstract-logger.d.ts +66 -0
  4. package/lib/abstract-logger.d.ts.map +1 -0
  5. package/lib/abstract-logger.js +110 -0
  6. package/lib/abstract-logger.js.map +1 -0
  7. package/lib/browser-runtime.d.ts +124 -0
  8. package/lib/browser-runtime.d.ts.map +1 -0
  9. package/lib/browser-runtime.js +11 -0
  10. package/lib/browser-runtime.js.map +1 -0
  11. package/lib/client/data-events.d.ts +49 -0
  12. package/lib/client/data-events.d.ts.map +1 -0
  13. package/lib/client/data-events.js +60 -0
  14. package/lib/client/data-events.js.map +1 -0
  15. package/lib/client/data-port.d.ts +95 -0
  16. package/lib/client/data-port.d.ts.map +1 -0
  17. package/lib/client/data-port.js +11 -0
  18. package/lib/client/data-port.js.map +1 -0
  19. package/lib/client/data-session.d.ts +129 -0
  20. package/lib/client/data-session.d.ts.map +1 -0
  21. package/lib/client/data-session.js +179 -0
  22. package/lib/client/data-session.js.map +1 -0
  23. package/lib/client/index.d.ts +39 -0
  24. package/lib/client/index.d.ts.map +1 -0
  25. package/lib/client/index.js +55 -0
  26. package/lib/client/index.js.map +1 -0
  27. package/lib/client/message-relay.d.ts +111 -0
  28. package/lib/client/message-relay.d.ts.map +1 -0
  29. package/lib/client/message-relay.js +139 -0
  30. package/lib/client/message-relay.js.map +1 -0
  31. package/lib/client/post-message-transport.d.ts +77 -0
  32. package/lib/client/post-message-transport.d.ts.map +1 -0
  33. package/lib/client/post-message-transport.js +95 -0
  34. package/lib/client/post-message-transport.js.map +1 -0
  35. package/lib/clock.d.ts +111 -0
  36. package/lib/clock.d.ts.map +1 -0
  37. package/lib/clock.js +74 -0
  38. package/lib/clock.js.map +1 -0
  39. package/lib/data/data-protocol-methods.d.ts +39 -0
  40. package/lib/data/data-protocol-methods.d.ts.map +1 -0
  41. package/lib/data/data-protocol-methods.js +61 -0
  42. package/lib/data/data-protocol-methods.js.map +1 -0
  43. package/lib/data/data-server-protocol.d.ts +264 -0
  44. package/lib/data/data-server-protocol.d.ts.map +1 -0
  45. package/lib/data/data-server-protocol.js +11 -0
  46. package/lib/data/data-server-protocol.js.map +1 -0
  47. package/lib/data/diagnostics.d.ts +97 -0
  48. package/lib/data/diagnostics.d.ts.map +1 -0
  49. package/lib/data/diagnostics.js +25 -0
  50. package/lib/data/diagnostics.js.map +1 -0
  51. package/lib/data/events.d.ts +107 -0
  52. package/lib/data/events.d.ts.map +1 -0
  53. package/lib/data/events.js +11 -0
  54. package/lib/data/events.js.map +1 -0
  55. package/lib/data/index.d.ts +15 -0
  56. package/lib/data/index.d.ts.map +1 -0
  57. package/lib/data/index.js +38 -0
  58. package/lib/data/index.js.map +1 -0
  59. package/lib/data/methods.d.ts +57 -0
  60. package/lib/data/methods.d.ts.map +1 -0
  61. package/lib/data/methods.js +60 -0
  62. package/lib/data/methods.js.map +1 -0
  63. package/lib/data/requests.d.ts +65 -0
  64. package/lib/data/requests.d.ts.map +1 -0
  65. package/lib/data/requests.js +11 -0
  66. package/lib/data/requests.js.map +1 -0
  67. package/lib/debouncer.d.ts +75 -0
  68. package/lib/debouncer.d.ts.map +1 -0
  69. package/lib/debouncer.js +91 -0
  70. package/lib/debouncer.js.map +1 -0
  71. package/lib/errors.d.ts +84 -0
  72. package/lib/errors.d.ts.map +1 -0
  73. package/lib/errors.js +113 -0
  74. package/lib/errors.js.map +1 -0
  75. package/lib/host-diagnostics.d.ts +58 -0
  76. package/lib/host-diagnostics.d.ts.map +1 -0
  77. package/lib/host-diagnostics.js +18 -0
  78. package/lib/host-diagnostics.js.map +1 -0
  79. package/lib/index.d.ts +34 -0
  80. package/lib/index.d.ts.map +1 -0
  81. package/lib/index.js +56 -0
  82. package/lib/index.js.map +1 -0
  83. package/lib/latency-collector.d.ts +126 -0
  84. package/lib/latency-collector.d.ts.map +1 -0
  85. package/lib/latency-collector.js +137 -0
  86. package/lib/latency-collector.js.map +1 -0
  87. package/lib/logger.d.ts +119 -0
  88. package/lib/logger.d.ts.map +1 -0
  89. package/lib/logger.js +126 -0
  90. package/lib/logger.js.map +1 -0
  91. package/lib/model-server.d.ts +352 -0
  92. package/lib/model-server.d.ts.map +1 -0
  93. package/lib/model-server.js +160 -0
  94. package/lib/model-server.js.map +1 -0
  95. package/lib/model-service/args.d.ts +64 -0
  96. package/lib/model-service/args.d.ts.map +1 -0
  97. package/lib/model-service/args.js +11 -0
  98. package/lib/model-service/args.js.map +1 -0
  99. package/lib/model-service/index.d.ts +11 -0
  100. package/lib/model-service/index.d.ts.map +1 -0
  101. package/lib/model-service/index.js +31 -0
  102. package/lib/model-service/index.js.map +1 -0
  103. package/lib/model-service/reference-candidate.d.ts +56 -0
  104. package/lib/model-service/reference-candidate.d.ts.map +1 -0
  105. package/lib/model-service/reference-candidate.js +11 -0
  106. package/lib/model-service/reference-candidate.js.map +1 -0
  107. package/lib/noop-logger.d.ts +25 -0
  108. package/lib/noop-logger.d.ts.map +1 -0
  109. package/lib/noop-logger.js +36 -0
  110. package/lib/noop-logger.js.map +1 -0
  111. package/lib/observable-value.d.ts +72 -0
  112. package/lib/observable-value.d.ts.map +1 -0
  113. package/lib/observable-value.js +52 -0
  114. package/lib/observable-value.js.map +1 -0
  115. package/lib/patch-merge.d.ts +123 -0
  116. package/lib/patch-merge.d.ts.map +1 -0
  117. package/lib/patch-merge.js +104 -0
  118. package/lib/patch-merge.js.map +1 -0
  119. package/lib/profile-session.d.ts +87 -0
  120. package/lib/profile-session.d.ts.map +1 -0
  121. package/lib/profile-session.js +98 -0
  122. package/lib/profile-session.js.map +1 -0
  123. package/lib/profiling.d.ts +83 -0
  124. package/lib/profiling.d.ts.map +1 -0
  125. package/lib/profiling.js +20 -0
  126. package/lib/profiling.js.map +1 -0
  127. package/lib/project.d.ts +121 -0
  128. package/lib/project.d.ts.map +1 -0
  129. package/lib/project.js +34 -0
  130. package/lib/project.js.map +1 -0
  131. package/lib/rpc/bind-rpc-methods.d.ts +100 -0
  132. package/lib/rpc/bind-rpc-methods.d.ts.map +1 -0
  133. package/lib/rpc/bind-rpc-methods.js +110 -0
  134. package/lib/rpc/bind-rpc-methods.js.map +1 -0
  135. package/lib/rpc/create-rpc-proxy.d.ts +188 -0
  136. package/lib/rpc/create-rpc-proxy.d.ts.map +1 -0
  137. package/lib/rpc/create-rpc-proxy.js +158 -0
  138. package/lib/rpc/create-rpc-proxy.js.map +1 -0
  139. package/lib/rpc/index.d.ts +12 -0
  140. package/lib/rpc/index.d.ts.map +1 -0
  141. package/lib/rpc/index.js +31 -0
  142. package/lib/rpc/index.js.map +1 -0
  143. package/lib/rpc/wire-prefix.d.ts +37 -0
  144. package/lib/rpc/wire-prefix.d.ts.map +1 -0
  145. package/lib/rpc/wire-prefix.js +49 -0
  146. package/lib/rpc/wire-prefix.js.map +1 -0
  147. package/lib/testing/data-doubles.d.ts +121 -0
  148. package/lib/testing/data-doubles.d.ts.map +1 -0
  149. package/lib/testing/data-doubles.js +102 -0
  150. package/lib/testing/data-doubles.js.map +1 -0
  151. package/lib/testing/fake-clock.d.ts +35 -0
  152. package/lib/testing/fake-clock.d.ts.map +1 -0
  153. package/lib/testing/fake-clock.js +86 -0
  154. package/lib/testing/fake-clock.js.map +1 -0
  155. package/lib/testing/harness.d.ts +24 -0
  156. package/lib/testing/harness.d.ts.map +1 -0
  157. package/lib/testing/harness.js +11 -0
  158. package/lib/testing/harness.js.map +1 -0
  159. package/lib/testing/index.d.ts +13 -0
  160. package/lib/testing/index.d.ts.map +1 -0
  161. package/lib/testing/index.js +41 -0
  162. package/lib/testing/index.js.map +1 -0
  163. package/lib/testing/node/duplex-connection.d.ts +25 -0
  164. package/lib/testing/node/duplex-connection.d.ts.map +1 -0
  165. package/lib/testing/node/duplex-connection.js +34 -0
  166. package/lib/testing/node/duplex-connection.js.map +1 -0
  167. package/lib/testing/node/duplex-stream.d.ts +32 -0
  168. package/lib/testing/node/duplex-stream.d.ts.map +1 -0
  169. package/lib/testing/node/duplex-stream.js +35 -0
  170. package/lib/testing/node/duplex-stream.js.map +1 -0
  171. package/lib/testing/node/index.d.ts +11 -0
  172. package/lib/testing/node/index.d.ts.map +1 -0
  173. package/lib/testing/node/index.js +37 -0
  174. package/lib/testing/node/index.js.map +1 -0
  175. package/lib/testing/wait-for.d.ts +69 -0
  176. package/lib/testing/wait-for.d.ts.map +1 -0
  177. package/lib/testing/wait-for.js +67 -0
  178. package/lib/testing/wait-for.js.map +1 -0
  179. package/lib/tracer.d.ts +115 -0
  180. package/lib/tracer.d.ts.map +1 -0
  181. package/lib/tracer.js +205 -0
  182. package/lib/tracer.js.map +1 -0
  183. package/lib/transfer-diagnostic.d.ts +73 -0
  184. package/lib/transfer-diagnostic.d.ts.map +1 -0
  185. package/lib/transfer-diagnostic.js +45 -0
  186. package/lib/transfer-diagnostic.js.map +1 -0
  187. package/lib/transfer-document.d.ts +73 -0
  188. package/lib/transfer-document.d.ts.map +1 -0
  189. package/lib/transfer-document.js +46 -0
  190. package/lib/transfer-document.js.map +1 -0
  191. package/lib/transfer-element.d.ts +39 -0
  192. package/lib/transfer-element.d.ts.map +1 -0
  193. package/lib/transfer-element.js +11 -0
  194. package/lib/transfer-element.js.map +1 -0
  195. package/lib/uri.d.ts +62 -0
  196. package/lib/uri.d.ts.map +1 -0
  197. package/lib/uri.js +28 -0
  198. package/lib/uri.js.map +1 -0
  199. package/lib/util.d.ts +147 -0
  200. package/lib/util.d.ts.map +1 -0
  201. package/lib/util.js +211 -0
  202. package/lib/util.js.map +1 -0
  203. package/package.json +100 -0
  204. package/src/abstract-logger.ts +120 -0
  205. package/src/browser-runtime.ts +135 -0
  206. package/src/client/data-events.ts +71 -0
  207. package/src/client/data-port.ts +99 -0
  208. package/src/client/data-session.ts +209 -0
  209. package/src/client/index.ts +40 -0
  210. package/src/client/message-relay.ts +216 -0
  211. package/src/client/post-message-transport.ts +167 -0
  212. package/src/clock.ts +173 -0
  213. package/src/data/data-protocol-methods.ts +79 -0
  214. package/src/data/data-server-protocol.ts +296 -0
  215. package/src/data/diagnostics.ts +116 -0
  216. package/src/data/events.ts +127 -0
  217. package/src/data/index.ts +23 -0
  218. package/src/data/methods.ts +59 -0
  219. package/src/data/requests.ts +70 -0
  220. package/src/debouncer.ts +118 -0
  221. package/src/errors.ts +127 -0
  222. package/src/host-diagnostics.ts +63 -0
  223. package/src/index.ts +41 -0
  224. package/src/latency-collector.ts +231 -0
  225. package/src/logger.ts +172 -0
  226. package/src/model-server.ts +479 -0
  227. package/src/model-service/args.ts +67 -0
  228. package/src/model-service/index.ts +16 -0
  229. package/src/model-service/reference-candidate.ts +58 -0
  230. package/src/noop-logger.ts +36 -0
  231. package/src/observable-value.ts +92 -0
  232. package/src/patch-merge.ts +165 -0
  233. package/src/profile-session.ts +164 -0
  234. package/src/profiling.ts +113 -0
  235. package/src/project.ts +125 -0
  236. package/src/rpc/README.md +163 -0
  237. package/src/rpc/bind-rpc-methods.ts +180 -0
  238. package/src/rpc/create-rpc-proxy.ts +292 -0
  239. package/src/rpc/index.ts +16 -0
  240. package/src/rpc/wire-prefix.ts +48 -0
  241. package/src/testing/data-doubles.ts +178 -0
  242. package/src/testing/fake-clock.ts +106 -0
  243. package/src/testing/harness.ts +24 -0
  244. package/src/testing/index.ts +26 -0
  245. package/src/testing/node/duplex-connection.ts +50 -0
  246. package/src/testing/node/duplex-stream.ts +51 -0
  247. package/src/testing/node/index.ts +22 -0
  248. package/src/testing/wait-for.ts +94 -0
  249. package/src/tracer.ts +277 -0
  250. package/src/transfer-diagnostic.ts +92 -0
  251. package/src/transfer-document.ts +99 -0
  252. package/src/transfer-element.ts +43 -0
  253. package/src/uri.ts +61 -0
  254. 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>;