@hydranium/core 1.0.0-next.62 → 1.0.0-next.63

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.
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
  export * from './completion/hydranium-completion-provider.js';
10
- export * from './instrument-connection.js';
10
+ export * from './lsp-latency.js';
11
11
  export * from './semantic-token-provider.js';
12
12
  export * from './language-module.js';
13
13
  export * from './shared-module.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lsp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+CAA+C,CAAC;AAC9D,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wCAAwC,CAAC;AAEvD;;;;;;GAMG;AACH,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lsp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+CAA+C,CAAC;AAC9D,cAAc,kBAAkB,CAAC;AACjC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wCAAwC,CAAC;AAEvD;;;;;;GAMG;AACH,cAAc,4BAA4B,CAAC"}
package/lib/lsp/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
  export * from './completion/hydranium-completion-provider.js';
10
- export * from './instrument-connection.js';
10
+ export * from './lsp-latency.js';
11
11
  export * from './semantic-token-provider.js';
12
12
  export * from './language-module.js';
13
13
  export * from './shared-module.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lsp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+CAA+C,CAAC;AAC9D,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wCAAwC,CAAC;AAEvD;;;;;;GAMG;AACH,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lsp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,cAAc,+CAA+C,CAAC;AAC9D,cAAc,kBAAkB,CAAC;AACjC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wCAAwC,CAAC;AAEvD;;;;;;GAMG;AACH,cAAc,4BAA4B,CAAC"}
