@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
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hydranium/protocol",
|
|
3
|
+
"version": "1.0.0-next.10",
|
|
4
|
+
"description": "Generic, language-agnostic types, constants, and pure utilities for the hydranium framework.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"hydranium",
|
|
7
|
+
"langium",
|
|
8
|
+
"language-server",
|
|
9
|
+
"lsp",
|
|
10
|
+
"rpc",
|
|
11
|
+
"transfer-model"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/eclipse-emfcloud/hydranium/tree/main/packages/protocol",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/eclipse-emfcloud/hydranium/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/eclipse-emfcloud/hydranium.git",
|
|
20
|
+
"directory": "packages/protocol"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "Hydranium Team"
|
|
25
|
+
},
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./lib/index.d.ts",
|
|
30
|
+
"default": "./lib/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./client": {
|
|
33
|
+
"types": "./lib/client/index.d.ts",
|
|
34
|
+
"default": "./lib/client/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./lib/client": {
|
|
37
|
+
"types": "./lib/client/index.d.ts",
|
|
38
|
+
"default": "./lib/client/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./data": {
|
|
41
|
+
"types": "./lib/data/index.d.ts",
|
|
42
|
+
"default": "./lib/data/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./testing": {
|
|
45
|
+
"types": "./lib/testing/index.d.ts",
|
|
46
|
+
"default": "./lib/testing/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./testing/node": {
|
|
49
|
+
"types": "./lib/testing/node/index.d.ts",
|
|
50
|
+
"default": "./lib/testing/node/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./lib/data": {
|
|
53
|
+
"types": "./lib/data/index.d.ts",
|
|
54
|
+
"default": "./lib/data/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./lib/testing": {
|
|
57
|
+
"types": "./lib/testing/index.d.ts",
|
|
58
|
+
"default": "./lib/testing/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./lib/testing/node": {
|
|
61
|
+
"types": "./lib/testing/node/index.d.ts",
|
|
62
|
+
"default": "./lib/testing/node/index.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"main": "lib/index.js",
|
|
66
|
+
"types": "lib/index.d.ts",
|
|
67
|
+
"files": [
|
|
68
|
+
"lib",
|
|
69
|
+
"src",
|
|
70
|
+
"!lib/**/*.tsbuildinfo"
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "tsc -b",
|
|
74
|
+
"clean": "rimraf lib tsconfig.tsbuildinfo",
|
|
75
|
+
"lint": "eslint src test --max-warnings 0",
|
|
76
|
+
"prepack": "node -e \"const m=require('./package.json'),fs=require('node:fs');const missing=[m.main,...Object.values(m.bin||{})].filter(entry=>entry&&!fs.existsSync(entry));if(missing.length){console.error('prepack '+m.name+': not built ('+missing.join(', ')+' missing). Run the build before packing: a files entry that matches nothing is skipped silently, so the tarball would ship src only.');process.exit(1);}\"",
|
|
77
|
+
"test": "npm run typecheck:test && vitest run",
|
|
78
|
+
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
|
|
79
|
+
"watch": "tsc -b -w --preserveWatchOutput"
|
|
80
|
+
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"fast-json-patch": "^3.1.1"
|
|
83
|
+
},
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"fast-check": "^3.23.2",
|
|
86
|
+
"rimraf": "^5.0.0",
|
|
87
|
+
"typescript": "^5.8.0",
|
|
88
|
+
"vscode-jsonrpc": "9.0.1"
|
|
89
|
+
},
|
|
90
|
+
"peerDependencies": {
|
|
91
|
+
"vscode-jsonrpc": "^8.0.0 || ^9.0.0"
|
|
92
|
+
},
|
|
93
|
+
"engines": {
|
|
94
|
+
"node": ">=22.13"
|
|
95
|
+
},
|
|
96
|
+
"publishConfig": {
|
|
97
|
+
"access": "public"
|
|
98
|
+
},
|
|
99
|
+
"//prepack": "The publish guard, and it deliberately is NOT a `prepare`: npm runs a workspace `prepare` BEFORE the root `postinstall` that applies patches/vscode-jsonrpc+9.0.1.patch, so building there fails on a cold clone and npm rolls the entire install back. `prepack` runs only when a tarball is made (`npm pack`, `npm publish`) and never on install, so it cannot break the install it has no business touching. It FAILS rather than rebuilds, because the rebuild is exactly the part that ordering defeats. What it defends against: `files` lists `lib`, `lib` is gitignored, and a `files` entry matching nothing is skipped SILENTLY — so `npm publish` from an unbuilt tree emits a tarball of `src` and nothing else, with no error."
|
|
100
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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 Clock, SystemClock } from './clock';
|
|
11
|
+
import { Format, LEVEL_LABELS, type LogLevel, Logger, type LogThreshold } from './logger';
|
|
12
|
+
|
|
13
|
+
/** Default time source for every {@link AbstractLogger}; overridable per subclass via {@link AbstractLogger.clock}. */
|
|
14
|
+
const systemClock = new SystemClock();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Cross-side abstract base for the {@link Logger} family. Centralises:
|
|
18
|
+
* - the {@link LogLevel}/{@link LogThreshold} contract and threshold gating;
|
|
19
|
+
* - public `error`/`warn`/`info`/`debug`/`trace`/`log` methods;
|
|
20
|
+
* - component-prefix derivation ({@link for}, {@link sub}, {@link with}, {@link withUri}).
|
|
21
|
+
*
|
|
22
|
+
* Emission only — measuring (timing, memory, profiling) lives on the
|
|
23
|
+
* `Tracer`, which composes a Logger for its output.
|
|
24
|
+
* Subclasses provide platform-specific output via {@link emit} and instance
|
|
25
|
+
* creation via {@link derive}. Format is `[<Label> - HH:MM:SS.mmm]
|
|
26
|
+
* [<component>] <message>` so client and server lines interleave cleanly under
|
|
27
|
+
* one Output panel entry.
|
|
28
|
+
*/
|
|
29
|
+
export abstract class AbstractLogger implements Logger {
|
|
30
|
+
constructor(protected component?: string) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Time source for this logger's timing helpers and timestamps. The base
|
|
34
|
+
* returns the process-wide {@link SystemClock}; subclasses with access to
|
|
35
|
+
* an injected {@link Clock} override this so logger time goes through the
|
|
36
|
+
* same fake-clock axis as the rest of the framework's gated logic. Inherited
|
|
37
|
+
* by derived loggers ({@link derive}), so a subclass override applies to its
|
|
38
|
+
* children without any extra wiring.
|
|
39
|
+
*/
|
|
40
|
+
protected clock(): Clock {
|
|
41
|
+
return systemClock;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Render the current wall-clock instant as `HH:MM:SS.mmm`, sourced from {@link clock}. */
|
|
45
|
+
protected timestamp(): string {
|
|
46
|
+
return Format.timestamp(new Date(this.clock().now()));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
error(message?: string, ...args: unknown[]): this {
|
|
50
|
+
this.send('error', message, args);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
warn(message?: string, ...args: unknown[]): this {
|
|
54
|
+
this.send('warn', message, args);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
info(message?: string, ...args: unknown[]): this {
|
|
58
|
+
this.send('info', message, args);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
debug(message?: string, ...args: unknown[]): this {
|
|
62
|
+
this.send('debug', message, args);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/** High-volume diagnostic lines, only emitted when the log level is set to 'trace'. */
|
|
66
|
+
trace(message?: string, ...args: unknown[]): this {
|
|
67
|
+
this.send('trace', message, args);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/** Info-level threshold rendered with the `Log` label instead of `Info`. */
|
|
71
|
+
log(message?: string, ...args: unknown[]): this {
|
|
72
|
+
this.send('info', message, args, 'Log');
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Emit `message` at `threshold`, or do nothing when `threshold === 'off'`. */
|
|
77
|
+
logAt(threshold: LogThreshold, message: string): this {
|
|
78
|
+
if (threshold === 'off') {
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
return this[threshold](message);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Replace the component prefix with `component`. */
|
|
85
|
+
for(component: string): this {
|
|
86
|
+
return this.derive(component);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Append `component` to the existing prefix as a nested `parent :: child` segment. */
|
|
90
|
+
sub(component: string): this {
|
|
91
|
+
return this.derive(this.component ? `${this.component} :: ${component}` : component);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Append `component` as a separate bracket: `[a] [b]` rather than `[a :: b]`. */
|
|
95
|
+
with(component: string): this {
|
|
96
|
+
return this.derive(this.component ? `${this.component}] [${component}` : component);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Default URI labelling: stringify the URI. Subclasses with workspace
|
|
101
|
+
* awareness override to render workspace-relative paths.
|
|
102
|
+
*/
|
|
103
|
+
withUri(uri: string): this {
|
|
104
|
+
return this.with(uri);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Threshold-gated send. Subclasses can override the label by passing one explicitly. */
|
|
108
|
+
protected send(level: LogLevel, message?: string, args: readonly unknown[] = [], label = LEVEL_LABELS[level]): void {
|
|
109
|
+
if (!message || !Logger.isLevelEnabled(level)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.emit(level, label, message, args);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Subclasses implement platform-specific output (console + file sink, OutputChannel, etc.). */
|
|
116
|
+
protected abstract emit(level: LogLevel, label: string, message: string, args: readonly unknown[]): void;
|
|
117
|
+
|
|
118
|
+
/** Subclasses construct a new instance preserving their constructor dependencies. */
|
|
119
|
+
protected abstract derive(component: string): this;
|
|
120
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
* Browser-origin renderer-capture shapes. They live in `@hydranium/protocol`
|
|
12
|
+
* (neutral — no DOM, no `node:` deps) so the client-theia live capture
|
|
13
|
+
* (`captureBrowserRuntime`) and the Playwright CDP bridge produce the SAME
|
|
14
|
+
* `browser-runtime.json` artefact type. One report with `source`-graded
|
|
15
|
+
* fidelity, so a reader has one shape and one file to interpret.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One renderer-runtime reading — JS-heap + precise-renderer-memory + object/render
|
|
20
|
+
* counters, every field optional (a counter a source cannot produce is omitted).
|
|
21
|
+
*/
|
|
22
|
+
export interface BrowserRuntimeSample {
|
|
23
|
+
/** JS heap used (CDP `JSHeapUsedSize` / `performance.memory.usedJSHeapSize`). */
|
|
24
|
+
jsHeapUsedBytes?: number;
|
|
25
|
+
/** JS heap total (CDP `JSHeapTotalSize` / `performance.memory.totalJSHeapSize`). */
|
|
26
|
+
jsHeapTotalBytes?: number;
|
|
27
|
+
/** JS heap limit (`performance.memory.jsHeapSizeLimit`; CDP does not report it). */
|
|
28
|
+
jsHeapLimitBytes?: number;
|
|
29
|
+
/** Total renderer memory across JS/DOM/workers (`measureUserAgentSpecificMemory` only). */
|
|
30
|
+
totalBytes?: number;
|
|
31
|
+
/** The precise API's per-type/per-container breakdown, passed through verbatim. */
|
|
32
|
+
breakdown?: unknown;
|
|
33
|
+
/** DOM node count (CDP `Nodes`) — a rising count is the classic Theia leak tell. */
|
|
34
|
+
nodes?: number;
|
|
35
|
+
/** Registered JS event listeners (CDP `JSEventListeners`) — the other leak tell. */
|
|
36
|
+
jsEventListeners?: number;
|
|
37
|
+
/** Live `Document` count (CDP `Documents`). */
|
|
38
|
+
documents?: number;
|
|
39
|
+
/** Frame count (CDP `Frames`). */
|
|
40
|
+
frames?: number;
|
|
41
|
+
/** Cumulative layout operations (CDP `LayoutCount`). */
|
|
42
|
+
layoutCount?: number;
|
|
43
|
+
/** Cumulative style recalculations (CDP `RecalcStyleCount`). */
|
|
44
|
+
recalcStyleCount?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The `browser-runtime.json` artefact — renderer memory + object/render counters,
|
|
49
|
+
* graded by `source`. `before`/`after`/`delta` are present for a two-point source
|
|
50
|
+
* (CDP, before vs after the scenario — the delta is the leak signal); `after`-only
|
|
51
|
+
* for a single gauge read (the live "Dump Frontend State" command).
|
|
52
|
+
*
|
|
53
|
+
* - `cdp-performance` — full: JS heap + `nodes`/`jsEventListeners`/`documents`/
|
|
54
|
+
* `frames`/`layoutCount`/`recalcStyleCount`; two-point.
|
|
55
|
+
* - `measureUserAgentSpecificMemory` — precise renderer bytes by type/container
|
|
56
|
+
* (`totalBytes`/`breakdown`; cross-origin isolation only); after-only.
|
|
57
|
+
* - `performance.memory` — JS-heap gauge only (used/total/limit); after-only.
|
|
58
|
+
* - `unavailable` — neither API; `after` is empty.
|
|
59
|
+
*/
|
|
60
|
+
export interface BrowserRuntimeReport {
|
|
61
|
+
/**
|
|
62
|
+
* Which API produced the reading, and therefore which sample fields CAN be
|
|
63
|
+
* populated — a reader must branch on it rather than assume a field is
|
|
64
|
+
* missing because the value was zero. Not a caller preference: each producer
|
|
65
|
+
* tries its best source and degrades, so the same code path can emit any of
|
|
66
|
+
* the four across runs.
|
|
67
|
+
*/
|
|
68
|
+
source: 'cdp-performance' | 'measureUserAgentSpecificMemory' | 'performance.memory' | 'unavailable';
|
|
69
|
+
/**
|
|
70
|
+
* The pre-scenario reading, present only for a two-point capture. Its absence
|
|
71
|
+
* is what marks a report as a single gauge read, and {@link delta} is then
|
|
72
|
+
* absent with it.
|
|
73
|
+
*/
|
|
74
|
+
before?: BrowserRuntimeSample;
|
|
75
|
+
/**
|
|
76
|
+
* The post-scenario reading, or the only reading for a single-point capture.
|
|
77
|
+
* Always present — an `unavailable` source yields an EMPTY object here rather
|
|
78
|
+
* than omitting the field, so presence proves nothing about content.
|
|
79
|
+
*/
|
|
80
|
+
after: BrowserRuntimeSample;
|
|
81
|
+
/**
|
|
82
|
+
* Per-field `after − before`, computed only for the numeric CDP counters and
|
|
83
|
+
* only where both samples carry the field, so a counter can appear in
|
|
84
|
+
* {@link after} and be absent here. Positive means growth over the scenario,
|
|
85
|
+
* which on `nodes` or `jsEventListeners` is the leak signal the two-point
|
|
86
|
+
* capture exists for.
|
|
87
|
+
*/
|
|
88
|
+
delta?: BrowserRuntimeSample;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** One main-thread timeline entry — a `longtask` (jank) or a user-timing `measure` span. */
|
|
92
|
+
export interface BrowserTimelineEntry {
|
|
93
|
+
/**
|
|
94
|
+
* The runtime's own label — an attribution name for a long task, the app's
|
|
95
|
+
* mark name for a user-timing span. Not unique: one name recurs once per
|
|
96
|
+
* occurrence.
|
|
97
|
+
*/
|
|
98
|
+
name: string;
|
|
99
|
+
/**
|
|
100
|
+
* The `PerformanceEntry` type. Only `longtask` and `measure` are observed,
|
|
101
|
+
* and only where the runtime lists them as supported — but the field is left
|
|
102
|
+
* open rather than a union, because it carries whatever the entry reported.
|
|
103
|
+
*/
|
|
104
|
+
entryType: string;
|
|
105
|
+
/**
|
|
106
|
+
* Milliseconds from the PAGE's time origin (the `performance.now` domain).
|
|
107
|
+
* Not wall-clock: it cannot be compared with a server timestamp, with a
|
|
108
|
+
* `Date.now` reading, or with an entry captured from another page.
|
|
109
|
+
*/
|
|
110
|
+
startTimeMs: number;
|
|
111
|
+
/**
|
|
112
|
+
* Milliseconds the entry spanned — main-thread blocking time for a long
|
|
113
|
+
* task, which the platform only reports above 50ms, and the mark-to-mark
|
|
114
|
+
* span for a `measure`.
|
|
115
|
+
*/
|
|
116
|
+
durationMs: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Both browser-origin signals of one capture — the runtime report and the main-thread timeline. */
|
|
120
|
+
export interface BrowserStateReport {
|
|
121
|
+
/**
|
|
122
|
+
* The page-side single-point read taken at capture time. A caller holding a
|
|
123
|
+
* CDP session is expected to supersede it with the richer two-point report
|
|
124
|
+
* before writing `browser-runtime.json`, so this field is the floor rather
|
|
125
|
+
* than the final artefact.
|
|
126
|
+
*/
|
|
127
|
+
runtime: BrowserRuntimeReport;
|
|
128
|
+
/**
|
|
129
|
+
* Entries in observer-delivery order, covering the span between starting the
|
|
130
|
+
* recorder and this capture. Empty carries two meanings this shape cannot
|
|
131
|
+
* separate — nothing janked, or the recorder was never started and the
|
|
132
|
+
* runtime supports neither entry type.
|
|
133
|
+
*/
|
|
134
|
+
timeline: BrowserTimelineEntry[];
|
|
135
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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 { Emitter, type Event } from 'vscode-jsonrpc';
|
|
11
|
+
import type { DataClientProtocol, ProjectsChangedEvent, TransferDocumentSavedEvent, TransferDocumentUpdatedEvent } from '../data';
|
|
12
|
+
import type { Project } from '../project';
|
|
13
|
+
import type { TransferDiagnostic } from '../transfer-diagnostic';
|
|
14
|
+
import type { TransferElement } from '../transfer-element';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The inbound half of a data connection, fanned out to local listeners.
|
|
18
|
+
*
|
|
19
|
+
* A connection has exactly one `DataClientProtocol` bound to it, so whoever
|
|
20
|
+
* that is becomes the only thing able to see server pushes. That does not suit
|
|
21
|
+
* a host with more than one interested party — a properties panel, a tree, a
|
|
22
|
+
* status contribution — so this sits in the slot instead and re-emits.
|
|
23
|
+
*
|
|
24
|
+
* The `on*` methods are the WIRE names, matched against
|
|
25
|
+
* `DATA_CLIENT_PROTOCOL_METHODS`; the `onDid*` events are the local ones.
|
|
26
|
+
* Keeping the two sets of names distinct is what makes it obvious which side of
|
|
27
|
+
* the boundary a given member belongs to.
|
|
28
|
+
*
|
|
29
|
+
* **Relationship to `@hydranium/data-client-theia`'s `EmitterDataClient`: two
|
|
30
|
+
* host flavours of one idea, not a duplicate to be collapsed.** The difference
|
|
31
|
+
* is the event type: `EmitterDataClient` fans out to a `@theia/core` `Event`,
|
|
32
|
+
* which is what a Theia frontend's `@injectable()` client wants, while this one
|
|
33
|
+
* uses vscode-jsonrpc's and is therefore usable from a webview or a plain
|
|
34
|
+
* browser client too. Pick by host; neither is deprecated.
|
|
35
|
+
*/
|
|
36
|
+
export class DataEvents<
|
|
37
|
+
TTransfer extends TransferElement,
|
|
38
|
+
TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
|
|
39
|
+
TProject extends Project = Project
|
|
40
|
+
> implements DataClientProtocol<TTransfer, TDiagnostic, TProject> {
|
|
41
|
+
protected readonly documentUpdatedEmitter = new Emitter<TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>>();
|
|
42
|
+
protected readonly documentSavedEmitter = new Emitter<TransferDocumentSavedEvent<TTransfer, TDiagnostic>>();
|
|
43
|
+
protected readonly projectsChangedEmitter = new Emitter<ProjectsChangedEvent<TProject>>();
|
|
44
|
+
|
|
45
|
+
/** A build-phase event for a watched document. Carries the originating `sourceClientId`. */
|
|
46
|
+
readonly onDidUpdateDocument: Event<TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>> = this.documentUpdatedEmitter.event;
|
|
47
|
+
/** A watched document was persisted to disk. */
|
|
48
|
+
readonly onDidSaveDocument: Event<TransferDocumentSavedEvent<TTransfer, TDiagnostic>> = this.documentSavedEmitter.event;
|
|
49
|
+
/** The project set changed. */
|
|
50
|
+
readonly onDidChangeProjects: Event<ProjectsChangedEvent<TProject>> = this.projectsChangedEmitter.event;
|
|
51
|
+
|
|
52
|
+
// --- DataClientProtocol (wire names) ---
|
|
53
|
+
|
|
54
|
+
onDocumentUpdated(event: TransferDocumentUpdatedEvent<TTransfer, TDiagnostic>): void {
|
|
55
|
+
this.documentUpdatedEmitter.fire(event);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onDocumentSaved(event: TransferDocumentSavedEvent<TTransfer, TDiagnostic>): void {
|
|
59
|
+
this.documentSavedEmitter.fire(event);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onProjectsChanged(event: ProjectsChangedEvent<TProject>): void {
|
|
63
|
+
this.projectsChangedEmitter.fire(event);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
dispose(): void {
|
|
67
|
+
this.documentUpdatedEmitter.dispose();
|
|
68
|
+
this.documentSavedEmitter.dispose();
|
|
69
|
+
this.projectsChangedEmitter.dispose();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
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 { Event, MessageConnection } from 'vscode-jsonrpc';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The one thing a host has to supply for the data head: a live JSON-RPC
|
|
14
|
+
* connection to the data server, plus the identity and failure sink that go
|
|
15
|
+
* with it.
|
|
16
|
+
*
|
|
17
|
+
* "Port" in the hexagonal sense — the host implements it, `DataSession`
|
|
18
|
+
* consumes it, and nothing on either side of the boundary imports the other.
|
|
19
|
+
*
|
|
20
|
+
* **This deliberately does NOT wrap the protocol methods.** `createRpcProxy`
|
|
21
|
+
* already takes a promise of a connection and produces the whole typed
|
|
22
|
+
* `DataServerProtocol` surface, so wrapping it would re-derive the framework's
|
|
23
|
+
* pass-throughs in a second place and lose the `as const satisfies keyof`
|
|
24
|
+
* method allowlists, which cannot drift. Everything a form or a tree actually
|
|
25
|
+
* does — the wire contract, the open/watch/update/close sequence, `baseVersion`
|
|
26
|
+
* conflict handling, echo filtering by `sourceClientId` — is host-invariant and
|
|
27
|
+
* lives above this interface. What varies between hosts is exactly the four
|
|
28
|
+
* members below.
|
|
29
|
+
*
|
|
30
|
+
* **Why the transport hop and not merely the protocol.** In a Theia frontend
|
|
31
|
+
* the client *is* the RPC endpoint and holds a `MessageConnection` directly. In
|
|
32
|
+
* a VS Code webview the client is a browser sandbox with no `net`, no reach into
|
|
33
|
+
* the extension host's connection, and only structured-clone `postMessage`. So
|
|
34
|
+
* the port abstracts *establishing* the connection, which lets a webview
|
|
35
|
+
* implementation ride the extension↔webview hop (see
|
|
36
|
+
* `createPostMessageTransport`) while a Theia one opens a channel — and
|
|
37
|
+
* lets both hand back the same `MessageConnection` the RPC machinery expects.
|
|
38
|
+
*
|
|
39
|
+
* Two constraints on an implementation:
|
|
40
|
+
*
|
|
41
|
+
* - **A webview implementation must import `vscode-jsonrpc/browser`**, not the
|
|
42
|
+
* package root. vscode-jsonrpc 9's root ships no runtime abstraction layer
|
|
43
|
+
* and throws `No runtime abstraction layer installed` on the first message.
|
|
44
|
+
* - **Return a connection that is already `listen()`ing.** The proxy queues
|
|
45
|
+
* calls on the promise but never calls `listen` itself.
|
|
46
|
+
*/
|
|
47
|
+
export interface DataPort {
|
|
48
|
+
/**
|
|
49
|
+
* Stable identity of this client on the data server, passed as `clientId`
|
|
50
|
+
* on every document request.
|
|
51
|
+
*
|
|
52
|
+
* It has to be stable for the session because it is the echo key: an
|
|
53
|
+
* inbound `onDocumentUpdated` carries the originating mutation's
|
|
54
|
+
* `clientId` as `sourceClientId`, and a client that cannot recognise its
|
|
55
|
+
* own echo treats its own write as a concurrent third-party one. It also
|
|
56
|
+
* has to be distinct per client, since it keys the server's per-
|
|
57
|
+
* `(uri, clientId)` watch bucket.
|
|
58
|
+
*
|
|
59
|
+
* Avoid the three values the framework itself uses as sentinels —
|
|
60
|
+
* `'language-client'`, `'unknown'` and `'revert-on-close'`.
|
|
61
|
+
*/
|
|
62
|
+
readonly clientId: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Open the transport and hand back a listening `MessageConnection`.
|
|
66
|
+
*
|
|
67
|
+
* Called at most once per connection generation. A rejection is a
|
|
68
|
+
* transport-construction failure and is reported through
|
|
69
|
+
* {@link reportError} by the consumer; it must leave the port reusable, so
|
|
70
|
+
* that a later generation can retry.
|
|
71
|
+
*/
|
|
72
|
+
connect(): Promise<MessageConnection>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Surface a failure to the user in whatever way the host does that — a
|
|
76
|
+
* notification, an output channel, a status entry.
|
|
77
|
+
*
|
|
78
|
+
* It exists because the alternative is worse in both directions: this tier
|
|
79
|
+
* cannot import a host's UI, and swallowing the error makes a dead
|
|
80
|
+
* connection look like an empty model. `context` names what was being
|
|
81
|
+
* attempted, not where in the code it happened.
|
|
82
|
+
*/
|
|
83
|
+
reportError(error: unknown, context: string): void;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Fires when the host tears the transport down and the current connection
|
|
87
|
+
* is no longer usable — a language-server restart being the case that
|
|
88
|
+
* forces this to exist, since a restarted server binds new ephemeral ports
|
|
89
|
+
* and nothing re-discovers them.
|
|
90
|
+
*
|
|
91
|
+
* `DataSession` drops its connection generation on this event and
|
|
92
|
+
* builds a fresh one on the next request.
|
|
93
|
+
*
|
|
94
|
+
* A local `Event`, deliberately: nothing here crosses a structured-clone
|
|
95
|
+
* boundary, so the `Disposable` an `Event` hands back is safe even though a
|
|
96
|
+
* `Disposable` on the *wire* would not be.
|
|
97
|
+
*/
|
|
98
|
+
readonly onDispose: Event<void>;
|
|
99
|
+
}
|