@hydranium/glsp-server 1.0.0-next.31 → 1.0.0-next.315

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 (217) hide show
  1. package/README.md +80 -88
  2. package/lib/browser/start-glsp-server-in-worker.d.ts +43 -42
  3. package/lib/browser/start-glsp-server-in-worker.d.ts.map +1 -1
  4. package/lib/browser/start-glsp-server-in-worker.js +62 -13
  5. package/lib/browser/start-glsp-server-in-worker.js.map +1 -1
  6. package/lib/command/diagram-edit-dropped-error.d.ts +31 -0
  7. package/lib/command/diagram-edit-dropped-error.d.ts.map +1 -0
  8. package/lib/command/diagram-edit-dropped-error.js +48 -0
  9. package/lib/command/diagram-edit-dropped-error.js.map +1 -0
  10. package/lib/command/hydranium-glsp-command-stack.d.ts +33 -0
  11. package/lib/command/hydranium-glsp-command-stack.d.ts.map +1 -0
  12. package/lib/command/hydranium-glsp-command-stack.js +63 -0
  13. package/lib/command/hydranium-glsp-command-stack.js.map +1 -0
  14. package/lib/command/hydranium-glsp-operation-action-handler.d.ts +64 -0
  15. package/lib/command/hydranium-glsp-operation-action-handler.d.ts.map +1 -0
  16. package/lib/command/hydranium-glsp-operation-action-handler.js +142 -0
  17. package/lib/command/hydranium-glsp-operation-action-handler.js.map +1 -0
  18. package/lib/command/hydranium-glsp-operation-command.d.ts +250 -0
  19. package/lib/command/hydranium-glsp-operation-command.d.ts.map +1 -0
  20. package/lib/command/hydranium-glsp-operation-command.js +491 -0
  21. package/lib/command/hydranium-glsp-operation-command.js.map +1 -0
  22. package/lib/command/hydranium-glsp-recording-command.d.ts +26 -87
  23. package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -1
  24. package/lib/command/hydranium-glsp-recording-command.js +40 -117
  25. package/lib/command/hydranium-glsp-recording-command.js.map +1 -1
  26. package/lib/command/hydranium-glsp-undo-redo-action-handler.d.ts +23 -0
  27. package/lib/command/hydranium-glsp-undo-redo-action-handler.d.ts.map +1 -0
  28. package/lib/command/hydranium-glsp-undo-redo-action-handler.js +41 -0
  29. package/lib/command/hydranium-glsp-undo-redo-action-handler.js.map +1 -0
  30. package/lib/command/index.d.ts +5 -0
  31. package/lib/command/index.d.ts.map +1 -1
  32. package/lib/command/index.js +5 -0
  33. package/lib/command/index.js.map +1 -1
  34. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts +1 -1
  35. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts.map +1 -1
  36. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js +2 -2
  37. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js.map +1 -1
  38. package/lib/dispatcher/server-action-dispatcher.d.ts +25 -24
  39. package/lib/dispatcher/server-action-dispatcher.d.ts.map +1 -1
  40. package/lib/dispatcher/server-action-dispatcher.js +38 -26
  41. package/lib/dispatcher/server-action-dispatcher.js.map +1 -1
  42. package/lib/index.d.ts +1 -0
  43. package/lib/index.d.ts.map +1 -1
  44. package/lib/index.js +1 -0
  45. package/lib/index.js.map +1 -1
  46. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts +35 -59
  47. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts.map +1 -1
  48. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js +60 -71
  49. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js.map +1 -1
  50. package/lib/launcher/drop-notifications-to-gone-client.d.ts +23 -0
  51. package/lib/launcher/drop-notifications-to-gone-client.d.ts.map +1 -0
  52. package/lib/launcher/drop-notifications-to-gone-client.js +43 -0
  53. package/lib/launcher/drop-notifications-to-gone-client.js.map +1 -0
  54. package/lib/launcher/glsp-app-module.d.ts +1 -2
  55. package/lib/launcher/glsp-app-module.d.ts.map +1 -1
  56. package/lib/launcher/glsp-app-module.js.map +1 -1
  57. package/lib/launcher/glsp-connection-logger.d.ts +19 -0
  58. package/lib/launcher/glsp-connection-logger.d.ts.map +1 -0
  59. package/lib/launcher/glsp-connection-logger.js +24 -0
  60. package/lib/launcher/glsp-connection-logger.js.map +1 -0
  61. package/lib/launcher/glsp-server-overrides.d.ts +10 -8
  62. package/lib/launcher/glsp-server-overrides.d.ts.map +1 -1
  63. package/lib/launcher/glsp-server-overrides.js +25 -11
  64. package/lib/launcher/glsp-server-overrides.js.map +1 -1
  65. package/lib/launcher/hydranium-glsp-server.d.ts +60 -15
  66. package/lib/launcher/hydranium-glsp-server.d.ts.map +1 -1
  67. package/lib/launcher/hydranium-glsp-server.js +145 -22
  68. package/lib/launcher/hydranium-glsp-server.js.map +1 -1
  69. package/lib/launcher/index.d.ts +2 -0
  70. package/lib/launcher/index.d.ts.map +1 -1
  71. package/lib/launcher/index.js +5 -4
  72. package/lib/launcher/index.js.map +1 -1
  73. package/lib/logging/glsp-client-logger.d.ts +10 -6
  74. package/lib/logging/glsp-client-logger.d.ts.map +1 -1
  75. package/lib/logging/glsp-client-logger.js +14 -7
  76. package/lib/logging/glsp-client-logger.js.map +1 -1
  77. package/lib/messages/index.d.ts +11 -6
  78. package/lib/messages/index.d.ts.map +1 -1
  79. package/lib/messages/index.js +11 -6
  80. package/lib/messages/index.js.map +1 -1
  81. package/lib/node/start-glsp-server.d.ts +50 -15
  82. package/lib/node/start-glsp-server.d.ts.map +1 -1
  83. package/lib/node/start-glsp-server.js +97 -30
  84. package/lib/node/start-glsp-server.js.map +1 -1
  85. package/lib/state/abstract-hydranium-glsp-state.d.ts +326 -102
  86. package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -1
  87. package/lib/state/abstract-hydranium-glsp-state.js +529 -139
  88. package/lib/state/abstract-hydranium-glsp-state.js.map +1 -1
  89. package/lib/state/diagram-status.d.ts +38 -0
  90. package/lib/state/diagram-status.d.ts.map +1 -0
  91. package/lib/state/diagram-status.js +27 -0
  92. package/lib/state/diagram-status.js.map +1 -0
  93. package/lib/state/full-text-hydranium-glsp-state.d.ts +22 -9
  94. package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -1
  95. package/lib/state/full-text-hydranium-glsp-state.js +45 -18
  96. package/lib/state/full-text-hydranium-glsp-state.js.map +1 -1
  97. package/lib/state/hydranium-glsp-index.d.ts +40 -29
  98. package/lib/state/hydranium-glsp-index.d.ts.map +1 -1
  99. package/lib/state/hydranium-glsp-index.js +82 -45
  100. package/lib/state/hydranium-glsp-index.js.map +1 -1
  101. package/lib/state/hydranium-shared-core-services.d.ts +16 -29
  102. package/lib/state/hydranium-shared-core-services.d.ts.map +1 -1
  103. package/lib/state/hydranium-shared-core-services.js +15 -28
  104. package/lib/state/hydranium-shared-core-services.js.map +1 -1
  105. package/lib/state/index.d.ts +1 -0
  106. package/lib/state/index.d.ts.map +1 -1
  107. package/lib/state/index.js +1 -0
  108. package/lib/state/index.js.map +1 -1
  109. package/lib/state/model-ready-timeout-error.d.ts.map +1 -1
  110. package/lib/state/model-ready-timeout-error.js.map +1 -1
  111. package/lib/state/reconcile-source-model-write.d.ts +13 -27
  112. package/lib/state/reconcile-source-model-write.d.ts.map +1 -1
  113. package/lib/state/reconcile-source-model-write.js +44 -32
  114. package/lib/state/reconcile-source-model-write.js.map +1 -1
  115. package/lib/state/reconciling-multi-document-glsp-state.d.ts +125 -69
  116. package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -1
  117. package/lib/state/reconciling-multi-document-glsp-state.js +242 -100
  118. package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -1
  119. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +36 -28
  120. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -1
  121. package/lib/state/reconciling-transfer-hydranium-glsp-state.js +59 -42
  122. package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -1
  123. package/lib/status/diagram-status-reporter.d.ts +44 -0
  124. package/lib/status/diagram-status-reporter.d.ts.map +1 -0
  125. package/lib/status/diagram-status-reporter.js +88 -0
  126. package/lib/status/diagram-status-reporter.js.map +1 -0
  127. package/lib/status/hydranium-glsp-request-model-action-handler.d.ts +27 -0
  128. package/lib/status/hydranium-glsp-request-model-action-handler.d.ts.map +1 -0
  129. package/lib/status/hydranium-glsp-request-model-action-handler.js +52 -0
  130. package/lib/status/hydranium-glsp-request-model-action-handler.js.map +1 -0
  131. package/lib/status/hydranium-glsp-set-edit-mode-action-handler.d.ts +27 -0
  132. package/lib/status/hydranium-glsp-set-edit-mode-action-handler.d.ts.map +1 -0
  133. package/lib/status/hydranium-glsp-set-edit-mode-action-handler.js +53 -0
  134. package/lib/status/hydranium-glsp-set-edit-mode-action-handler.js.map +1 -0
  135. package/lib/status/index.d.ts +12 -0
  136. package/lib/status/index.d.ts.map +1 -0
  137. package/lib/status/index.js +12 -0
  138. package/lib/status/index.js.map +1 -0
  139. package/lib/storage/hydranium-glsp-request-save-model-action-handler.d.ts +30 -0
  140. package/lib/storage/hydranium-glsp-request-save-model-action-handler.d.ts.map +1 -0
  141. package/lib/storage/hydranium-glsp-request-save-model-action-handler.js +47 -0
  142. package/lib/storage/hydranium-glsp-request-save-model-action-handler.js.map +1 -0
  143. package/lib/storage/hydranium-glsp-storage.d.ts +324 -143
  144. package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -1
  145. package/lib/storage/hydranium-glsp-storage.js +569 -213
  146. package/lib/storage/hydranium-glsp-storage.js.map +1 -1
  147. package/lib/storage/index.d.ts +2 -1
  148. package/lib/storage/index.d.ts.map +1 -1
  149. package/lib/storage/index.js +2 -1
  150. package/lib/storage/index.js.map +1 -1
  151. package/lib/storage/save-delivery-policy.d.ts +51 -0
  152. package/lib/storage/save-delivery-policy.d.ts.map +1 -0
  153. package/lib/storage/{save-conflict-policy.js → save-delivery-policy.js} +7 -8
  154. package/lib/storage/save-delivery-policy.js.map +1 -0
  155. package/lib/submission/hydranium-glsp-submission-handler.d.ts +44 -4
  156. package/lib/submission/hydranium-glsp-submission-handler.d.ts.map +1 -1
  157. package/lib/submission/hydranium-glsp-submission-handler.js +82 -6
  158. package/lib/submission/hydranium-glsp-submission-handler.js.map +1 -1
  159. package/lib/testing/glsp-harness.d.ts +34 -7
  160. package/lib/testing/glsp-harness.d.ts.map +1 -1
  161. package/lib/testing/glsp-harness.js +47 -19
  162. package/lib/testing/glsp-harness.js.map +1 -1
  163. package/lib/validation/diagnostic-markers.d.ts +3 -3
  164. package/lib/validation/diagnostic-markers.d.ts.map +1 -1
  165. package/lib/validation/diagnostic-markers.js +1 -1
  166. package/lib/validation/diagnostic-markers.js.map +1 -1
  167. package/lib/validation/hydranium-glsp-model-validator.d.ts +1 -2
  168. package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -1
  169. package/lib/validation/hydranium-glsp-model-validator.js +1 -1
  170. package/lib/validation/hydranium-glsp-model-validator.js.map +1 -1
  171. package/package.json +31 -52
  172. package/src/browser/start-glsp-server-in-worker.ts +79 -51
  173. package/src/command/diagram-edit-dropped-error.ts +51 -0
  174. package/src/command/hydranium-glsp-command-stack.ts +47 -0
  175. package/src/command/hydranium-glsp-operation-action-handler.ts +141 -0
  176. package/src/command/hydranium-glsp-operation-command.ts +602 -0
  177. package/src/command/hydranium-glsp-recording-command.ts +40 -121
  178. package/src/command/hydranium-glsp-undo-redo-action-handler.ts +29 -0
  179. package/src/command/index.ts +10 -0
  180. package/src/computed-bounds/hydranium-glsp-computed-bounds-action-handler.ts +2 -2
  181. package/src/dispatcher/server-action-dispatcher.ts +39 -31
  182. package/src/index.ts +1 -0
  183. package/src/launcher/abstract-hydranium-glsp-diagram-module.ts +79 -91
  184. package/src/launcher/drop-notifications-to-gone-client.ts +47 -0
  185. package/src/launcher/glsp-app-module.ts +1 -2
  186. package/src/launcher/glsp-connection-logger.ts +27 -0
  187. package/src/launcher/glsp-server-overrides.ts +27 -11
  188. package/src/launcher/hydranium-glsp-server.ts +153 -23
  189. package/src/launcher/index.ts +5 -4
  190. package/src/logging/glsp-client-logger.ts +16 -9
  191. package/src/messages/index.ts +16 -6
  192. package/src/node/start-glsp-server.ts +133 -40
  193. package/src/state/abstract-hydranium-glsp-state.ts +609 -154
  194. package/src/state/diagram-status.ts +41 -0
  195. package/src/state/full-text-hydranium-glsp-state.ts +46 -18
  196. package/src/state/hydranium-glsp-index.ts +85 -41
  197. package/src/state/hydranium-shared-core-services.ts +16 -29
  198. package/src/state/index.ts +1 -0
  199. package/src/state/reconcile-source-model-write.ts +51 -53
  200. package/src/state/reconciling-multi-document-glsp-state.ts +276 -102
  201. package/src/state/reconciling-transfer-hydranium-glsp-state.ts +67 -45
  202. package/src/status/diagram-status-reporter.ts +106 -0
  203. package/src/status/hydranium-glsp-request-model-action-handler.ts +43 -0
  204. package/src/status/hydranium-glsp-set-edit-mode-action-handler.ts +38 -0
  205. package/src/status/index.ts +12 -0
  206. package/src/storage/hydranium-glsp-request-save-model-action-handler.ts +40 -0
  207. package/src/storage/hydranium-glsp-storage.ts +647 -223
  208. package/src/storage/index.ts +2 -1
  209. package/src/storage/save-delivery-policy.ts +49 -0
  210. package/src/submission/hydranium-glsp-submission-handler.ts +91 -5
  211. package/src/testing/glsp-harness.ts +67 -25
  212. package/src/validation/diagnostic-markers.ts +3 -3
  213. package/src/validation/hydranium-glsp-model-validator.ts +4 -5
  214. package/lib/storage/save-conflict-policy.d.ts +0 -56
  215. package/lib/storage/save-conflict-policy.d.ts.map +0 -1
  216. package/lib/storage/save-conflict-policy.js.map +0 -1
  217. package/src/storage/save-conflict-policy.ts +0 -52