@@ -0,0 +1,39 @@
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 { LatencyCollector } from '@hydranium/protocol';
10
+ import type { ConnectionOptions } from 'vscode-jsonrpc';
11
+ /**
12
+ * Connection options that time every inbound LSP message into `latency` under
13
+ * its method name — the LSP analog of the data-server RPC chokepoint.
14
+ *
15
+ * Pass the result to `createConnection` where the head builds its connection:
16
+ * `createConnection(ProposedFeatures.all, lspLatencyOptions(latency))`. The
17
+ * transport is untouched, so a head keeps whatever `--stdio` / `--socket`
18
+ * selection its arguments make.
19
+ *
20
+ * **`messageStrategy` is the seam because it is the only one the handlers
21
+ * actually pass through.** `vscode-languageserver` builds its `Connection` as an
22
+ * object whose `onHover`, `onCompletion` and every other named helper register
23
+ * against the inner protocol connection captured in their closure — not against
24
+ * the returned object's own `onRequest`. Langium registers exclusively through
25
+ * those helpers, so decorating the `Connection` reaches none of them and reports
26
+ * an empty LSP half no matter how much traffic ran. `handleMessage` sits on the
27
+ * dispatch path itself, which every request and notification crosses, so it also
28
+ * catches `initialize` — special-cased into `onInitialize` and invisible to any
29
+ * `onRequest` decoration.
30
+ *
31
+ * What it measures is **dispatch plus handler**, not handler alone: the wait a
32
+ * client actually experiences, including time queued behind other work.
33
+ *
34
+ * When `latency` is `undefined` the seam is off and this returns `undefined`, so
35
+ * a head can pass the result of an env-gated factory (`latencyFromEnv()`)
36
+ * straight through and pay nothing when timing is not enabled.
37
+ */
38
+ export declare function lspLatencyOptions(latency: LatencyCollector | undefined): ConnectionOptions | undefined;
39
+ //# sourceMappingURL=lsp-latency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lsp-latency.d.ts","sourceRoot":"","sources":["../../src/lsp/lsp-latency.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAW,MAAM,gBAAgB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAiBtG"}
@@ -0,0 +1,58 @@
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
+ * Connection options that time every inbound LSP message into `latency` under
11
+ * its method name — the LSP analog of the data-server RPC chokepoint.
12
+ *
13
+ * Pass the result to `createConnection` where the head builds its connection:
14
+ * `createConnection(ProposedFeatures.all, lspLatencyOptions(latency))`. The
15
+ * transport is untouched, so a head keeps whatever `--stdio` / `--socket`
16
+ * selection its arguments make.
17
+ *
18
+ * **`messageStrategy` is the seam because it is the only one the handlers
19
+ * actually pass through.** `vscode-languageserver` builds its `Connection` as an
20
+ * object whose `onHover`, `onCompletion` and every other named helper register
21
+ * against the inner protocol connection captured in their closure — not against
22
+ * the returned object's own `onRequest`. Langium registers exclusively through
23
+ * those helpers, so decorating the `Connection` reaches none of them and reports
24
+ * an empty LSP half no matter how much traffic ran. `handleMessage` sits on the
25
+ * dispatch path itself, which every request and notification crosses, so it also
26
+ * catches `initialize` — special-cased into `onInitialize` and invisible to any
27
+ * `onRequest` decoration.
28
+ *
29
+ * What it measures is **dispatch plus handler**, not handler alone: the wait a
30
+ * client actually experiences, including time queued behind other work.
31
+ *
32
+ * When `latency` is `undefined` the seam is off and this returns `undefined`, so
33
+ * a head can pass the result of an env-gated factory (`latencyFromEnv()`)
34
+ * straight through and pay nothing when timing is not enabled.
35
+ */
36
+ export function lspLatencyOptions(latency) {
37
+ if (!latency) {
38
+ return undefined;
39
+ }
40
+ return {
41
+ messageStrategy: {
42
+ handleMessage: (message, next) => {
43
+ // Responses to requests the SERVER sent carry no method and are not
44
+ // work this head performed; timing them would report the client's
45
+ // latency under a name this collector cannot supply.
46
+ if (!hasMethod(message)) {
47
+ return next(message);
48
+ }
49
+ return latency.time(message.method, () => next(message));
50
+ }
51
+ }
52
+ };
53
+ }
54
+ /** Narrow to the messages that name an operation — requests and notifications. */
55
+ function hasMethod(message) {
56
+ return typeof message.method === 'string';
57
+ }
58
+ //# sourceMappingURL=lsp-latency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lsp-latency.js","sourceRoot":"","sources":["../../src/lsp/lsp-latency.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAKlF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAqC;IACpE,IAAI,CAAC,OAAO,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACpB,CAAC;IACD,OAAO;QACJ,eAAe,EAAE;YACd,aAAa,EAAE,CAAC,OAAgB,EAAE,IAAgD,EAAE,EAAE;gBACnF,oEAAoE;gBACpE,kEAAkE;gBAClE,qDAAqD;gBACrD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBACvB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;gBACD,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5D,CAAC;SACH;KACH,CAAC;AACL,CAAC;AAED,kFAAkF;AAClF,SAAS,SAAS,CAAC,OAAgB;IAChC,OAAO,OAAQ,OAAgC,CAAC,MAAM,KAAK,QAAQ,CAAC;AACvE,CAAC"}
@@ -23,7 +23,7 @@ export declare const DEFAULT_LATENCY_ENV = "HYDRANIUM_LATENCY";
23
23
  export declare function isLatencyEnabled(): boolean;
