@hydranium/protocol 1.0.0-next.29 → 1.0.0-next.299

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 (259) hide show
  1. package/README.md +44 -49
  2. package/lib/abstract-logger.d.ts +5 -0
  3. package/lib/abstract-logger.d.ts.map +1 -1
  4. package/lib/abstract-logger.js +7 -0
  5. package/lib/abstract-logger.js.map +1 -1
  6. package/lib/client/data-connection.d.ts +302 -0
  7. package/lib/client/data-connection.d.ts.map +1 -0
  8. package/lib/client/data-connection.js +533 -0
  9. package/lib/client/data-connection.js.map +1 -0
  10. package/lib/client/data-events.d.ts +17 -1
  11. package/lib/client/data-events.d.ts.map +1 -1
  12. package/lib/client/data-events.js +25 -0
  13. package/lib/client/data-events.js.map +1 -1
  14. package/lib/client/data-port.d.ts +34 -24
  15. package/lib/client/data-port.d.ts.map +1 -1
  16. package/lib/client/data-session.d.ts +657 -89
  17. package/lib/client/data-session.d.ts.map +1 -1
  18. package/lib/client/data-session.js +1113 -115
  19. package/lib/client/data-session.js.map +1 -1
  20. package/lib/client/index.d.ts +15 -9
  21. package/lib/client/index.d.ts.map +1 -1
  22. package/lib/client/index.js +15 -9
  23. package/lib/client/index.js.map +1 -1
  24. package/lib/client/message-relay.d.ts +4 -4
  25. package/lib/client/message-relay.js +4 -4
  26. package/lib/client/message-relay.js.map +1 -1
  27. package/lib/client/post-message-transport.d.ts +64 -3
  28. package/lib/client/post-message-transport.d.ts.map +1 -1
  29. package/lib/client/post-message-transport.js +175 -1
  30. package/lib/client/post-message-transport.js.map +1 -1
  31. package/lib/client/rpc-connection.d.ts +157 -0
  32. package/lib/client/rpc-connection.d.ts.map +1 -0
  33. package/lib/client/rpc-connection.js +214 -0
  34. package/lib/client/rpc-connection.js.map +1 -0
  35. package/lib/client/worker-handshake.d.ts +72 -0
  36. package/lib/client/worker-handshake.d.ts.map +1 -0
  37. package/lib/client/worker-handshake.js +51 -0
  38. package/lib/client/worker-handshake.js.map +1 -0
  39. package/lib/client-ids.d.ts +45 -0
  40. package/lib/client-ids.d.ts.map +1 -0
  41. package/lib/client-ids.js +48 -0
  42. package/lib/client-ids.js.map +1 -0
  43. package/lib/clock.d.ts +50 -10
  44. package/lib/clock.d.ts.map +1 -1
  45. package/lib/clock.js +36 -1
  46. package/lib/clock.js.map +1 -1
  47. package/lib/console-logger.d.ts +23 -0
  48. package/lib/console-logger.d.ts.map +1 -0
  49. package/lib/console-logger.js +39 -0
  50. package/lib/console-logger.js.map +1 -0
  51. package/lib/data/data-protocol-methods.d.ts +4 -4
  52. package/lib/data/data-protocol-methods.d.ts.map +1 -1
  53. package/lib/data/data-protocol-methods.js +13 -1
  54. package/lib/data/data-protocol-methods.js.map +1 -1
  55. package/lib/data/data-server-protocol.d.ts +165 -48
  56. package/lib/data/data-server-protocol.d.ts.map +1 -1
  57. package/lib/data/events.d.ts +137 -21
  58. package/lib/data/events.d.ts.map +1 -1
  59. package/lib/data/requests.d.ts +71 -11
  60. package/lib/data/requests.d.ts.map +1 -1
  61. package/lib/debouncer.d.ts +3 -3
  62. package/lib/debouncer.d.ts.map +1 -1
  63. package/lib/debouncer.js +6 -9
  64. package/lib/debouncer.js.map +1 -1
  65. package/lib/errors.d.ts +175 -30
  66. package/lib/errors.d.ts.map +1 -1
  67. package/lib/errors.js +260 -32
  68. package/lib/errors.js.map +1 -1
  69. package/lib/glsp-request-model-args.d.ts +16 -0
  70. package/lib/glsp-request-model-args.d.ts.map +1 -0
  71. package/lib/glsp-request-model-args.js +19 -0
  72. package/lib/glsp-request-model-args.js.map +1 -0
  73. package/lib/glsp-save-model-actions.d.ts +50 -0
  74. package/lib/glsp-save-model-actions.d.ts.map +1 -0
  75. package/lib/glsp-save-model-actions.js +28 -0
  76. package/lib/glsp-save-model-actions.js.map +1 -0
  77. package/lib/index.d.ts +6 -0
  78. package/lib/index.d.ts.map +1 -1
  79. package/lib/index.js +6 -0
  80. package/lib/index.js.map +1 -1
  81. package/lib/latency-collector.d.ts +8 -4
  82. package/lib/latency-collector.d.ts.map +1 -1
  83. package/lib/latency-collector.js.map +1 -1
  84. package/lib/logger.d.ts +38 -7
  85. package/lib/logger.d.ts.map +1 -1
  86. package/lib/logger.js +42 -4
  87. package/lib/logger.js.map +1 -1
  88. package/lib/messages/index.d.ts +4 -2
  89. package/lib/messages/index.d.ts.map +1 -1
  90. package/lib/messages/index.js +14 -4
  91. package/lib/messages/index.js.map +1 -1
  92. package/lib/messages/primitives.d.ts +71 -10
  93. package/lib/messages/primitives.d.ts.map +1 -1
  94. package/lib/messages/primitives.js +55 -3
  95. package/lib/messages/primitives.js.map +1 -1
  96. package/lib/model-server.d.ts +60 -13
  97. package/lib/model-server.d.ts.map +1 -1
  98. package/lib/model-server.js +4 -2
  99. package/lib/model-server.js.map +1 -1
  100. package/lib/model-service/base-version.d.ts +67 -0
  101. package/lib/model-service/base-version.d.ts.map +1 -0
  102. package/lib/model-service/base-version.js +44 -0
  103. package/lib/model-service/base-version.js.map +1 -0
  104. package/lib/model-service/index.d.ts +1 -1
  105. package/lib/model-service/index.d.ts.map +1 -1
  106. package/lib/model-service/index.js +4 -5
  107. package/lib/model-service/index.js.map +1 -1
  108. package/lib/model-service/reference-candidate.d.ts +5 -3
  109. package/lib/model-service/reference-candidate.d.ts.map +1 -1
  110. package/lib/{model-service/args.js → node/index.d.ts} +2 -3
  111. package/lib/node/index.d.ts.map +1 -0
  112. package/lib/node/index.js +29 -0
  113. package/lib/node/index.js.map +1 -0
  114. package/lib/node/process-memory.d.ts +66 -0
  115. package/lib/node/process-memory.d.ts.map +1 -0
  116. package/lib/node/process-memory.js +291 -0
  117. package/lib/node/process-memory.js.map +1 -0
  118. package/lib/noop-logger.d.ts.map +1 -1
  119. package/lib/noop-logger.js.map +1 -1
  120. package/lib/observable-value.js.map +1 -1
  121. package/lib/patch-merge.d.ts +63 -35
  122. package/lib/patch-merge.d.ts.map +1 -1
  123. package/lib/patch-merge.js +171 -25
  124. package/lib/patch-merge.js.map +1 -1
  125. package/lib/profile-session.d.ts +8 -4
  126. package/lib/profile-session.d.ts.map +1 -1
  127. package/lib/profile-session.js.map +1 -1
  128. package/lib/project.d.ts +4 -1
  129. package/lib/project.d.ts.map +1 -1
  130. package/lib/project.js.map +1 -1
  131. package/lib/random-uuid.d.ts +14 -0
  132. package/lib/random-uuid.d.ts.map +1 -0
  133. package/lib/random-uuid.js +24 -0
  134. package/lib/random-uuid.js.map +1 -0
  135. package/lib/reconcile-write.d.ts +67 -0
  136. package/lib/reconcile-write.d.ts.map +1 -0
  137. package/lib/reconcile-write.js +69 -0
  138. package/lib/reconcile-write.js.map +1 -0
  139. package/lib/rpc/bind-rpc-methods.d.ts +33 -3
  140. package/lib/rpc/bind-rpc-methods.d.ts.map +1 -1
  141. package/lib/rpc/bind-rpc-methods.js +32 -3
  142. package/lib/rpc/bind-rpc-methods.js.map +1 -1
  143. package/lib/rpc/create-rpc-proxy.d.ts +10 -0
  144. package/lib/rpc/create-rpc-proxy.d.ts.map +1 -1
  145. package/lib/rpc/create-rpc-proxy.js +12 -2
  146. package/lib/rpc/create-rpc-proxy.js.map +1 -1
  147. package/lib/rpc/index.d.ts +1 -0
  148. package/lib/rpc/index.d.ts.map +1 -1
  149. package/lib/rpc/index.js +1 -0
  150. package/lib/rpc/index.js.map +1 -1
  151. package/lib/rpc/send-by-method-name.d.ts +76 -0
  152. package/lib/rpc/send-by-method-name.d.ts.map +1 -0
  153. package/lib/rpc/send-by-method-name.js +120 -0
  154. package/lib/rpc/send-by-method-name.js.map +1 -0
  155. package/lib/rpc/wire-prefix.js.map +1 -1
  156. package/lib/testing/catalogue-audit.d.ts +80 -0
  157. package/lib/testing/catalogue-audit.d.ts.map +1 -0
  158. package/lib/testing/catalogue-audit.js +94 -0
  159. package/lib/testing/catalogue-audit.js.map +1 -0
  160. package/lib/testing/data-doubles.d.ts +41 -12
  161. package/lib/testing/data-doubles.d.ts.map +1 -1
  162. package/lib/testing/data-doubles.js +56 -8
  163. package/lib/testing/data-doubles.js.map +1 -1
  164. package/lib/testing/fake-clock.d.ts +17 -2
  165. package/lib/testing/fake-clock.d.ts.map +1 -1
  166. package/lib/testing/fake-clock.js +61 -46
  167. package/lib/testing/fake-clock.js.map +1 -1
  168. package/lib/testing/index.d.ts +1 -0
  169. package/lib/testing/index.d.ts.map +1 -1
  170. package/lib/testing/index.js +5 -2
  171. package/lib/testing/index.js.map +1 -1
  172. package/lib/testing/node/duplex-connection.d.ts.map +1 -1
  173. package/lib/testing/node/duplex-connection.js +3 -2
  174. package/lib/testing/node/duplex-connection.js.map +1 -1
  175. package/lib/testing/node/duplex-stream.js.map +1 -1
  176. package/lib/testing/node/index.d.ts +1 -0
  177. package/lib/testing/node/index.d.ts.map +1 -1
  178. package/lib/testing/node/index.js +2 -2
  179. package/lib/testing/node/index.js.map +1 -1
  180. package/lib/testing/node/message-port-pair.d.ts +25 -0
  181. package/lib/testing/node/message-port-pair.d.ts.map +1 -0
  182. package/lib/testing/node/message-port-pair.js +26 -0
  183. package/lib/testing/node/message-port-pair.js.map +1 -0
  184. package/lib/testing/wait-for.d.ts +9 -3
  185. package/lib/testing/wait-for.d.ts.map +1 -1
  186. package/lib/testing/wait-for.js +57 -11
  187. package/lib/testing/wait-for.js.map +1 -1
  188. package/lib/tracer.d.ts.map +1 -1
  189. package/lib/tracer.js +5 -9
  190. package/lib/tracer.js.map +1 -1
  191. package/lib/transfer-diagnostic.js.map +1 -1
  192. package/lib/transfer-document.d.ts +95 -32
  193. package/lib/transfer-document.d.ts.map +1 -1
  194. package/lib/transfer-document.js +17 -9
  195. package/lib/transfer-document.js.map +1 -1
  196. package/lib/uri.d.ts.map +1 -1
  197. package/lib/uri.js.map +1 -1
  198. package/lib/util.d.ts +8 -2
  199. package/lib/util.d.ts.map +1 -1
  200. package/lib/util.js +32 -4
  201. package/lib/util.js.map +1 -1
  202. package/package.json +24 -40
  203. package/src/abstract-logger.ts +8 -0
  204. package/src/client/data-connection.ts +653 -0
  205. package/src/client/data-events.ts +37 -1
  206. package/src/client/data-port.ts +36 -25
  207. package/src/client/data-session.ts +1427 -137
  208. package/src/client/index.ts +15 -9
  209. package/src/client/message-relay.ts +4 -4
  210. package/src/client/post-message-transport.ts +219 -4
  211. package/src/client/rpc-connection.ts +281 -0
  212. package/src/client/worker-handshake.ts +98 -0
  213. package/src/client-ids.ts +49 -0
  214. package/src/clock.ts +68 -10
  215. package/src/console-logger.ts +39 -0
  216. package/src/data/data-protocol-methods.ts +14 -4
  217. package/src/data/data-server-protocol.ts +192 -48
  218. package/src/data/events.ts +143 -21
  219. package/src/data/requests.ts +77 -11
  220. package/src/debouncer.ts +7 -10
  221. package/src/errors.ts +309 -40
  222. package/src/glsp-request-model-args.ts +16 -0
  223. package/src/glsp-save-model-actions.ts +59 -0
  224. package/src/index.ts +6 -0
  225. package/src/latency-collector.ts +8 -3
  226. package/src/logger.ts +44 -8
  227. package/src/messages/index.ts +4 -2
  228. package/src/messages/primitives.ts +102 -10
  229. package/src/model-server.ts +64 -19
  230. package/src/model-service/base-version.ts +75 -0
  231. package/src/model-service/index.ts +4 -5
  232. package/src/model-service/reference-candidate.ts +5 -3
  233. package/src/node/index.ts +14 -0
  234. package/src/node/process-memory.ts +299 -0
  235. package/src/patch-merge.ts +230 -45
  236. package/src/profile-session.ts +9 -4
  237. package/src/project.ts +4 -1
  238. package/src/random-uuid.ts +21 -0
  239. package/src/reconcile-write.ts +128 -0
  240. package/src/rpc/README.md +6 -6
  241. package/src/rpc/bind-rpc-methods.ts +59 -4
  242. package/src/rpc/create-rpc-proxy.ts +20 -2
  243. package/src/rpc/index.ts +1 -0
  244. package/src/rpc/send-by-method-name.ts +140 -0
  245. package/src/testing/catalogue-audit.ts +111 -0
  246. package/src/testing/data-doubles.ts +141 -19
  247. package/src/testing/fake-clock.ts +76 -48
  248. package/src/testing/index.ts +5 -2
  249. package/src/testing/node/duplex-connection.ts +3 -2
  250. package/src/testing/node/index.ts +2 -2
  251. package/src/testing/node/message-port-pair.ts +40 -0
  252. package/src/testing/wait-for.ts +61 -13
  253. package/src/tracer.ts +5 -9
  254. package/src/transfer-document.ts +115 -34
  255. package/src/util.ts +33 -6
  256. package/lib/model-service/args.d.ts +0 -64
  257. package/lib/model-service/args.d.ts.map +0 -1
  258. package/lib/model-service/args.js.map +0 -1
  259. package/src/model-service/args.ts +0 -67
