@hydranium/protocol 1.0.0-next.32 → 1.0.0-next.320
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -83
- package/lib/abstract-logger.d.ts +5 -0
- package/lib/abstract-logger.d.ts.map +1 -1
- package/lib/abstract-logger.js +7 -0
- package/lib/abstract-logger.js.map +1 -1
- package/lib/client/data-connection.d.ts +302 -0
- package/lib/client/data-connection.d.ts.map +1 -0
- package/lib/client/data-connection.js +533 -0
- package/lib/client/data-connection.js.map +1 -0
- package/lib/client/data-events.d.ts +17 -1
- package/lib/client/data-events.d.ts.map +1 -1
- package/lib/client/data-events.js +25 -0
- package/lib/client/data-events.js.map +1 -1
- package/lib/client/data-port.d.ts +34 -24
- package/lib/client/data-port.d.ts.map +1 -1
- package/lib/client/data-session.d.ts +657 -89
- package/lib/client/data-session.d.ts.map +1 -1
- package/lib/client/data-session.js +1113 -115
- package/lib/client/data-session.js.map +1 -1
- package/lib/client/index.d.ts +15 -9
- package/lib/client/index.d.ts.map +1 -1
- package/lib/client/index.js +15 -9
- package/lib/client/index.js.map +1 -1
- package/lib/client/message-relay.d.ts +4 -4
- package/lib/client/message-relay.js +4 -4
- package/lib/client/message-relay.js.map +1 -1
- package/lib/client/post-message-transport.d.ts +64 -3
- package/lib/client/post-message-transport.d.ts.map +1 -1
- package/lib/client/post-message-transport.js +175 -1
- package/lib/client/post-message-transport.js.map +1 -1
- package/lib/client/rpc-connection.d.ts +157 -0
- package/lib/client/rpc-connection.d.ts.map +1 -0
- package/lib/client/rpc-connection.js +214 -0
- package/lib/client/rpc-connection.js.map +1 -0
- package/lib/client/worker-handshake.d.ts +86 -0
- package/lib/client/worker-handshake.d.ts.map +1 -0
- package/lib/client/worker-handshake.js +51 -0
- package/lib/client/worker-handshake.js.map +1 -0
- package/lib/client-ids.d.ts +45 -0
- package/lib/client-ids.d.ts.map +1 -0
- package/lib/client-ids.js +48 -0
- package/lib/client-ids.js.map +1 -0
- package/lib/clock.d.ts +50 -10
- package/lib/clock.d.ts.map +1 -1
- package/lib/clock.js +36 -1
- package/lib/clock.js.map +1 -1
- package/lib/console-logger.d.ts +23 -0
- package/lib/console-logger.d.ts.map +1 -0
- package/lib/console-logger.js +39 -0
- package/lib/console-logger.js.map +1 -0
- package/lib/data/data-protocol-methods.d.ts +4 -4
- package/lib/data/data-protocol-methods.d.ts.map +1 -1
- package/lib/data/data-protocol-methods.js +13 -1
- package/lib/data/data-protocol-methods.js.map +1 -1
- package/lib/data/data-server-protocol.d.ts +165 -48
- package/lib/data/data-server-protocol.d.ts.map +1 -1
- package/lib/data/events.d.ts +137 -21
- package/lib/data/events.d.ts.map +1 -1
- package/lib/data/requests.d.ts +71 -11
- package/lib/data/requests.d.ts.map +1 -1
- package/lib/debouncer.d.ts +3 -3
- package/lib/debouncer.d.ts.map +1 -1
- package/lib/debouncer.js +6 -9
- package/lib/debouncer.js.map +1 -1
- package/lib/errors.d.ts +183 -30
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +268 -32
- package/lib/errors.js.map +1 -1
- package/lib/glsp-request-model-args.d.ts +16 -0
- package/lib/glsp-request-model-args.d.ts.map +1 -0
- package/lib/glsp-request-model-args.js +19 -0
- package/lib/glsp-request-model-args.js.map +1 -0
- package/lib/glsp-save-model-actions.d.ts +50 -0
- package/lib/glsp-save-model-actions.d.ts.map +1 -0
- package/lib/glsp-save-model-actions.js +28 -0
- package/lib/glsp-save-model-actions.js.map +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -1
- package/lib/latency-collector.d.ts +8 -4
- package/lib/latency-collector.d.ts.map +1 -1
- package/lib/latency-collector.js.map +1 -1
- package/lib/logger.d.ts +38 -7
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js +42 -4
- package/lib/logger.js.map +1 -1
- package/lib/messages/index.d.ts +4 -2
- package/lib/messages/index.d.ts.map +1 -1
- package/lib/messages/index.js +15 -4
- package/lib/messages/index.js.map +1 -1
- package/lib/messages/primitives.d.ts +59 -4
- package/lib/messages/primitives.d.ts.map +1 -1
- package/lib/messages/primitives.js +55 -3
- package/lib/messages/primitives.js.map +1 -1
- package/lib/model-server.d.ts +60 -13
- package/lib/model-server.d.ts.map +1 -1
- package/lib/model-server.js +4 -2
- package/lib/model-server.js.map +1 -1
- package/lib/model-service/base-version.d.ts +67 -0
- package/lib/model-service/base-version.d.ts.map +1 -0
- package/lib/model-service/base-version.js +44 -0
- package/lib/model-service/base-version.js.map +1 -0
- package/lib/model-service/index.d.ts +1 -1
- package/lib/model-service/index.d.ts.map +1 -1
- package/lib/model-service/index.js +4 -5
- package/lib/model-service/index.js.map +1 -1
- package/lib/model-service/reference-candidate.d.ts +5 -3
- package/lib/model-service/reference-candidate.d.ts.map +1 -1
- package/lib/node/index.d.ts +10 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +29 -0
- package/lib/node/index.js.map +1 -0
- package/lib/node/process-memory.d.ts +66 -0
- package/lib/node/process-memory.d.ts.map +1 -0
- package/lib/node/process-memory.js +291 -0
- package/lib/node/process-memory.js.map +1 -0
- package/lib/noop-logger.d.ts.map +1 -1
- package/lib/noop-logger.js.map +1 -1
- package/lib/observable-value.js.map +1 -1
- package/lib/patch-merge.d.ts +63 -35
- package/lib/patch-merge.d.ts.map +1 -1
- package/lib/patch-merge.js +171 -25
- package/lib/patch-merge.js.map +1 -1
- package/lib/profile-session.d.ts +8 -4
- package/lib/profile-session.d.ts.map +1 -1
- package/lib/profile-session.js.map +1 -1
- package/lib/project.d.ts +4 -1
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js.map +1 -1
- package/lib/random-uuid.d.ts +14 -0
- package/lib/random-uuid.d.ts.map +1 -0
- package/lib/random-uuid.js +24 -0
- package/lib/random-uuid.js.map +1 -0
- package/lib/reconcile-write.d.ts +67 -0
- package/lib/reconcile-write.d.ts.map +1 -0
- package/lib/reconcile-write.js +69 -0
- package/lib/reconcile-write.js.map +1 -0
- package/lib/rpc/bind-rpc-methods.d.ts +6 -2
- package/lib/rpc/bind-rpc-methods.d.ts.map +1 -1
- package/lib/rpc/bind-rpc-methods.js +17 -7
- package/lib/rpc/bind-rpc-methods.js.map +1 -1
- package/lib/rpc/create-rpc-proxy.d.ts +3 -0
- package/lib/rpc/create-rpc-proxy.d.ts.map +1 -1
- package/lib/rpc/create-rpc-proxy.js +6 -1
- package/lib/rpc/create-rpc-proxy.js.map +1 -1
- package/lib/rpc/index.d.ts +1 -0
- package/lib/rpc/index.d.ts.map +1 -1
- package/lib/rpc/index.js +1 -0
- package/lib/rpc/index.js.map +1 -1
- package/lib/rpc/send-by-method-name.d.ts +76 -0
- package/lib/rpc/send-by-method-name.d.ts.map +1 -0
- package/lib/rpc/send-by-method-name.js +120 -0
- package/lib/rpc/send-by-method-name.js.map +1 -0
- package/lib/rpc/wire-prefix.js.map +1 -1
- package/lib/save-text-document.d.ts +40 -0
- package/lib/save-text-document.d.ts.map +1 -0
- package/lib/save-text-document.js +34 -0
- package/lib/save-text-document.js.map +1 -0
- package/lib/testing/catalogue-audit.d.ts +1 -1
- package/lib/testing/catalogue-audit.js +1 -1
- package/lib/testing/catalogue-audit.js.map +1 -1
- package/lib/testing/data-doubles.d.ts +41 -12
- package/lib/testing/data-doubles.d.ts.map +1 -1
- package/lib/testing/data-doubles.js +56 -8
- package/lib/testing/data-doubles.js.map +1 -1
- package/lib/testing/fake-clock.d.ts +17 -2
- package/lib/testing/fake-clock.d.ts.map +1 -1
- package/lib/testing/fake-clock.js +61 -46
- package/lib/testing/fake-clock.js.map +1 -1
- package/lib/testing/index.d.ts +1 -0
- package/lib/testing/index.d.ts.map +1 -1
- package/lib/testing/index.js +4 -3
- package/lib/testing/index.js.map +1 -1
- package/lib/testing/node/duplex-connection.d.ts.map +1 -1
- package/lib/testing/node/duplex-connection.js +3 -2
- package/lib/testing/node/duplex-connection.js.map +1 -1
- package/lib/testing/node/duplex-stream.js.map +1 -1
- package/lib/testing/node/index.d.ts +1 -0
- package/lib/testing/node/index.d.ts.map +1 -1
- package/lib/testing/node/index.js +2 -2
- package/lib/testing/node/index.js.map +1 -1
- package/lib/testing/node/message-port-pair.d.ts +25 -0
- package/lib/testing/node/message-port-pair.d.ts.map +1 -0
- package/lib/testing/node/message-port-pair.js +26 -0
- package/lib/testing/node/message-port-pair.js.map +1 -0
- package/lib/testing/transfer-node-builder.d.ts +81 -0
- package/lib/testing/transfer-node-builder.d.ts.map +1 -0
- package/lib/testing/transfer-node-builder.js +47 -0
- package/lib/testing/transfer-node-builder.js.map +1 -0
- package/lib/testing/wait-for.d.ts +9 -3
- package/lib/testing/wait-for.d.ts.map +1 -1
- package/lib/testing/wait-for.js +57 -11
- package/lib/testing/wait-for.js.map +1 -1
- package/lib/tracer.d.ts.map +1 -1
- package/lib/tracer.js +5 -9
- package/lib/tracer.js.map +1 -1
- package/lib/transfer-diagnostic.js.map +1 -1
- package/lib/transfer-document.d.ts +95 -32
- package/lib/transfer-document.d.ts.map +1 -1
- package/lib/transfer-document.js +17 -9
- package/lib/transfer-document.js.map +1 -1
- package/lib/transfer-element.d.ts +4 -4
- package/lib/types.d.ts +32 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/{model-service/args.js → types.js} +2 -2
- package/lib/types.js.map +1 -0
- package/lib/uri.d.ts.map +1 -1
- package/lib/uri.js.map +1 -1
- package/lib/util.d.ts +8 -2
- package/lib/util.d.ts.map +1 -1
- package/lib/util.js +32 -4
- package/lib/util.js.map +1 -1
- package/package.json +24 -40
- package/src/abstract-logger.ts +8 -0
- package/src/client/data-connection.ts +653 -0
- package/src/client/data-events.ts +37 -1
- package/src/client/data-port.ts +36 -25
- package/src/client/data-session.ts +1427 -137
- package/src/client/index.ts +15 -9
- package/src/client/message-relay.ts +4 -4
- package/src/client/post-message-transport.ts +219 -4
- package/src/client/rpc-connection.ts +281 -0
- package/src/client/worker-handshake.ts +113 -0
- package/src/client-ids.ts +49 -0
- package/src/clock.ts +68 -10
- package/src/console-logger.ts +39 -0
- package/src/data/data-protocol-methods.ts +14 -4
- package/src/data/data-server-protocol.ts +192 -48
- package/src/data/events.ts +143 -21
- package/src/data/requests.ts +77 -11
- package/src/debouncer.ts +7 -10
- package/src/errors.ts +326 -40
- package/src/glsp-request-model-args.ts +16 -0
- package/src/glsp-save-model-actions.ts +59 -0
- package/src/index.ts +8 -0
- package/src/latency-collector.ts +8 -3
- package/src/logger.ts +44 -8
- package/src/messages/index.ts +4 -2
- package/src/messages/primitives.ts +90 -4
- package/src/model-server.ts +64 -19
- package/src/model-service/base-version.ts +75 -0
- package/src/model-service/index.ts +4 -5
- package/src/model-service/reference-candidate.ts +5 -3
- package/src/node/index.ts +14 -0
- package/src/node/process-memory.ts +299 -0
- package/src/patch-merge.ts +230 -45
- package/src/profile-session.ts +9 -4
- package/src/project.ts +4 -1
- package/src/random-uuid.ts +21 -0
- package/src/reconcile-write.ts +128 -0
- package/src/rpc/README.md +6 -6
- package/src/rpc/bind-rpc-methods.ts +18 -8
- package/src/rpc/create-rpc-proxy.ts +6 -1
- package/src/rpc/index.ts +1 -0
- package/src/rpc/send-by-method-name.ts +140 -0
- package/src/save-text-document.ts +39 -0
- package/src/testing/catalogue-audit.ts +1 -1
- package/src/testing/data-doubles.ts +141 -19
- package/src/testing/fake-clock.ts +76 -48
- package/src/testing/index.ts +4 -3
- package/src/testing/node/duplex-connection.ts +3 -2
- package/src/testing/node/index.ts +2 -2
- package/src/testing/node/message-port-pair.ts +40 -0
- package/src/testing/transfer-node-builder.ts +107 -0
- package/src/testing/wait-for.ts +61 -13
- package/src/tracer.ts +5 -9
- package/src/transfer-document.ts +115 -34
- package/src/transfer-element.ts +4 -4
- package/src/types.ts +30 -0
- package/src/util.ts +33 -6
- package/lib/model-service/args.d.ts +0 -64
- package/lib/model-service/args.d.ts.map +0 -1
- package/lib/model-service/args.js.map +0 -1
- package/src/model-service/args.ts +0 -67
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
/** The files the heads came up on, when the worker's host named a root to read them under. */
|
|
68
|
+
readonly workspace?: WorkerWorkspace;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* What the worker's filesystem holds under {@link rootUri} once its heads are
|
|
73
|
+
* up, which is what a page opens its editors on: once a store can differ from
|
|
74
|
+
* the seed, an editor opened on the seed overwrites the restored document.
|
|
75
|
+
*/
|
|
76
|
+
export interface WorkerWorkspace {
|
|
77
|
+
readonly rootUri: string;
|
|
78
|
+
/** Every file's content, keyed by its path relative to {@link rootUri}. */
|
|
79
|
+
readonly files: Readonly<Record<string, string>>;
|
|
80
|
+
/** The paths of {@link files} that came out of the store rather than the seed. */
|
|
81
|
+
readonly restored: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isWorkerReadyMessage(value: unknown): value is WorkerReadyMessage {
|
|
85
|
+
return typeof value === 'object' && value !== null && (value as Partial<WorkerReadyMessage>).type === WORKER_READY_MESSAGE_TYPE;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Discriminator of a failure report the worker posts on the global.
|
|
90
|
+
*
|
|
91
|
+
* A worker has no console anyone is looking at, and the failures that matter
|
|
92
|
+
* most reach nobody by default: a workspace walk that throws has no reply to
|
|
93
|
+
* reject, so it looks exactly like a workspace with no documents in it.
|
|
94
|
+
*/
|
|
95
|
+
export const WORKER_ERROR_MESSAGE_TYPE = 'hydranium/worker-error';
|
|
96
|
+
|
|
97
|
+
/** The phase a failed bootstrap reports under; no head answers after it. */
|
|
98
|
+
export const WORKER_BOOTSTRAP_PHASE = 'bootstrap';
|
|
99
|
+
|
|
100
|
+
export interface WorkerErrorMessage {
|
|
101
|
+
readonly type: typeof WORKER_ERROR_MESSAGE_TYPE;
|
|
102
|
+
/** What the worker was doing, {@link WORKER_BOOTSTRAP_PHASE} for a failed bootstrap. */
|
|
103
|
+
readonly phase: string;
|
|
104
|
+
readonly message: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function isWorkerErrorMessage(value: unknown): value is WorkerErrorMessage {
|
|
108
|
+
if (typeof value !== 'object' || value === null) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const candidate = value as Partial<WorkerErrorMessage>;
|
|
112
|
+
return candidate.type === WORKER_ERROR_MESSAGE_TYPE && typeof candidate.phase === 'string' && typeof candidate.message === 'string';
|
|
113
|
+
}
|
|
@@ -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
|
|
18
|
-
* `Date.now` delta go backwards; a stopwatch cannot).
|
|
19
|
-
*
|
|
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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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:
|
|
56
|
-
*
|
|
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
|
|
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 = [
|
|
63
|
-
|
|
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>;
|