@@ -0,0 +1,27 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 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 } from '@eclipse-glsp/server';
11
+ import type { Logger as ConnectionLogger } from 'vscode-jsonrpc';
12
+
13
+ /**
14
+ * A GLSP logger in the shape a `vscode-jsonrpc` connection logs to. A launcher
15
+ * that overrides `createConnection` passes it over its injected `logger`, or
16
+ * the connection logs to `console` or nowhere instead of the adopter's logger.
17
+ * GLSP's logger has no `log`; the connection's `log` lines go to `info`, the
18
+ * threshold the framework's own `Logger.log` emits at.
19
+ */
20
+ export function createGlspConnectionLogger(logger: GlspLogger): ConnectionLogger {
21
+ return {
22
+ error: message => logger.error(message),
23
+ warn: message => logger.warn(message),
24
+ info: message => logger.info(message),
25
+ log: message => logger.info(message)
26
+ };
27
+ }
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { GLSPServer } from '@eclipse-glsp/server';
10
+ import { DefaultGLSPServer, GLSPServer, LoggerFactory } from '@eclipse-glsp/server';
11
11
  import { ContainerModule } from 'inversify';
12
12
  import { HydraniumGlspServer } from './hydranium-glsp-server.js';
13
13
 
@@ -18,15 +18,17 @@ import { HydraniumGlspServer } from './hydranium-glsp-server.js';
18
18
  *