@@ -0,0 +1,98 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 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
+ * The handshake between a page and the worker hosting its heads, shared by
12
+ * both ends so neither can drift from the other.
13
+ *
14
+ * **Every head gets its own `MessagePort`; none is bound to the worker
15
+ * global.** The heads share one worker so they share one Langium store, and
16
+ * that makes the global contended: a head bound to it receives every other
17
+ * head's traffic, since a JSON-RPC reader filters nothing. The global is
18
+ * unusable even for a single head: GLSP's worker launcher posts its startup
19
+ * handshake through the global `postMessage` regardless of the connection it
20
+ * was configured with, a plain string no JSON-RPC reader can parse.
21
+ *
22
+ * The global therefore carries the host protocol: the bootstrap in, the ready
23
+ * message and failure reports out, and whatever else the host adds for
24
+ * itself. A message that belongs to a head belongs on that head's port.
25
+ *
26
+ * The ports are {@link TransferredMessagePort}s, because this package compiles
27
+ * without the DOM lib and the structural type is also what rejects a `Worker`
28
+ * or the worker global at an adopter's call site.
29
+ */
30
+
31
+ import type { TransferredMessagePort } from './post-message-transport';
32
+
33
+ /** Discriminator of the bootstrap message a page posts on the worker global. */
34
+ export const WORKER_BOOTSTRAP_MESSAGE_TYPE = 'hydranium/bootstrap';
35
+
36
+ /** One transferred port per head. */
37
+ export interface HeadPorts {
38
+ readonly lsp: TransferredMessagePort;
39
+ readonly data: TransferredMessagePort;
40
+ readonly glsp: TransferredMessagePort;
41
+ }
42
+
43
+ /** The one message the worker answers, by starting its heads on the ports. */
44
+ export interface WorkerBootstrapMessage {
45
+ readonly type: typeof WORKER_BOOTSTRAP_MESSAGE_TYPE;
46
+ readonly ports: HeadPorts;
47
+ }
48
+
49
+ export function isWorkerBootstrapMessage(value: unknown): value is WorkerBootstrapMessage {
50
+ if (typeof value !== 'object' || value === null) {
51
+ return false;
52
+ }
53
+ const candidate = value as Partial<WorkerBootstrapMessage>;
54
+ return candidate.type === WORKER_BOOTSTRAP_MESSAGE_TYPE && typeof candidate.ports === 'object' && candidate.ports !== null;
55
+ }
56
+
57
+ /**
58
+ * Discriminator of the message the worker posts on the global once every head
59
+ * is up. Until it arrives, the page takes the `Worker`'s `error` event as a
60
+ * worker that never started and ends the heads; after it, as an uncaught error
61
+ * the worker runs on from, which it reports and keeps the heads through.
62
+ */
63
+ export const WORKER_READY_MESSAGE_TYPE = 'hydranium/ready';
64
+
65
+ export interface WorkerReadyMessage {
66
+ readonly type: typeof WORKER_READY_MESSAGE_TYPE;
67
+ }
68
+
69
+ export function isWorkerReadyMessage(value: unknown): value is WorkerReadyMessage {
70
+ return typeof value === 'object' && value !== null && (value as Partial<WorkerReadyMessage>).type === WORKER_READY_MESSAGE_TYPE;
71
+ }
72
+
73
+ /**
74
+ * Discriminator of a failure report the worker posts on the global.
75
+ *
76
+ * A worker has no console anyone is looking at, and the failures that matter
77
+ * most reach nobody by default: a workspace walk that throws has no reply to
78
+ * reject, so it looks exactly like a workspace with no documents in it.
79
+ */
80
+ export const WORKER_ERROR_MESSAGE_TYPE = 'hydranium/worker-error';
81
+
82
+ /** The phase a failed bootstrap reports under; no head answers after it. */
83
+ export const WORKER_BOOTSTRAP_PHASE = 'bootstrap';
84
+
85
+ export interface WorkerErrorMessage {
86
+ readonly type: typeof WORKER_ERROR_MESSAGE_TYPE;
87
+ /** What the worker was doing, {@link WORKER_BOOTSTRAP_PHASE} for a failed bootstrap. */
88
+ readonly phase: string;
89
+ readonly message: string;
90
+ }
91
+
92
+ export function isWorkerErrorMessage(value: unknown): value is WorkerErrorMessage {
93
+ if (typeof value !== 'object' || value === null) {
94
+ return false;
95
+ }
96
+ const candidate = value as Partial<WorkerErrorMessage>;
97
+ return candidate.type === WORKER_ERROR_MESSAGE_TYPE && typeof candidate.phase === 'string' && typeof candidate.message === 'string';
98
+ }
@@ -0,0 +1,49 @@
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
+ * Well-known values of the wire's `clientId` / `sourceClientId` field.
12
+ *
13
+ * The framework tracks each document as belonging to one or more "clients" —
14
+ * typically an LSP-text client (Monaco, VS Code, …), a GLSP diagram client,
15
+ * and/or a structured-form client — and keys its holds and watches per
16
+ * `(uri, clientId)`. These three values are not real participants, so a client
17
+ * minting its own id must avoid them.
18
+ *
19
+ * **Here rather than in the server package, because the side that has to
20
+ * RECOGNISE them is the client.** An inbound `onDocumentUpdated` carries
21
+ * `sourceClientId`, and a consumer deciding what to do with it is asking which
22
+ * of these it is — so a frontend would otherwise have to depend on the server
23
+ * tier to name three strings, and in practice retypes the literal instead.
24
+ */
25
+
26
+ /** Identifier for an LSP-text client (Monaco, VS Code, …). */
27
+ export const LANGUAGE_CLIENT_ID = 'language-client';
28
+
29
+ /**
30
+ * The id an event names when no single client is behind it: the source of a
31
+ * `'rebuilt'` event, a document no client authored, or a build with more than
32
+ * one cause.
33
+ */
34
+ export const UNKNOWN_CLIENT_ID = 'unknown';
35
+
36
+ /**
37
+ * Synthetic author id on the `onDocumentUpdated` broadcast a data-server
38
+ * emits for the build that follows a document's release, once no client holds
39
+ * it; by default that build reverts the document to its disk content,
40
+ * discarding unsaved edits. Not a real client: it lets consumers tell the
41
+ * release broadcast from client-authored updates.
42
+ */
43
+ export const DOCUMENT_RELEASE_CLIENT_ID = 'document-release';
44
+
45
+ /**
46
+ * Every id the framework reserves, for a client checking that the identity it
47
+ * is about to mint collides with none of them.
48
+ */
49
+ export const FRAMEWORK_CLIENT_IDS: readonly string[] = [LANGUAGE_CLIENT_ID, UNKNOWN_CLIENT_ID, DOCUMENT_RELEASE_CLIENT_ID];
package/src/clock.ts CHANGED
@@ -14,9 +14,11 @@ import { Disposable } from './util';
14
14
  * there is no `start` — you create it at the moment timing begins.
