@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
@@ -10,7 +10,8 @@
10
10
  import { type JsonModelState } from '@eclipse-glsp/server';
11
11
  import { injectable } from 'inversify';
12
12
  import { type AstNode } from '@hydranium/langium';
13
- import { type TransferElement } from '@hydranium/protocol';
13
+ import { type BaseVersion, type TransferElement, type VersionedModel } from '@hydranium/protocol';
14
+ import { type OperationTransition } from '../command/hydranium-glsp-operation-command.js';
14
15
  import { AbstractHydraniumGlspState } from './abstract-hydranium-glsp-state.js';
15
16
  import { reconcileSourceModelWrite } from './reconcile-source-model-write.js';
16
17
 
@@ -27,16 +28,16 @@ import { reconcileSourceModelWrite } from './reconcile-source-model-write.js';
27
28
  * field-level shape is what lets `fast-json-patch` diff per field, so undo /
28
29
  * redo and forward-write reconcile act per field instead of clobbering the
29
30
  * whole document.
30
- * - {@link baseline} — the last in-sync projection, captured on every
31
- * {@link setSourceRoot}. A forward-write conflict reconciles the user's
32
- * intent (baseline → attempted) against the fresh server root.
31
+ * - {@link base} — the last in-sync projection, taken on every capture of
32
+ * the built root. A forward-write conflict reconciles the user's
33
+ * intent (base → ours) against the server's current root (theirs).
33
34
  * - {@link updateSourceModel} — the concrete reconcile template: persist,
34
35
  * and on a `ConflictError` consult the injected `conflictResolver` and act
35
- * on the merged / no-op / conflict / unavailable outcome.
36
+ * on its outcome.
36
37
  * - {@link persist} / {@link refetch} — the only I/O seams. Defaults route
37
- * through `ModelService.update` / `.validated` (reachable from the base via
38
- * `sharedServices.model`); adopters whose document round-trip differs
39
- * override these without touching the orchestration.
38
+ * through the diagram session's `update` and `ModelService.validated`;
39
+ * adopters whose document round-trip differs override these without
40
+ * touching the orchestration.
40
41
  *
41
42
  * Adopters whose source model is whole-document text (a bare `{ text }`
42
43
  * source model) or that are read-only do NOT extend this class — they extend
@@ -51,13 +52,10 @@ export class ReconcilingTransferHydraniumGlspState<TRoot extends AstNode, TSourc
51
52
  implements JsonModelState<TSourceModel>
