@hydranium/protocol 1.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/lib/abstract-logger.d.ts +66 -0
- package/lib/abstract-logger.d.ts.map +1 -0
- package/lib/abstract-logger.js +110 -0
- package/lib/abstract-logger.js.map +1 -0
- package/lib/browser-runtime.d.ts +124 -0
- package/lib/browser-runtime.d.ts.map +1 -0
- package/lib/browser-runtime.js +11 -0
- package/lib/browser-runtime.js.map +1 -0
- package/lib/client/data-events.d.ts +49 -0
- package/lib/client/data-events.d.ts.map +1 -0
- package/lib/client/data-events.js +60 -0
- package/lib/client/data-events.js.map +1 -0
- package/lib/client/data-port.d.ts +95 -0
- package/lib/client/data-port.d.ts.map +1 -0
- package/lib/client/data-port.js +11 -0
- package/lib/client/data-port.js.map +1 -0
- package/lib/client/data-session.d.ts +129 -0
- package/lib/client/data-session.d.ts.map +1 -0
- package/lib/client/data-session.js +179 -0
- package/lib/client/data-session.js.map +1 -0
- package/lib/client/index.d.ts +39 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/index.js +55 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client/message-relay.d.ts +111 -0
- package/lib/client/message-relay.d.ts.map +1 -0
- package/lib/client/message-relay.js +139 -0
- package/lib/client/message-relay.js.map +1 -0
- package/lib/client/post-message-transport.d.ts +77 -0
- package/lib/client/post-message-transport.d.ts.map +1 -0
- package/lib/client/post-message-transport.js +95 -0
- package/lib/client/post-message-transport.js.map +1 -0
- package/lib/clock.d.ts +111 -0
- package/lib/clock.d.ts.map +1 -0
- package/lib/clock.js +74 -0
- package/lib/clock.js.map +1 -0
- package/lib/data/data-protocol-methods.d.ts +39 -0
- package/lib/data/data-protocol-methods.d.ts.map +1 -0
- package/lib/data/data-protocol-methods.js +61 -0
- package/lib/data/data-protocol-methods.js.map +1 -0
- package/lib/data/data-server-protocol.d.ts +264 -0
- package/lib/data/data-server-protocol.d.ts.map +1 -0
- package/lib/data/data-server-protocol.js +11 -0
- package/lib/data/data-server-protocol.js.map +1 -0
- package/lib/data/diagnostics.d.ts +97 -0
- package/lib/data/diagnostics.d.ts.map +1 -0
- package/lib/data/diagnostics.js +25 -0
- package/lib/data/diagnostics.js.map +1 -0
- package/lib/data/events.d.ts +107 -0
- package/lib/data/events.d.ts.map +1 -0
- package/lib/data/events.js +11 -0
- package/lib/data/events.js.map +1 -0
- package/lib/data/index.d.ts +15 -0
- package/lib/data/index.d.ts.map +1 -0
- package/lib/data/index.js +38 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/methods.d.ts +57 -0
- package/lib/data/methods.d.ts.map +1 -0
- package/lib/data/methods.js +60 -0
- package/lib/data/methods.js.map +1 -0
- package/lib/data/requests.d.ts +65 -0
- package/lib/data/requests.d.ts.map +1 -0
- package/lib/data/requests.js +11 -0
- package/lib/data/requests.js.map +1 -0
- package/lib/debouncer.d.ts +75 -0
- package/lib/debouncer.d.ts.map +1 -0
- package/lib/debouncer.js +91 -0
- package/lib/debouncer.js.map +1 -0
- package/lib/errors.d.ts +84 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +113 -0
- package/lib/errors.js.map +1 -0
- package/lib/host-diagnostics.d.ts +58 -0
- package/lib/host-diagnostics.d.ts.map +1 -0
- package/lib/host-diagnostics.js +18 -0
- package/lib/host-diagnostics.js.map +1 -0
- package/lib/index.d.ts +34 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/latency-collector.d.ts +126 -0
- package/lib/latency-collector.d.ts.map +1 -0
- package/lib/latency-collector.js +137 -0
- package/lib/latency-collector.js.map +1 -0
- package/lib/logger.d.ts +119 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +126 -0
- package/lib/logger.js.map +1 -0
- package/lib/model-server.d.ts +352 -0
- package/lib/model-server.d.ts.map +1 -0
- package/lib/model-server.js +160 -0
- package/lib/model-server.js.map +1 -0
- package/lib/model-service/args.d.ts +64 -0
- package/lib/model-service/args.d.ts.map +1 -0
- package/lib/model-service/args.js +11 -0
- package/lib/model-service/args.js.map +1 -0
- package/lib/model-service/index.d.ts +11 -0
- package/lib/model-service/index.d.ts.map +1 -0
- package/lib/model-service/index.js +31 -0
- package/lib/model-service/index.js.map +1 -0
- package/lib/model-service/reference-candidate.d.ts +56 -0
- package/lib/model-service/reference-candidate.d.ts.map +1 -0
- package/lib/model-service/reference-candidate.js +11 -0
- package/lib/model-service/reference-candidate.js.map +1 -0
- package/lib/noop-logger.d.ts +25 -0
- package/lib/noop-logger.d.ts.map +1 -0
- package/lib/noop-logger.js +36 -0
- package/lib/noop-logger.js.map +1 -0
- package/lib/observable-value.d.ts +72 -0
- package/lib/observable-value.d.ts.map +1 -0
- package/lib/observable-value.js +52 -0
- package/lib/observable-value.js.map +1 -0
- package/lib/patch-merge.d.ts +123 -0
- package/lib/patch-merge.d.ts.map +1 -0
- package/lib/patch-merge.js +104 -0
- package/lib/patch-merge.js.map +1 -0
- package/lib/profile-session.d.ts +87 -0
- package/lib/profile-session.d.ts.map +1 -0
- package/lib/profile-session.js +98 -0
- package/lib/profile-session.js.map +1 -0
- package/lib/profiling.d.ts +83 -0
- package/lib/profiling.d.ts.map +1 -0
- package/lib/profiling.js +20 -0
- package/lib/profiling.js.map +1 -0
- package/lib/project.d.ts +121 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +34 -0
- package/lib/project.js.map +1 -0
- package/lib/rpc/bind-rpc-methods.d.ts +100 -0
- package/lib/rpc/bind-rpc-methods.d.ts.map +1 -0
- package/lib/rpc/bind-rpc-methods.js +110 -0
- package/lib/rpc/bind-rpc-methods.js.map +1 -0
- package/lib/rpc/create-rpc-proxy.d.ts +188 -0
- package/lib/rpc/create-rpc-proxy.d.ts.map +1 -0
- package/lib/rpc/create-rpc-proxy.js +158 -0
- package/lib/rpc/create-rpc-proxy.js.map +1 -0
- package/lib/rpc/index.d.ts +12 -0
- package/lib/rpc/index.d.ts.map +1 -0
- package/lib/rpc/index.js +31 -0
- package/lib/rpc/index.js.map +1 -0
- package/lib/rpc/wire-prefix.d.ts +37 -0
- package/lib/rpc/wire-prefix.d.ts.map +1 -0
- package/lib/rpc/wire-prefix.js +49 -0
- package/lib/rpc/wire-prefix.js.map +1 -0
- package/lib/testing/data-doubles.d.ts +121 -0
- package/lib/testing/data-doubles.d.ts.map +1 -0
- package/lib/testing/data-doubles.js +102 -0
- package/lib/testing/data-doubles.js.map +1 -0
- package/lib/testing/fake-clock.d.ts +35 -0
- package/lib/testing/fake-clock.d.ts.map +1 -0
- package/lib/testing/fake-clock.js +86 -0
- package/lib/testing/fake-clock.js.map +1 -0
- package/lib/testing/harness.d.ts +24 -0
- package/lib/testing/harness.d.ts.map +1 -0
- package/lib/testing/harness.js +11 -0
- package/lib/testing/harness.js.map +1 -0
- package/lib/testing/index.d.ts +13 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +41 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/node/duplex-connection.d.ts +25 -0
- package/lib/testing/node/duplex-connection.d.ts.map +1 -0
- package/lib/testing/node/duplex-connection.js +34 -0
- package/lib/testing/node/duplex-connection.js.map +1 -0
- package/lib/testing/node/duplex-stream.d.ts +32 -0
- package/lib/testing/node/duplex-stream.d.ts.map +1 -0
- package/lib/testing/node/duplex-stream.js +35 -0
- package/lib/testing/node/duplex-stream.js.map +1 -0
- package/lib/testing/node/index.d.ts +11 -0
- package/lib/testing/node/index.d.ts.map +1 -0
- package/lib/testing/node/index.js +37 -0
- package/lib/testing/node/index.js.map +1 -0
- package/lib/testing/wait-for.d.ts +69 -0
- package/lib/testing/wait-for.d.ts.map +1 -0
- package/lib/testing/wait-for.js +67 -0
- package/lib/testing/wait-for.js.map +1 -0
- package/lib/tracer.d.ts +115 -0
- package/lib/tracer.d.ts.map +1 -0
- package/lib/tracer.js +205 -0
- package/lib/tracer.js.map +1 -0
- package/lib/transfer-diagnostic.d.ts +73 -0
- package/lib/transfer-diagnostic.d.ts.map +1 -0
- package/lib/transfer-diagnostic.js +45 -0
- package/lib/transfer-diagnostic.js.map +1 -0
- package/lib/transfer-document.d.ts +73 -0
- package/lib/transfer-document.d.ts.map +1 -0
- package/lib/transfer-document.js +46 -0
- package/lib/transfer-document.js.map +1 -0
- package/lib/transfer-element.d.ts +39 -0
- package/lib/transfer-element.d.ts.map +1 -0
- package/lib/transfer-element.js +11 -0
- package/lib/transfer-element.js.map +1 -0
- package/lib/uri.d.ts +62 -0
- package/lib/uri.d.ts.map +1 -0
- package/lib/uri.js +28 -0
- package/lib/uri.js.map +1 -0
- package/lib/util.d.ts +147 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +211 -0
- package/lib/util.js.map +1 -0
- package/package.json +100 -0
- package/src/abstract-logger.ts +120 -0
- package/src/browser-runtime.ts +135 -0
- package/src/client/data-events.ts +71 -0
- package/src/client/data-port.ts +99 -0
- package/src/client/data-session.ts +209 -0
- package/src/client/index.ts +40 -0
- package/src/client/message-relay.ts +216 -0
- package/src/client/post-message-transport.ts +167 -0
- package/src/clock.ts +173 -0
- package/src/data/data-protocol-methods.ts +79 -0
- package/src/data/data-server-protocol.ts +296 -0
- package/src/data/diagnostics.ts +116 -0
- package/src/data/events.ts +127 -0
- package/src/data/index.ts +23 -0
- package/src/data/methods.ts +59 -0
- package/src/data/requests.ts +70 -0
- package/src/debouncer.ts +118 -0
- package/src/errors.ts +127 -0
- package/src/host-diagnostics.ts +63 -0
- package/src/index.ts +41 -0
- package/src/latency-collector.ts +231 -0
- package/src/logger.ts +172 -0
- package/src/model-server.ts +479 -0
- package/src/model-service/args.ts +67 -0
- package/src/model-service/index.ts +16 -0
- package/src/model-service/reference-candidate.ts +58 -0
- package/src/noop-logger.ts +36 -0
- package/src/observable-value.ts +92 -0
- package/src/patch-merge.ts +165 -0
- package/src/profile-session.ts +164 -0
- package/src/profiling.ts +113 -0
- package/src/project.ts +125 -0
- package/src/rpc/README.md +163 -0
- package/src/rpc/bind-rpc-methods.ts +180 -0
- package/src/rpc/create-rpc-proxy.ts +292 -0
- package/src/rpc/index.ts +16 -0
- package/src/rpc/wire-prefix.ts +48 -0
- package/src/testing/data-doubles.ts +178 -0
- package/src/testing/fake-clock.ts +106 -0
- package/src/testing/harness.ts +24 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/node/duplex-connection.ts +50 -0
- package/src/testing/node/duplex-stream.ts +51 -0
- package/src/testing/node/index.ts +22 -0
- package/src/testing/wait-for.ts +94 -0
- package/src/tracer.ts +277 -0
- package/src/transfer-diagnostic.ts +92 -0
- package/src/transfer-document.ts +99 -0
- package/src/transfer-element.ts +43 -0
- package/src/uri.ts +61 -0
- package/src/util.ts +250 -0
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
import { type Event, type MessageConnection } from 'vscode-jsonrpc';
|
|
10
|
+
import { type BindRpcMethodsOptions } from './bind-rpc-methods';
|
|
11
|
+
/**
|
|
12
|
+
* Lifecycle events surfaced on every {@link createRpcProxy} return — `onDidOpenConnection`
|
|
13
|
+
* fires once after the underlying `MessageConnection` resolves; `onDidCloseConnection`
|
|
14
|
+
* fires when the connection's `onClose` triggers. Useful for proxy-only consumers
|
|
15
|
+
* (widgets, downstream services) that don't hold the `MessageConnection` directly
|
|
16
|
+
* and would otherwise need it threaded through just to react to disconnects.
|
|
17
|
+
*
|
|
18
|
+
* Reconnection is NOT modelled — the framework assumes a single connection
|
|
19
|
+
* lifetime per proxy. If the transport drops, the adopter constructs a new
|
|
20
|
+
* connection + proxy.
|
|
21
|
+
*
|
|
22
|
+
* **Reserved property names on `RpcProxy<T>`.** The proxy's get-trap
|
|
23
|
+
* intercepts four property names — any wire method declared on `T` with
|
|
24
|
+
* one of these names would shadow the reserved behaviour instead of
|
|
25
|
+
* dispatching a wire call. Adopters defining wire-method names should
|
|
26
|
+
* avoid:
|
|
27
|
+
*
|
|
28
|
+
* - `onDidOpenConnection` / `onDidCloseConnection` — return the
|
|
29
|
+
* lifecycle events declared on this interface.
|
|
30
|
+
* - `then` — returns `undefined` so the proxy is not auto-awaited
|
|
31
|
+
* when caught by Promise-detection in the host environment.
|
|
32
|
+
* - `toJSON` — returns `undefined` so JSON serializers do not try to
|
|
33
|
+
* flatten the proxy.
|
|
34
|
+
*
|
|
35
|
+
* Symbol property accesses also return `undefined` (the proxy is not
|
|
36
|
+
* iterable, not a thenable, not serialisable).
|
|
37
|
+
*/
|
|
38
|
+
export interface RpcProxyLifecycle {
|
|
39
|
+
/**
|
|
40
|
+
* Fires exactly once, when the underlying connection promise resolves. It
|
|
41
|
+
* does NOT replay: a proxy built over an already-resolved connection fires
|
|
42
|
+
* on the next microtask, so a subscriber attached after that never hears
|
|
43
|
+
* anything. Subscribe in the same synchronous block that builds the proxy,
|
|
44
|
+
* or track readiness yourself.
|
|
45
|
+
*
|
|
46
|
+
* Never fires if the connection promise rejects — a failed transport
|
|
47
|
+
* construction is indistinguishable here from one still pending, and is
|
|
48
|
+
* meant to be observed where the connection is built.
|
|
49
|
+
*/
|
|
50
|
+
readonly onDidOpenConnection: Event<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Fires when the transport closes. Reconnection is not modelled, so it fires
|
|
53
|
+
* at most once and is never followed by another open — a consumer that has
|
|
54
|
+
* to survive a drop constructs a new proxy rather than waiting here.
|
|
55
|
+
*/
|
|
56
|
+
readonly onDidCloseConnection: Event<void>;
|
|
57
|
+
}
|
|
58
|
+
/** Proxy of `T` plus the framework's connection-lifecycle events. */
|
|
59
|
+
export type RpcProxy<T extends object> = T & RpcProxyLifecycle;
|
|
60
|
+
/**
|
|
61
|
+
* Options controlling how method-name properties on the returned proxy
|
|
62
|
+
* are translated into wire calls.
|
|
63
|
+
*
|
|
64
|
+
* Generic over `TLocal` — the type of an OPTIONAL inbound handler target
|
|
65
|
+
* (see {@link localTarget} / {@link localMethods}). `TLocal` defaults to
|
|
66
|
+
* `never`, so callers that only want an outbound proxy omit the local
|
|
67
|
+
* fields entirely and the proxy behaves exactly as a pure remote proxy.
|
|
68
|
+
*/
|
|
69
|
+
export interface CreateRpcProxyOptions<TLocal extends object = never> {
|
|
70
|
+
/**
|
|
71
|
+
* Prefix prepended to the property name to form the JSON-RPC method
|
|
72
|
+
* string. For example, prefix `'data-server/'` turns a property access
|
|
73
|
+
* `proxy.getModelDocument` into the wire method
|
|
74
|
+
* `'data-server/getModelDocument'`. Defaults to the empty string —
|
|
75
|
+
* the property name is the wire name.
|
|
76
|
+
*
|
|
77
|
+
* When {@link localTarget} is supplied, the SAME prefix applies to the
|
|
78
|
+
* inbound handler registrations — both ends of a combined connection
|
|
79
|
+
* share one namespace.
|
|
80
|
+
*/
|
|
81
|
+
readonly methodNamespace?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Predicate to discriminate notification methods from request methods
|
|
84
|
+
* by their TS property name. Notification methods lower to
|
|
85
|
+
* `connection.sendNotification` and return `void`; request methods
|
|
86
|
+
* lower to `connection.sendRequest` and return `Promise<TResult>`.
|
|
87
|
+
*
|
|
88
|
+
* Default: property names starting with `'on'` followed by an uppercase
|
|
89
|
+
* letter are notifications. Adopters defining typed contracts in the
|
|
90
|
+
* `DataClientProtocol` style get the right routing without per-method
|
|
91
|
+
* configuration.
|
|
92
|
+
*
|
|
93
|
+
* Applies symmetrically to the {@link localTarget} binding, so the
|
|
94
|
+
* inbound and outbound sides agree on which methods are notifications.
|
|
95
|
+
*/
|
|
96
|
+
readonly isNotification?: (methodName: string) => boolean;
|
|
97
|
+
/**
|
|
98
|
+
* OPTIONAL inbound-handler target. When supplied together with
|
|
99
|
+
* {@link localMethods}, `createRpcProxy` ALSO binds those methods of
|
|
100
|
+
* `localTarget` as inbound request/notification handlers on the same
|
|
101
|
+
* connection (delegating to `bindRpcMethods`) — collapsing the common
|
|
102
|
+
* "proxy the remote + handle the local" two-step into one call. The
|
|
103
|
+
* binding is torn down on `connection.onClose`; the return value stays
|
|
104
|
+
* just the remote proxy (no `Disposable` surfaced — there is no use
|
|
105
|
+
* case for unbinding a connection-scoped target before its connection
|
|
106
|
+
* closes). Callers that need the explicit `Disposable` call
|
|
107
|
+
* `bindRpcMethods` directly instead.
|
|
108
|
+
*
|
|
109
|
+
* Omit (the `TLocal = never` default) for a pure outbound proxy.
|
|
110
|
+
*/
|
|
111
|
+
readonly localTarget?: TLocal;
|
|
112
|
+
/**
|
|
113
|
+
* Method names of {@link localTarget} to bind as inbound handlers.
|
|
114
|
+
* REQUIRED for the binding to happen (no auto-enumeration — TypeScript
|
|
115
|
+
* access modifiers are erased at runtime, so a class instance's
|
|
116
|
+
* prototype carries internal helpers that must not become wire
|
|
117
|
+
* endpoints; the explicit list is the allowlist). Declare it
|
|
118
|
+
* `as const satisfies keyof TLocal` at the call site for a compile-time
|
|
119
|
+
* drift check; `bindRpcMethods` additionally verifies each name exists
|
|
120
|
+
* on the target at attach time.
|
|
121
|
+
*/
|
|
122
|
+
readonly localMethods?: readonly (keyof TLocal & string)[];
|
|
123
|
+
/**
|
|
124
|
+
* Forwarded to the inbound {@link localTarget} binding: when supplied, every
|
|
125
|
+
* inbound handler is timed into this collector under its wire name. Lets a
|
|
126
|
+
* head that owns its server through `createRpcProxy` (rather than a bare
|
|
127
|
+
* `bindRpcMethods` call) still capture per-method latency. Absent by default.
|
|
128
|
+
*/
|
|
129
|
+
readonly latency?: BindRpcMethodsOptions['latency'];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Default notification discriminator — `on`-followed-by-an-uppercase-letter
|
|
133
|
+
* methods are notifications (`onProgress`, `onDocumentUpdated`).
|
|
134
|
+
*
|
|
135
|
+
* The uppercase requirement is what keeps the heuristic honest: a
|
|
136
|
+
* request-shaped method that merely *starts* with the letters "on"
|
|
137
|
+
* (`onboardUser`, `onlineCheck`) stays a request, instead of being misrouted
|
|
138
|
+
* as a fire-and-forget notification with its `Promise` result silently
|
|
139
|
+
* dropped. It follows the observer-callback convention the framework's
|
|
140
|
+
* contracts already use, where the capital always marks the event name.
|
|
141
|
+
*
|
|
142
|
+
* Still lexical, so it cannot catch everything — `onDemandRebuild` reads as a
|
|
143
|
+
* request but matches. Supply a custom
|
|
144
|
+
* {@link CreateRpcProxyOptions.isNotification} for contracts that don't fit
|
|
145
|
+
* the convention, and pass the same predicate to both ends.
|
|
146
|
+
*/
|
|
147
|
+
export declare function defaultIsNotification(methodName: string): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Build a typed RPC proxy `T` over a vscode-jsonrpc {@link MessageConnection}.
|
|
150
|
+
* Every method access on the returned object lowers transparently to
|
|
151
|
+
* `connection.sendRequest` (request methods) or `connection.sendNotification`
|
|
152
|
+
* (notification methods, by default `on*`-prefixed). The single-arg
|
|
153
|
+
* payload shape is preserved: `proxy.foo(args)` sends `(method, args)`
|
|
154
|
+
* over the wire and resolves with the response.
|
|
155
|
+
*
|
|
156
|
+
* `on` + an uppercase letter is the only notification marker — see
|
|
157
|
+
* {@link defaultIsNotification}, and pass a custom `isNotification` to both
|
|
158
|
+
* ends for a contract that doesn't fit.
|
|
159
|
+
*
|
|
160
|
+
* **Wire tracing.** The proxy adds no tracing layer of its own, deliberately:
|
|
161
|
+
* vscode-jsonrpc's own `connection.trace` already covers wire method names,
|
|
162
|
+
* params, results and errors, and a second layer here would double every
|
|
163
|
+
* traced line.
|
|
164
|
+
*
|
|
165
|
+
* Accepts either a ready connection or a `Promise<MessageConnection>` —
|
|
166
|
+
* proxy methods called before the promise resolves queue until it does,
|
|
167
|
+
* then dispatch, so adopters can wire the proxy before its underlying
|
|
168
|
+
* transport is available (e.g. before the Langium services finish
|
|
169
|
+
* constructing).
|
|
170
|
+
*
|
|
171
|
+
* A few property names are intercepted rather than dispatched — see
|
|
172
|
+
* {@link RpcProxyLifecycle} for the reserved list and why each is guarded.
|
|
173
|
+
*
|
|
174
|
+
* Each method of `T` dispatches under `<methodNamespace><methodName>`: a
|
|
175
|
+
* request method resolves through `connection.sendRequest`, while one the
|
|
176
|
+
* notification heuristic matches goes out through `sendNotification` and
|
|
177
|
+
* returns nothing, so a caller that awaits it waits on `undefined` rather
|
|
178
|
+
* than on delivery.
|
|
179
|
+
*
|
|
180
|
+
* **Combined proxy + inbound binding.** Supply `localTarget` + `localMethods`
|
|
181
|
+
* to ALSO register inbound handlers on the same connection in one call —
|
|
182
|
+
* the typical both-ends-of-a-bidirectional-connection setup. The remote
|
|
183
|
+
* proxy is still the return value; the inbound binding tears down on
|
|
184
|
+
* `connection.onClose`, so binding twice over one connection leaks the
|
|
185
|
+
* first set of handlers until it closes.
|
|
186
|
+
*/
|
|
187
|
+
export declare function createRpcProxy<T extends object, TLocal extends object = never>(connection: MessageConnection | Promise<MessageConnection>, options?: CreateRpcProxyOptions<TLocal>): RpcProxy<T>;
|
|
188
|
+
//# sourceMappingURL=create-rpc-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-rpc-proxy.d.ts","sourceRoot":"","sources":["../../src/rpc/create-rpc-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAW,KAAK,KAAK,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,KAAK,qBAAqB,EAAkB,MAAM,oBAAoB,CAAC;AAGhF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,iBAAiB;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;CAC7C;AAED,qEAAqE;AACrE,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG,iBAAiB,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB,CAAC,MAAM,SAAS,MAAM,GAAG,KAAK;IACjE;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAElC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IAE1D;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;;;OASG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAE3D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;CACtD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,SAAS,MAAM,GAAG,KAAK,EAC3E,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,EAC1D,OAAO,GAAE,qBAAqB,CAAC,MAAM,CAAM,GAC3C,QAAQ,CAAC,CAAC,CAAC,CA0Eb"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.defaultIsNotification = defaultIsNotification;
|
|
12
|
+
exports.createRpcProxy = createRpcProxy;
|
|
13
|
+
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
|
|
14
|
+
const bind_rpc_methods_1 = require("./bind-rpc-methods");
|
|
15
|
+
const wire_prefix_1 = require("./wire-prefix");
|
|
16
|
+
/**
|
|
17
|
+
* Default notification discriminator — `on`-followed-by-an-uppercase-letter
|
|
18
|
+
* methods are notifications (`onProgress`, `onDocumentUpdated`).
|
|
19
|
+
*
|
|
20
|
+
* The uppercase requirement is what keeps the heuristic honest: a
|
|
21
|
+
* request-shaped method that merely *starts* with the letters "on"
|
|
22
|
+
* (`onboardUser`, `onlineCheck`) stays a request, instead of being misrouted
|
|
23
|
+
* as a fire-and-forget notification with its `Promise` result silently
|
|
24
|
+
* dropped. It follows the observer-callback convention the framework's
|
|
25
|
+
* contracts already use, where the capital always marks the event name.
|
|
26
|
+
*
|
|
27
|
+
* Still lexical, so it cannot catch everything — `onDemandRebuild` reads as a
|
|
28
|
+
* request but matches. Supply a custom
|
|
29
|
+
* {@link CreateRpcProxyOptions.isNotification} for contracts that don't fit
|
|
30
|
+
* the convention, and pass the same predicate to both ends.
|
|
31
|
+
*/
|
|
32
|
+
function defaultIsNotification(methodName) {
|
|
33
|
+
return /^on[A-Z]/.test(methodName);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Runtime enforcement of the single-arg convention. TypeScript catches
|
|
37
|
+
* misuse at compile time for typed contract callers, but loosely-typed
|
|
38
|
+
* callsites (`any` / `unknown` proxy) would silently drop extra args.
|
|
39
|
+
* Throw loudly so the violation is visible.
|
|
40
|
+
*/
|
|
41
|
+
function assertSingleArg(wireName, args) {
|
|
42
|
+
if (args.length > 1) {
|
|
43
|
+
throw new Error(`RPC method '${wireName}' called with ${args.length} arguments — typed contracts use a single params object. ` +
|
|
44
|
+
'Pass a single object instead: proxy.foo({ ...args }).');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build a typed RPC proxy `T` over a vscode-jsonrpc {@link MessageConnection}.
|
|
49
|
+
* Every method access on the returned object lowers transparently to
|
|
50
|
+
* `connection.sendRequest` (request methods) or `connection.sendNotification`
|
|
51
|
+
* (notification methods, by default `on*`-prefixed). The single-arg
|
|
52
|
+
* payload shape is preserved: `proxy.foo(args)` sends `(method, args)`
|
|
53
|
+
* over the wire and resolves with the response.
|
|
54
|
+
*
|
|
55
|
+
* `on` + an uppercase letter is the only notification marker — see
|
|
56
|
+
* {@link defaultIsNotification}, and pass a custom `isNotification` to both
|
|
57
|
+
* ends for a contract that doesn't fit.
|
|
58
|
+
*
|
|
59
|
+
* **Wire tracing.** The proxy adds no tracing layer of its own, deliberately:
|
|
60
|
+
* vscode-jsonrpc's own `connection.trace` already covers wire method names,
|
|
61
|
+
* params, results and errors, and a second layer here would double every
|
|
62
|
+
* traced line.
|
|
63
|
+
*
|
|
64
|
+
* Accepts either a ready connection or a `Promise<MessageConnection>` —
|
|
65
|
+
* proxy methods called before the promise resolves queue until it does,
|
|
66
|
+
* then dispatch, so adopters can wire the proxy before its underlying
|
|
67
|
+
* transport is available (e.g. before the Langium services finish
|
|
68
|
+
* constructing).
|
|
69
|
+
*
|
|
70
|
+
* A few property names are intercepted rather than dispatched — see
|
|
71
|
+
* {@link RpcProxyLifecycle} for the reserved list and why each is guarded.
|
|
72
|
+
*
|
|
73
|
+
* Each method of `T` dispatches under `<methodNamespace><methodName>`: a
|
|
74
|
+
* request method resolves through `connection.sendRequest`, while one the
|
|
75
|
+
* notification heuristic matches goes out through `sendNotification` and
|
|
76
|
+
* returns nothing, so a caller that awaits it waits on `undefined` rather
|
|
77
|
+
* than on delivery.
|
|
78
|
+
*
|
|
79
|
+
* **Combined proxy + inbound binding.** Supply `localTarget` + `localMethods`
|
|
80
|
+
* to ALSO register inbound handlers on the same connection in one call —
|
|
81
|
+
* the typical both-ends-of-a-bidirectional-connection setup. The remote
|
|
82
|
+
* proxy is still the return value; the inbound binding tears down on
|
|
83
|
+
* `connection.onClose`, so binding twice over one connection leaks the
|
|
84
|
+
* first set of handlers until it closes.
|
|
85
|
+
*/
|
|
86
|
+
function createRpcProxy(connection, options = {}) {
|
|
87
|
+
const methodNamespace = options.methodNamespace ?? '';
|
|
88
|
+
(0, wire_prefix_1.assertValidMethodNamespace)(methodNamespace, 'createRpcProxy');
|
|
89
|
+
const isNotification = options.isNotification ?? defaultIsNotification;
|
|
90
|
+
const resolvedConnection = Promise.resolve(connection);
|
|
91
|
+
// Tied to `connection.onClose` so the inbound handlers release with the
|
|
92
|
+
// connection; see `localTarget` for why no `Disposable` is surfaced.
|
|
93
|
+
const { localTarget, localMethods } = options;
|
|
94
|
+
if (localTarget && localMethods && localMethods.length > 0) {
|
|
95
|
+
const binding = (0, bind_rpc_methods_1.bindRpcMethods)(connection, localTarget, localMethods, { methodNamespace, isNotification, latency: options.latency });
|
|
96
|
+
resolvedConnection.then(conn => conn.onClose(() => binding.dispose())).catch(() => undefined);
|
|
97
|
+
}
|
|
98
|
+
const onDidOpenConnectionEmitter = new vscode_jsonrpc_1.Emitter();
|
|
99
|
+
const onDidCloseConnectionEmitter = new vscode_jsonrpc_1.Emitter();
|
|
100
|
+
resolvedConnection
|
|
101
|
+
.then(conn => {
|
|
102
|
+
onDidOpenConnectionEmitter.fire(undefined);
|
|
103
|
+
conn.onClose(() => onDidCloseConnectionEmitter.fire(undefined));
|
|
104
|
+
})
|
|
105
|
+
.catch(() => {
|
|
106
|
+
// Connection promise rejected — treat as never-opened; lifecycle
|
|
107
|
+
// events simply never fire. Adopters observe transport-construction
|
|
108
|
+
// failures through their own connection-construction error handling.
|
|
109
|
+
});
|
|
110
|
+
const target = Object.create(null);
|
|
111
|
+
return new Proxy(target, {
|
|
112
|
+
get(_t, prop) {
|
|
113
|
+
if (typeof prop !== 'string') {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
// Keep the proxy out of thenable / serializer code paths so it
|
|
117
|
+
// doesn't trigger spurious requests.
|
|
118
|
+
if (prop === 'then' || prop === 'toJSON') {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
if (prop === 'onDidOpenConnection') {
|
|
122
|
+
return onDidOpenConnectionEmitter.event;
|
|
123
|
+
}
|
|
124
|
+
if (prop === 'onDidCloseConnection') {
|
|
125
|
+
return onDidCloseConnectionEmitter.event;
|
|
126
|
+
}
|
|
127
|
+
const wireName = methodNamespace + prop;
|
|
128
|
+
if (isNotification(prop)) {
|
|
129
|
+
return (...args) => {
|
|
130
|
+
assertSingleArg(wireName, args);
|
|
131
|
+
// Fire-and-forget; rejection (e.g. connection closed) is swallowed
|
|
132
|
+
// to match the notification contract — adopters observe transport
|
|
133
|
+
// failures via the connection's own close / error events, never
|
|
134
|
+
// via a notification's return value.
|
|
135
|
+
void resolvedConnection.then(connection => connection.sendNotification(wireName, args[0])).catch(() => undefined);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return (...args) => {
|
|
139
|
+
assertSingleArg(wireName, args);
|
|
140
|
+
// Capture the calling stack frame BEFORE the await so debugging gets
|
|
141
|
+
// both client- and server-side stacks on rejection. vscode-jsonrpc's
|
|
142
|
+
// default error envelope surfaces only the server-side stack; without
|
|
143
|
+
// this, a rejected RPC promise looks like it came from "somewhere
|
|
144
|
+
// inside vscode-jsonrpc" rather than from the calling code.
|
|
145
|
+
const capturedError = new Error(`RPC request '${wireName}' failed`);
|
|
146
|
+
return resolvedConnection
|
|
147
|
+
.then(connection => connection.sendRequest(wireName, args[0]))
|
|
148
|
+
.catch((err) => {
|
|
149
|
+
if (err instanceof Error && capturedError.stack) {
|
|
150
|
+
err.stack = `${err.stack ?? err.message}\nCaused by request from:\n${capturedError.stack}`;
|
|
151
|
+
}
|
|
152
|
+
throw err;
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=create-rpc-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-rpc-proxy.js","sourceRoot":"","sources":["../../src/rpc/create-rpc-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
export * from './bind-rpc-methods';
|
|
10
|
+
export * from './create-rpc-proxy';
|
|
11
|
+
export * from './wire-prefix';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rpc/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAMlF,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
|
package/lib/rpc/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
22
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
// Generic JSON-RPC primitives shared across the framework's typed
|
|
26
|
+
// protocol heads — see `data/data-protocol-methods.ts` for the
|
|
27
|
+
// specialised composition.
|
|
28
|
+
__exportStar(require("./bind-rpc-methods"), exports);
|
|
29
|
+
__exportStar(require("./create-rpc-proxy"), exports);
|
|
30
|
+
__exportStar(require("./wire-prefix"), exports);
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rpc/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,kEAAkE;AAClE,+DAA+D;AAC/D,2BAA2B;AAE3B,qDAAmC;AACnC,qDAAmC;AACnC,gDAA8B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
* Validates that a wire-name prefix — the `methodNamespace` option shared
|
|
11
|
+
* by `bindRpcMethods` and
|
|
12
|
+
* `createRpcProxy` — carries a
|
|
13
|
+
* trailing `/`.
|
|
14
|
+
*
|
|
15
|
+
* **Why this exists.** Adopters reading the LSP-namespace analogy
|
|
16
|
+
* (`textDocument/*`, `workspace/*`) naturally pass `'mylang'` expecting
|
|
17
|
+
* `'mylang/getModelDocument'` on the wire. Concatenating verbatim instead
|
|
18
|
+
* yields `'mylanggetModelDocument'`, and because the proxy and binding sites
|
|
19
|
+
* pick the wrong prefix in lockstep the mismatch is silent on a single-process
|
|
20
|
+
* adopter, audible only as a per-method "Unhandled method" on the wire log.
|
|
21
|
+
*
|
|
22
|
+
* Throwing is preferred over coercion: coercion would disguise the case where
|
|
23
|
+
* the adopter intended a different separator (`'.'`, `':'`), and throwing at
|
|
24
|
+
* construction time surfaces the bug at the adopter's own callsite rather than
|
|
25
|
+
* at the first wire response.
|
|
26
|
+
*
|
|
27
|
+
* Accepts the empty string (explicit no-prefix mode) or any non-empty string
|
|
28
|
+
* ending with `/`; anything else throws {@link TypeError} synchronously.
|
|
29
|
+
*
|
|
30
|
+
* @param methodNamespace The configured prefix (e.g. `'mylang/'`,
|
|
31
|
+
* `'data-server/'`, `''`).
|
|
32
|
+
* @param caller Name of the validating function, embedded in the thrown
|
|
33
|
+
* message so the error reads as if it came from the adopter-facing
|
|
34
|
+
* function rather than this helper.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertValidMethodNamespace(methodNamespace: string, caller: string): void;
|
|
37
|
+
//# sourceMappingURL=wire-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-prefix.d.ts","sourceRoot":"","sources":["../../src/rpc/wire-prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAWxF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.assertValidMethodNamespace = assertValidMethodNamespace;
|
|
12
|
+
/**
|
|
13
|
+
* Validates that a wire-name prefix — the `methodNamespace` option shared
|
|
14
|
+
* by `bindRpcMethods` and
|
|
15
|
+
* `createRpcProxy` — carries a
|
|
16
|
+
* trailing `/`.
|
|
17
|
+
*
|
|
18
|
+
* **Why this exists.** Adopters reading the LSP-namespace analogy
|
|
19
|
+
* (`textDocument/*`, `workspace/*`) naturally pass `'mylang'` expecting
|
|
20
|
+
* `'mylang/getModelDocument'` on the wire. Concatenating verbatim instead
|
|
21
|
+
* yields `'mylanggetModelDocument'`, and because the proxy and binding sites
|
|
22
|
+
* pick the wrong prefix in lockstep the mismatch is silent on a single-process
|
|
23
|
+
* adopter, audible only as a per-method "Unhandled method" on the wire log.
|
|
24
|
+
*
|
|
25
|
+
* Throwing is preferred over coercion: coercion would disguise the case where
|
|
26
|
+
* the adopter intended a different separator (`'.'`, `':'`), and throwing at
|
|
27
|
+
* construction time surfaces the bug at the adopter's own callsite rather than
|
|
28
|
+
* at the first wire response.
|
|
29
|
+
*
|
|
30
|
+
* Accepts the empty string (explicit no-prefix mode) or any non-empty string
|
|
31
|
+
* ending with `/`; anything else throws {@link TypeError} synchronously.
|
|
32
|
+
*
|
|
33
|
+
* @param methodNamespace The configured prefix (e.g. `'mylang/'`,
|
|
34
|
+
* `'data-server/'`, `''`).
|
|
35
|
+
* @param caller Name of the validating function, embedded in the thrown
|
|
36
|
+
* message so the error reads as if it came from the adopter-facing
|
|
37
|
+
* function rather than this helper.
|
|
38
|
+
*/
|
|
39
|
+
function assertValidMethodNamespace(methodNamespace, caller) {
|
|
40
|
+
if (methodNamespace === '' || methodNamespace.endsWith('/')) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
throw new TypeError(`${caller}: methodNamespace '${methodNamespace}' must end with '/' (or be empty). ` +
|
|
44
|
+
"Adopters reading the LSP-namespace analogy ('textDocument/*', 'workspace/*') often " +
|
|
45
|
+
`forget the trailing slash, producing wire names like '${methodNamespace}someMethod' ` +
|
|
46
|
+
`that do not match handler bindings under '${methodNamespace}/someMethod'. ` +
|
|
47
|
+
`Either pass '${methodNamespace}/' or use the empty string for no prefix.`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=wire-prefix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-prefix.js","sourceRoot":"","sources":["../../src/rpc/wire-prefix.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;AA6BlF,gEAWC;AAtCD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAgB,0BAA0B,CAAC,eAAuB,EAAE,MAAc;IAC/E,IAAI,eAAe,KAAK,EAAE,IAAI,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,OAAO;IACV,CAAC;IACD,MAAM,IAAI,SAAS,CAChB,GAAG,MAAM,sBAAsB,eAAe,qCAAqC;QAChF,qFAAqF;QACrF,yDAAyD,eAAe,cAAc;QACtF,6CAA6C,eAAe,gBAAgB;QAC5E,gBAAgB,eAAe,2CAA2C,CAC/E,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
* The two CLIENT-side doubles of the data head: a {@link DataPort} whose
|
|
11
|
+
* transport the test supplies, and a {@link DataClientProtocol} that records
|
|
12
|
+
* what arrived.
|
|
13
|
+
*
|
|
14
|
+
* # Why standalone, when a harness already builds one
|
|
15
|
+
*
|
|
16
|
+
* `makeDataServerHarness` builds a capturing client too, but fused into a
|
|
17
|
+
* server-bearing harness: taking it means taking a duplex pair, a `DataServer`
|
|
18
|
+
* and a proxy. That is the right shape for testing the server. It is the wrong
|
|
19
|
+
* shape — and not available at all — for testing the CLIENT half: a
|
|
20
|
+
* `DataSession`, a widget model, a host's port adapter. Those need to stand a
|
|
21
|
+
* double on one side of the boundary and something else entirely on the other,
|
|
22
|
+
* which is why both halves get hand-rolled per suite instead.
|
|
23
|
+
*
|
|
24
|
+
* # Runner-agnostic, deliberately
|
|
25
|
+
*
|
|
26
|
+
* No `jest.fn`, no `vi.fn`, no `expect`. The recorded arrays ARE the assertion
|
|
27
|
+
* surface, so the same double works under vitest, jest and a bare script. A
|
|
28
|
+
* mock-framework double would also make "was it called" the observable, when
|
|
29
|
+
* what a data-head test actually asserts is the CONTENT of what arrived —
|
|
30
|
+
* which `sourceClientId`, which version, which reason.
|
|
31
|
+
*/
|
|
32
|
+
import { type MessageConnection } from 'vscode-jsonrpc';
|
|
33
|
+
import type { DataPort } from '../client/data-port';
|
|
34
|
+
import type { DataClientProtocol } from '../data/data-server-protocol';
|
|
35
|
+
import type { ProjectsChangedEvent, TransferDocumentSavedEvent, TransferDocumentUpdatedEvent } from '../data/events';
|
|
36
|
+
import type { Project } from '../project';
|
|
37
|
+
import type { TransferDiagnostic } from '../transfer-diagnostic';
|
|
38
|
+
import type { TransferElement } from '../transfer-element';
|
|
39
|
+
/** Inputs to {@link makeFakeDataPort}. */
|
|
40
|
+
export interface FakeDataPortOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Establish one transport generation and hand back a LISTENING connection —
|
|
43
|
+
* the port's whole contract. Called once per generation, so a test that
|
|
44
|
+
* asserts on reconnection must return a FRESH connection each time rather
|
|
45
|
+
* than closing over one.
|
|
46
|
+
*
|
|
47
|
+
* Throwing (or rejecting) here is the transport-construction failure path,
|
|
48
|
+
* which the consumer surfaces through {@link FakeDataPort.reported}.
|
|
49
|
+
*/
|
|
50
|
+
connect(): MessageConnection | Promise<MessageConnection>;
|
|
51
|
+
/**
|
|
52
|
+
* Stable client identity. Defaults to `'fake-data-port'`, which avoids the
|
|
53
|
+
* three sentinels the framework reserves (`'language-client'`, `'unknown'`,
|
|
54
|
+
* `'revert-on-close'`); override it when a test needs two distinguishable
|
|
55
|
+
* clients on one server.
|
|
56
|
+
*/
|
|
57
|
+
clientId?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A {@link DataPort} that records what passed through it. */
|
|
60
|
+
export interface FakeDataPort extends DataPort {
|
|
61
|
+
/**
|
|
62
|
+
* Every connection {@link FakeDataPortOptions.connect} handed back, in
|
|
63
|
+
* order. Read from outside: "one connection shared across concurrent
|
|
64
|
+
* callers" and "a fresh connection after a teardown" are assertions about
|
|
65
|
+
* this length and about nothing else observable — a session that memoised a
|
|
66
|
+
* rejected promise, for instance, differs from one that retries ONLY here.
|
|
67
|
+
*/
|
|
68
|
+
readonly connections: readonly MessageConnection[];
|
|
69
|
+
/**
|
|
70
|
+
* Every {@link DataPort.reportError} call, in order. Read from outside: this
|
|
71
|
+
* is the only place a transport failure surfaces, so a test for the failure
|
|
72
|
+
* path asserts on the `context` string here rather than on a rejection that
|
|
73
|
+
* the consumer may legitimately swallow.
|
|
74
|
+
*/
|
|
75
|
+
readonly reported: readonly {
|
|
76
|
+
readonly error: unknown;
|
|
77
|
+
readonly context: string;
|
|
78
|
+
}[];
|
|
79
|
+
/**
|
|
80
|
+
* Fire {@link DataPort.onDispose} — the host tearing the transport down, a
|
|
81
|
+
* language-server restart being the case that forces the event to exist.
|
|
82
|
+
* Drives the consumer's reconnection path, which has no other trigger.
|
|
83
|
+
*/
|
|
84
|
+
fireDispose(): void;
|
|
85
|
+
/** Release the emitter. Idempotent; does not close the connections, which the test owns. */
|
|
86
|
+
dispose(): void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A {@link DataPort} over a transport the caller supplies.
|
|
90
|
+
*
|
|
91
|
+
* It deliberately does not create the transport itself. What varies between the
|
|
92
|
+
* cases worth testing is exactly that: an in-process duplex pair, a socket, a
|
|
93
|
+
* connection with no server behind it, a different one per generation.
|
|
94
|
+
*/
|
|
95
|
+
export declare function makeFakeDataPort(options: FakeDataPortOptions): FakeDataPort;
|
|
96
|
+
/** A {@link DataClientProtocol} plus the arrays it records into. */
|
|
97
|
+
export interface CapturingDataClient<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic = TransferDiagnostic, TProject extends Project = Project> {
|
|
98
|
+
/** The client to hand to a `DataSession` or bind as an RPC `localTarget`. */
|
|
99
|
+
readonly client: DataClientProtocol<TTransfer, TDiagnostic, TProject>;
|
|
100
|
+
/** Every `onDocumentUpdated` event, in arrival order. */
|
|
101
|
+
readonly updates: TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>[];
|
|
102
|
+
/** Every `onDocumentSaved` event, in arrival order. */
|
|
103
|
+
readonly saves: TransferDocumentSavedEvent<TTransfer, TDiagnostic>[];
|
|
104
|
+
/** Every `onProjectsChanged` event, in arrival order. */
|
|
105
|
+
readonly projectsChanges: ProjectsChangedEvent<TProject>[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A {@link DataClientProtocol} that records every notification it receives.
|
|
109
|
+
*
|
|
110
|
+
* Recording ALL THREE channels even when a suite reads one is deliberate: an
|
|
111
|
+
* event delivered on the wrong channel is a real defect of the data head, and a
|
|
112
|
+
* double that drops the other two turns it into silence on the one being
|
|
113
|
+
* watched.
|
|
114
|
+
*
|
|
115
|
+
* An override replaces a channel's recording rather than supplementing it, so
|
|
116
|
+
* the corresponding array stays empty — that is what makes an override usable
|
|
117
|
+
* as a barrier (rejecting, counting differently, throwing) rather than only as
|
|
118
|
+
* a spy.
|
|
119
|
+
*/
|
|
120
|
+
export declare function makeCapturingDataClient<TTransfer extends TransferElement, TDiagnostic extends TransferDiagnostic = TransferDiagnostic, TProject extends Project = Project>(overrides?: Partial<DataClientProtocol<TTransfer, TDiagnostic, TProject>>): CapturingDataClient<TTransfer, TDiagnostic, TProject>;
|
|
121
|
+
//# sourceMappingURL=data-doubles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-doubles.d.ts","sourceRoot":"","sources":["../../src/testing/data-doubles.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAW,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACrH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IACjC;;;;;;;;OAQG;IACH,OAAO,IAAI,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpF;;;;OAIG;IACH,WAAW,IAAI,IAAI,CAAC;IACpB,4FAA4F;IAC5F,OAAO,IAAI,IAAI,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAwB3E;AAED,oEAAoE;AACpE,MAAM,WAAW,mBAAmB,CACjC,SAAS,SAAS,eAAe,EACjC,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,QAAQ,SAAS,OAAO,GAAG,OAAO;IAElC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACtE,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,4BAA4B,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;IACzE,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;IACrE,yDAAyD;IACzD,QAAQ,CAAC,eAAe,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;CAC7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACpC,SAAS,SAAS,eAAe,EACjC,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,QAAQ,SAAS,OAAO,GAAG,OAAO,EACnC,SAAS,GAAE,OAAO,CAAC,kBAAkB,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAM,GAAG,mBAAmB,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAsBtI"}
|