@hydranium/core 1.0.0-next.7 → 1.0.0-next.71

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 (286) hide show
  1. package/lib/documents/ast-document-manager.d.ts +58 -5
  2. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  3. package/lib/documents/ast-document-manager.js +50 -18
  4. package/lib/documents/ast-document-manager.js.map +1 -1
  5. package/lib/documents/client-ids.d.ts +5 -19
  6. package/lib/documents/client-ids.d.ts.map +1 -1
  7. package/lib/documents/client-ids.js +5 -19
  8. package/lib/documents/client-ids.js.map +1 -1
  9. package/lib/documents/hydranium-text-documents.d.ts +51 -17
  10. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  11. package/lib/documents/hydranium-text-documents.js +136 -40
  12. package/lib/documents/hydranium-text-documents.js.map +1 -1
  13. package/lib/documents/language-client-text-shadow.d.ts +41 -9
  14. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  15. package/lib/documents/language-client-text-shadow.js +56 -11
  16. package/lib/documents/language-client-text-shadow.js.map +1 -1
  17. package/lib/documents/self-save-registry.d.ts +37 -6
  18. package/lib/documents/self-save-registry.d.ts.map +1 -1
  19. package/lib/documents/self-save-registry.js +28 -15
  20. package/lib/documents/self-save-registry.js.map +1 -1
  21. package/lib/index.d.ts +2 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +5 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
  26. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  27. package/lib/langium/ast-extension/ast-node-builder.js +60 -22
  28. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  29. package/lib/langium/bootstrap.d.ts.map +1 -1
  30. package/lib/langium/bootstrap.js +16 -9
  31. package/lib/langium/bootstrap.js.map +1 -1
  32. package/lib/langium/config/configuration-provider.d.ts +81 -0
  33. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  34. package/lib/langium/config/configuration-provider.js +112 -0
  35. package/lib/langium/config/configuration-provider.js.map +1 -0
  36. package/lib/langium/config/index.d.ts +1 -0
  37. package/lib/langium/config/index.d.ts.map +1 -1
  38. package/lib/langium/config/index.js +1 -0
  39. package/lib/langium/config/index.js.map +1 -1
  40. package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
  41. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  42. package/lib/langium/diagnostics/lsp-logger.js +40 -6
  43. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  44. package/lib/langium/document-builder/build-pipeline-integration.d.ts +13 -1
  45. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  46. package/lib/langium/document-builder/build-pipeline-integration.js +1 -1
  47. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  48. package/lib/langium/document-builder/build-session.d.ts +93 -0
  49. package/lib/langium/document-builder/build-session.d.ts.map +1 -0
  50. package/lib/langium/document-builder/build-session.js +72 -0
  51. package/lib/langium/document-builder/build-session.js.map +1 -0
  52. package/lib/langium/document-builder/document-builder.d.ts +150 -7
  53. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  54. package/lib/langium/document-builder/document-builder.js +298 -11
  55. package/lib/langium/document-builder/document-builder.js.map +1 -1
  56. package/lib/langium/document-builder/index.d.ts +1 -0
  57. package/lib/langium/document-builder/index.d.ts.map +1 -1
  58. package/lib/langium/document-builder/index.js +1 -0
  59. package/lib/langium/document-builder/index.js.map +1 -1
  60. package/lib/langium/integration-services.d.ts +45 -1
  61. package/lib/langium/integration-services.d.ts.map +1 -1
  62. package/lib/langium/integration-services.js +10 -5
  63. package/lib/langium/integration-services.js.map +1 -1
  64. package/lib/langium/integrity/integrity-service.d.ts +5 -0
  65. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  66. package/lib/langium/integrity/integrity-service.js +34 -1
  67. package/lib/langium/integrity/integrity-service.js.map +1 -1
  68. package/lib/langium/keys/containment.d.ts +64 -0
  69. package/lib/langium/keys/containment.d.ts.map +1 -0
  70. package/lib/langium/keys/containment.js +53 -0
  71. package/lib/langium/keys/containment.js.map +1 -0
  72. package/lib/langium/keys/index.d.ts +1 -0
  73. package/lib/langium/keys/index.d.ts.map +1 -1
  74. package/lib/langium/keys/index.js +1 -0
  75. package/lib/langium/keys/index.js.map +1 -1
  76. package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
  77. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  78. package/lib/langium/keys/name-based-key-provider.js +4 -0
  79. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  80. package/lib/langium/model-service/model-service.d.ts +103 -7
  81. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  82. package/lib/langium/model-service/model-service.js +70 -89
  83. package/lib/langium/model-service/model-service.js.map +1 -1
  84. package/lib/langium/module.d.ts +82 -13
  85. package/lib/langium/module.d.ts.map +1 -1
  86. package/lib/langium/module.js +26 -12
  87. package/lib/langium/module.js.map +1 -1
  88. package/lib/langium/naming/name-provider.d.ts +10 -0
  89. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  90. package/lib/langium/naming/name-provider.js.map +1 -1
  91. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  92. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  93. package/lib/langium/naming/name-separator-validation.js +30 -1
  94. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  95. package/lib/langium/residency/cst-residency-service.d.ts +31 -9
  96. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  97. package/lib/langium/residency/cst-residency-service.js +13 -56
  98. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  99. package/lib/langium/scope/hydranium-scope-provider.d.ts +32 -13
  100. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  101. package/lib/langium/scope/hydranium-scope-provider.js +37 -20
  102. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  103. package/lib/langium/shared-services.d.ts +15 -2
  104. package/lib/langium/shared-services.d.ts.map +1 -1
  105. package/lib/langium/shared-services.js.map +1 -1
  106. package/lib/langium/transfer/transfer-encoder.d.ts +33 -2
  107. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  108. package/lib/langium/transfer/transfer-encoder.js +14 -3
  109. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  110. package/lib/langium/validation/document-validator.d.ts +145 -6
  111. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  112. package/lib/langium/validation/document-validator.js +211 -4
  113. package/lib/langium/validation/document-validator.js.map +1 -1
  114. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  115. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  116. package/lib/langium/workspace/document-uri-policy.js +3 -4
  117. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  118. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  119. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  120. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  121. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  122. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  123. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  124. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  125. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  126. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +8 -0
  127. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  128. package/lib/langium/workspace/in-memory-file-system-provider.js +11 -2
  129. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  130. package/lib/langium/workspace/initialize-workspace.d.ts +22 -2
  131. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  132. package/lib/langium/workspace/initialize-workspace.js +13 -5
  133. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  134. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  135. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  136. package/lib/langium/workspace/langium-documents.js +83 -28
  137. package/lib/langium/workspace/langium-documents.js.map +1 -1
  138. package/lib/locale/index.d.ts +10 -0
  139. package/lib/locale/index.d.ts.map +1 -0
  140. package/lib/locale/index.js +10 -0
  141. package/lib/locale/index.js.map +1 -0
  142. package/lib/locale/server-locale.d.ts +73 -0
  143. package/lib/locale/server-locale.d.ts.map +1 -0
  144. package/lib/locale/server-locale.js +61 -0
  145. package/lib/locale/server-locale.js.map +1 -0
  146. package/lib/lsp/hydranium-document-update-handler.js +1 -1
  147. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  148. package/lib/lsp/index.d.ts +1 -1
  149. package/lib/lsp/index.d.ts.map +1 -1
  150. package/lib/lsp/index.js +1 -1
  151. package/lib/lsp/index.js.map +1 -1
  152. package/lib/lsp/lsp-latency.d.ts +39 -0
  153. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  154. package/lib/lsp/lsp-latency.js +58 -0
  155. package/lib/lsp/lsp-latency.js.map +1 -0
  156. package/lib/messages/carriers.d.ts +54 -0
  157. package/lib/messages/carriers.d.ts.map +1 -0
  158. package/lib/messages/carriers.js +62 -0
  159. package/lib/messages/carriers.js.map +1 -0
  160. package/lib/messages/index.d.ts +25 -0
  161. package/lib/messages/index.d.ts.map +1 -0
  162. package/lib/messages/index.js +25 -0
  163. package/lib/messages/index.js.map +1 -0
  164. package/lib/messages/renderer.d.ts +136 -0
  165. package/lib/messages/renderer.d.ts.map +1 -0
  166. package/lib/messages/renderer.js +159 -0
  167. package/lib/messages/renderer.js.map +1 -0
  168. package/lib/node/latency-from-env.d.ts +1 -1
  169. package/lib/node/latency-from-env.js +1 -1
  170. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  171. package/lib/node/node-file-system-provider.js +1 -40
  172. package/lib/node/node-file-system-provider.js.map +1 -1
  173. package/lib/node/profiling-run.d.ts +2 -2
  174. package/lib/node/profiling-run.js +1 -1
  175. package/lib/node/profiling-run.js.map +1 -1
  176. package/lib/node/rename-over-open-readers.d.ts +52 -0
  177. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  178. package/lib/node/rename-over-open-readers.js +66 -0
  179. package/lib/node/rename-over-open-readers.js.map +1 -0
  180. package/lib/testing/fake-document.d.ts +14 -3
  181. package/lib/testing/fake-document.d.ts.map +1 -1
  182. package/lib/testing/fake-document.js +18 -3
  183. package/lib/testing/fake-document.js.map +1 -1
  184. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  185. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  186. package/lib/testing/make-noop-shared-services.js +28 -2
  187. package/lib/testing/make-noop-shared-services.js.map +1 -1
  188. package/lib/testing/make-test-services.d.ts +31 -4
  189. package/lib/testing/make-test-services.d.ts.map +1 -1
  190. package/lib/testing/make-test-services.js +19 -3
  191. package/lib/testing/make-test-services.js.map +1 -1
  192. package/lib/testing/node/spawned-server.d.ts +14 -0
  193. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  194. package/lib/testing/node/spawned-server.js +33 -1
  195. package/lib/testing/node/spawned-server.js.map +1 -1
  196. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  197. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  198. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  199. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  200. package/lib/testing/playwright/index.d.ts +1 -0
  201. package/lib/testing/playwright/index.d.ts.map +1 -1
  202. package/lib/testing/playwright/index.js +1 -0
  203. package/lib/testing/playwright/index.js.map +1 -1
  204. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  205. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  206. package/lib/testing/playwright/server-log-capture.js +21 -4
  207. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  208. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  209. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  210. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  211. package/lib/testing/stub-ast-document-manager.d.ts +3 -4
  212. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  213. package/lib/testing/stub-ast-document-manager.js +2 -5
  214. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  215. package/lib/testing/stub-document-builder.d.ts +8 -0
  216. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  217. package/lib/testing/stub-document-builder.js +22 -3
  218. package/lib/testing/stub-document-builder.js.map +1 -1
  219. package/lib/testing/stub-model-service.d.ts +1 -1
  220. package/lib/testing/stub-model-service.d.ts.map +1 -1
  221. package/lib/testing/stub-model-service.js +2 -2
  222. package/lib/testing/stub-model-service.js.map +1 -1
  223. package/package.json +17 -8
  224. package/src/documents/ast-document-manager.ts +99 -23
  225. package/src/documents/client-ids.ts +5 -22
  226. package/src/documents/hydranium-text-documents.ts +161 -55
  227. package/src/documents/language-client-text-shadow.ts +59 -11
  228. package/src/documents/self-save-registry.ts +52 -13
  229. package/src/index.ts +5 -0
  230. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  231. package/src/langium/bootstrap.ts +16 -9
  232. package/src/langium/config/configuration-provider.ts +119 -0
  233. package/src/langium/config/index.ts +1 -0
  234. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  235. package/src/langium/document-builder/build-pipeline-integration.ts +14 -1
  236. package/src/langium/document-builder/build-session.ts +87 -0
  237. package/src/langium/document-builder/document-builder.ts +339 -11
  238. package/src/langium/document-builder/index.ts +1 -0
  239. package/src/langium/integration-services.ts +67 -6
  240. package/src/langium/integrity/integrity-service.ts +35 -1
  241. package/src/langium/keys/containment.ts +87 -0
  242. package/src/langium/keys/index.ts +1 -0
  243. package/src/langium/keys/name-based-key-provider.ts +4 -0
  244. package/src/langium/model-service/model-service.ts +145 -22
  245. package/src/langium/module.ts +104 -19
  246. package/src/langium/naming/name-provider.ts +10 -0
  247. package/src/langium/naming/name-separator-validation.ts +34 -5
  248. package/src/langium/residency/cst-residency-service.ts +36 -12
  249. package/src/langium/scope/hydranium-scope-provider.ts +40 -22
  250. package/src/langium/shared-services.ts +19 -2
  251. package/src/langium/transfer/transfer-encoder.ts +45 -3
  252. package/src/langium/validation/document-validator.ts +255 -8
  253. package/src/langium/workspace/document-uri-policy.ts +3 -4
  254. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  255. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  256. package/src/langium/workspace/in-memory-file-system-provider.ts +13 -3
  257. package/src/langium/workspace/initialize-workspace.ts +34 -5
  258. package/src/langium/workspace/langium-documents.ts +106 -33
  259. package/src/locale/index.ts +10 -0
  260. package/src/locale/server-locale.ts +86 -0
  261. package/src/lsp/hydranium-document-update-handler.ts +1 -1
  262. package/src/lsp/index.ts +1 -1
  263. package/src/lsp/lsp-latency.ts +62 -0
  264. package/src/messages/carriers.ts +78 -0
  265. package/src/messages/index.ts +27 -0
  266. package/src/messages/renderer.ts +204 -0
  267. package/src/node/latency-from-env.ts +1 -1
  268. package/src/node/node-file-system-provider.ts +1 -41
  269. package/src/node/profiling-run.ts +3 -3
  270. package/src/node/rename-over-open-readers.ts +78 -0
  271. package/src/testing/fake-document.ts +22 -4
  272. package/src/testing/make-noop-shared-services.ts +50 -2
  273. package/src/testing/make-test-services.ts +49 -7
  274. package/src/testing/node/spawned-server.ts +35 -1
  275. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  276. package/src/testing/playwright/index.ts +1 -0
  277. package/src/testing/playwright/server-log-capture.ts +24 -5
  278. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  279. package/src/testing/stub-ast-document-manager.ts +5 -10
  280. package/src/testing/stub-document-builder.ts +32 -3
  281. package/src/testing/stub-model-service.ts +2 -2
  282. package/lib/lsp/instrument-connection.d.ts +0 -36
  283. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  284. package/lib/lsp/instrument-connection.js +0 -62
  285. package/lib/lsp/instrument-connection.js.map +0 -1
  286. package/src/lsp/instrument-connection.ts +0 -66