52
53
  {
53
54
  /**
54
- * Last in-sync source-model projection, captured on every
55
- * {@link setSourceRoot} (initial load + post-update). The baseline a
56
- * forward-write conflict reconciles against: it stays the pre-command
57
- * state because operation handlers mutate `_sourceRoot` in place during
58
- * `execute` while `setSourceRoot` only re-runs once the write commits.
55
+ * The projection a forward-write conflict reconciles the user's intent
56
+ * from, taken from the built root on every capture.
59
57
  */
60
- protected baseline?: TSourceModel;
58
+ protected base!: TSourceModel;
61
59
 
62
60
  /**
63
61
  * Persisted-shape projection of the current source root, consumed by GLSP's
@@ -68,65 +66,89 @@ export class ReconcilingTransferHydraniumGlspState<TRoot extends AstNode, TSourc
68
66
  * Synchronous — the encoder walks the in-memory AST without serialising.
69
67
  */
70
68
  get sourceModel(): TSourceModel {
71
- return this.sharedServices.model.TransferEncoder.toTransfer(this._sourceRoot, 'grammar') as unknown as TSourceModel;
69
+ return this.projectRoot(this.sourceRoot);
72
70
  }
73
71
 
74
- override setSourceRoot(uri: string, root: TRoot): void {
75
- super.setSourceRoot(uri, root);
76
- this.baseline = this.sourceModel;
72
+ /**
73
+ * The source model of `root`. The single projection seam: the source
74
+ * model, the refetch and {@link normalizeTransition} all project through it.
75
+ */
76
+ protected projectRoot(root: AstNode): TSourceModel {
77
+ return this.sharedServices.model.TransferEncoder.toTransfer(root, 'grammar') as unknown as TSourceModel;
78
+ }
79
+
80
+ /** Both ends serialized for the source document and parsed back, unless they are equal. */
81
+ override async normalizeTransition(transition: OperationTransition<TSourceModel>): Promise<OperationTransition<TSourceModel>> {
82
+ if (JSON.stringify(transition.from) === JSON.stringify(transition.to)) {
83
+ return transition;
84
+ }
85
+ return {
86
+ from: this.projectRoot(await this.roundTrip(this._sourceUri, transition.from)),
87
+ to: this.projectRoot(await this.roundTrip(this._sourceUri, transition.to))
88
+ };
89
+ }
90
+
91
+ protected override captureSourceRoot(uri: string, root: TRoot): void {
92
+ super.captureSourceRoot(uri, root);
93
+ this.base = this.sourceModel;
77
94
  }
78
95
 
79
96
  /**
80
97
  * Persist `model` back to the document store, then capture the resulting
81
- * AST root. On a `ConflictError` (the based-on version was superseded),
82
- * reconcile the user's intent against the fresh server root via the bound
98
+ * AST root. On a `ConflictError` (the base version was superseded),
99
+ * reconcile the user's intent against the server's current root via the bound
83
100
  * `conflictResolver` and act on the outcome — one declarative policy
84
101
  * (force = last-writer-wins, reconciling = field-level merge) shared with
85
102
  * undo / redo.
86
103
  */
87
- async updateSourceModel(model: TSourceModel, version?: number): Promise<void> {
104
+ async updateSourceModel(model: TSourceModel, baseVersion: BaseVersion = this.baseVersion): Promise<void> {
88
105
  // Orchestration lives in `reconcileSourceModelWrite` so the multi-document
89
106
  // state gets the identical conflict handling; this method supplies only
90
107
  // the single-document meaning of persist / project.
91
- return reconcileSourceModelWrite<TSourceModel>(model, version, {
92
- persist: async (candidate, baseVersion) => {
93
- const { root } = await this.persist(candidate, baseVersion);
94
- this.setSourceRoot(this._sourceUri, root);
108
+ return reconcileSourceModelWrite<TSourceModel>(model, baseVersion, {
109
+ persist: async (candidate, candidateBaseVersion) => {
110
+ const { root } = await this.persist(candidate, candidateBaseVersion);
111
+ this.captureWrittenRoot(root);
95
112
  },
96
113
  refetch: () => this.refetch(),
97
- baseline: this.baseline,
114
+ base: this.base,
98
115
  conflictResolver: this.conflictResolver,
99
116
  logger: this.logger,
100
- onConflictDropped: () => this.refreshSourceRoot()
117
+ onConflictDropped: () => this.writeDropped(),
118
+ onAlreadyApplied: () => this.captureBuiltRoot(),
119
+ maxWrites: this.maxSourceModelWrites
101
120
  });
102
121
  }
103
122
 
104
123
  /**
105
124
  * Persist hook — the only write-side I/O. Default routes the structured
106
- * model through `ModelService.update` (serialize → reparse), opting into
107
- * the `ConflictError` gate when `version` is given. Adopters whose document
108
- * round-trip differs override this; the orchestration in
125
+ * model through the diagram session's `update` (serialize → reparse),
126
+ * opting into the `ConflictError` gate unless `baseVersion` is `'any'`,
127
+ * and throws without a session. Adopters whose document
128
+ * round-trip differs override this, and {@link normalizeTransition}
129
+ * alongside so undo compares what this writes; the orchestration in
109
130
  * {@link updateSourceModel} is unchanged.
110
131
  */
111
- protected async persist(model: TSourceModel, baseVersion?: number): Promise<{ root: TRoot }> {
112
- const document = await this.sharedServices.model.ModelService.update({
113
- uri: this._sourceUri,
114
- model,
115
- clientId: this.clientId,
116
- baseVersion
117
- });
132
+ protected async persist(model: TSourceModel, baseVersion: BaseVersion): Promise<{ root: TRoot }> {
133
+ const document = await this.requireModelSession().update({ uri: this._sourceUri, model, baseVersion });
118
134
  return document as unknown as { root: TRoot };
119
135
  }
120
136
 
121
137
  /**
122
- * Refetch hook — reads the current settled server root as a persisted-shape
123
- * projection (or `undefined` when unavailable). The `conflictResolver`
124
- * replays the user's intent against this fresh state. Default uses
125
- * `ModelService.validated` + the framework encoder's `'grammar'` mode;
126
- * adopters override alongside {@link persist}.
138
+ * Refetch hook — the stored text parsed afresh, as a persisted-shape
139
+ * projection with the version of that text (or `undefined` when
140
+ * unavailable): theirs, which the `conflictResolver` replays the user's
141
+ * intent onto. Default uses {@link AbstractHydraniumGlspState.readCurrentRoot}
142
+ * + the framework encoder's `'grammar'` mode; adopters override alongside
143
+ * {@link persist}.
127
144
  */
128
- protected async refetch(): Promise<TSourceModel | undefined> {
129
- const fresh = await this.sharedServices.model.ModelService.validated(this._sourceUri).catch(() => undefined);
130
- return fresh ? (this.sharedServices.model.TransferEncoder.toTransfer(fresh.root, 'grammar') as unknown as TSourceModel) : undefined;
145
+ protected async refetch(): Promise<VersionedModel<TSourceModel> | undefined> {
146
+ const theirs = await this.readCurrentRoot(this._sourceUri);
147
+ return (
148
+ theirs && {
149
+ model: this.projectRoot(theirs.root),
150
+ baseVersion: theirs.version
151
+ }
152
+ );
131
153
  }
132
154
  }
@@ -0,0 +1,106 @@
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 Action,
12
+ ActionDispatcher,
13
+ type ClientSession,
14
+ type ClientSessionInitializer,
15
+ type ClientSessionListener,
16
+ ClientSessionManager,
17
+ EditMode,
18
+ ModelState,
19
+ SetEditModeAction,
20
+ StatusAction
21
+ } from '@eclipse-glsp/server';
22
+ import { type AstNode } from '@hydranium/langium';
23
+ import { DisposableCollection, describeErrorWithStack } from '@hydranium/protocol';
24
+ import { inject, injectable } from 'inversify';
25
+ import { type AbstractHydraniumGlspState } from '../state/abstract-hydranium-glsp-state.js';
26
+ import { type DiagramStatusEntry } from '../state/diagram-status.js';
27
+
28
+ /**
29
+ * Sends the client what {@link AbstractHydraniumGlspState.onStatusChanged}
30
+ * announces: the current status for its status overlay and the edit mode.
31
+ *
32
+ * The only sender of either action. A status or edit mode dispatched from
33
+ * anywhere else is overwritten by the next change, and the state's account of
34
+ * why the diagram is read-only no longer matches the client's.
35
+ */
36
+ export interface DiagramStatusReporter extends ClientSessionInitializer {
37
+ /**
38
+ * The client switched itself to `editMode`, as GLSP's client does before it
39
+ * forwards an edit-mode request. Sends the effective mode back if it differs.
40
+ */
41
+ clientEditModeChanged(editMode: EditMode): void;
42
+ }
43
+ export const DiagramStatusReporter = Symbol('DiagramStatusReporter');
44
+
45
+ @injectable()
46
+ export class DefaultDiagramStatusReporter implements DiagramStatusReporter, ClientSessionListener {
47
+ @inject(ModelState) protected readonly state!: AbstractHydraniumGlspState<AstNode, unknown>;
48
+ @inject(ActionDispatcher) protected readonly actionDispatcher!: ActionDispatcher;
49
+ @inject(ClientSessionManager) protected readonly sessionManager!: ClientSessionManager;
50
+
51
+ protected readonly toDispose = new DisposableCollection();
52
+ /** What the client shows; it starts editable with an empty overlay. */
53
+ protected sentStatus = statusKey(undefined);
54
+ protected sentEditMode: string = EditMode.EDITABLE;
55
+
56
+ initialize(): void {
57
+ this.sessionManager.addListener(this, this.state.clientId);
58
+ this.toDispose.push(this.state.onStatusChanged(() => this.send()));
59
+ }
60
+
61
+ clientEditModeChanged(editMode: EditMode): void {
62
+ this.sentEditMode = editMode;
63
+ this.send();
64
+ }
65
+
66
+ protected send(): void {
67
+ this.actionDispatcher
68
+ .dispatchAll(this.changedActions())
69
+ .catch((error: unknown) => this.state.logger.warn(`Could not send the diagram status: ${describeErrorWithStack(error)}`));
70
+ }
71
+
72
+ /** The actions that bring the client up to date, recording them as sent. */
73
+ protected changedActions(): Action[] {
74
+ const actions: Action[] = [];
75
+ const current = this.state.currentStatus;
76
+ if (statusKey(current) !== this.sentStatus) {
77
+ this.sentStatus = statusKey(current);
78
+ actions.push(
79
+ current?.message === undefined
80
+ ? // `NONE` is the client's spelling for "clear": an empty message at any
81
+ // other severity leaves an empty band with an icon on the canvas.
82
+ StatusAction.create('', { severity: 'NONE' })
83
+ : StatusAction.create(current.message, { severity: current.severity ?? 'INFO' })
84
+ );
85
+ }
86
+ if (this.state.editMode !== this.sentEditMode) {
87
+ this.sentEditMode = this.state.editMode;
88
+ actions.push(SetEditModeAction.create(this.state.editMode));
89
+ }
90
+ if (actions.length > 0) {
91
+ this.state.logger.debug(
92
+ `Sending status ${this.sentStatus} (from ${this.state.currentStatusSource ?? 'none'}); ` +
93
+ `edit mode ${this.sentEditMode} (held by ${this.state.readonlyStatuses.join(', ') || 'none'})`
94
+ );
95
+ }
96
+ return actions;
97
+ }
98
+
99
+ sessionDisposed(_clientSession: ClientSession): void {
100
+ this.toDispose.dispose();
101
+ }
102
+ }
103
+
104
+ function statusKey(entry: DiagramStatusEntry | undefined): string {
105
+ return entry?.message === undefined ? 'none' : `${entry.severity ?? 'INFO'} "${entry.message}"`;
106
+ }
@@ -0,0 +1,43 @@
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 { ModelState, type ProgressMonitor, RequestModelActionHandler } from '@eclipse-glsp/server';
11
+ import { type AstNode } from '@hydranium/langium';
12
+ import { inject, injectable } from 'inversify';
13
+ import { type AbstractHydraniumGlspState } from '../state/abstract-hydranium-glsp-state.js';
14
+ import { DiagramStatus } from '../state/diagram-status.js';
15
+
16
+ /**
17
+ * Reports model loading as {@link DiagramStatus.MODEL_LOAD} rather than
18
+ * writing the client's status overlay directly, whose closing clear would
19
+ * erase a status set while the model loaded.
20
+ *
21
+ * The status is withdrawn on a failed load as well: GLSP rethrows from
22
+ * `handleModelLoadingError` before it reports the load finished, which would
23
+ * leave the loading message standing.
24
+ */
25
+ @injectable()
26
+ export class HydraniumGlspRequestModelActionHandler extends RequestModelActionHandler {
27
+ @inject(ModelState) declare protected modelState: AbstractHydraniumGlspState<AstNode, unknown>;
28
+
29
+ protected override reportModelLoading(message: string): ProgressMonitor | undefined {
30
+ this.modelState.setStatus(DiagramStatus.MODEL_LOAD, { message, severity: 'INFO' });
31
+ return this.progressService.start(message);
32
+ }
33
+
34
+ protected override reportModelLoadingFinished(monitor?: ProgressMonitor): void {
35
+ this.modelState.setStatus(DiagramStatus.MODEL_LOAD, undefined);
36
+ monitor?.end();
37
+ }
38
+
39
+ protected override handleModelLoadingError(error: unknown, monitor?: ProgressMonitor): void {
40
+ this.modelState.setStatus(DiagramStatus.MODEL_LOAD, undefined);
41
+ super.handleModelLoadingError(error, monitor);
42
+ }
43
+ }
@@ -0,0 +1,38 @@
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 Action, ClientAction, EditMode, ModelState, type SetEditModeAction, SetEditModeActionHandler } from '@eclipse-glsp/server';
11
+ import { type AstNode } from '@hydranium/langium';
12
+ import { inject, injectable } from 'inversify';
13
+ import { type AbstractHydraniumGlspState } from '../state/abstract-hydranium-glsp-state.js';
14
+ import { DiagramStatus } from '../state/diagram-status.js';
15
+ import { DiagramStatusReporter } from './diagram-status-reporter.js';
16
+
17
+ /**
18
+ * Records a client's request for a read-only diagram as
19
+ * {@link DiagramStatus.CLIENT_REQUEST}, instead of assigning the edit mode
20
+ * over every other status that holds it.
21
+ *
22
+ * Only an action the client sent counts. GLSP also runs this handler for the
23
+ * edit mode the server itself sends, and recording that as a request would
24
+ * keep the diagram read-only after its own reason has gone.
25
+ */
26
+ @injectable()
27
+ export class HydraniumGlspSetEditModeActionHandler extends SetEditModeActionHandler {
28
+ @inject(ModelState) declare protected modelState: AbstractHydraniumGlspState<AstNode, unknown>;
29
+ @inject(DiagramStatusReporter) protected readonly reporter!: DiagramStatusReporter;
30
+
31
+ override async execute(action: SetEditModeAction): Promise<Action[]> {
32
+ if (ClientAction.is(action)) {
33
+ this.modelState.setStatus(DiagramStatus.CLIENT_REQUEST, action.editMode === EditMode.READONLY ? { readonly: true } : undefined);
34
+ this.reporter.clientEditModeChanged(action.editMode);
35
+ }
36
+ return [];
37
+ }
38
+ }
@@ -0,0 +1,12 @@
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 './diagram-status-reporter.js';
11
+ export * from './hydranium-glsp-request-model-action-handler.js';
12
+ export * from './hydranium-glsp-set-edit-mode-action-handler.js';
@@ -0,0 +1,40 @@
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 Action, SaveModelAction, SaveModelActionHandler } from '@eclipse-glsp/server';
11
+ import { ModelSavedAction, RequestSaveModelAction } from '@hydranium/protocol';
12
+ import { injectable } from 'inversify';
13
+
14
+ /**
15
+ * Answers a {@link RequestSaveModelAction}: saves exactly as GLSP's
16
+ * {@link SaveModelActionHandler} saves a `SaveModelAction` carrying the same
17
+ * `fileUri`, dirty-state change included, then replies with a
18
+ * {@link ModelSavedAction}.
19
+ *
20
+ * A failed save throws, and the server turns the throw into a `RejectAction`
21
+ * for the request. The save handler's own `SaveModelAction` stays registered,
22
+ * so a client that sends it is unaffected.
23
+ *
24
+ * `AbstractHydraniumGlspDiagramModule` registers it. A diagram module that does
25
+ * not extend that base adds it in `configureActionHandlers`. Without it no
26
+ * handler is advertised for the request, and the Theia client saves as GLSP's
27
+ * saveable does: a save nothing answers, so a failed one surfaces only as a
28
+ * timeout, and a window can close over a save still under way.
29
+ */
30
+ @injectable()
31
+ export class HydraniumGlspRequestSaveModelActionHandler extends SaveModelActionHandler {
32
+ override actionKinds = [RequestSaveModelAction.KIND];
33
+
34
+ // Typed `Action`, the one parameter type both this kind and the base's accept.
35
+ override async execute(action: Action): Promise<Action[]> {
36
+ const fileUri = 'fileUri' in action && typeof action.fileUri === 'string' ? action.fileUri : undefined;
37
+ const actions = await super.execute(SaveModelAction.create({ fileUri }));
38
+ return [...actions, ModelSavedAction.create()];
39
+ }
40
+ }