15
15
  *
16
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).
17
+ * precise and immune to wall-clock steps (a time sync or a manual change can
18
+ * make a `Date.now` delta go backwards; a stopwatch cannot). In Node on Linux
19
+ * it also leaves out time the system spends suspended, so a duration spanning
20
+ * a suspend reads as the awake time only. Use this for every duration, a TTL
21
+ * included, and {@link Clock.now} for "what time is it".
20
22
  *
21
23
  * It holds no scheduled resource and is therefore NOT a {@link Disposable} —
22
24
  * the disposable thing is a {@link Clock.setTimer}. Reads are lazy, so a fake
@@ -45,16 +47,16 @@ export interface Stopwatch {
45
47
  * testable instead of waiting on real wall-clock. The {@link SystemClock}
46
48
  * default delegates to the platform; tests bind `makeFakeClock`.
47
49
  *
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.
50
+ * Two distinct readings on purpose: {@link now} is wall-clock, for a
51
+ * timestamp, while {@link stopwatch} measures durations off a monotonic
52
+ * source. They are different clocks; do not subtract two {@link now} readings
53
+ * to measure a duration. Framework code reads time only through a `Clock`, so
54
+ * a platform workaround lives in one place.
52
55
  *
53
56
  * A cross-head contract, so it carries no platform or DI-framework coupling.
54
57
  *
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
+ * Note: code that measures real CPU, heap or event-loop time never takes the
59
+ * bound `Clock`: faking the time it measures would invert its meaning.
58
60
  */
59
61
  export interface Clock {
60
62
  /** Current wall-clock time in milliseconds (the `Date.now` reading). */
@@ -76,8 +78,30 @@ export interface Clock {
76
78
  */
77
79
  measure<T>(callback: () => Promise<T>): Promise<Timed<T>>;
78
80
  measure<T>(callback: () => T): Timed<T>;
81
+ /**
82
+ * Settle with `promise`, or with {@link TIMED_OUT} once `ms` have elapsed
83
+ * first. A promise that rejects first rejects this one; one that rejects
84
+ * after the timer is ignored. The timer is disposed the moment `promise`
85
+ * settles, so a won race leaves nothing scheduled, but `promise` itself
86
+ * runs on: nothing here can cancel it.
87
+ *
88
+ * Unlike VS Code's `raceTimeout`, this settles with a sentinel rather than
89
+ * `undefined`, so a promise resolving to `undefined` is not taken for a
90
+ * timeout; the caller branches on the result instead of passing an
91
+ * `onTimeout`.
92
+ *
93
+ * A custom implementation extends {@link SystemClock}, whose race runs over
94
+ * the subclass's {@link setTimer}.
95
+ */
96
+ raceTimer<T>(promise: Promise<T>, ms: number): Promise<T | typeof TIMED_OUT>;
79
97
  }
80
98
 
99
+ /**
100
+ * What {@link Clock.raceTimer} settles with when its timer fires first.
101
+ * Registered under a global key, so two copies of this package agree on it.
102
+ */
103
+ export const TIMED_OUT = Symbol.for('hydranium/protocol/timed-out');
104
+
81
105
  /**
82
106
  * The result of {@link Clock.measure}: a callback's return value paired with
83
107
  * its elapsed wall-time in milliseconds. A plain value object — no methods, no
@@ -144,6 +168,10 @@ function unrefTimer(handle: { unref?: () => void }): void {
144
168
  * Default {@link Clock}: delegates to the platform's `Date.now`,
145
169
  * `performance.now`, and `setTimeout`/`clearTimeout`. Bound everywhere in
146
170
  * production; swapped for `makeFakeClock` in tests.
171
+ *
172
+ * {@link measure} and {@link raceTimer} run over {@link stopwatch} and
173
+ * {@link setTimer}, so a clock that extends this one and replaces those
174
+ * measures and races on its own time, as the test clock does.
147
175
  */
148
176
  export class SystemClock implements Clock {
149
177
  now(): number {
@@ -170,4 +198,34 @@ export class SystemClock implements Clock {
170
198
  }
171
199
  return { result: result as T, elapsedMs: stopwatch.elapsedMs };
172
200
  }
201
+
202
+ /**
203
+ * The timer settles the race one microtask after it fires. A real timer
204
+ * runs only once pending resolutions are delivered, while a fake one fires
205
+ * inside its `advance`, so settling at once would let a promise already
206
+ * resolved when `advance` runs lose to a timer it beats in production. The
207
+ * deferral covers only that promise: one that settles through further
208
+ * chained reactions, such as a `.then` chain, an async function or
209
+ * `Promise.allSettled`, can still lose to an `advance` in the same turn,
210
+ * while it wins on the platform's timer. The handlers go on `promise`
211
+ * before {@link setTimer} runs, so a `setTimer` that throws still leaves
212
+ * `promise` observed.
213
+ */
214
+ raceTimer<T>(promise: Promise<T>, ms: number): Promise<T | typeof TIMED_OUT> {
215
+ return new Promise<T | typeof TIMED_OUT>((resolve, reject) => {
216
+ // eslint-disable-next-line prefer-const -- with a `const`, a `setTimer` that throws leaves it uninitialised, and the handlers' later read of it is an unhandled ReferenceError
217
+ let timer: Disposable | undefined;
218
+ promise.then(
219
+ value => {
220
+ timer?.dispose();
221
+ resolve(value);
222
+ },
223
+ (err: unknown) => {
224
+ timer?.dispose();
225
+ reject(err);
226
+ }
227
+ );
228
+ timer = this.setTimer(() => void Promise.resolve().then(() => resolve(TIMED_OUT)), ms);
229
+ });
230
+ }
173
231
  }
@@ -0,0 +1,39 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 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 { AbstractLogger } from './abstract-logger';
11
+ import { type LogLevel, type Logger } from './logger';
12
+
13
+ /** `console.trace` prints a stack with every line, so `trace` goes out at `debug`. */
14
+ const CONSOLE_METHODS: Record<LogLevel, 'error' | 'warn' | 'info' | 'debug'> = {
15
+ error: 'error',
16
+ warn: 'warn',
17
+ info: 'info',
18
+ debug: 'debug',
19
+ trace: 'debug'
20
+ };
21
+
22
+ /**
23
+ * {@link Logger} that writes each line to `console`, at the method matching its
24
+ * level: the framework's `[Label - time] [component]` prefix and the message,
25
+ * then any trailing arguments as they are, so a browser console can expand
26
+ * them. For a browser page, webview or extension host, which has no other sink
27
+ * to hand. On Node, `info` and `debug` reach stdout, so a process whose stdout
28
+ * carries a protocol stream must not use it.
29
+ */
30
+ export class ConsoleLogger extends AbstractLogger implements Logger {
31
+ protected emit(level: LogLevel, label: string, message: string, args: readonly unknown[]): void {
32
+ console[CONSOLE_METHODS[level]](`${this.formatLinePrefix(label)} ${message}`, ...args);
33
+ }
34
+
35
+ protected derive(component: string): this {
36
+ const Subclass = this.constructor as new (component?: string) => this;
37
+ return new Subclass(component);
38
+ }
39
+ }
@@ -20,11 +20,17 @@ import type {
20
20
 
21
21
  /** Request-method names on {@link DocumentServerProtocol}. */
22
22
  export const DOCUMENT_SERVER_PROTOCOL_METHODS = [
23
+ 'createSession',
24
+ 'closeSession',
25
+ 'createModelDocument',
26
+ 'updateModelDocuments',
23
27
  'openModelDocument',
24
28
  'closeModelDocument',
25
29
  'getModelDocument',
26
30
  'updateModelDocument',
27
31
  'saveModelDocument',
32
+ 'persistModelDocument',
33
+ 'deleteModelDocument',
28
34
  'watchModelDocument',
29
35
  'unwatchModelDocument',
30
36
  'waitForReady'
@@ -56,12 +62,16 @@ export const REFERENCE_SERVER_PROTOCOL_METHODS = [
56
62
  'findReferenceCandidates',
57
63
  'resolveReference',
58
64
  'findNextName'
59
- ] as const satisfies ReadonlyArray<keyof ReferenceServerProtocol<TransferElement> & string>;
65
+ ] as const satisfies ReadonlyArray<keyof ReferenceServerProtocol & string>;
60
66
 
61
67
  /** 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
- >;
68
+ export const DOCUMENT_CLIENT_PROTOCOL_METHODS = [
69
+ 'onDocumentUpdated',
70
+ 'onDocumentSaved',
71
+ 'onDocumentDirtyChanged',
72
+ 'onDocumentDeleted',
73
+ 'onDocumentsBuilt'
74
+ ] as const satisfies ReadonlyArray<keyof DocumentClientProtocol<TransferElement> & string>;
65
75
 
66
76
  /** Notification-method names on {@link ProjectClientProtocol}. */
67
77
  export const PROJECT_CLIENT_PROTOCOL_METHODS = ['onProjectsChanged'] as const satisfies ReadonlyArray<keyof ProjectClientProtocol & string>;