@@ -7,30 +7,34 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { type Clock, type Logger, type Project, type Tracer, type TransferDiagnostic, NoopLogger, SystemClock } from '@hydranium/protocol';
10
+ import { type Clock, type Logger, type Project, type Tracer, NoopLogger, SystemClock } from '@hydranium/protocol';
11
+ import { DefaultServerLocale, type ServerLocale } from '../locale/server-locale.js';
12
+ import { DefaultMessageRenderer, type MessageRenderer } from '../messages/renderer.js';
11
13
  import { ServerTracer } from './diagnostics/server-tracer.js';
12
14
  import { HydraniumLangiumProfiler } from './diagnostics/hydranium-langium-profiler.js';
13
15
  import { type AstNode, type Module } from '@hydranium/langium';
14
16
  import { type DefaultSharedModuleContext, type LangiumSharedServices, type PartialLangiumSharedServices } from '@hydranium/langium/lsp';
15
17
  import { type TextDocument } from 'vscode-languageserver-textdocument';
16
- import { ModelService } from './model-service/model-service.js';
18
+ import { DefaultModelService, type ModelService } from './model-service/model-service.js';
17
19
  import { type ProjectManager } from './project/project-manager.js';
18
20
  import { SingleProjectManager } from './project/single-project-manager.js';
