@hydranium/glsp-server 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 (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/lib/browser/index.d.ts +10 -0
  4. package/lib/browser/index.d.ts.map +1 -0
  5. package/lib/browser/index.js +15 -0
  6. package/lib/browser/index.js.map +1 -0
  7. package/lib/browser/start-glsp-server-in-worker.d.ts +121 -0
  8. package/lib/browser/start-glsp-server-in-worker.d.ts.map +1 -0
  9. package/lib/browser/start-glsp-server-in-worker.js +102 -0
  10. package/lib/browser/start-glsp-server-in-worker.js.map +1 -0
  11. package/lib/command/hydranium-glsp-recording-command.d.ts +132 -0
  12. package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -0
  13. package/lib/command/hydranium-glsp-recording-command.js +159 -0
  14. package/lib/command/hydranium-glsp-recording-command.js.map +1 -0
  15. package/lib/command/index.d.ts +10 -0
  16. package/lib/command/index.d.ts.map +1 -0
  17. package/lib/command/index.js +10 -0
  18. package/lib/command/index.js.map +1 -0
  19. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts +61 -0
  20. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts.map +1 -0
  21. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js +93 -0
  22. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js.map +1 -0
  23. package/lib/computed-bounds/index.d.ts +10 -0
  24. package/lib/computed-bounds/index.d.ts.map +1 -0
  25. package/lib/computed-bounds/index.js +10 -0
  26. package/lib/computed-bounds/index.js.map +1 -0
  27. package/lib/dispatcher/index.d.ts +10 -0
  28. package/lib/dispatcher/index.d.ts.map +1 -0
  29. package/lib/dispatcher/index.js +10 -0
  30. package/lib/dispatcher/index.js.map +1 -0
  31. package/lib/dispatcher/server-action-dispatcher.d.ts +52 -0
  32. package/lib/dispatcher/server-action-dispatcher.d.ts.map +1 -0
  33. package/lib/dispatcher/server-action-dispatcher.js +79 -0
  34. package/lib/dispatcher/server-action-dispatcher.js.map +1 -0
  35. package/lib/index.d.ts +19 -0
  36. package/lib/index.d.ts.map +1 -0
  37. package/lib/index.js +20 -0
  38. package/lib/index.js.map +1 -0
  39. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts +75 -0
  40. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts.map +1 -0
  41. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js +100 -0
  42. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js.map +1 -0
  43. package/lib/launcher/glsp-app-module.d.ts +87 -0
  44. package/lib/launcher/glsp-app-module.d.ts.map +1 -0
  45. package/lib/launcher/glsp-app-module.js +71 -0
  46. package/lib/launcher/glsp-app-module.js.map +1 -0
  47. package/lib/launcher/glsp-framework-overrides.d.ts +32 -0
  48. package/lib/launcher/glsp-framework-overrides.d.ts.map +1 -0
  49. package/lib/launcher/glsp-framework-overrides.js +49 -0
  50. package/lib/launcher/glsp-framework-overrides.js.map +1 -0
  51. package/lib/launcher/glsp-server-overrides.d.ts +39 -0
  52. package/lib/launcher/glsp-server-overrides.d.ts.map +1 -0
  53. package/lib/launcher/glsp-server-overrides.js +45 -0
  54. package/lib/launcher/glsp-server-overrides.js.map +1 -0
  55. package/lib/launcher/hydranium-glsp-server.d.ts +62 -0
  56. package/lib/launcher/hydranium-glsp-server.d.ts.map +1 -0
  57. package/lib/launcher/hydranium-glsp-server.js +101 -0
  58. package/lib/launcher/hydranium-glsp-server.js.map +1 -0
  59. package/lib/launcher/index.d.ts +12 -0
  60. package/lib/launcher/index.d.ts.map +1 -0
  61. package/lib/launcher/index.js +22 -0
  62. package/lib/launcher/index.js.map +1 -0
  63. package/lib/logging/glsp-client-logger.d.ts +110 -0
  64. package/lib/logging/glsp-client-logger.d.ts.map +1 -0
  65. package/lib/logging/glsp-client-logger.js +151 -0
  66. package/lib/logging/glsp-client-logger.js.map +1 -0
  67. package/lib/logging/index.d.ts +10 -0
  68. package/lib/logging/index.d.ts.map +1 -0
  69. package/lib/logging/index.js +10 -0
  70. package/lib/logging/index.js.map +1 -0
  71. package/lib/node/index.d.ts +10 -0
  72. package/lib/node/index.d.ts.map +1 -0
  73. package/lib/node/index.js +13 -0
  74. package/lib/node/index.js.map +1 -0
  75. package/lib/node/start-glsp-server.d.ts +122 -0
  76. package/lib/node/start-glsp-server.d.ts.map +1 -0
  77. package/lib/node/start-glsp-server.js +103 -0
  78. package/lib/node/start-glsp-server.js.map +1 -0
  79. package/lib/state/abstract-hydranium-glsp-state.d.ts +333 -0
  80. package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -0
  81. package/lib/state/abstract-hydranium-glsp-state.js +451 -0
  82. package/lib/state/abstract-hydranium-glsp-state.js.map +1 -0
  83. package/lib/state/full-text-hydranium-glsp-state.d.ts +43 -0
  84. package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -0
  85. package/lib/state/full-text-hydranium-glsp-state.js +60 -0
  86. package/lib/state/full-text-hydranium-glsp-state.js.map +1 -0
  87. package/lib/state/hydranium-glsp-index.d.ts +220 -0
  88. package/lib/state/hydranium-glsp-index.d.ts.map +1 -0
  89. package/lib/state/hydranium-glsp-index.js +320 -0
  90. package/lib/state/hydranium-glsp-index.js.map +1 -0
  91. package/lib/state/hydranium-shared-core-services.d.ts +90 -0
  92. package/lib/state/hydranium-shared-core-services.d.ts.map +1 -0
  93. package/lib/state/hydranium-shared-core-services.js +89 -0
  94. package/lib/state/hydranium-shared-core-services.js.map +1 -0
  95. package/lib/state/index.d.ts +17 -0
  96. package/lib/state/index.d.ts.map +1 -0
  97. package/lib/state/index.js +17 -0
  98. package/lib/state/index.js.map +1 -0
  99. package/lib/state/model-ready-timeout-error.d.ts +28 -0
  100. package/lib/state/model-ready-timeout-error.d.ts.map +1 -0
  101. package/lib/state/model-ready-timeout-error.js +36 -0
  102. package/lib/state/model-ready-timeout-error.js.map +1 -0
  103. package/lib/state/reconcile-source-model-write.d.ts +43 -0
  104. package/lib/state/reconcile-source-model-write.d.ts.map +1 -0
  105. package/lib/state/reconcile-source-model-write.js +47 -0
  106. package/lib/state/reconcile-source-model-write.js.map +1 -0
  107. package/lib/state/reconciling-multi-document-glsp-state.d.ts +164 -0
  108. package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -0
  109. package/lib/state/reconciling-multi-document-glsp-state.js +222 -0
  110. package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -0
  111. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +91 -0
  112. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -0
  113. package/lib/state/reconciling-transfer-hydranium-glsp-state.js +129 -0
  114. package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -0
  115. package/lib/storage/hydranium-glsp-storage.d.ts +348 -0
  116. package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -0
  117. package/lib/storage/hydranium-glsp-storage.js +634 -0
  118. package/lib/storage/hydranium-glsp-storage.js.map +1 -0
  119. package/lib/storage/index.d.ts +11 -0
  120. package/lib/storage/index.d.ts.map +1 -0
  121. package/lib/storage/index.js +11 -0
  122. package/lib/storage/index.js.map +1 -0
  123. package/lib/storage/save-conflict-policy.d.ts +56 -0
  124. package/lib/storage/save-conflict-policy.d.ts.map +1 -0
  125. package/lib/storage/save-conflict-policy.js +19 -0
  126. package/lib/storage/save-conflict-policy.js.map +1 -0
  127. package/lib/submission/hydranium-glsp-submission-handler.d.ts +109 -0
  128. package/lib/submission/hydranium-glsp-submission-handler.d.ts.map +1 -0
  129. package/lib/submission/hydranium-glsp-submission-handler.js +157 -0
  130. package/lib/submission/hydranium-glsp-submission-handler.js.map +1 -0
  131. package/lib/submission/index.d.ts +10 -0
  132. package/lib/submission/index.d.ts.map +1 -0
  133. package/lib/submission/index.js +10 -0
  134. package/lib/submission/index.js.map +1 -0
  135. package/lib/testing/glsp-harness.d.ts +170 -0
  136. package/lib/testing/glsp-harness.d.ts.map +1 -0
  137. package/lib/testing/glsp-harness.js +231 -0
  138. package/lib/testing/glsp-harness.js.map +1 -0
  139. package/lib/testing/index.d.ts +11 -0
  140. package/lib/testing/index.d.ts.map +1 -0
  141. package/lib/testing/index.js +13 -0
  142. package/lib/testing/index.js.map +1 -0
  143. package/lib/testing/make-noop-glsp-logger.d.ts +42 -0
  144. package/lib/testing/make-noop-glsp-logger.d.ts.map +1 -0
  145. package/lib/testing/make-noop-glsp-logger.js +47 -0
  146. package/lib/testing/make-noop-glsp-logger.js.map +1 -0
  147. package/lib/util/index.d.ts +10 -0
  148. package/lib/util/index.d.ts.map +1 -0
  149. package/lib/util/index.js +10 -0
  150. package/lib/util/index.js.map +1 -0
  151. package/lib/util/service-identifier.d.ts +46 -0
  152. package/lib/util/service-identifier.d.ts.map +1 -0
  153. package/lib/util/service-identifier.js +47 -0
  154. package/lib/util/service-identifier.js.map +1 -0
  155. package/lib/validation/diagnostic-markers.d.ts +48 -0
  156. package/lib/validation/diagnostic-markers.d.ts.map +1 -0
  157. package/lib/validation/diagnostic-markers.js +68 -0
  158. package/lib/validation/diagnostic-markers.js.map +1 -0
  159. package/lib/validation/hydranium-glsp-model-validator.d.ts +51 -0
  160. package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -0
  161. package/lib/validation/hydranium-glsp-model-validator.js +118 -0
  162. package/lib/validation/hydranium-glsp-model-validator.js.map +1 -0
  163. package/lib/validation/index.d.ts +11 -0
  164. package/lib/validation/index.d.ts.map +1 -0
  165. package/lib/validation/index.js +11 -0
  166. package/lib/validation/index.js.map +1 -0
  167. package/package.json +111 -0
  168. package/src/browser/index.ts +15 -0
  169. package/src/browser/start-glsp-server-in-worker.ts +202 -0
  170. package/src/command/hydranium-glsp-recording-command.ts +183 -0
  171. package/src/command/index.ts +10 -0
  172. package/src/computed-bounds/hydranium-glsp-computed-bounds-action-handler.ts +103 -0
  173. package/src/computed-bounds/index.ts +10 -0
  174. package/src/dispatcher/index.ts +10 -0
  175. package/src/dispatcher/server-action-dispatcher.ts +72 -0
  176. package/src/index.ts +20 -0
  177. package/src/launcher/abstract-hydranium-glsp-diagram-module.ts +117 -0
  178. package/src/launcher/glsp-app-module.ts +106 -0
  179. package/src/launcher/glsp-framework-overrides.ts +51 -0
  180. package/src/launcher/glsp-server-overrides.ts +46 -0
  181. package/src/launcher/hydranium-glsp-server.ts +104 -0
  182. package/src/launcher/index.ts +22 -0
  183. package/src/logging/glsp-client-logger.ts +184 -0
  184. package/src/logging/index.ts +10 -0
  185. package/src/node/index.ts +13 -0
  186. package/src/node/start-glsp-server.ts +214 -0
  187. package/src/state/abstract-hydranium-glsp-state.ts +503 -0
  188. package/src/state/full-text-hydranium-glsp-state.ts +63 -0
  189. package/src/state/hydranium-glsp-index.ts +337 -0
  190. package/src/state/hydranium-shared-core-services.ts +92 -0
  191. package/src/state/index.ts +17 -0
  192. package/src/state/model-ready-timeout-error.ts +33 -0
  193. package/src/state/reconcile-source-model-write.ts +77 -0
  194. package/src/state/reconciling-multi-document-glsp-state.ts +248 -0
  195. package/src/state/reconciling-transfer-hydranium-glsp-state.ts +132 -0
  196. package/src/storage/hydranium-glsp-storage.ts +660 -0
  197. package/src/storage/index.ts +11 -0
  198. package/src/storage/save-conflict-policy.ts +52 -0
  199. package/src/submission/hydranium-glsp-submission-handler.ts +170 -0
  200. package/src/submission/index.ts +10 -0
  201. package/src/testing/glsp-harness.ts +422 -0
  202. package/src/testing/index.ts +14 -0
  203. package/src/testing/make-noop-glsp-logger.ts +66 -0
  204. package/src/util/index.ts +10 -0
  205. package/src/util/service-identifier.ts +49 -0
  206. package/src/validation/diagnostic-markers.ts +93 -0
  207. package/src/validation/hydranium-glsp-model-validator.ts +105 -0
  208. package/src/validation/index.ts +11 -0
@@ -0,0 +1,51 @@
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 { Logger as GlspLogger, LoggerFactory, getRequestParentName } from '@eclipse-glsp/server';
11
+ import { ContainerModule } from 'inversify';
12
+ import type { ServerSharedServices } from '@hydranium/core';
13
+ import { HydraniumTypes } from '../state/hydranium-shared-core-services.js';
14
+
15
+ /**
16
+ * The framework bindings layered on top of GLSP's own app module, shared by
17
+ * every bringup regardless of transport.
18
+ *
19
+ * Two things happen here. GLSP's `Logger` / `LoggerFactory` are replaced with
20
+ * the adopter's — unbind-then-bind, the same pattern `createAppModule` uses
21
+ * internally — so GLSP server logs leave through the adopter's sink rather than
22
+ * a console the host may not own. And `HydraniumTypes.Tracer` is added,
23
+ * caller-tagged the same way the logger is, so an injected tracer's timing lines
24
+ * carry the requesting class's component without every call site naming itself.
25
+ *
26
+ * **Shared rather than written once per launcher, because the two would drift
27
+ * silently.** A transport-specific copy that fell behind would still bind
28
+ * something for each symbol, so nothing would fail — the head would merely log
29
+ * or trace differently depending on how it was started, which no test asks
30
+ * about.
31
+ *
32
+ * `createLogger` is called once per Inversify resolution, with `caller` set to
33
+ * the requesting parent's class name, and once per `LoggerFactory` invocation.
34
+ */
35
+ export function createGlspFrameworkOverrides(createLogger: (caller?: string) => GlspLogger): ContainerModule {
36
+ return new ContainerModule((bind, unbind, isBound) => {
37
+ if (isBound(GlspLogger)) {
38
+ unbind(GlspLogger);
39
+ }
40
+ if (isBound(LoggerFactory)) {
41
+ unbind(LoggerFactory);
42
+ }
43
+ bind(GlspLogger).toDynamicValue(ctx => createLogger(getRequestParentName(ctx)));
44
+ bind(LoggerFactory).toFactory(() => (caller: string) => createLogger(caller));
45
+ bind(HydraniumTypes.Tracer).toDynamicValue(ctx => {
46
+ const tracer = ctx.container.get<ServerSharedServices>(HydraniumTypes.SharedCoreServices).Tracer;
47
+ const caller = getRequestParentName(ctx);
48
+ return caller ? tracer.for(caller) : tracer;
49
+ });
50
+ });
51
+ }
@@ -0,0 +1,46 @@
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 { GLSPServer } from '@eclipse-glsp/server';
11
+ import { ContainerModule } from 'inversify';
12
+ import { HydraniumGlspServer } from './hydranium-glsp-server.js';
13
+
14
+ /**
15
+ * The framework bindings layered on top of the adopter's `ServerModule`, in the
16
+ * SERVER container — one per connected application, a child of the app
17
+ * container every bringup composes.
18
+ *
19
+ * **The tier matters, and the app container cannot stand in for it.** GLSP's
20
+ * launcher creates the server container as a child and loads the adopter's
21
+ * `ServerModule` into it, so a `GLSPServer` binding made further up is shadowed
22
+ * rather than consulted, and inversify refuses to rebind a parent's binding
23
+ * from a child at all. The framework's app-tier overrides therefore cannot
24
+ * reach this symbol.
25
+ *
26
+ * **Load this AFTER the adopter's `ServerModule`, never beside it.** Both bind
27
+ * `GLSPServer`, so a plain second binding resolves to an ambiguous-match error
28
+ * rather than to either one; `rebind` needs the first binding to already be
29
+ * present.
30
+ *
31
+ * Keeping this out of the adopter's hands is the point: an adopter passes their
32
+ * own `ServerModule` in, so anything they must add themselves is a fix the
33
+ * adopters who most need it will not have.
34
+ *
35
+ * **It also decides what GLSP's own log lines are tagged with.** GLSP resolves
36
+ * a logger per injecting class and labels it with the bound implementation's
37
+ * name, so every line upstream's server logs carries
38
+ * {@link HydraniumGlspServer} rather than `DefaultGLSPServer`. Anything keyed
39
+ * on that label — a log filter, an assertion — reads the class bound here, and
40
+ * a further adopter subclass moves it again.
41
+ */
42
+ export function createGlspServerOverrides(): ContainerModule {
43
+ return new ContainerModule((_bind, _unbind, _isBound, rebind) => {
44
+ rebind(GLSPServer).to(HydraniumGlspServer).inSingletonScope();
45
+ });
46
+ }
@@ -0,0 +1,104 @@
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 {
11
+ type ClientSession,
12
+ DefaultGLSPServer,
13
+ GLSPServerError,
14
+ RejectAction,
15
+ type RequestAction,
16
+ type ResponseAction
17
+ } from '@eclipse-glsp/server';
18
+ import { injectable } from 'inversify';
19
+
20
+ /**
21
+ * The GLSP server the framework binds in place of upstream's
22
+ * {@link DefaultGLSPServer}, so a failed REQUEST reaches its reader with the
23
+ * text the thrower wrote.
24
+ *
25
+ * **What breaks without it.** Upstream projects a failing request's detail as
26
+ * `error.cause?.toString()` whenever the error is a {@link GLSPServerError},
27
+ * and that one value feeds both the server log and the client's
28
+ * {@link RejectAction}. A {@link GLSPServerError} carrying no `cause` therefore
29
+ * reaches no reader at all — not the toast, not the details pane, not the log —
30
+ * while a plain `Error` survives, because the other branch reads the error
31
+ * itself. The typed error is the trap, and it is the one upstream's own
32
+ * `getOrThrow` helper raises: that helper's signature cannot pass a `cause`, so
33
+ * an adopter using the sanctioned helper has no fix available on their side.
34
+ * Restating the cause at each throw site is the alternative, and it costs
35
+ * every adopter a rule they must know and cannot apply to `getOrThrow`.
36
+ *
37
+ * Only the REQUEST path needs this. Non-request actions fail through
38
+ * upstream's `handleProcessError`, which reads `message` directly.
39
+ *
40
+ * Adopters who bind their own GLSP server should extend this class rather than
41
+ * {@link DefaultGLSPServer}; the framework's server-container override replaces
42
+ * whatever the adopter's `ServerModule` bound, so a subclass of upstream's
43
+ * server would be discarded.
44
+ */
45
+ @injectable()
46
+ export class HydraniumGlspServer extends DefaultGLSPServer {
47
+ /**
48
+ * The detail projected into the client's {@link RejectAction} and the server
49
+ * log when a request fails.
50
+ *
51
+ * A {@link GLSPServerError} with no `cause` falls back to its `message`; a
52
+ * nullish `cause` counts as absent, matching the optional chaining upstream
53
+ * applies. Anything else stringifies as upstream does, so a plain `Error`
54
+ * keeps its `"Error: …"` prefix and callers that match on it still match.
55
+ */
56
+ protected requestFailureDetail(error: unknown): string | undefined {
57
+ if (error instanceof GLSPServerError) {
58
+ return error.cause === undefined || error.cause === null ? error.message : String(error.cause);
59
+ }
60
+ return error === undefined || error === null ? undefined : String(error);
61
+ }
62
+
63
+ /**
64
+ * Lifted verbatim from `@eclipse-glsp/server` 2.7.0's
65
+ * {@link DefaultGLSPServer.handleClientRequest}, with the detail computation
66
+ * delegated to {@link requestFailureDetail}. Nothing else here — the
67
+ * timeout branch, the response dispatch, the nested send-failure handling —
68
+ * relates to that fallback.
69
+ *
70
+ * **This body does not track upstream.** A release that changes request
71
+ * handling would silently not apply, on the path every request takes. There
72
+ * is no narrower seam to override: upstream computes the detail inline. The
73
+ * accompanying test pins upstream's own source for this method, so a bump
74
+ * that changes it fails rather than diverging quietly; re-lift the body when
75
+ * it does.
76
+ */
77
+ protected override async handleClientRequest(
78
+ clientSession: ClientSession,
79
+ action: RequestAction<ResponseAction>,
80
+ clientId: string
81
+ ): Promise<void> {
82
+ try {
83
+ const response =
84
+ action.timeout !== undefined
85
+ ? await clientSession.actionDispatcher.requestUntil(action, action.timeout, true)
86
+ : await clientSession.actionDispatcher.request(action);
87
+ if (response) {
88
+ this.sendResponseToClient(clientId, response);
89
+ }
90
+ } catch (error: unknown) {
91
+ const detail = this.requestFailureDetail(error);
92
+ this.logger.error(`Failed to handle request '${action.kind}' (${action.requestId}):`, detail);
93
+ try {
94
+ const reject = RejectAction.create(`Failed to handle request '${action.kind}' (${action.requestId})`, {
95
+ responseId: action.requestId,
96
+ detail
97
+ });
98
+ this.sendResponseToClient(clientId, reject);
99
+ } catch (sendError: unknown) {
100
+ this.logger.error(`Failed to send rejection for request '${action.requestId}':`, sendError);
101
+ }
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,22 @@
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
+ export * from './glsp-app-module.js';
11
+ export * from './abstract-hydranium-glsp-diagram-module.js';
12
+ // Exported so an adopter binding their own GLSP server extends the framework's
13
+ // rather than upstream's — the server-container override discards a subclass of
14
+ // `DefaultGLSPServer`.
15
+ export * from './hydranium-glsp-server.js';
16
+ // The socket bringup lives in `@hydranium/glsp-server/node` and the worker
17
+ // bringup in `@hydranium/glsp-server/browser`, not here: each pulls the
18
+ // upstream build for its platform, which the portable `.` entry must stay free
19
+ // of. `glsp-framework-overrides.js` and `glsp-server-overrides.js` are
20
+ // deliberately NOT re-exported — they are the bindings those two bringups share,
21
+ // reached by relative import, and an adopter composing a container by hand wants
22
+ // the launcher rather than its internals.
@@ -0,0 +1,184 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2023-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 Logger as GlspLogger, LogLevel } from '@eclipse-glsp/server';
11
+ import { LspLogger, type LspLoggerOptions, type ServerSharedServices } from '@hydranium/core';
12
+ import { Logger, type LogThreshold } from '@hydranium/protocol';
13
+
14
+ /**
15
+ * Options accepted by {@link GlspClientLogger}. Extends
16
+ * {@link LspLoggerOptions} (which carries the optional `component`
17
+ * label) with the GLSP-specific per-instance {@link logLevel} threshold.
18
+ */
19
+ export interface GlspClientLoggerOptions extends LspLoggerOptions {
20
+ /**
21
+ * Per-instance NARROWING of the framework threshold. Omit it — the default
22
+ * tracks `Logger.getLevel()` live, which is what makes one
23
+ * `HYDRANIUM_LOG_LEVEL` / one LSP setting govern GLSP output too.
24
+ *
25
+ * It can only narrow, never widen, and that is not a policy choice:
26
+ * `AbstractLogger.send` gates every emission on the process-wide threshold
27
+ * before it reaches the output channel, so a value more verbose than the
28
+ * global is silently ineffective. Set it only to make ONE component quieter
29
+ * than the rest.
30
+ */
31
+ readonly logLevel?: LogLevel;
32
+ }
33
+
34
+ /**
35
+ * Map the framework's {@link LogThreshold} onto GLSP's numeric enum.
36
+ *
37
+ * Two vocabularies have to meet here: ours is a string union with `'off'` and
38
+ * `'trace'`, GLSP's a numeric enum whose most verbose tier is `debug`. `'off'`
39
+ * becomes `none`, and **`'trace'` also becomes `debug`** — GLSP has no finer
40
+ * tier, and mapping it to `none` would silence GLSP at the most verbose
41
+ * framework setting, which is the opposite of what asking for `trace` means.
42
+ */
43
+ export function glspLogLevelOf(threshold: LogThreshold): LogLevel {
44
+ switch (threshold) {
45
+ case 'off':
46
+ return LogLevel.none;
47
+ case 'error':
48
+ return LogLevel.error;
49
+ case 'warn':
50
+ return LogLevel.warn;
51
+ case 'info':
52
+ return LogLevel.info;
53
+ case 'debug':
54
+ case 'trace':
55
+ return LogLevel.debug;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Adapter from {@link LspLogger} (the framework's LSP-bound logger in
61
+ * `@hydranium/core`) to GLSP's server-side {@link GlspLogger}
62
+ * abstract base. Lets the GLSP container resolve a logger whose output
63
+ * lines interleave with the LSP head's existing log channel — the only
64
+ * way to debug request/response flow across the two heads in one place.
65
+ *
66
+ * GLSP-specific concerns layered on top of {@link LspLogger}:
67
+ *
68
+ * - A {@link logLevel} field, because GLSP's `Logger` contract declares one.
69
+ * **It tracks the framework's process-wide threshold by default**, resolving
70
+ * `Logger.getLevel()` through {@link glspLogLevelOf} on every read — so
71
+ * `HYDRANIUM_LOG_LEVEL`, the LSP log-level setting and the Theia preference
72
+ * all reach GLSP output without a second knob. Assigning the field (or
73
+ * passing `logLevel`) pins an explicit per-instance NARROWING; see
74
+ * {@link GlspClientLoggerOptions.logLevel} for why it cannot widen.
75
+ * Resolved lazily rather than captured in the constructor: the root logger is
76
+ * built during container setup, before the LSP client has sent its
77
+ * configuration, so a captured value would freeze the launch-time default.
78
+ * - The {@link caller} accessor satisfies GLSP's abstract `Logger.caller`
79
+ * contract; it is backed by {@link LspLogger}'s `component` field.
80
+ * - {@link info} / {@link warn} / {@link error} / {@link debug} accept GLSP's
81
+ * `(message, ...params)` shape and {@link combine} the params into the
82
+ * message via {@link formatParam} so objects render as JSON and errors
83
+ * render with stack. GLSP server code routinely calls
84
+ * `logger.error('Failed', error)` expecting the second argument to be
85
+ * surfaced in the log line.
86
+ *
87
+ * Subclasses can override {@link withUri} to render workspace-relative
88
+ * paths instead of stringified URIs (the typical adopter customisation,
89
+ * mirroring what an adopter does in its LSP-side logger).
90
+ */
91
+ export class GlspClientLogger extends LspLogger implements GlspLogger {
92
+ /**
93
+ * Explicit per-instance narrowing, or `undefined` to track the framework
94
+ * threshold. Kept separate from the resolved value so {@link derive} can pass
95
+ * the OVERRIDE to children rather than the resolution — passing the resolved
96
+ * level would pin every child at whatever the global happened to be when the
97
+ * parent was derived, and container-time derivation is exactly when that
98
+ * value is still the launch-time default.
99
+ */
100
+ protected logLevelOverride?: LogLevel;
101
+
102
+ constructor(services: ServerSharedServices, options: GlspClientLoggerOptions = {}) {
103
+ super(services, options);
104
+ this.logLevelOverride = options.logLevel;
105
+ }
106
+
107
+ /** GLSP's `Logger.logLevel` contract; the framework threshold unless pinned. */
108
+ get logLevel(): LogLevel {
109
+ return this.logLevelOverride ?? glspLogLevelOf(Logger.getLevel());
110
+ }
111
+
112
+ set logLevel(level: LogLevel) {
113
+ this.logLevelOverride = level;
114
+ }
115
+
116
+ /** GLSP's `Logger.caller` contract; reads {@link LspLogger.component}. */
117
+ get caller(): string | undefined {
118
+ return this.component;
119
+ }
120
+
121
+ override info(message: string, ...params: unknown[]): this {
122
+ if (this.enabled(LogLevel.info)) {
123
+ super.info(this.combine(message, params));
124
+ }
125
+ return this;
126
+ }
127
+
128
+ override warn(message: string, ...params: unknown[]): this {
129
+ if (this.enabled(LogLevel.warn)) {
130
+ super.warn(this.combine(message, params));
131
+ }
132
+ return this;
133
+ }
134
+
135
+ override error(message: string, ...params: unknown[]): this {
136
+ if (this.enabled(LogLevel.error)) {
137
+ super.error(this.combine(message, params));
138
+ }
139
+ return this;
140
+ }
141
+
142
+ override debug(message: string, ...params: unknown[]): this {
143
+ if (this.enabled(LogLevel.debug)) {
144
+ super.debug(this.combine(message, params));
145
+ }
146
+ return this;
147
+ }
148
+
149
+ /** True when {@link logLevel} permits emission at the given level. */
150
+ protected enabled(level: LogLevel): boolean {
151
+ return this.logLevel !== LogLevel.none && level <= this.logLevel;
152
+ }
153
+
154
+ /** Concatenate a message with its varargs, formatting each via {@link formatParam}. */
155
+ protected combine(message: string, params: readonly unknown[]): string {
156
+ return params.length === 0 ? message : `${message} ${params.map(p => this.formatParam(p)).join(',\n')}`;
157
+ }
158
+
159
+ /** Format a single param: errors → `message\nstack`; everything else → JSON.stringify(p, undefined, 4). */
160
+ protected formatParam(param: unknown): string {
161
+ if (param instanceof Error) {
162
+ return `${param.message}\n${param.stack ?? ''}`;
163
+ }
164
+ try {
165
+ return JSON.stringify(param, undefined, 4);
166
+ } catch {
167
+ return '';
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Override of {@link LspLogger.derive} that carries the per-instance
173
+ * narrowing. The base `derive(component)` constructs the subclass with
174
+ * `{ component }` only, which would drop it.
175
+ *
176
+ * It passes {@link logLevelOverride}, NOT `this.logLevel`: an unpinned parent
177
+ * must yield an unpinned child, or every derived logger freezes at the
178
+ * threshold in force when the container was built.
179
+ */
180
+ protected override derive(component: string): this {
181
+ const Subclass = this.constructor as new (services: ServerSharedServices, options?: GlspClientLoggerOptions) => this;
182
+ return new Subclass(this.services, { logLevel: this.logLevelOverride, component });
183
+ }
184
+ }
@@ -0,0 +1,10 @@
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
+ export * from './glsp-client-logger.js';
@@ -0,0 +1,13 @@
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
+ // Server-only entry (`@hydranium/glsp-server/node`). Holds the socket launcher,
11
+ // which binds GLSP's node `SocketServerLauncher` (`@eclipse-glsp/server/node`).
12
+ // The portable `.` entry stays free of that import so it bundles for the browser.
13
+ export * from './start-glsp-server.js';
@@ -0,0 +1,214 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2023-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 {
11
+ type Logger as GlspLogger,
12
+ type ServerModule,
13
+ type SocketLaunchOptions,
14
+ SocketServerLauncher,
15
+ createAppModule,
16
+ defaultSocketLaunchOptions
17
+ } from '@eclipse-glsp/server/node.js';
18
+ import { Container, type ContainerModule } from 'inversify';
19
+ import type { Logger } from '@hydranium/protocol';
20
+ import { Deferred } from '@hydranium/langium';
21
+ import type * as net from 'node:net';
22
+ import type { IntegratedServer } from '@hydranium/core';
23
+ import { createGlspFrameworkOverrides } from '../launcher/glsp-framework-overrides.js';
24
+ import { createGlspServerOverrides } from '../launcher/glsp-server-overrides.js';
25
+ import type { LspConnectionLike } from '@hydranium/core/node';
26
+
27
+ /**
28
+ * Options for {@link startGlspServer}.
29
+ *
30
+ * @see {@link startGlspServer} for the lifecycle semantics.
31
+ */
32
+ export interface GlspServerOptions {
33
+ /**
34
+ * Port to bind. Default `0` — OS-assigned ephemeral. Resolve the
35
+ * actual port off {@link StartedGlspServer.port} once `started`
36
+ * resolves (only then is the address info available).
37
+ */
38
+ readonly port?: number;
39
+ /**
40
+ * Host to bind. Default `'127.0.0.1'` (LOCAL ONLY) — the GLSP head is
41
+ * traffic-only-from-the-companion-client by design; rebinding to a
42
+ * routable address is an explicit opt-in that adopters must do
43
+ * deliberately.
44
+ */
45
+ readonly host?: string;
46
+ /**
47
+ * Adopter-supplied logger factory. Called once per Inversify resolution
48
+ * (with `caller` set to the requesting parent's class name) AND once
49
+ * per `LoggerFactory` invocation.
50
+ *
51
+ * **The threshold belongs in here, not beside it.** GLSP's own log level is
52
+ * a property of the logger this factory returns — e.g.
53
+ * `GlspClientLogger` filters
54
+ * each call against its own `logLevel` option. There is deliberately no
55
+ * launcher-level `logLevel`: the framework replaces GLSP's `Logger` binding
56
+ * outright, so anything passed to GLSP's app module would be discarded, and
57
+ * an option that looks authoritative and is discarded is worse than none.
58
+ */
59
+ readonly createLogger: (caller?: string) => GlspLogger;
60
+ /**
61
+ * The GLSP {@link ServerModule} with per-diagram modules
62
+ * pre-configured via `new ServerModule().configureDiagramModule(...)`.
63
+ */
64
+ readonly serverModule: ServerModule;
65
+ /**
66
+ * Additional Inversify modules to load on the app container AFTER GLSP's
67
+ * own app module (via `createAppModule`, which binds `InjectionContainer`
68
+ * plus the GLSP-version-specific app bindings) and the framework overrides
69
+ * that route GLSP's logger and logger factory through the adopter logger
70
+ * and add the shared Tracer. Adopters wire language-services bindings
71
+ * (e.g. their own shared-services symbol) here.
72
+ */
73
+ readonly appModules?: ReadonlyArray<ContainerModule>;
74
+ /**
75
+ * Optional LSP connection used to register a port-discovery request
76
+ * handler. When both `lspConnection` and `portCommand` are set, the
77
+ * framework registers `lspConnection.onRequest(portCommand, () => port)`
78
+ * after the GLSP socket is listening — the standard handshake adopters
79
+ * use to bridge LSP clients to the GLSP head.
80
+ */
81
+ readonly lspConnection?: LspConnectionLike;
82
+ /**
83
+ * LSP request method name to register. Required when `lspConnection` is
84
+ * set. Adopters define this constant in their own protocol package.
85
+ */
86
+ readonly portCommand?: string;
87
+ /**
88
+ * Optional framework-lifecycle logger. Receives `listening` / `error`
89
+ * lifecycle messages from the launcher itself (the GLSP container's
90
+ * own logger handles application-level GLSP logs).
91
+ */
92
+ readonly logger?: Logger;
93
+ /**
94
+ * Optional diagnostic hook called for every incoming `net.Socket` the
95
+ * GLSP server's `net.Server` accepts. Fires as a side-effect of the
96
+ * framework's own `'connection'` listener — GLSP's per-connection setup
97
+ * runs independently from its own listener registered by
98
+ * `SocketServerLauncher`. Adopters typically attach `'data'` / `'close'`
99
+ * listeners inside the hook for byte-level observability when debugging
100
+ * wire-level handshake issues.
101
+ */
102
+ readonly onClientConnection?: (socket: net.Socket) => void;
103
+ }
104
+
105
+ /**
106
+ * Live GLSP server handle returned by {@link startGlspServer}.
107
+ *
108
+ * - {@link IntegratedServer.started} resolves once the GLSP socket is
109
+ * listening AND (if configured) the port-discovery handler has been
110
+ * registered on the LSP connection.
111
+ * - {@link IntegratedServer.stopped} resolves once GLSP's
112
+ * `SocketServerLauncher.start` promise settles (i.e. the underlying
113
+ * `net.Server` has closed).
114
+ * - {@link port} is `undefined` until `started` resolves.
115
+ */
116
+ export interface StartedGlspServer extends IntegratedServer {
117
+ readonly port: number | undefined;
118
+ }
119
+
120
+ /**
121
+ * Start a GLSP socket head. Builds the GLSP app container with framework-
122
+ * default `Logger` / `LoggerFactory` bindings, layers adopter modules,
123
+ * resolves {@link SocketServerLauncher}, configures the adopter
124
+ * {@link ServerModule}, and starts listening.
125
+ *
126
+ * **GLSP launcher coupling.** GLSP's {@link SocketServerLauncher} owns the
127
+ * `net.Server` and per-connection `MessageConnection` construction;
128
+ * the framework wrapper composes ABOVE that launcher rather than replacing
129
+ * it. The framework's own `startSocketServer`
130
+ * helper is NOT used here — it's designed for stdio/raw-JSON-RPC heads
131
+ * (data-server's pattern), not GLSP's Inversify-driven per-connection
132
+ * server-instance model. Bridging the two would lose GLSP's action-handler
133
+ * registry plumbing.
134
+ */
135
+ export function startGlspServer(options: GlspServerOptions): StartedGlspServer {
136
+ const launchOptions: SocketLaunchOptions = {
137
+ ...defaultSocketLaunchOptions,
138
+ host: options.host ?? '127.0.0.1',
139
+ port: options.port ?? 0
140
+ };
141
+ const lifecycle = options.logger;
142
+
143
+ // Start from GLSP's own app module rather than re-deriving its bindings by
144
+ // hand. createAppModule binds InjectionContainer + Logger/LoggerFactory and
145
+ // — crucially — whatever else the resolved GLSP version's app container needs,
146
+ // so the framework tracks GLSP across versions instead of drifting. consoleLog /
147
+ // fileLog are forced off so GLSP binds a throwaway NullLogger with no winston
148
+ // instance; the override module below replaces Logger/LoggerFactory with the
149
+ // adopter's logger, so GLSP server logs flow through options.createLogger.
150
+ const glspAppModule = createAppModule({ ...launchOptions, consoleLog: false, fileLog: false });
151
+
152
+ const appContainer = new Container();
153
+ appContainer.load(glspAppModule, createGlspFrameworkOverrides(options.createLogger), ...(options.appModules ?? []));
154
+
155
+ const launcher = appContainer.resolve<SocketServerLauncher>(SocketServerLauncher);
156
+ // Passed as an additional module rather than folded into the app container:
157
+ // the launcher loads these into the per-connection SERVER container, which is
158
+ // the only tier where the adopter's `ServerModule` binding of `GLSPServer`
159
+ // can be rebound.
160
+ launcher.configure(options.serverModule, createGlspServerOverrides());
161
+
162
+ const started = new Deferred<void>();
163
+ const portHandle: { port: number | undefined } = { port: undefined };
164
+
165
+ try {
166
+ const stoppedPromise = Promise.resolve(launcher.start(launchOptions));
167
+
168
+ // GLSP's `SocketServerLauncher.netServer` field is `protected`; we
169
+ // access it via property indexing. The alternative is to fork the
170
+ // launcher or add a 'listening' callback to GLSP upstream.
171
+ const netServer = (launcher as unknown as { netServer: net.Server }).netServer;
172
+
173
+ netServer.on('listening', () => {
174
+ const address = netServer.address();
175
+ if (address && typeof address !== 'string') {
176
+ portHandle.port = address.port;
177
+ if (options.lspConnection && options.portCommand) {
178
+ options.lspConnection.onRequest(options.portCommand, () => portHandle.port);
179
+ }
180
+ lifecycle?.info(`[GlspServer] Ready to accept new client requests on port: ${address.port}`);
181
+ started.resolve();
182
+ } else {
183
+ const message = address === null ? 'address is null' : `bound to non-TCP "${String(address)}"`;
184
+ lifecycle?.error(`[GlspServer] Could not resolve address info — ${message}. Shutting down.`);
185
+ started.reject(new Error(`startGlspServer could not resolve address info — ${message}`));
186
+ netServer.close();
187
+ }
188
+ });
189
+ netServer.on('error', (err: Error) => {
190
+ lifecycle?.error(`[GlspServer] Error: ${err.message}`);
191
+ started.reject(err);
192
+ });
193
+ if (options.onClientConnection) {
194
+ netServer.on('connection', options.onClientConnection);
195
+ }
196
+
197
+ return {
198
+ get port() {
199
+ return portHandle.port;
200
+ },
201
+ started: started.promise,
202
+ stopped: stoppedPromise
203
+ };
204
+ } catch (error) {
205
+ lifecycle?.error('Error in GLSP server launcher:', error);
206
+ return {
207
+ get port() {
208
+ return portHandle.port;
209
+ },
210
+ started: Promise.reject(error),
211
+ stopped: Promise.resolve()
212
+ };
213
+ }
214
+ }