@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.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/lib/abstract-logger.d.ts +66 -0
  4. package/lib/abstract-logger.d.ts.map +1 -0
  5. package/lib/abstract-logger.js +110 -0
  6. package/lib/abstract-logger.js.map +1 -0
  7. package/lib/browser-runtime.d.ts +124 -0
  8. package/lib/browser-runtime.d.ts.map +1 -0
  9. package/lib/browser-runtime.js +11 -0
  10. package/lib/browser-runtime.js.map +1 -0
  11. package/lib/client/data-events.d.ts +49 -0
  12. package/lib/client/data-events.d.ts.map +1 -0
  13. package/lib/client/data-events.js +60 -0
  14. package/lib/client/data-events.js.map +1 -0
  15. package/lib/client/data-port.d.ts +95 -0
  16. package/lib/client/data-port.d.ts.map +1 -0
  17. package/lib/client/data-port.js +11 -0
  18. package/lib/client/data-port.js.map +1 -0
  19. package/lib/client/data-session.d.ts +129 -0
  20. package/lib/client/data-session.d.ts.map +1 -0
  21. package/lib/client/data-session.js +179 -0
  22. package/lib/client/data-session.js.map +1 -0
  23. package/lib/client/index.d.ts +39 -0
  24. package/lib/client/index.d.ts.map +1 -0
  25. package/lib/client/index.js +55 -0
  26. package/lib/client/index.js.map +1 -0
  27. package/lib/client/message-relay.d.ts +111 -0
  28. package/lib/client/message-relay.d.ts.map +1 -0
  29. package/lib/client/message-relay.js +139 -0
  30. package/lib/client/message-relay.js.map +1 -0
  31. package/lib/client/post-message-transport.d.ts +77 -0
  32. package/lib/client/post-message-transport.d.ts.map +1 -0
  33. package/lib/client/post-message-transport.js +95 -0
  34. package/lib/client/post-message-transport.js.map +1 -0
  35. package/lib/clock.d.ts +111 -0
  36. package/lib/clock.d.ts.map +1 -0
  37. package/lib/clock.js +74 -0
  38. package/lib/clock.js.map +1 -0
  39. package/lib/data/data-protocol-methods.d.ts +39 -0
  40. package/lib/data/data-protocol-methods.d.ts.map +1 -0
  41. package/lib/data/data-protocol-methods.js +61 -0
  42. package/lib/data/data-protocol-methods.js.map +1 -0
  43. package/lib/data/data-server-protocol.d.ts +264 -0
  44. package/lib/data/data-server-protocol.d.ts.map +1 -0
  45. package/lib/data/data-server-protocol.js +11 -0
  46. package/lib/data/data-server-protocol.js.map +1 -0
  47. package/lib/data/diagnostics.d.ts +97 -0
  48. package/lib/data/diagnostics.d.ts.map +1 -0
  49. package/lib/data/diagnostics.js +25 -0
  50. package/lib/data/diagnostics.js.map +1 -0
  51. package/lib/data/events.d.ts +107 -0
  52. package/lib/data/events.d.ts.map +1 -0
  53. package/lib/data/events.js +11 -0
  54. package/lib/data/events.js.map +1 -0
  55. package/lib/data/index.d.ts +15 -0
  56. package/lib/data/index.d.ts.map +1 -0
  57. package/lib/data/index.js +38 -0
  58. package/lib/data/index.js.map +1 -0
  59. package/lib/data/methods.d.ts +57 -0
  60. package/lib/data/methods.d.ts.map +1 -0
  61. package/lib/data/methods.js +60 -0
  62. package/lib/data/methods.js.map +1 -0
  63. package/lib/data/requests.d.ts +65 -0
  64. package/lib/data/requests.d.ts.map +1 -0
  65. package/lib/data/requests.js +11 -0
  66. package/lib/data/requests.js.map +1 -0
  67. package/lib/debouncer.d.ts +75 -0
  68. package/lib/debouncer.d.ts.map +1 -0
  69. package/lib/debouncer.js +91 -0
  70. package/lib/debouncer.js.map +1 -0
  71. package/lib/errors.d.ts +84 -0
  72. package/lib/errors.d.ts.map +1 -0
  73. package/lib/errors.js +113 -0
  74. package/lib/errors.js.map +1 -0
  75. package/lib/host-diagnostics.d.ts +58 -0
  76. package/lib/host-diagnostics.d.ts.map +1 -0
  77. package/lib/host-diagnostics.js +18 -0
  78. package/lib/host-diagnostics.js.map +1 -0
  79. package/lib/index.d.ts +34 -0
  80. package/lib/index.d.ts.map +1 -0
  81. package/lib/index.js +56 -0
  82. package/lib/index.js.map +1 -0
  83. package/lib/latency-collector.d.ts +126 -0
  84. package/lib/latency-collector.d.ts.map +1 -0
  85. package/lib/latency-collector.js +137 -0
  86. package/lib/latency-collector.js.map +1 -0
  87. package/lib/logger.d.ts +119 -0
  88. package/lib/logger.d.ts.map +1 -0
  89. package/lib/logger.js +126 -0
  90. package/lib/logger.js.map +1 -0
  91. package/lib/model-server.d.ts +352 -0
  92. package/lib/model-server.d.ts.map +1 -0
  93. package/lib/model-server.js +160 -0
  94. package/lib/model-server.js.map +1 -0
  95. package/lib/model-service/args.d.ts +64 -0
  96. package/lib/model-service/args.d.ts.map +1 -0
  97. package/lib/model-service/args.js +11 -0
  98. package/lib/model-service/args.js.map +1 -0
  99. package/lib/model-service/index.d.ts +11 -0
  100. package/lib/model-service/index.d.ts.map +1 -0
  101. package/lib/model-service/index.js +31 -0
  102. package/lib/model-service/index.js.map +1 -0
  103. package/lib/model-service/reference-candidate.d.ts +56 -0
  104. package/lib/model-service/reference-candidate.d.ts.map +1 -0
  105. package/lib/model-service/reference-candidate.js +11 -0
  106. package/lib/model-service/reference-candidate.js.map +1 -0
  107. package/lib/noop-logger.d.ts +25 -0
  108. package/lib/noop-logger.d.ts.map +1 -0
  109. package/lib/noop-logger.js +36 -0
  110. package/lib/noop-logger.js.map +1 -0
  111. package/lib/observable-value.d.ts +72 -0
  112. package/lib/observable-value.d.ts.map +1 -0
  113. package/lib/observable-value.js +52 -0
  114. package/lib/observable-value.js.map +1 -0
  115. package/lib/patch-merge.d.ts +123 -0
  116. package/lib/patch-merge.d.ts.map +1 -0
  117. package/lib/patch-merge.js +104 -0
  118. package/lib/patch-merge.js.map +1 -0
  119. package/lib/profile-session.d.ts +87 -0
  120. package/lib/profile-session.d.ts.map +1 -0
  121. package/lib/profile-session.js +98 -0
  122. package/lib/profile-session.js.map +1 -0
  123. package/lib/profiling.d.ts +83 -0
  124. package/lib/profiling.d.ts.map +1 -0
  125. package/lib/profiling.js +20 -0
  126. package/lib/profiling.js.map +1 -0
  127. package/lib/project.d.ts +121 -0
  128. package/lib/project.d.ts.map +1 -0
  129. package/lib/project.js +34 -0
  130. package/lib/project.js.map +1 -0
  131. package/lib/rpc/bind-rpc-methods.d.ts +100 -0
  132. package/lib/rpc/bind-rpc-methods.d.ts.map +1 -0
  133. package/lib/rpc/bind-rpc-methods.js +110 -0
  134. package/lib/rpc/bind-rpc-methods.js.map +1 -0
  135. package/lib/rpc/create-rpc-proxy.d.ts +188 -0
  136. package/lib/rpc/create-rpc-proxy.d.ts.map +1 -0
  137. package/lib/rpc/create-rpc-proxy.js +158 -0
  138. package/lib/rpc/create-rpc-proxy.js.map +1 -0
  139. package/lib/rpc/index.d.ts +12 -0
  140. package/lib/rpc/index.d.ts.map +1 -0
  141. package/lib/rpc/index.js +31 -0
  142. package/lib/rpc/index.js.map +1 -0
  143. package/lib/rpc/wire-prefix.d.ts +37 -0
  144. package/lib/rpc/wire-prefix.d.ts.map +1 -0
  145. package/lib/rpc/wire-prefix.js +49 -0
  146. package/lib/rpc/wire-prefix.js.map +1 -0
  147. package/lib/testing/data-doubles.d.ts +121 -0
  148. package/lib/testing/data-doubles.d.ts.map +1 -0
  149. package/lib/testing/data-doubles.js +102 -0
  150. package/lib/testing/data-doubles.js.map +1 -0
  151. package/lib/testing/fake-clock.d.ts +35 -0
  152. package/lib/testing/fake-clock.d.ts.map +1 -0
  153. package/lib/testing/fake-clock.js +86 -0
  154. package/lib/testing/fake-clock.js.map +1 -0
  155. package/lib/testing/harness.d.ts +24 -0
  156. package/lib/testing/harness.d.ts.map +1 -0
  157. package/lib/testing/harness.js +11 -0
  158. package/lib/testing/harness.js.map +1 -0
  159. package/lib/testing/index.d.ts +13 -0
  160. package/lib/testing/index.d.ts.map +1 -0
  161. package/lib/testing/index.js +41 -0
  162. package/lib/testing/index.js.map +1 -0
  163. package/lib/testing/node/duplex-connection.d.ts +25 -0
  164. package/lib/testing/node/duplex-connection.d.ts.map +1 -0
  165. package/lib/testing/node/duplex-connection.js +34 -0
  166. package/lib/testing/node/duplex-connection.js.map +1 -0
  167. package/lib/testing/node/duplex-stream.d.ts +32 -0
  168. package/lib/testing/node/duplex-stream.d.ts.map +1 -0
  169. package/lib/testing/node/duplex-stream.js +35 -0
  170. package/lib/testing/node/duplex-stream.js.map +1 -0
  171. package/lib/testing/node/index.d.ts +11 -0
  172. package/lib/testing/node/index.d.ts.map +1 -0
  173. package/lib/testing/node/index.js +37 -0
  174. package/lib/testing/node/index.js.map +1 -0
  175. package/lib/testing/wait-for.d.ts +69 -0
  176. package/lib/testing/wait-for.d.ts.map +1 -0
  177. package/lib/testing/wait-for.js +67 -0
  178. package/lib/testing/wait-for.js.map +1 -0
  179. package/lib/tracer.d.ts +115 -0
  180. package/lib/tracer.d.ts.map +1 -0
  181. package/lib/tracer.js +205 -0
  182. package/lib/tracer.js.map +1 -0
  183. package/lib/transfer-diagnostic.d.ts +73 -0
  184. package/lib/transfer-diagnostic.d.ts.map +1 -0
  185. package/lib/transfer-diagnostic.js +45 -0
  186. package/lib/transfer-diagnostic.js.map +1 -0
  187. package/lib/transfer-document.d.ts +73 -0
  188. package/lib/transfer-document.d.ts.map +1 -0
  189. package/lib/transfer-document.js +46 -0
  190. package/lib/transfer-document.js.map +1 -0
  191. package/lib/transfer-element.d.ts +39 -0
  192. package/lib/transfer-element.d.ts.map +1 -0
  193. package/lib/transfer-element.js +11 -0
  194. package/lib/transfer-element.js.map +1 -0
  195. package/lib/uri.d.ts +62 -0
  196. package/lib/uri.d.ts.map +1 -0
  197. package/lib/uri.js +28 -0
  198. package/lib/uri.js.map +1 -0
  199. package/lib/util.d.ts +147 -0
  200. package/lib/util.d.ts.map +1 -0
  201. package/lib/util.js +211 -0
  202. package/lib/util.js.map +1 -0
  203. package/package.json +100 -0
  204. package/src/abstract-logger.ts +120 -0
  205. package/src/browser-runtime.ts +135 -0
  206. package/src/client/data-events.ts +71 -0
  207. package/src/client/data-port.ts +99 -0
  208. package/src/client/data-session.ts +209 -0
  209. package/src/client/index.ts +40 -0
  210. package/src/client/message-relay.ts +216 -0
  211. package/src/client/post-message-transport.ts +167 -0
  212. package/src/clock.ts +173 -0
  213. package/src/data/data-protocol-methods.ts +79 -0
  214. package/src/data/data-server-protocol.ts +296 -0
  215. package/src/data/diagnostics.ts +116 -0
  216. package/src/data/events.ts +127 -0
  217. package/src/data/index.ts +23 -0
  218. package/src/data/methods.ts +59 -0
  219. package/src/data/requests.ts +70 -0
  220. package/src/debouncer.ts +118 -0
  221. package/src/errors.ts +127 -0
  222. package/src/host-diagnostics.ts +63 -0
  223. package/src/index.ts +41 -0
  224. package/src/latency-collector.ts +231 -0
  225. package/src/logger.ts +172 -0
  226. package/src/model-server.ts +479 -0
  227. package/src/model-service/args.ts +67 -0
  228. package/src/model-service/index.ts +16 -0
  229. package/src/model-service/reference-candidate.ts +58 -0
  230. package/src/noop-logger.ts +36 -0
  231. package/src/observable-value.ts +92 -0
  232. package/src/patch-merge.ts +165 -0
  233. package/src/profile-session.ts +164 -0
  234. package/src/profiling.ts +113 -0
  235. package/src/project.ts +125 -0
  236. package/src/rpc/README.md +163 -0
  237. package/src/rpc/bind-rpc-methods.ts +180 -0
  238. package/src/rpc/create-rpc-proxy.ts +292 -0
  239. package/src/rpc/index.ts +16 -0
  240. package/src/rpc/wire-prefix.ts +48 -0
  241. package/src/testing/data-doubles.ts +178 -0
  242. package/src/testing/fake-clock.ts +106 -0
  243. package/src/testing/harness.ts +24 -0
  244. package/src/testing/index.ts +26 -0
  245. package/src/testing/node/duplex-connection.ts +50 -0
  246. package/src/testing/node/duplex-stream.ts +51 -0
  247. package/src/testing/node/index.ts +22 -0
  248. package/src/testing/wait-for.ts +94 -0
  249. package/src/tracer.ts +277 -0
  250. package/src/transfer-diagnostic.ts +92 -0
  251. package/src/transfer-document.ts +99 -0
  252. package/src/transfer-element.ts +43 -0
  253. package/src/uri.ts +61 -0
  254. 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
+ }