19
19
  * **The tier matters, and the app container cannot stand in for it.** GLSP's
20
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.
21
+ * `ServerModule` into it. The framework's server is a singleton of that
22
+ * container; bound in the app container, every connected application would
23
+ * share one server.
25
24
  *
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.
25
+ * **An adopter's server is kept when it extends {@link HydraniumGlspServer}.**
26
+ * Any other `GLSPServer` the adopter's `ServerModule` binds, upstream's default
27
+ * included, is replaced when the container first creates it, so a server that
28
+ * extends upstream's directly is discarded, with a warning naming its class.
29
+ * The replacement happens on activation because `rebind` would replace an
30
+ * adopter's subclass as well; the discarded server is constructed once and
31
+ * never used.
30
32
  *
31
33
  * Keeping this out of the adopter's hands is the point: an adopter passes their
32
34
  * own `ServerModule` in, so anything they must add themselves is a fix the
@@ -40,7 +42,21 @@ import { HydraniumGlspServer } from './hydranium-glsp-server.js';
40
42
  * a further adopter subclass moves it again.
41
43
  */
42
44
  export function createGlspServerOverrides(): ContainerModule {
43
- return new ContainerModule((_bind, _unbind, _isBound, rebind) => {
44
- rebind(GLSPServer).to(HydraniumGlspServer).inSingletonScope();
45
+ return new ContainerModule((bind, _unbind, _isBound, _rebind, _unbindAsync, onActivation) => {
46
+ bind(HydraniumGlspServer).toSelf().inSingletonScope();
47
+ onActivation<GLSPServer>(GLSPServer, (context, server) => {
48
+ if (server instanceof HydraniumGlspServer) {
49
+ return server;
50
+ }
51
+ if (server.constructor !== DefaultGLSPServer) {
52
+ context.container
53
+ .get<LoggerFactory>(LoggerFactory)('HydraniumGlspServer')
54
+ .warn(
55
+ `Replacing the bound GLSP server ${server.constructor.name} with HydraniumGlspServer; ` +
56
+ 'extend HydraniumGlspServer to keep its behaviour.'
57
+ );
58
+ }
59
+ return context.container.get(HydraniumGlspServer);
60
+ });
45
61
  });
46
62
  }
@@ -8,42 +8,122 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import {
11
+ type ActionMessage,
12
+ ClientAction,
11
13
  type ClientSession,
12
14
  DefaultGLSPServer,
13
15
  GLSPServerError,
16
+ type InitializeClientSessionParameters,
17
+ MessageAction,
14
18
  RejectAction,
15
- type RequestAction,
16
- type ResponseAction
19
+ RequestAction,
20
+ type ResponseAction,
21
+ SourceModelStorage
17
22
  } from '@eclipse-glsp/server';
23
+ import { describeErrorWithStack, RequestSaveModelAction } from '@hydranium/protocol';
18
24
  import { injectable } from 'inversify';
25
+ import { logActionFailure } from '../command/diagram-edit-dropped-error.js';
26
+ import { HydraniumGlspStorage } from '../storage/hydranium-glsp-storage.js';
19
27
 
20
28
  /**
21
29
  * The GLSP server the framework binds in place of upstream's
22
30
  * {@link DefaultGLSPServer}, so a failed REQUEST reaches its reader with the
23
- * text the thrower wrote.
31
+ * text the thrower wrote, and a diagram whose connection ends keeps its unsaved
32
+ * text for the release grace (see {@link shutdown}).
24
33
  *
25
34
  * **What breaks without it.** Upstream projects a failing request's detail as
26
35
  * `error.cause?.toString()` whenever the error is a {@link GLSPServerError},
27
36
  * and that one value feeds both the server log and the client's
28
37
  * {@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
38
+ * reaches its readers as `undefined` — the log line and the client's
39
+ * action-dispatcher warning both print it — while a plain `Error` survives,
40
+ * because the other branch reads the error itself. The typed error is the trap,
41
+ * and it is the one upstream's own
32
42
  * `getOrThrow` helper raises: that helper's signature cannot pass a `cause`, so
33
43
  * an adopter using the sanctioned helper has no fix available on their side.
34
44
  * Restating the cause at each throw site is the alternative, and it costs
35
45
  * every adopter a rule they must know and cannot apply to `getOrThrow`.
36
46
  *
37
- * Only the REQUEST path needs this. Non-request actions fail through
38
- * upstream's `handleProcessError`, which reads `message` directly.
47
+ * Only the REQUEST path needs this. A non-request action fails through
48
+ * {@link handleProcessError}, which reads `message` directly; it and
49
+ * {@link process} are lifted for their logging only.
39
50
  *
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.
51
+ * An adopter who binds their own GLSP server extends this class rather than
52
+ * {@link DefaultGLSPServer}: the framework's server-container override keeps a
53
+ * server that extends this one and replaces any other, so a subclass of
54
+ * upstream's server is discarded, and with it both behaviours.
44
55
  */
45
56
  @injectable()
46
57
  export class HydraniumGlspServer extends DefaultGLSPServer {
58
+ /**
59
+ * End each diagram's client session as lost rather than closed, before
60
+ * upstream disposes the sessions, so its unsaved text waits out the release
61
+ * grace for the client to reconnect. A shutdown is how the client's
62
+ * connection ending reaches the server; a client that stops on purpose
63
+ * shuts the server down the same way, and its diagrams are held as well.
64
+ */
65
+ override shutdown(): void {
66
+ for (const session of this.clientSessions.values()) {
67
+ const storage = session.container.isBound(SourceModelStorage) ? session.container.get(SourceModelStorage) : undefined;
68
+ if (storage instanceof HydraniumGlspStorage) {
69
+ storage.dispose('lost');
70
+ } else if (storage) {
71
+ this.logger.debug(`Shutdown leaves ending ${session.id} to its storage, which is not a HydraniumGlspStorage`);
72
+ }
73
+ }
74
+ super.shutdown();
75
+ }
76
+
77
+ /**
78
+ * End a session the id still has before initializing it again. A model
79
+ * source initializes once, so a second initialize under one id is a new
80
+ * widget, a reopened tab whose closed predecessor's dispose never arrived;
81
+ * upstream would hand it that stale session.
82
+ */
83
+ override async initializeClientSession(params: InitializeClientSessionParameters): Promise<void> {
84
+ if (!this.clientSessions.has(params.clientSessionId)) {
85
+ return super.initializeClientSession(params);
86
+ }
87
+ this.logger.debug(`Client session ${params.clientSessionId} initialized again before its dispose arrived; ending the old one`);
88
+ // Not awaited before the initialize: both drop and set the session before
89
+ // their first await, and the model request that follows is read without
90
+ // waiting for this one to settle.
91
+ const disposed = this.disposeClientSession({ clientSessionId: params.clientSessionId });
92
+ const initialized = super.initializeClientSession(params);
93
+ await Promise.all([disposed, initialized]);
94
+ }
95
+
96
+ /**
97
+ * Lifted from `@eclipse-glsp/server` 2.7.0's {@link DefaultGLSPServer.process}:
98
+ * each action is logged at `debug` rather than `info`, and an action that
99
+ * fails once its session has ended, which disposing the session's dispatcher
100
+ * does to every action it holds, is logged at `debug` and told to no client,
101
+ * since the one that sent it has gone. The accompanying test pins upstream's
102
+ * source.
103
+ */
104
+ override process(message: ActionMessage): void {
105
+ this.validateServerInitialized();
106
+ this.logger.debug(`process [action=${message.action.kind}, clientId=${message.clientId}]`);
107
+ const clientSessionId = message.clientId;
108
+ const clientSession = this.clientSessions.get(clientSessionId);
109
+ if (!clientSession) {
110
+ throw new Error(`No client session has been initialized for client id: ${clientSessionId}`);
111
+ }
112
+ const action = message.action;
113
+ ClientAction.mark(action);
114
+ if (RequestAction.is(action)) {
115
+ void this.handleClientRequest(clientSession, action, message.clientId);
116
+ return;
117
+ }
118
+ clientSession.actionDispatcher.dispatch(action).catch((error: unknown) => {
119
+ if (this.clientSessions.get(clientSessionId) === clientSession) {
120
+ void this.handleProcessError(message, error);
121
+ } else {
122
+ this.logger.debug(`Action '${action.kind}' ended with its session: ${describeErrorWithStack(error)}`);
123
+ }
124
+ });
125
+ }
126
+
47
127
  /**
48
128
  * The detail projected into the client's {@link RejectAction} and the server
49
129
  * log when a request fails.
@@ -61,11 +141,40 @@ export class HydraniumGlspServer extends DefaultGLSPServer {
61
141
  }
62
142
 
63
143
  /**
64
- * Lifted verbatim from `@eclipse-glsp/server` 2.7.0's
144
+ * Lifted from `@eclipse-glsp/server` 2.7.0's
145
+ * {@link DefaultGLSPServer.handleProcessError}, logging the failure once
146
+ * with {@link logActionFailure} where upstream logs the action and the
147
+ * reason as two lines.
148
+ *
149
+ * The notification is upstream's, text and all, and this body does not
150
+ * track upstream: the accompanying test pins upstream's source as it does
151
+ * for {@link handleClientRequest}.
152
+ */
153
+ protected override handleProcessError(message: ActionMessage, reason: unknown): void {
154
+ const kind = message.action.kind;
155
+ logActionFailure(this.logger, message.action, reason);
156
+ let errorMsg = `Could not process action: '${kind}`;
157
+ let details = stringifyAsUpstream(reason);
158
+ if (reason instanceof GLSPServerError) {
159
+ details = stringifyAsUpstream(reason.cause);
160
+ errorMsg = reason.message;
161
+ }
162
+ this.sendToClient({ clientId: message.clientId, action: MessageAction.create(errorMsg, { severity: 'ERROR', details }) });
163
+ }
164
+
165
+ /**
166
+ * Lifted from `@eclipse-glsp/server` 2.7.0's
65
167
  * {@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.
168
+ * delegated to {@link requestFailureDetail}, the failure logged once with
169
+ * its stack, a request that fails once its session has ended logged at
170
+ * `debug` and answered to no client, as {@link process} treats an action,
171
+ * and one addition: a failed {@link RequestSaveModelAction} also
172
+ * raises the error notification a failed `SaveModelAction` raises, through
173
+ * {@link handleProcessError}, which then logs it instead. A rejection
174
+ * reaches only the client's log, and a Theia host only logs a rejected save,
175
+ * so without it a save that failed would tell the user nothing. Nothing else
176
+ * here — the timeout branch, the response dispatch, the nested send-failure
177
+ * handling — relates to either.
69
178
  *
70
179
  * **This body does not track upstream.** A release that changes request
71
180
  * handling would silently not apply, on the path every request takes. There
@@ -88,17 +197,38 @@ export class HydraniumGlspServer extends DefaultGLSPServer {
88
197
  this.sendResponseToClient(clientId, response);
89
198
  }
90
199
  } catch (error: unknown) {
200
+ if (this.clientSessions.get(clientSession.id) !== clientSession) {
201
+ this.logger.debug(`Request '${action.kind}' (${action.requestId}) ended with its session: ${describeErrorWithStack(error)}`);
202
+ return;
203
+ }
91
204
  const detail = this.requestFailureDetail(error);
92
- this.logger.error(`Failed to handle request '${action.kind}' (${action.requestId}):`, detail);
205
+ const isSave = action.kind === RequestSaveModelAction.KIND;
206
+ if (!isSave) {
207
+ this.logger.error(`Failed to handle request '${action.kind}' (${action.requestId}): ${describeErrorWithStack(error)}`);
208
+ }
209
+ // Inside the guard: `process` does not await this method, so a throw
210
+ // here would be an unhandled rejection.
93
211
  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);
212
+ try {
213
+ const reject = RejectAction.create(`Failed to handle request '${action.kind}' (${action.requestId})`, {
214
+ responseId: action.requestId,
215
+ detail
216
+ });
217
+ this.sendResponseToClient(clientId, reject);
218
+ } finally {
219
+ // Also when the reject could not be sent, since this logs the save's failure.
220
+ if (isSave) {
221
+ this.handleProcessError({ clientId, action }, error);
222
+ }
223
+ }
99
224
  } catch (sendError: unknown) {
100
- this.logger.error(`Failed to send rejection for request '${action.requestId}':`, sendError);
225
+ this.logger.error(`Failed to send rejection for request '${action.requestId}': ${describeErrorWithStack(sendError)}`);
101
226
  }
102
227
  }
103
228
  }
104
229
  }
230
+
231
+ /** `value?.toString?.()`, as upstream projects a reason into a notification's details. */
232
+ function stringifyAsUpstream(value: unknown): string | undefined {
233
+ return (value as { toString?: () => string } | null | undefined)?.toString?.();
234
+ }
@@ -13,10 +13,11 @@ export * from './abstract-hydranium-glsp-diagram-module.js';
13
13
  // rather than upstream's — the server-container override discards a subclass of
14
14
  // `DefaultGLSPServer`.
15
15
  export * from './hydranium-glsp-server.js';
16
+ export * from './drop-notifications-to-gone-client.js';
17
+ export * from './glsp-connection-logger.js';
16
18
  // The socket bringup lives in `@hydranium/glsp-server/node` and the worker
17
19
  // bringup in `@hydranium/glsp-server/browser`, not here: each pulls the
18
20
  // 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.
21
+ // of. The container overrides those two bringups share stay unexported, reached
22
+ // by relative import: an adopter composing a container by hand wants the
23
+ // launcher rather than its internals.
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { type Logger as GlspLogger, LogLevel } from '@eclipse-glsp/server';
11
11
  import { LspLogger, type LspLoggerOptions, type ServerSharedServices } from '@hydranium/core';
12
- import { Logger, type LogThreshold } from '@hydranium/protocol';
12
+ import { describeErrorWithStack, LEVEL_ORDER, Logger, type LogThreshold } from '@hydranium/protocol';
13
13
 
14
14
  /**
15
15
  * Options accepted by {@link GlspClientLogger}. Extends
@@ -19,11 +19,11 @@ import { Logger, type LogThreshold } from '@hydranium/protocol';
19
19
  export interface GlspClientLoggerOptions extends LspLoggerOptions {
20
20
  /**
21
21
  * Per-instance NARROWING of the framework threshold. Omit it — the default
22
- * tracks `Logger.getLevel()` live, which is what makes one
22
+ * tracks the framework thresholds live, which is what makes one
23
23
  * `HYDRANIUM_LOG_LEVEL` / one LSP setting govern GLSP output too.
24
24
  *
25
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
26
+ * `AbstractLogger.send` gates every emission on `Logger.isLevelEnabled`
27
27
  * before it reaches the output channel, so a value more verbose than the
28
28
  * global is silently ineffective. Set it only to make ONE component quieter
29
29
  * than the rest.
@@ -66,8 +66,8 @@ export function glspLogLevelOf(threshold: LogThreshold): LogLevel {
66
66
  * GLSP-specific concerns layered on top of {@link LspLogger}:
67
67
  *
68
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
69
+ * **It tracks the framework's thresholds by default**, resolving them
70
+ * through {@link glspLogLevelOf} on every read — so
71
71
  * `HYDRANIUM_LOG_LEVEL`, the LSP log-level setting and the Theia preference
72
72
  * all reach GLSP output without a second knob. Assigning the field (or
73
73
  * passing `logLevel`) pins an explicit per-instance NARROWING; see
@@ -104,9 +104,16 @@ export class GlspClientLogger extends LspLogger implements GlspLogger {
104
104
  this.logLevelOverride = options.logLevel;
105
105
  }
106
106
 
107
- /** GLSP's `Logger.logLevel` contract; the framework threshold unless pinned. */
107
+ /**
108
+ * GLSP's `Logger.logLevel` contract; unless pinned, the more verbose of
109
+ * `Logger.getLevel()` and `Logger.getFileLevel()`. The process threshold
110
+ * alone drops GLSP lines a log file with its own threshold asks for.
111
+ */
108
112
  get logLevel(): LogLevel {
109
- return this.logLevelOverride ?? glspLogLevelOf(Logger.getLevel());
113
+ const level = Logger.getLevel();
114
+ const fileLevel = Logger.getFileLevel();
115
+ const widest = fileLevel && LEVEL_ORDER[fileLevel] > LEVEL_ORDER[level] ? fileLevel : level;
116
+ return this.logLevelOverride ?? glspLogLevelOf(widest);
110
117
  }
111
118
 
112
119
  set logLevel(level: LogLevel) {
@@ -156,10 +163,10 @@ export class GlspClientLogger extends LspLogger implements GlspLogger {
156
163
  return params.length === 0 ? message : `${message} ${params.map(p => this.formatParam(p)).join(',\n')}`;
157
164
  }
158
165
 
159
- /** Format a single param: errors → `message\nstack`; everything else → JSON.stringify(p, undefined, 4). */
166
+ /** Format a single param: errors → their stack and causes; everything else → JSON.stringify(p, undefined, 4). */
160
167
  protected formatParam(param: unknown): string {
161
168
  if (param instanceof Error) {
162
- return `${param.message}\n${param.stack ?? ''}`;
169
+ return describeErrorWithStack(param);
163
170
  }
164
171
  try {
165
172
  return JSON.stringify(param, undefined, 4);
@@ -10,11 +10,21 @@
10
10
  /**
11
11
  * Every user-facing message `@hydranium/glsp-server` raises.
12
12
  *
13
- * Enumeration only: GLSP's action protocol carries no slot for a message
14
- * identity on any of its notification actions, so a diagram error reaches the
15
- * client as English by upstream constraint. The declarations still earn their
16
- * place — an adopter rendering their own diagram chrome can key on the code, and
17
- * the barrel is how they discover the codes exist.
13
+ * **Each one is rendered AT ITS RAISE SITE**, through the shared
14
+ * `MessageRenderer`, because GLSP's action protocol carries no slot for an
15
+ * identity on any of its actions — so the identity is gone the moment a raise
16
+ * site formats its text, and there is nothing at the error boundary to render
17
+ * from. That makes this head the one place where a new message can ship
18
+ * unrendered without any single chokepoint noticing, which is why the package's
19
+ * own test suite scans for a declaration reached by `.format()` instead.
20
+ *
21
+ * The barrel is also how an adopter rendering their own diagram chrome
22
+ * discovers the codes exist.
18
23
  */
19
24
 
20
- export { SAVE_TARGET_UNKNOWN, SOURCE_URI_MISSING } from '../storage/hydranium-glsp-storage.js';
25
+ export {
26
+ DIAGRAM_READONLY_PARSE_ERROR,
27
+ DIAGRAM_SESSION_REFUSED,
28
+ SAVE_TARGET_UNKNOWN,
29
+ SOURCE_URI_MISSING
30
+ } from '../storage/hydranium-glsp-storage.js';