24
24
  /**
25
25
  * A {@link LatencyCollector} when {@link DEFAULT_LATENCY_ENV} is set, otherwise
26
- * `undefined`. Heads pass the result straight to `instrumentLspConnection`, the
26
+ * `undefined`. Heads pass the result straight to `lspLatencyOptions`, the
27
27
  * `DataServer` `latency` option and the RPC binder; all three treat `undefined`
28
28
  * as "seam off", so this single factory is the one place a head decides whether
29
29
  * timing runs. `clock` is forwarded to the collector so tests stay deterministic.
@@ -25,7 +25,7 @@ export function isLatencyEnabled() {
25
25
  }
26
26
  /**
27
27
  * A {@link LatencyCollector} when {@link DEFAULT_LATENCY_ENV} is set, otherwise
28
- * `undefined`. Heads pass the result straight to `instrumentLspConnection`, the
28
+ * `undefined`. Heads pass the result straight to `lspLatencyOptions`, the
29
29
  * `DataServer` `latency` option and the RPC binder; all three treat `undefined`
30
30
  * as "seam off", so this single factory is the one place a head decides whether
31
31
  * timing runs. `clock` is forwarded to the collector so tests stay deterministic.
@@ -27,8 +27,8 @@ export interface ProfilingRunOptions {
27
27
  /**
28
28
  * Optional latency collector. When present, {@link ProfilingRun.finish} writes
29
29
  * its report to `server-latency.json` and records a `server-latency` manifest artefact. A
30
- * head feeds it via the RPC binding / `instrumentLspConnection`; a pure
31
- * headless build with no request traffic leaves it empty.
30
+ * head feeds it via the RPC binding / `lspLatencyOptions`; a pure headless
31
+ * build with no request traffic leaves it empty.
32
32
  */
33
33
  latency?: LatencyCollector;
34
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/core",
3
- "version": "1.0.0-next.62",
3
+ "version": "1.0.0-next.63",
4
4
  "description": "Foundational runtime of the hydranium framework: AST coordination layer (services, integrity, AST extension, serialization, multi-client documents) plus the LSP textual head at the /lsp subpath. Consumed by protocol-head packages (@hydranium/data-server, @hydranium/glsp-server).",
5
5
  "keywords": [
6
6
  "hydranium",
@@ -104,8 +104,8 @@
104
104
  "diff": "^5.2.0"
105
105
  },
106
106
  "devDependencies": {
107
- "@hydranium/langium": "1.0.0-next.62",
108
- "@hydranium/protocol": "1.0.0-next.62",
107
+ "@hydranium/langium": "1.0.0-next.63",
108
+ "@hydranium/protocol": "1.0.0-next.63",
109
109
  "@playwright/test": "^1.40.0",
110
110
  "@types/diff": "^5.2.0",
111
111
  "rimraf": "^5.0.0",
@@ -117,8 +117,8 @@
117
117
  "vscode-languageserver-types": "^3.17.5"
118
118
  },