19
- import { TransferEncoder } from './transfer/transfer-encoder.js';
21
+ import { DefaultTransferEncoder, type TransferEncoder } from './transfer/transfer-encoder.js';
20
22
  import { HydraniumDocumentBuilder } from './document-builder/document-builder.js';
21
- import { BuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
23
+ import { DefaultBuildPipelineIntegration, type BuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
22
24
  import { type BuildPhasePassContribution } from './build-phase-pass/build-phase-pass.js';
23
25
  import { type BuildPhasePassService, DefaultBuildPhasePassService } from './build-phase-pass/build-phase-pass-service.js';
24
- import { CstResidencyService } from './residency/index.js';
26
+ import { DefaultCstResidencyService, type CstResidencyService } from './residency/index.js';
27
+ import { HydraniumConfigurationProvider } from './config/configuration-provider.js';
25
28
  import { HydraniumIndexManager } from './workspace/index-manager.js';
26
29
  import { HydraniumWorkspaceManager } from './workspace/hydranium-workspace-manager.js';
27
30
  import { HydraniumWorkspaceLock } from './workspace/hydranium-workspace-lock.js';
28
31
  import { HydraniumLangiumDocumentFactory } from './workspace/hydranium-langium-document-factory.js';
32
+ import { type HydraniumDocumentRegistry, HydraniumLangiumDocuments } from './workspace/langium-documents.js';
29
33
  import { type AdditionalDocumentContribution } from './workspace/additional-document-contribution.js';
30
34
  import { DefaultDocumentUriPolicy, type DocumentUriPolicy } from './workspace/document-uri-policy.js';
31
35
  import { HydraniumTextDocuments } from '../documents/hydranium-text-documents.js';
32
- import { AstDocumentManager, type WritableFileSystemProvider } from '../documents/ast-document-manager.js';
33
- import { SelfSaveRegistry } from '../documents/self-save-registry.js';
36
+ import { DefaultAstDocumentManager, type AstDocumentManager, type WritableFileSystemProvider } from '../documents/ast-document-manager.js';
37
+ import { DefaultSelfSaveRegistry, type SelfSaveRegistry } from '../documents/self-save-registry.js';
34
38
  import { DefaultEmptyFileSystemProvider } from './workspace/file-system-provider.js';
35
39
  import { type ServerLanguageServices } from './language-module.js';
36
40
  import { ExtendedServiceRegistry } from './service-registry.js';
@@ -91,6 +95,24 @@ export interface ServerAddedSharedServices<TProject extends Project = Project> {
91
95
  * plain `DefaultTracer` with their own `MemoryReader`.
92
96
  */
93
97
  Tracer: Tracer;
98
+ /**
99
+ * Renders each user-facing message once, before the server sends it — so
100
+ * every message is rendered by the side that knows the reading user's
101
+ * language. All three heads inherit one diagnostics pass, which is what
102
+ * makes this the single slot an adopter with i18n rebinds; the same binding
103
+ * serves framework codes and their own `defineMessage` codes alike.
104
+ *
105
+ * The framework ships English only and selects no locale, so the default
106
+ * returns every sentence unchanged. Adopters subclass and override
107
+ * `translationsFor`.
108
+ */
109
+ MessageRenderer: MessageRenderer;
110
+ /**
111
+ * The locale an init handed the server, for whoever renders in it. Held
112
+ * apart from the renderer so replacing the renderer cannot drop locale
113
+ * handling.
114
+ */
115
+ ServerLocale: ServerLocale;
94
116
  /**
95
117
  * LSP-bound slots layered on top of Langium's `LangiumSharedLSPServices`.
96
118
  * The framework contributes a single string slot here —
@@ -113,6 +135,12 @@ export interface ServerAddedSharedServices<TProject extends Project = Project> {
113
135
  workspace: {
114
136
  /* override */ TextDocuments: HydraniumTextDocuments<TextDocument>;
115
137
  /* override */ WorkspaceManager: HydraniumWorkspaceManager;
138
+ /**
139
+ * Narrow Langium's registry slot to the framework surface, which adds
140
+ * `createEmptyDocument` — reachable only through this narrowing, and
141
+ * wanted by a scope provider querying a URI before the file exists.
142
+ */
143
+ /* override */ LangiumDocuments: HydraniumDocumentRegistry;
116
144
  /**
117
145
  * Narrow Langium's `IndexManager` slot to the framework subclass, which
118
146
  * layers an `elementsByName` map over Langium's index and exposes
@@ -228,14 +256,22 @@ export interface ServerAddedSharedServices<TProject extends Project = Project> {
228
256
  * heads read both slots from shared services rather than constructor
229
257
  * arguments.
230
258
  *
231
- * Slot types use the upper bounds (`unknown` for encoder map / facade
232
- * generics, {@link TransferDiagnostic} for the encoder's wire-diagnostic
233
- * shape) so adopter subclasses with narrower typed overlays satisfy
234
- * the slot via output-position covariance — `TDiagnostic` appears
235
- * only in encoder return positions.
259
+ * `TransferEncoder` is the INTERFACE, not `DefaultTransferEncoder` — an
260
+ * adopter can therefore REPLACE this declaration (see
261
+ * {@link WithServiceOverrides}) rather than intersect with it, which is what
262
+ * keeps slot resolution independent of the order a services type is written
263
+ * in. `ModelService` is still a class and does not yet have that property.
264
+ *
265
+ * A class in a slot costs two things, both measured. Its `protected` members
266
+ * join every assignability check and are compared NOMINALLY, so a subclass
267
+ * declared against a second physical copy of this package cannot satisfy it.
268
+ * And where the class is generic over a map reached through `keyof`, that
269
+ * parameter is measured INVARIANT — instantiations then relate only when
270
+ * their arguments are mutually assignable, which no adopter map is with the
271
+ * framework's. Neither survives on an interface.
236
272
  */
237
273
  model: {
238
- TransferEncoder: TransferEncoder<unknown, TransferDiagnostic>;
274
+ TransferEncoder: TransferEncoder;
239
275
  ModelService: ModelService<AstNode, unknown>;
240
276
  };
241
277
  /**
@@ -279,6 +315,45 @@ export type ServerSharedServices<TProject extends Project = Project> = Omit<Lang
279
315
  workspace: Omit<LangiumSharedServices['workspace'], 'TextDocuments' | 'DocumentBuilder'>;
280
316
  } & ServerAddedSharedServices<TProject>;
281
317
 
318
+ /**
319
+ * The service-tree namespaces — the keys whose value groups further slots
320
+ * rather than being a slot itself.
321
+ *
322
+ * Consumed by {@link WithServiceOverrides} to know where to merge one level
323
+ * deeper. **Add a namespace to {@link ServerAddedSharedServices} and you must
324
+ * add it here**, and the failure of forgetting is silent in the direction that
325
+ * matters: an unlisted namespace is treated as a leaf, so an adopter overriding
326
+ * one slot inside it replaces the WHOLE namespace and loses the sibling slots
327
+ * with no diagnostic. The reverse mistake — listing a leaf — fails loudly.
328
+ */
329
+ type ServiceNamespace = 'lsp' | 'workspace' | 'model';
330
+
331
+ /**
332
+ * Compose an adopter's service tree so its declarations REPLACE the framework's
333
+ * rather than intersecting with them.
334
+ *
335
+ * Langium composes services by intersection, which accumulates: two
336
+ * declarations of one slot survive as an overload set, and which one a call
337
+ * resolves to depends on the order the intersection was written in — silently,
338
+ * with no diagnostic at the point a reorder changes it. There is no override
339
+ * operator for intersections, so the framework's declaration has to be removed
340
+ * before the adopter's is added. This does that, one level deep for each
341
+ * {@link ServiceNamespace}, so a narrowed slot replaces its framework twin
342
+ * while its siblings survive.
343
+ *
344
+ * Only slots typed as INTERFACES can be replaced this way. A class-typed slot
345
+ * drags its `protected` members into the assignability check — compared
346
+ * nominally — so an adopter subclass does not satisfy it and the framework's
347
+ * declaration cannot be dropped.
348
+ */
349
+ export type WithServiceOverrides<TBase, TOverrides> = Omit<TBase, keyof TOverrides> & {
350
+ [K in keyof TOverrides]: K extends ServiceNamespace
351
+ ? K extends keyof TBase
352
+ ? Omit<TBase[K], keyof TOverrides[K]> & TOverrides[K]
353
+ : TOverrides[K]
354
+ : TOverrides[K];
355
+ };
356
+
282
357
  /**
283
358
  * Construction context for {@link createServerSharedModule}.
284
359
  *
@@ -327,6 +402,8 @@ export function createServerSharedModule(
327
402
  ServiceRegistry: services => new ExtendedServiceRegistry<ServerLanguageServices>(services),
328
403
  Logger: () => new NoopLogger(),
329
404
  Tracer: services => new ServerTracer(services.Logger, services.Clock),
405
+ ServerLocale: services => new DefaultServerLocale(services),
406
+ MessageRenderer: services => new DefaultMessageRenderer(services),
330
407
  // Bind Langium's per-grammar-rule / per-`$type` parse/link/validate
331
408
  // profiler (the data the framework's own `Tracer`/`ProfileSession` passes
332
409
  // cannot produce), routed through our `Logger` and debug-gated. At the
@@ -388,16 +465,24 @@ export function createServerSharedModule(
388
465
  TextDocuments: services => new HydraniumTextDocuments(services),
389
466
  WorkspaceManager: services => new HydraniumWorkspaceManager(services),
390
467
  IndexManager: services => new HydraniumIndexManager(services),
468
+ // Langium's default asks only about sections named after registered
469
+ // language ids, so a section `lsp.configurationRoot` is bound to reads
470
+ // `undefined` forever with no request on the wire.
471
+ ConfigurationProvider: services => new HydraniumConfigurationProvider(services),
391
472
  // Override Langium's factory so a `fromModel` (code-built) virtual
392
473
  // document retains serialized text via the per-language Serializer,
393
474
  // making it re-read-safe through the virtual-aware FileSystemProvider.
394
475
  LangiumDocumentFactory: services => new HydraniumLangiumDocumentFactory(services),
476
+ // Langium's default routes through no identity seam and treats every
477
+ // failed load alike, so leaving this unbound opts a server out of both
478
+ // with nothing to signal it.
479
+ LangiumDocuments: services => new HydraniumLangiumDocuments(services),
395
480
  DocumentBuilder: services => new HydraniumDocumentBuilder(services),
396
481
  // The write-lock scope this marks is inert unless a host installs a
397
482
  // scope tracker — `@hydranium/core/node` does.
398
483
  WorkspaceLock: () => new HydraniumWorkspaceLock(),
399
484
  ProjectManager: services => new SingleProjectManager(services),
400
- SelfSaveRegistry: services => new SelfSaveRegistry(services),
485
+ SelfSaveRegistry: services => new DefaultSelfSaveRegistry(services),
401
486
  DocumentUriPolicy: () => new DefaultDocumentUriPolicy(),
402
487
  // Writable filesystem with two paths:
403
488
  // - Adopter passed `context.fileSystemProvider` and it returned a
@@ -421,20 +506,20 @@ export function createServerSharedModule(
421
506
  }
422
507
  return new DefaultEmptyFileSystemProvider(services);
423
508
  },
424
- AstDocumentManager: services => new AstDocumentManager(services),
509
+ AstDocumentManager: services => new DefaultAstDocumentManager(services),
425
510
  // Eagerly constructed, so its build-phase listeners attach before the
426
511
  // first build.
427
- BuildPipelineIntegration: services => new BuildPipelineIntegration(services),
512
+ BuildPipelineIntegration: services => new DefaultBuildPipelineIntegration(services),
428
513
  BuildPhasePassService: services => new DefaultBuildPhasePassService(services),
429
514
  // Eagerly constructed, so its `Validated` pass registers before the
430
515
  // first build.
431
- CstResidencyService: services => new CstResidencyService(services)
516
+ CstResidencyService: services => new DefaultCstResidencyService(services)
432
517
  },
433
518
  model: {
434
519
  // Generic walker — adopters with a typed `$type → wire shape` overlay
435
520
  // rebind this slot with a subclass.
436
- TransferEncoder: services => new TransferEncoder(services),
437
- ModelService: services => new ModelService(services)
521
+ TransferEncoder: services => new DefaultTransferEncoder(services),
522
+ ModelService: services => new DefaultModelService(services)
438
523
  },
439
524
  // Empty default so `services.buildPhasePasses` always resolves (Langium
440
525
  // throws on access to an unbound slot). The framework's own integrity /
@@ -59,6 +59,16 @@ export interface NameProviderOptions {
59
59
  * an `id` field) override explicitly; the explicit override at
60
60
  * the adopter side documents the grammar's convention rather than
61
61
  * burying it in a framework default.
62
+ *
63
+ * **The override is not optional where `name` is a display LABEL rather
64
+ * than an identifier**, and getting it wrong is quiet at first and then
65
+ * loud. A `name=STRING` holding `"Order.Line"` is legitimate content, but
66
+ * the default reads it as the identifier: `getName` composes a qualified
67
+ * name whose segments cannot be told apart from the label's own dots, and
68
+ * `nameSeparatorCheck` reports `hydranium/core/separator-in-name` on every
69
+ * such node. That diagnostic then names the wrong remedy — it asks for a
70
+ * different character, when the fix is to point this option at the property
71
+ * that really is the identifier.
62
72
  */
63
73
  readonly nameProperties?: readonly string[];
64
74
  }
@@ -8,10 +8,39 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import { type AstNode, type ValidationAcceptor, type ValidationChecks } from '@hydranium/langium';
11
+ import { defineMessage } from '@hydranium/protocol';
12
+ import { acceptMessage } from '../../messages/carriers.js';
11
13
  import { type ServerLanguageServices } from '../language-module.js';
12
14
  import { type ValidationCheckContribution, type ValidationCheckRegistry } from '../validation/validation-contribution.js';
13
15
  import type { NameProvider } from './name-provider.js';
14
16
 
17
+ /**
18
+ * A name value that collides with the qualified-name separator.
19
+ *
20
+ * **The sentence addresses a MODELLER, and the remedy it names is theirs**:
21
+ * where `name` really is the identifier, a different character is the fix and
22
+ * the only one available to whoever is typing.
23
+ *
24
+ * **The adopter-facing failure is a different one, and this message cannot
25
+ * carry it.** With `nameProperties` left at its default `['name']` over a
26
+ * grammar where `name` is a display LABEL — a `STRING` legitimately holding
27
+ * `"Order.Line"` — this fires on every dotted label at once, and "use a
28
+ * different character" is then advice nobody can act on: the content is
29
+ * correct and the configuration is not. A burst of this diagnostic across
30
+ * unrelated nodes is that misconfiguration, not a naming problem, and the fix
31
+ * is `nameProperties`. Widening the sentence to say both was rejected: a
32
+ * modeller cannot act on a DI option, and naming one in an editor squiggle
33
+ * teaches the wrong audience.
34
+ *
35
+ * Unreachable from a grammar whose identifier charset excludes the separator,
36
+ * which is every example here and the reference adopter — so it reads as dead
37
+ * and is not: it guards the configuration, not the content.
38
+ */
39
+ export const SEPARATOR_IN_NAME = defineMessage(
40
+ 'hydranium/core/separator-in-name',
41
+ "Name '{name}' contains the configured name separator '{separator}', which is reserved for qualified-name composition — use a different character."
42
+ );
43
+
15
44
  /**
16
45
  * Per-node validation check: flag name-bearing AST nodes whose name
17
46
  * value contains the configured name separator. Such values produce
@@ -38,11 +67,11 @@ export function nameSeparatorCheck(nameProvider: NameProvider): (node: AstNode,
38
67
  }
39
68
  const separator = nameProvider.nameSeparator;
40
69
  if (ownName.includes(separator)) {
41
- accept(
42
- 'error',
43
- `Name '${ownName}' contains the configured name separator '${separator}', which is reserved for qualified-name composition — use a different character.`,
44
- { node }
45
- );
70
+ // `{ node }` rather than `{ node, property: 'name' }`: moving the
71
+ // squiggle onto the name property is a behaviour change to the one
72
+ // diagnostic every adopter receives by default, so it is decided on its
73
+ // own merits rather than carried in by a mechanical migration.
74
+ acceptMessage(accept, 'error', SEPARATOR_IN_NAME, { node }, { name: ownName, separator });
46
75
  }
47
76
  };
48
77
  }
@@ -24,9 +24,13 @@ import { type ServerSharedServicesMinimal } from '../shared-services.js';
24
24
  /**
25
25
  * Default priority of the CST-residency pass. Deliberately very high so it runs
26
26
  * *last* among `Validated` build-phase passes (the registry iterates priority
27
- * ascending): every other reaction to the build — an adopter's own derived-state
28
- * pass included — settles while the CST is still guaranteed resident, before the
29
- * residency policy arms the shed.
27
+ * ascending).
28
+ *
29
+ * The ordering is defensive, not load-bearing: the pass only arms or cancels an
30
+ * idle timer, so every pass at `Validated` reads a resident CST whichever order
31
+ * they run in, and lowering it changes nothing observable while that holds. It
32
+ * becomes load-bearing the moment the shed stops being deferred — a pass that
33
+ * frees the CST synchronously has to run after every reader of it.
30
34
  */
31
35
  export const CST_RESIDENCY_PASS_PRIORITY = 1_000_000;
32
36
 
@@ -225,9 +229,29 @@ function rehydrateCst(document: LangiumDocument, factory: LangiumDocumentFactory
225
229
  * demand by the identity-preserving graft of {@link rehydrateNode} /
226
230
  * {@link rehydrate}, driven transparently from the framework
227
231
  * `NameProvider.getNameNode` chokepoint, the comment provider, and
228
- * `AbstractHydraniumLangiumDocuments.getOrCreateDocument`.
232
+ * `HydraniumLangiumDocuments.getOrCreateDocument`.
233
+ */
234
+ /**
235
+ * Keeps a document's CST available to the readers that need one, shedding it
236
+ * for documents that have gone idle and re-grafting on demand.
229
237
  */
230
- export class CstResidencyService {
238
+ export interface CstResidencyService {
239
+ /**
240
+ * Ensure `document` has a CST, re-parsing its retained text if it was shed.
241
+ * A resident document is left exactly as it is, so this is safe to call on
242
+ * a hot path and safe to call twice.
243
+ */
244
+ rehydrate(document: LangiumDocument): void;
245
+
246
+ /**
247
+ * Node-level convenience over {@link rehydrate}, cheap enough for an
248
+ * unconditional call: a node whose `$cstNode` is already set returns
249
+ * without resolving its document.
250
+ */
251
+ rehydrateNode(node: AstNode): void;
252
+ }
253
+
254
+ export class DefaultCstResidencyService implements CstResidencyService {
231
255
  protected readonly strategy: CstResidencyStrategy;
232
256
  /** Bytes-per-node for the optional reclaim estimate in traces; `0` disables it. */
233
257
  protected readonly estimatedBytesPerShedNode: number;
@@ -277,8 +301,8 @@ export class CstResidencyService {
277
301
  * until navigation latency on shed files is actually observed to hurt. This
278
302
  * method is where that hook goes.
279
303
  */
280
- rehydrate(document: LangiumDocument): boolean {
281
- return rehydrateCst(document, this.services.workspace.LangiumDocumentFactory);
304
+ rehydrate(document: LangiumDocument): void {
305
+ rehydrateCst(document, this.services.workspace.LangiumDocumentFactory);
282
306
  }
283
307
 
284
308
  /**
@@ -287,11 +311,11 @@ export class CstResidencyService {
287
311
  * `true` without even resolving the document, so hot read paths
288
312
  * (`getNameNode`, comment lookup) call this unconditionally.
289
313
  */
290
- rehydrateNode(node: AstNode): boolean {
314
+ rehydrateNode(node: AstNode): void {
291
315
  if (node.$cstNode !== undefined) {
292
- return true;
316
+ return;
293
317
  }
294
- return this.rehydrate(AstUtils.getDocument(node));
318
+ this.rehydrate(AstUtils.getDocument(node));
295
319
  }
296
320
 
297
321
  /**
@@ -353,8 +377,8 @@ export class CstResidencyService {
353
377
  this.shedTotal += this.shed(document);
354
378
  }
355
379
 
356
- /** Cancel and drop every idle timer — for clean shutdown and deterministic tests. */
357
- dispose(): void {
380
+ /** Cancel every armed shed, so a document that has gone idle keeps its CST. */
381
+ cancelPendingShed(): void {
358
382
  for (const timer of this.idleTimers.values()) {
359
383
  timer.dispose();
360
384
  }
@@ -24,7 +24,6 @@ import {
24
24
  type AstNodeLocator,
25
25
  AstUtils,
26
26
  DefaultScopeProvider,
27
- type LangiumDocuments,
28
27
  MapScope,
29
28
  type ReferenceInfo,
30
29
  type Scope,
@@ -35,9 +34,11 @@ import {
35
34
  WorkspaceCache,
36
35
  stream
37
36
  } from '@hydranium/langium';
37
+ import { buildAstNode } from '../ast-extension/ast-node-builder.js';
38
38
  import { type LogNameOptions } from '../diagnostics/logger.js';
39
39
  import { type HydraniumLanguageServices } from '../language-module.js';
40
40
  import { type NameProvider } from '../naming/name-provider.js';
41
+ import { type HydraniumDocumentRegistry } from '../workspace/langium-documents.js';
41
42
  import { type ScopeExtensionService } from './scope-extension-service.js';
42
43
  import { isTieredDescription } from './scoped-ast-node-description.js';
43
44
 
@@ -143,7 +144,7 @@ export interface ScopeContext {
143
144
  * `reference-candidate-provider.ts` module.
144
145
  */
145
146
  export class HydraniumScopeProvider extends DefaultScopeProvider {
146
- protected readonly langiumDocuments: LangiumDocuments;
147
+ protected readonly langiumDocuments: HydraniumDocumentRegistry;
147
148
  protected readonly astNodeLocator: AstNodeLocator;
148
149
  protected readonly scopeExtensionService: ScopeExtensionService;
149
150
  protected readonly options: HydraniumScopeProviderOptions;
@@ -469,12 +470,13 @@ export class HydraniumScopeProvider extends DefaultScopeProvider {
469
470
  * Convert a protocol-layer {@link ReferenceContext} into a Langium
470
471
  * {@link ReferenceInfo} the rest of the scope/linking machinery accepts.
471
472
  *
472
- * Walks `syntheticPath` by FABRICATING a type-only stub per step, so a
473
- * scope can be queried for an element that does not exist yet — which is
474
- * what synthetic paths are for. The stubs carry no children, so a caller
475
- * that needs to read a collection off the leaf wants
476
- * {@link resolveReferenceSource} instead, which walks the same path into
477
- * the real tree.
473
+ * Walks `syntheticPath` by FABRICATING a stub per step, so a scope can be
474
+ * queried for an element that does not exist yet — which is what synthetic
475
+ * paths are for. A stub carries the grammar's declared defaults and nothing
476
+ * else, so its containment lists are present but EMPTY: a caller that needs
477
+ * to read a populated collection off the leaf wants
478
+ * {@link resolveReferenceSource} instead, which walks the same path into the
479
+ * real tree.
478
480
  *
479
481
  * Throws if the context references an unresolvable source; callers
480
482
  * that prefer a soft failure should catch and fall back to
@@ -486,16 +488,15 @@ export class HydraniumScopeProvider extends DefaultScopeProvider {
486
488
  throw new Error('Invalid reference source');
487
489
  }
488
490
  for (const step of ctx.syntheticPath ?? []) {
489
- container = {
491
+ container = buildAstNode(this.services.shared.AstReflection, step.type, {
490
492
  $container: container,
491
493
  $containerProperty: step.containerProperty,
492
494
  // Part of Langium's own container contract, so a stub that omits it
493
495
  // is an under-specified node: anything reading position off the
494
496
  // chain (a key provider, an adopter scope extension) sees
495
497
  // `undefined` where the caller named a slot.
496
- $containerIndex: step.index,
497
- $type: step.type
498
- };
498
+ $containerIndex: step.index
499
+ });
499
500
  }
500
501
  return {
501
502
  reference: { $refText: '', ref: undefined },
@@ -586,18 +587,35 @@ export class HydraniumScopeProvider extends DefaultScopeProvider {
586
587
  }
587
588
 
588
589
  /**
589
- * Build a transient AST-node stub for a {@link SyntheticSource} request.
590
- * Default: looks up the document via `LangiumDocuments` and uses its parse
591
- * root as the container; returns `undefined` if the document isn't loaded.
592
- * Consumers that need to materialise an empty document on demand (e.g.
593
- * to query the scope before a file exists) override this.
590
+ * Build a transient AST-node stub for a {@link SyntheticSource} request: a
591
+ * node of the requested type, contained by the document at `source.uri`.
592
+ *
593
+ * **The container is materialised when no document is loaded there**, which
594
+ * is the case the source type exists for — a `SyntheticSource` names a node
595
+ * that does not exist yet, and the create-element flow asks at the FOLDER
596
+ * the file is about to be written into. Abstaining there would answer an
597
+ * empty candidate list, which a client cannot tell from "nothing matches".
598
+ *
599
+ * The stand-in parses under THIS provider's grammar, because a folder URI
600
+ * carries no extension for the routing ladder to end on and this provider is
601
+ * bound per grammar. Reaching for `createEmptyDocument(uri)` without the id
602
+ * instead fails inside the parse, on an empty extension rather than on the
603
+ * URI.
604
+ *
605
+ * The materialised document is unregistered and transient; see
606
+ * `createEmptyDocument`. Overriding is still open to a consumer whose
607
+ * "container" is an inner element rather than the parse root, or one that
608
+ * deliberately declines to answer for an absent document.
609
+ *
610
+ * The stub carries the grammar's declared defaults, so an extension reading a
611
+ * containment list off it sees an empty array rather than `undefined`.
594
612
  */
595
613
  protected resolveSyntheticSource(source: SyntheticSource): AstNode | undefined {
596
- const document = this.langiumDocuments.getDocument(UriUtils.toUri(source.uri));
597
- if (!document) {
598
- return undefined;
599
- }
600
- return { $type: source.type, $container: document.parseResult.value };
614
+ const uri = UriUtils.toUri(source.uri);
615
+ const document =
616
+ this.langiumDocuments.getDocument(uri) ??
617
+ this.langiumDocuments.createEmptyDocument(uri, this.services.LanguageMetaData.languageId);
618
+ return buildAstNode(this.services.shared.AstReflection, source.type, { $container: document.parseResult.value });
601
619
  }
602
620
 
603
621
  /**
@@ -9,8 +9,11 @@
9
9
 
10
10
  import type { Clock, Logger, Project, Tracer } from '@hydranium/protocol';
11
11
  import type { LangiumSharedCoreServices } from '@hydranium/langium';
12
+ import type { ServerLocale } from '../locale/server-locale.js';
13
+ import type { MessageRenderer } from '../messages/renderer.js';
12
14
  import type { SelfSaveRegistry } from '../documents/self-save-registry.js';
13
15
  import type { WritableFileSystemProvider } from '../documents/ast-document-manager.js';
16
+ import type { HydraniumDocumentRegistry } from './workspace/langium-documents.js';
14
17
  import type { BuildPhasePassService } from './build-phase-pass/build-phase-pass-service.js';
15
18
  import type { CstResidencyService } from './residency/cst-residency-service.js';
16
19
  import type { BuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
@@ -27,8 +30,9 @@ import type { AdditionalDocumentContribution } from './workspace/additional-docu
27
30
  * framework additions that other framework services consume: the dedicated
28
31
  * {@link Clock}, {@link Logger} and {@link Tracer} top-level slots (injectable
29
32
  * time source, emission-only logger, and the measure-and-emit tracer composed
30
- * from the other two), the {@link AdditionalDocumentContribution} group, an
31
- * {@link ExtendedServiceRegistry}, and on `workspace` a writable file-system
33
+ * from the other two), the {@link AdditionalDocumentContribution} group, the
34
+ * {@link MessageRenderer} and the {@link ServerLocale} it renders in,
35
+ * an {@link ExtendedServiceRegistry}, and on `workspace` a writable file-system
32
36
  * provider plus {@link HydraniumWorkspaceManager}, {@link ProjectManager},
33
37
  * {@link SelfSaveRegistry}, {@link BuildPipelineIntegration},
34
38
  * {@link BuildPhasePassService}, {@link CstResidencyService} and
@@ -57,6 +61,14 @@ export interface ServerSharedServicesMinimal<TProject extends Project = Project>
57
61
  * adopters deep-merge contributions that seed built-in / stdlib documents.
58
62
  */
59
63
  additionalDocuments: Record<string, AdditionalDocumentContribution>;
64
+ /**
65
+ * Renders each user-facing message once, before the server sends it. On the
66
+ * shared tier with no workspace dependency, because all three heads'
67
+ * messages pass through it.
68
+ */
69
+ MessageRenderer: MessageRenderer;
70
+ /** The locale an init handed the server, for whoever renders in it. */
71
+ ServerLocale: ServerLocale;
60
72
  /**
61
73
  * Narrows Langium's base `ServiceRegistry` slot to the framework impl the
62
74
  * framework always binds, so the abstaining lookups — `getServicesFor`
@@ -75,6 +87,11 @@ export interface ServerSharedServicesMinimal<TProject extends Project = Project>
75
87
  // framework always binds, so `wsRelativePath` and the folder-walk are
76
88
  // reachable from the minimal surface without a cast.
77
89
  WorkspaceManager: HydraniumWorkspaceManager;
90
+ // Same, for the registry: the framework always binds
91
+ // `HydraniumLangiumDocuments`, and `createEmptyDocument` is reachable
92
+ // only through this narrowing — a scope provider querying a URI before
93
+ // the file exists is the caller that needs it.
94
+ /* override */ LangiumDocuments: HydraniumDocumentRegistry;
78
95
  ProjectManager: ProjectManager<TProject>;
79
96
  SelfSaveRegistry: SelfSaveRegistry;
80
97
  BuildPipelineIntegration: BuildPipelineIntegration;
@@ -8,6 +8,7 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import {
11
+ hasMessageIdentity,
11
12
  type Tracer,
12
13
  type TransferDiagnostic,
13
14
  type TransferElement,
@@ -79,6 +80,37 @@ export interface TransferEnvelopeSource<TDiagnostic> {
79
80
  readonly diagnostics: readonly (TDiagnostic | Diagnostic)[];
80
81
  }
81
82
 
83
+ /**
84
+ * The AST→transfer contract a DI slot holds, implemented by
85
+ * {@link DefaultTransferEncoder}.
86
+ *
87
+ * **This is an interface and not the class, and that is load-bearing.** A class
88
+ * in a slot drags its `protected` members into every assignability check, which
89
+ * TypeScript compares NOMINALLY — so the slot type could not be satisfied by a
90
+ * subclass declared against a second physical copy of this package, and an
91
+ * adopter narrowing the slot had to intersect with the framework's declaration
92
+ * rather than replace it, leaving method resolution dependent on declaration
93
+ * order. An interface has no protected members and is compared structurally, so
94
+ * neither applies.
95
+ *
96
+ * **The transfer returns are the structural base ({@link TransferElement}), not
97
+ * the per-grammar overlay.** The overlay cannot parameterise this contract:
98
+ * `TransferTypeFor` reaches `keyof TTransferMap`, which makes that parameter
99
+ * measured-INVARIANT, and invariant parameters relate only when their arguments
100
+ * are mutually assignable — so a map-parameterised contract admits no adopter
101
+ * instantiation at all. Heads read this contract and do not know adopter maps;
102
+ * an adopter recovers the precise type by declaring its own slot, which it can
103
+ * now do by replacement.
104
+ */
105
+ export interface TransferEncoder<TDiagnostic extends TransferDiagnostic = TransferDiagnostic> {
106
+ toTransfer<T extends AstNode>(ast: T, mode?: TransferMode): TransferElement;
107
+ toTransferDocument(langiumDocument: LangiumDocument): TransferDocument<TransferElement, TDiagnostic>;
108
+ astDocumentToTransferDocument<TAst extends AstNode>(
109
+ document: AstDocument<TAst, TDiagnostic | TransferLspDiagnostic>
110
+ ): TransferDocument<TransferElement, TDiagnostic>;
111
+ toTransferDiagnostic(diagnostic: TransferLspDiagnostic): TDiagnostic;
112
+ }
113
+
82
114
  /**
83
115
  * One-directional structural mapper from a Langium AST into the wire
84
116
  * transfer-model shape ({@link TransferElement}-based JSON).
@@ -123,7 +155,7 @@ export interface TransferEnvelopeSource<TDiagnostic> {
123
155
  * - {@link toTransferDiagnostic} — project a {@link TransferLspDiagnostic}
124
156
  * (the framework validator's output) to {@link TDiagnostic}.
125
157
  */
126
- export class TransferEncoder<
158
+ export class DefaultTransferEncoder<
127
159
  // The map is intentionally unconstrained — adopters supply typed
128
160
  // overlays whose specific-key shapes don't structurally satisfy a
129
161
  // `Record<string, TransferElement>` index signature. `TransferTypeFor`
@@ -387,7 +419,10 @@ export class TransferEncoder<
387
419
  * LSP severity enum to the wire string union, discriminates the
388
420
  * diagnostic type from Langium's `data.code` (`'lexing-error'` /
389
421
  * `'parsing-error'` else `'validation-error'`), passes through
390
- * message / element / property / code. Adopters whose validator
422
+ * message / element / property / code, and lifts the framework message
423
+ * params out of `data` so a translating surface can render a
424
+ * parameterised diagnostic rather than a template with its placeholders
425
+ * left standing. Adopters whose validator
391
426
  * doesn't decorate diagnostics with `element` still get a valid
392
427
  * `TransferDiagnostic` — the field falls back to `''`.
393
428
  *
@@ -396,6 +431,12 @@ export class TransferEncoder<
396
431
  */
397
432
  toTransferDiagnostic(diagnostic: TransferLspDiagnostic): TDiagnostic {
398
433
  const langiumCode = (diagnostic.data as { code?: string } | undefined)?.code;
434
+ // The same `data` the Langium code comes out of also carries the framework
435
+ // message identity, under its own key so the two conventions co-exist.
436
+ // Taking the params from there rather than from `code` is what lets a
437
+ // translating surface render a PARAMETERISED diagnostic: `code` survives on
438
+ // its own and yields a template with its placeholders left standing.
439
+ const identity = hasMessageIdentity(diagnostic.data) ? diagnostic.data.hydranium : undefined;
399
440
  const result: TransferDiagnostic = {
400
441
  type: langiumCode === 'lexing-error' ? 'lexing-error' : langiumCode === 'parsing-error' ? 'parsing-error' : 'validation-error',
401
442
  element: diagnostic.element ?? '',
@@ -408,7 +449,8 @@ export class TransferEncoder<
408
449
  : diagnostic.severity === DiagnosticSeverity.Warning
409
450
  ? 'warning'
410
451
  : 'info',
411
- code: typeof diagnostic.code === 'number' || typeof diagnostic.code === 'string' ? diagnostic.code : langiumCode
452
+ code: typeof diagnostic.code === 'number' || typeof diagnostic.code === 'string' ? diagnostic.code : langiumCode,
453
+ params: identity?.params
412
454
  };
413
455
  return result as TDiagnostic;
414
456
  }