119
119
  "peerDependencies": {
120
- "@hydranium/langium": "1.0.0-next.62",
121
- "@hydranium/protocol": "1.0.0-next.62",
120
+ "@hydranium/langium": "1.0.0-next.63",
121
+ "@hydranium/protocol": "1.0.0-next.63",
122
122
  "@playwright/test": "^1.40.0",
123
123
  "vscode-jsonrpc": "9.0.1",
124
124
  "vscode-languageserver": "~10.0.1",
package/src/lsp/index.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  export * from './completion/hydranium-completion-provider.js';
11
- export * from './instrument-connection.js';
11
+ export * from './lsp-latency.js';
12
12
  export * from './semantic-token-provider.js';
13
13
  export * from './language-module.js';
14
14
  export * from './shared-module.js';
@@ -0,0 +1,62 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+
10
+ import type { LatencyCollector } from '@hydranium/protocol';
11
+ import type { ConnectionOptions, Message } from 'vscode-jsonrpc';
12
+
13
+ /**
14
+ * Connection options that time every inbound LSP message into `latency` under
15
+ * its method name — the LSP analog of the data-server RPC chokepoint.
16
+ *
17
+ * Pass the result to `createConnection` where the head builds its connection:
18
+ * `createConnection(ProposedFeatures.all, lspLatencyOptions(latency))`. The
19
+ * transport is untouched, so a head keeps whatever `--stdio` / `--socket`
20
+ * selection its arguments make.
21
+ *
22
+ * **`messageStrategy` is the seam because it is the only one the handlers
23
+ * actually pass through.** `vscode-languageserver` builds its `Connection` as an
24
+ * object whose `onHover`, `onCompletion` and every other named helper register
25
+ * against the inner protocol connection captured in their closure — not against
26
+ * the returned object's own `onRequest`. Langium registers exclusively through
27
+ * those helpers, so decorating the `Connection` reaches none of them and reports
28
+ * an empty LSP half no matter how much traffic ran. `handleMessage` sits on the
29
+ * dispatch path itself, which every request and notification crosses, so it also
30
+ * catches `initialize` — special-cased into `onInitialize` and invisible to any
31
+ * `onRequest` decoration.
32
+ *
33
+ * What it measures is **dispatch plus handler**, not handler alone: the wait a
34
+ * client actually experiences, including time queued behind other work.
35
+ *
36
+ * When `latency` is `undefined` the seam is off and this returns `undefined`, so
37
+ * a head can pass the result of an env-gated factory (`latencyFromEnv()`)
38
+ * straight through and pay nothing when timing is not enabled.
39
+ */
40
+ export function lspLatencyOptions(latency: LatencyCollector | undefined): ConnectionOptions | undefined {
41
+ if (!latency) {
42
+ return undefined;
43
+ }
44
+ return {
45
+ messageStrategy: {
46
+ handleMessage: (message: Message, next: (message: Message) => void | Promise<void>) => {
47
+ // Responses to requests the SERVER sent carry no method and are not
48
+ // work this head performed; timing them would report the client's
49
+ // latency under a name this collector cannot supply.
50
+ if (!hasMethod(message)) {
51
+ return next(message);
52
+ }
53
+ return latency.time(message.method, () => next(message));
54
+ }
55
+ }
56
+ };
57
+ }
58
+
59
+ /** Narrow to the messages that name an operation — requests and notifications. */
60
+ function hasMethod(message: Message): message is Message & { method: string } {
61
+ return typeof (message as { method?: unknown }).method === 'string';
62
+ }
@@ -29,7 +29,7 @@ export function isLatencyEnabled(): boolean {
29
29
 
30
30
  /**
31
31
  * A {@link LatencyCollector} when {@link DEFAULT_LATENCY_ENV} is set, otherwise
32
- * `undefined`. Heads pass the result straight to `instrumentLspConnection`, the
32
+ * `undefined`. Heads pass the result straight to `lspLatencyOptions`, the
33
33
  * `DataServer` `latency` option and the RPC binder; all three treat `undefined`
34
34
  * as "seam off", so this single factory is the one place a head decides whether
35
35
  * timing runs. `clock` is forwarded to the collector so tests stay deterministic.
@@ -55,8 +55,8 @@ export interface ProfilingRunOptions {
55
55
  /**
56
56
  * Optional latency collector. When present, {@link ProfilingRun.finish} writes
57
57
  * its report to `server-latency.json` and records a `server-latency` manifest artefact. A
58
- * head feeds it via the RPC binding / `instrumentLspConnection`; a pure
59
- * headless build with no request traffic leaves it empty.
58
+ * head feeds it via the RPC binding / `lspLatencyOptions`; a pure headless
59
+ * build with no request traffic leaves it empty.
60
60
  */
61
61
  latency?: LatencyCollector;
62
62
  }
@@ -1,36 +0,0 @@
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 { LatencyCollector } from '@hydranium/protocol';
10
- /**
11
- * Wrap an LSP `Connection` so every request handler registered through
12
- * `onRequest` is timed into `latency` under its method name — the LSP analog of
13
- * the data-server RPC chokepoint. Langium's `startLanguageServer` registers
14
- * `onCompletion`/`onHover`/… one handler at a time, so there is no single
15
- * dispatch point; but every registration funnels through the ONE `onRequest`
16
- * on the shared `Connection`, so decorating it catches every method with no
17
- * Langium fork and no per-handler code.
18
- *
19
- * Opt in where the head creates the connection, before it hands it to the
20
- * services factory — wrapping it afterwards is too late, because Langium has
21
- * already registered its handlers against the raw connection.
22
- *
23
- * `onRequest` has several overloads: most register a handler for a specific
24
- * method (a string, or a typed request whose `.method` names it) and one is the
25
- * `StarRequestHandler` catch-all `onRequest(handler)`. The star form fires only
26
- * for methods with no registered handler, so timing it would be misleading — it
27
- * is passed through untouched. Every other member of the connection is
28
- * forwarded unchanged.
29
- *
30
- * When `latency` is `undefined` the seam is off: the connection is returned
31
- * unchanged (no Proxy, no per-request timing), so a head can pass the result of
32
- * an env-gated factory (`latencyFromEnv()`) straight through and pay nothing
33
- * when latency is not enabled.
34
- */
35
- export declare function instrumentLspConnection<C extends object>(connection: C, latency: LatencyCollector | undefined): C;
36
- //# sourceMappingURL=instrument-connection.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrument-connection.d.ts","sourceRoot":"","sources":["../../src/lsp/instrument-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,GAAG,CAAC,CA2BjH"}
@@ -1,62 +0,0 @@
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
- * Wrap an LSP `Connection` so every request handler registered through
11
- * `onRequest` is timed into `latency` under its method name — the LSP analog of
12
- * the data-server RPC chokepoint. Langium's `startLanguageServer` registers
13
- * `onCompletion`/`onHover`/… one handler at a time, so there is no single
14
- * dispatch point; but every registration funnels through the ONE `onRequest`
15
- * on the shared `Connection`, so decorating it catches every method with no
16
- * Langium fork and no per-handler code.
17
- *
18
- * Opt in where the head creates the connection, before it hands it to the
19
- * services factory — wrapping it afterwards is too late, because Langium has
20
- * already registered its handlers against the raw connection.
21
- *
22
- * `onRequest` has several overloads: most register a handler for a specific
23
- * method (a string, or a typed request whose `.method` names it) and one is the
24
- * `StarRequestHandler` catch-all `onRequest(handler)`. The star form fires only
25
- * for methods with no registered handler, so timing it would be misleading — it
26
- * is passed through untouched. Every other member of the connection is
27
- * forwarded unchanged.
28
- *
29
- * When `latency` is `undefined` the seam is off: the connection is returned
30
- * unchanged (no Proxy, no per-request timing), so a head can pass the result of
31
- * an env-gated factory (`latencyFromEnv()`) straight through and pay nothing
32
- * when latency is not enabled.
33
- */
34
- export function instrumentLspConnection(connection, latency) {
35
- if (!latency) {
36
- return connection;
37
- }
38
- return new Proxy(connection, {
39
- get(target, property, receiver) {
40
- const value = Reflect.get(target, property, receiver);
41
- if (property !== 'onRequest' || typeof value !== 'function') {
42
- return value;
43
- }
44
- const onRequest = value;
45
- return (...args) => {
46
- const [first, handler] = args;
47
- // Star form `onRequest(handler)` — pass through; it is not a timer.
48
- if (typeof first === 'function' || typeof handler !== 'function') {
49
- return onRequest.apply(target, args);
50
- }
51
- const method = typeof first === 'string' ? first : first.method;
52
- if (typeof method !== 'string') {
53
- return onRequest.apply(target, args);
54
- }
55
- const original = handler;
56
- const timed = (...handlerArgs) => latency.time(method, () => original(...handlerArgs));
57
- return onRequest.apply(target, [first, timed, ...args.slice(2)]);
58
- };
59
- }
60
- });
61
- }
62
- //# sourceMappingURL=instrument-connection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrument-connection.js","sourceRoot":"","sources":["../../src/lsp/instrument-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAMlF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,uBAAuB,CAAmB,UAAa,EAAE,OAAqC;IAC3G,IAAI,CAAC,OAAO,EAAE,CAAC;QACZ,OAAO,UAAU,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,UAAU,EAAE;QAC1B,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC3B,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/D,IAAI,QAAQ,KAAK,WAAW,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;gBAC3D,OAAO,KAAK,CAAC;YAChB,CAAC;YACD,MAAM,SAAS,GAAG,KAAwC,CAAC;YAC3D,OAAO,CAAC,GAAG,IAAe,EAAW,EAAE;gBACpC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;gBAC9B,oEAAoE;gBACpE,IAAI,OAAO,KAAK,KAAK,UAAU,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;oBAChE,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACxC,CAAC;gBACD,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,KAA8B,CAAC,MAAM,CAAC;gBAC1F,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC9B,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACxC,CAAC;gBACD,MAAM,QAAQ,GAAG,OAAyB,CAAC;gBAC3C,MAAM,KAAK,GAAmB,CAAC,GAAG,WAAW,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;gBACvG,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC,CAAC;QACL,CAAC;KACH,CAAC,CAAC;AACN,CAAC"}
@@ -1,66 +0,0 @@
1
- /********************************************************************************
2
- * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
- *
4
- * This program and the accompanying materials are made available under the
5
- * terms of the MIT License which is available in the project root.
6
- *
7
- * SPDX-License-Identifier: MIT
8
- ********************************************************************************/
9
-
10
- import type { LatencyCollector } from '@hydranium/protocol';
11
-
12
- type RequestHandler = (...args: unknown[]) => unknown;
13
-
14
- /**
15
- * Wrap an LSP `Connection` so every request handler registered through
16
- * `onRequest` is timed into `latency` under its method name — the LSP analog of
17
- * the data-server RPC chokepoint. Langium's `startLanguageServer` registers
18
- * `onCompletion`/`onHover`/… one handler at a time, so there is no single
19
- * dispatch point; but every registration funnels through the ONE `onRequest`
20
- * on the shared `Connection`, so decorating it catches every method with no
21
- * Langium fork and no per-handler code.
22
- *
23
- * Opt in where the head creates the connection, before it hands it to the
24
- * services factory — wrapping it afterwards is too late, because Langium has
25
- * already registered its handlers against the raw connection.
26
- *
27
- * `onRequest` has several overloads: most register a handler for a specific
28
- * method (a string, or a typed request whose `.method` names it) and one is the
29
- * `StarRequestHandler` catch-all `onRequest(handler)`. The star form fires only
30
- * for methods with no registered handler, so timing it would be misleading — it
31
- * is passed through untouched. Every other member of the connection is
32
- * forwarded unchanged.
33
- *
34
- * When `latency` is `undefined` the seam is off: the connection is returned
35
- * unchanged (no Proxy, no per-request timing), so a head can pass the result of
36
- * an env-gated factory (`latencyFromEnv()`) straight through and pay nothing
37
- * when latency is not enabled.
38
- */
39
- export function instrumentLspConnection<C extends object>(connection: C, latency: LatencyCollector | undefined): C {
40
- if (!latency) {
41
- return connection;
42
- }
43
- return new Proxy(connection, {
44
- get(target, property, receiver): unknown {
45
- const value: unknown = Reflect.get(target, property, receiver);
46
- if (property !== 'onRequest' || typeof value !== 'function') {
47
- return value;
48
- }
49
- const onRequest = value as (...args: unknown[]) => unknown;
50
- return (...args: unknown[]): unknown => {
51
- const [first, handler] = args;
52
- // Star form `onRequest(handler)` — pass through; it is not a timer.
53
- if (typeof first === 'function' || typeof handler !== 'function') {
54
- return onRequest.apply(target, args);
55
- }
56
- const method = typeof first === 'string' ? first : (first as { method?: unknown }).method;
57
- if (typeof method !== 'string') {
58
- return onRequest.apply(target, args);
59
- }
60
- const original = handler as RequestHandler;
61
- const timed: RequestHandler = (...handlerArgs) => latency.time(method, () => original(...handlerArgs));
62
- return onRequest.apply(target, [first, timed, ...args.slice(2)]);
63
- };
64
- }
65
- });
66
- }