@hydranium/core 1.0.0-next.8 → 1.0.0-next.86

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 (311) hide show
  1. package/lib/documents/ast-document-manager.d.ts +75 -16
  2. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  3. package/lib/documents/ast-document-manager.js +57 -19
  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 +55 -21
  10. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  11. package/lib/documents/hydranium-text-documents.js +142 -46
  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 +149 -10
  81. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  82. package/lib/langium/model-service/model-service.js +120 -93
  83. package/lib/langium/model-service/model-service.js.map +1 -1
  84. package/lib/langium/module.d.ts +84 -15
  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 +25 -4
  89. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  90. package/lib/langium/naming/name-provider.js +2 -1
  91. package/lib/langium/naming/name-provider.js.map +1 -1
  92. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  93. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  94. package/lib/langium/naming/name-separator-validation.js +30 -1
  95. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  96. package/lib/langium/residency/cst-residency-service.d.ts +31 -9
  97. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  98. package/lib/langium/residency/cst-residency-service.js +13 -56
  99. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  100. package/lib/langium/scope/hydranium-scope-provider.d.ts +32 -13
  101. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  102. package/lib/langium/scope/hydranium-scope-provider.js +37 -20
  103. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  104. package/lib/langium/service-registry.d.ts +5 -1
  105. package/lib/langium/service-registry.d.ts.map +1 -1
  106. package/lib/langium/service-registry.js +4 -0
  107. package/lib/langium/service-registry.js.map +1 -1
  108. package/lib/langium/shared-services.d.ts +15 -2
  109. package/lib/langium/shared-services.d.ts.map +1 -1
  110. package/lib/langium/shared-services.js.map +1 -1
  111. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  112. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  113. package/lib/langium/transfer/transfer-encoder.js +38 -16
  114. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  115. package/lib/langium/validation/document-validator.d.ts +294 -10
  116. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  117. package/lib/langium/validation/document-validator.js +448 -5
  118. package/lib/langium/validation/document-validator.js.map +1 -1
  119. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  120. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  121. package/lib/langium/workspace/document-uri-policy.js +3 -4
  122. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  123. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  124. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  125. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  126. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  127. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  128. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  129. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  130. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  131. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +8 -0
  132. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  133. package/lib/langium/workspace/in-memory-file-system-provider.js +11 -2
  134. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  135. package/lib/langium/workspace/initialize-workspace.d.ts +22 -2
  136. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  137. package/lib/langium/workspace/initialize-workspace.js +13 -5
  138. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  139. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  140. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  141. package/lib/langium/workspace/langium-documents.js +83 -28
  142. package/lib/langium/workspace/langium-documents.js.map +1 -1
  143. package/lib/locale/index.d.ts +10 -0
  144. package/lib/locale/index.d.ts.map +1 -0
  145. package/lib/locale/index.js +10 -0
  146. package/lib/locale/index.js.map +1 -0
  147. package/lib/locale/server-locale.d.ts +73 -0
  148. package/lib/locale/server-locale.d.ts.map +1 -0
  149. package/lib/locale/server-locale.js +61 -0
  150. package/lib/locale/server-locale.js.map +1 -0
  151. package/lib/lsp/hydranium-document-update-handler.js +1 -1
  152. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  153. package/lib/lsp/index.d.ts +1 -1
  154. package/lib/lsp/index.d.ts.map +1 -1
  155. package/lib/lsp/index.js +1 -1
  156. package/lib/lsp/index.js.map +1 -1
  157. package/lib/lsp/lsp-latency.d.ts +39 -0
  158. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  159. package/lib/lsp/lsp-latency.js +58 -0
  160. package/lib/lsp/lsp-latency.js.map +1 -0
  161. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  162. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  163. package/lib/lsp/semantic-token-provider.js +55 -8
  164. package/lib/lsp/semantic-token-provider.js.map +1 -1
  165. package/lib/messages/carriers.d.ts +54 -0
  166. package/lib/messages/carriers.d.ts.map +1 -0
  167. package/lib/messages/carriers.js +62 -0
  168. package/lib/messages/carriers.js.map +1 -0
  169. package/lib/messages/index.d.ts +25 -0
  170. package/lib/messages/index.d.ts.map +1 -0
  171. package/lib/messages/index.js +25 -0
  172. package/lib/messages/index.js.map +1 -0
  173. package/lib/messages/renderer.d.ts +136 -0
  174. package/lib/messages/renderer.d.ts.map +1 -0
  175. package/lib/messages/renderer.js +159 -0
  176. package/lib/messages/renderer.js.map +1 -0
  177. package/lib/node/heap-ceiling.d.ts +61 -0
  178. package/lib/node/heap-ceiling.d.ts.map +1 -0
  179. package/lib/node/heap-ceiling.js +72 -0
  180. package/lib/node/heap-ceiling.js.map +1 -0
  181. package/lib/node/index.d.ts +1 -0
  182. package/lib/node/index.d.ts.map +1 -1
  183. package/lib/node/index.js +1 -0
  184. package/lib/node/index.js.map +1 -1
  185. package/lib/node/latency-from-env.d.ts +1 -1
  186. package/lib/node/latency-from-env.js +1 -1
  187. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  188. package/lib/node/node-file-system-provider.js +1 -40
  189. package/lib/node/node-file-system-provider.js.map +1 -1
  190. package/lib/node/profiling-run.d.ts +2 -2
  191. package/lib/node/profiling-run.js +1 -1
  192. package/lib/node/profiling-run.js.map +1 -1
  193. package/lib/node/rename-over-open-readers.d.ts +52 -0
  194. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  195. package/lib/node/rename-over-open-readers.js +66 -0
  196. package/lib/node/rename-over-open-readers.js.map +1 -0
  197. package/lib/testing/fake-document.d.ts +16 -4
  198. package/lib/testing/fake-document.d.ts.map +1 -1
  199. package/lib/testing/fake-document.js +18 -3
  200. package/lib/testing/fake-document.js.map +1 -1
  201. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  202. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  203. package/lib/testing/make-noop-shared-services.js +28 -2
  204. package/lib/testing/make-noop-shared-services.js.map +1 -1
  205. package/lib/testing/make-test-services.d.ts +44 -8
  206. package/lib/testing/make-test-services.d.ts.map +1 -1
  207. package/lib/testing/make-test-services.js +19 -3
  208. package/lib/testing/make-test-services.js.map +1 -1
  209. package/lib/testing/node/spawned-server.d.ts +14 -0
  210. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  211. package/lib/testing/node/spawned-server.js +33 -1
  212. package/lib/testing/node/spawned-server.js.map +1 -1
  213. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  214. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  215. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  216. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  217. package/lib/testing/playwright/index.d.ts +1 -0
  218. package/lib/testing/playwright/index.d.ts.map +1 -1
  219. package/lib/testing/playwright/index.js +1 -0
  220. package/lib/testing/playwright/index.js.map +1 -1
  221. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  222. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  223. package/lib/testing/playwright/server-log-capture.js +21 -4
  224. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  225. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  226. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  227. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  228. package/lib/testing/stub-ast-document-manager.d.ts +5 -5
  229. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  230. package/lib/testing/stub-ast-document-manager.js +4 -6
  231. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  232. package/lib/testing/stub-document-builder.d.ts +8 -0
  233. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  234. package/lib/testing/stub-document-builder.js +22 -3
  235. package/lib/testing/stub-document-builder.js.map +1 -1
  236. package/lib/testing/stub-langium-documents.d.ts +3 -2
  237. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  238. package/lib/testing/stub-langium-documents.js.map +1 -1
  239. package/lib/testing/stub-model-service.d.ts +5 -4
  240. package/lib/testing/stub-model-service.d.ts.map +1 -1
  241. package/lib/testing/stub-model-service.js +2 -2
  242. package/lib/testing/stub-model-service.js.map +1 -1
  243. package/package.json +17 -8
  244. package/src/documents/ast-document-manager.ts +129 -36
  245. package/src/documents/client-ids.ts +5 -22
  246. package/src/documents/hydranium-text-documents.ts +167 -61
  247. package/src/documents/language-client-text-shadow.ts +59 -11
  248. package/src/documents/self-save-registry.ts +52 -13
  249. package/src/index.ts +5 -0
  250. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  251. package/src/langium/bootstrap.ts +16 -9
  252. package/src/langium/config/configuration-provider.ts +119 -0
  253. package/src/langium/config/index.ts +1 -0
  254. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  255. package/src/langium/document-builder/build-pipeline-integration.ts +14 -1
  256. package/src/langium/document-builder/build-session.ts +87 -0
  257. package/src/langium/document-builder/document-builder.ts +339 -11
  258. package/src/langium/document-builder/index.ts +1 -0
  259. package/src/langium/integration-services.ts +67 -6
  260. package/src/langium/integrity/integrity-service.ts +35 -1
  261. package/src/langium/keys/containment.ts +87 -0
  262. package/src/langium/keys/index.ts +1 -0
  263. package/src/langium/keys/name-based-key-provider.ts +4 -0
  264. package/src/langium/model-service/model-service.ts +211 -30
  265. package/src/langium/module.ts +106 -21
  266. package/src/langium/naming/name-provider.ts +27 -5
  267. package/src/langium/naming/name-separator-validation.ts +34 -5
  268. package/src/langium/residency/cst-residency-service.ts +36 -12
  269. package/src/langium/scope/hydranium-scope-provider.ts +40 -22
  270. package/src/langium/service-registry.ts +6 -2
  271. package/src/langium/shared-services.ts +19 -2
  272. package/src/langium/transfer/transfer-encoder.ts +85 -24
  273. package/src/langium/validation/document-validator.ts +537 -11
  274. package/src/langium/workspace/document-uri-policy.ts +3 -4
  275. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  276. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  277. package/src/langium/workspace/in-memory-file-system-provider.ts +13 -3
  278. package/src/langium/workspace/initialize-workspace.ts +34 -5
  279. package/src/langium/workspace/langium-documents.ts +106 -33
  280. package/src/locale/index.ts +10 -0
  281. package/src/locale/server-locale.ts +86 -0
  282. package/src/lsp/hydranium-document-update-handler.ts +1 -1
  283. package/src/lsp/index.ts +1 -1
  284. package/src/lsp/lsp-latency.ts +62 -0
  285. package/src/lsp/semantic-token-provider.ts +74 -7
  286. package/src/messages/carriers.ts +78 -0
  287. package/src/messages/index.ts +36 -0
  288. package/src/messages/renderer.ts +204 -0
  289. package/src/node/heap-ceiling.ts +108 -0
  290. package/src/node/index.ts +1 -0
  291. package/src/node/latency-from-env.ts +1 -1
  292. package/src/node/node-file-system-provider.ts +1 -41
  293. package/src/node/profiling-run.ts +3 -3
  294. package/src/node/rename-over-open-readers.ts +78 -0
  295. package/src/testing/fake-document.ts +24 -5
  296. package/src/testing/make-noop-shared-services.ts +50 -2
  297. package/src/testing/make-test-services.ts +73 -18
  298. package/src/testing/node/spawned-server.ts +35 -1
  299. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  300. package/src/testing/playwright/index.ts +1 -0
  301. package/src/testing/playwright/server-log-capture.ts +24 -5
  302. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  303. package/src/testing/stub-ast-document-manager.ts +10 -14
  304. package/src/testing/stub-document-builder.ts +32 -3
  305. package/src/testing/stub-langium-documents.ts +3 -2
  306. package/src/testing/stub-model-service.ts +7 -6
  307. package/lib/lsp/instrument-connection.d.ts +0 -36
  308. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  309. package/lib/lsp/instrument-connection.js +0 -62
  310. package/lib/lsp/instrument-connection.js.map +0 -1
  311. package/src/lsp/instrument-connection.ts +0 -66
@@ -78,15 +78,13 @@ export type AstNodeBuilder<TMap> = <TType extends string & keyof TMap>(
78
78
  * AST-interface mapping, so call sites get full inference from the type
79
79
  * constant alone — no explicit generic parameter at every call site.
80
80
  *
81
- * Runtime behaviour: reads `reflection.getTypeMetaData(type).properties` and,
82
- * for every property whose metadata carries a `defaultValue`, pre-fills the
83
- * result before merging the caller's `init`. Array defaults are materialised
84
- * as a fresh `[]` per call (never shared with the metadata's array instance,
85
- * which downstream callers mutate); scalar defaults (`false`, `0`, `''`, …)
86
- * are copied as-is since they're immutable. Grammar-generated containment
87
- * lists and `?=`-style boolean flags therefore default automatically;
88
- * TS-augmented fields (declared via `declare module './generated/ast.js'`)
89
- * are unaffected by the runtime defaulting but remain typed at call sites.
81
+ * Runtime behaviour: pre-fills every grammar-declared default (see
82
+ * {@link withTypeDefaults}) before merging the caller's `init`, so containment
83
+ * lists and `?=`-style boolean flags default automatically. TS-augmented fields
84
+ * (declared via `declare module './generated/ast.js'`) are unaffected by the
85
+ * runtime defaulting but remain typed at call sites.
86
+ *
87
+ * Use {@link buildAstNode} instead when the type name is only known at runtime.
90
88
  *
91
89
  * The init shape only relaxes array-typed fields to optional (via
92
90
  * {@link ArrayKeys}) — there is no equivalent structural signal for "this
@@ -101,19 +99,7 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
101
99
  init: AstNodeInit<Extract<TMap[TType], AstNode>>,
102
100
  extras?: Record<string, unknown>
103
101
  ): Extract<TMap[TType], AstNode> {
104
- const meta = reflection.getTypeMetaData(typeConstant.$type);
105
- const result: Record<string, unknown> = { $type: typeConstant.$type };
106
- if (meta?.properties) {
107
- for (const [propName, propInfo] of Object.entries(meta.properties)) {
108
- const defaultValue = propInfo.defaultValue;
109
- if (defaultValue === undefined) {
110
- continue;
111
- }
112
- // Arrays are duplicated so each node carries its own mutable instance;
113
- // scalars are immutable and safe to copy by reference.
114
- result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
115
- }
116
- }
102
+ const result = withTypeDefaults(reflection, typeConstant.$type);
117
103
  Object.assign(result, init);
118
104
  if (extras) {
119
105
  // Extras win over init: this is the explicit escape hatch, so when a
@@ -123,3 +109,61 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
123
109
  return result as unknown as Extract<TMap[TType], AstNode>;
124
110
  };
125
111
  }
112
+
113
+ /**
114
+ * Build a node whose type is only known as a RUNTIME string.
115
+ *
116
+ * The typed {@link makeAstNodeBuilder} cannot express this: its `TMap` generic
117
+ * resolves the AST interface from a `$type` literal, and a type name that
118
+ * arrives over the wire — a protocol-layer reference request naming a node that
119
+ * does not exist yet — has no literal to resolve from. Callers that DO know the
120
+ * type at compile time want the typed builder, which checks their mandatory
121
+ * fields; this one checks nothing beyond the reflection lookup.
122
+ *
123
+ * Applies the same reflection-driven defaulting, so a node built here carries
124
+ * grammar-declared containment arrays rather than leaving them `undefined`. An
125
+ * unknown type name yields a node with `$type` and whatever `init` supplied,
126
+ * because reflection answers no metadata for it and refusing would turn a
127
+ * permissive lookup into a throw at a call site that can only guess.
128
+ *
129
+ * **The `type` argument is authoritative: `init` cannot rebind `$type`.** The
130
+ * typed builder gets this for free — `AstNodeInit` omits `$type`, so a call site
131
+ * cannot reach it — but here `init` is loosely typed, and the case that needs
132
+ * the guarantee is RETYPING: a caller spreading an existing node to produce one
133
+ * of a different type (`{ ...source }`) carries the SOURCE's `$type` in, which
134
+ * would silently win over the type actually requested and hand back a node of
135
+ * the wrong type with no error anywhere.
136
+ */
137
+ export function buildAstNode<TAst extends AstNode = AstNode>(
138
+ reflection: AstReflection,
139
+ type: string,
140
+ init?: Partial<AstNode> & Record<string, unknown>
141
+ ): TAst {
142
+ const result = withTypeDefaults(reflection, type);
143
+ if (init) {
144
+ Object.assign(result, init);
145
+ result.$type = type;
146
+ }
147
+ return result as unknown as TAst;
148
+ }
149
+
150
+ /**
151
+ * Seed a node with `$type` plus every property the grammar declares a default
152
+ * for. Array defaults are materialised as a fresh `[]` per call — never shared
153
+ * with the metadata's own array instance, which downstream callers mutate —
154
+ * while scalar defaults are copied as-is since they are immutable.
155
+ */
156
+ function withTypeDefaults(reflection: AstReflection, type: string): Record<string, unknown> {
157
+ const result: Record<string, unknown> = { $type: type };
158
+ const meta = reflection.getTypeMetaData(type);
159
+ if (meta?.properties) {
160
+ for (const [propName, propInfo] of Object.entries(meta.properties)) {
161
+ const defaultValue = propInfo.defaultValue;
162
+ if (defaultValue === undefined) {
163
+ continue;
164
+ }
165
+ result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
166
+ }
167
+ }
168
+ return result;
169
+ }
@@ -12,15 +12,15 @@ import { type LangiumSharedCoreServices } from '@hydranium/langium';
12
12
  import { type LangiumServices, type LangiumSharedServices } from '@hydranium/langium/lsp';
13
13
  import type { ServerSharedServicesMinimal } from './shared-services.js';
14
14
  import { collectProducibleTypes } from './language-types.js';
15
- import { AstDocumentManager } from '../documents/ast-document-manager.js';
15
+ import { DefaultAstDocumentManager } from '../documents/ast-document-manager.js';
16
16
  import { HydraniumTextDocuments } from '../documents/hydranium-text-documents.js';
17
- import { BuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
17
+ import { DefaultBuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
18
18
  import { HydraniumDocumentBuilder } from './document-builder/document-builder.js';
19
- import { ModelService } from './model-service/model-service.js';
19
+ import { DefaultModelService } from './model-service/model-service.js';
20
20
  import { HydraniumScopeComputation } from './scope/hydranium-scope-computation.js';
21
21
  import { HydraniumScopeProvider } from './scope/hydranium-scope-provider.js';
22
22
  import { processEnv } from '../util/environment.js';
23
- import { TransferEncoder } from './transfer/transfer-encoder.js';
23
+ import { DefaultTransferEncoder } from './transfer/transfer-encoder.js';
24
24
  import { HydraniumWorkspaceManager } from './workspace/hydranium-workspace-manager.js';
25
25
 
26
26
  /**
@@ -73,7 +73,14 @@ const EXPECTED_SHARED_SLOTS: readonly ExpectedSlot[] = [
73
73
  side: 'shared',
74
74
  path: 'workspace.BuildPipelineIntegration',
75
75
  get: services => nested(services, 'workspace', 'BuildPipelineIntegration')
76
- }
76
+ },
77
+ // Unbound, this fails where the cause is least visible: the render pass runs
78
+ // inside the `Validated` phase, so a `TypeError` there propagates out of
79
+ // `notifyDocumentPhase` and the document reaches `Validated` with Langium's
80
+ // publisher never invoked — the client receives no diagnostics for the file
81
+ // and the stack names the document builder.
82
+ { side: 'shared', path: 'MessageRenderer', get: services => nested(services, 'MessageRenderer') },
83
+ { side: 'shared', path: 'ServerLocale', get: services => nested(services, 'ServerLocale') }
77
84
  ];
78
85
 
79
86
  /**
@@ -199,17 +206,17 @@ const STRICT_SHARED_SLOTS: readonly StrictSlot[] = [
199
206
  {
200
207
  side: 'shared',
201
208
  path: 'workspace.AstDocumentManager',
202
- base: AstDocumentManager,
209
+ base: DefaultAstDocumentManager,
203
210
  get: s => nested(s, 'workspace', 'AstDocumentManager')
204
211
  },
205
212
  {
206
213
  side: 'shared',
207
214
  path: 'workspace.BuildPipelineIntegration',
208
- base: BuildPipelineIntegration,
215
+ base: DefaultBuildPipelineIntegration,
209
216
  get: s => nested(s, 'workspace', 'BuildPipelineIntegration')
210
217
  },
211
- { side: 'shared', path: 'model.TransferEncoder', base: TransferEncoder, get: s => nested(s, 'model', 'TransferEncoder') },
212
- { side: 'shared', path: 'model.ModelService', base: ModelService, get: s => nested(s, 'model', 'ModelService') }
218
+ { side: 'shared', path: 'model.TransferEncoder', base: DefaultTransferEncoder, get: s => nested(s, 'model', 'TransferEncoder') },
219
+ { side: 'shared', path: 'model.ModelService', base: DefaultModelService, get: s => nested(s, 'model', 'ModelService') }
213
220
  ];
214
221
 
215
222
  const STRICT_LANGUAGE_SLOTS: readonly StrictSlot[] = [
@@ -0,0 +1,119 @@
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 ConfigurationInitializedParams, DefaultConfigurationProvider } from '@hydranium/langium';
11
+ import type { ConfigurationItem, DidChangeConfigurationRegistrationOptions } from 'vscode-languageserver';
12
+
13
+ /**
14
+ * Configuration provider that asks the client about a section the first time
15
+ * something reads it.
16
+ *
17
+ * Langium's base requests one section per registered language, named after the
18
+ * language id, and never asks again. A read of any other section — including
19
+ * whatever `lsp.configurationRoot` is bound to — then resolves `undefined` with
20
+ * no request on the wire, which is indistinguishable from a setting the user has
21
+ * not set.
22
+ *
23
+ * Asking on first read rather than from a list collected up front: a reader
24
+ * names its section during DI, after `initialized` has taken its one-shot pass,
25
+ * so a list would have to be complete before the server initialized.
26
+ */
27
+ export class HydraniumConfigurationProvider extends DefaultConfigurationProvider {
28
+ /**
29
+ * The client-facing hooks, captured because they arrive once as arguments to
30
+ * {@link initialized} and are needed on every later read. Absent in a
31
+ * headless composition, which has no connection to ask.
32
+ */
33
+ protected registerForSection?: (params: DidChangeConfigurationRegistrationOptions) => void;
34
+ protected fetchSections?: (items: ConfigurationItem[]) => Promise<unknown>;
35
+
36
+ /**
37
+ * Sections already dealt with, holding the in-flight work so concurrent
38
+ * readers share one round trip. Entries stay after they settle: they record
39
+ * that the section was HANDLED, and without that an unset section is
40
+ * re-requested on every read, "unset" and "not fetched" being the same
41
+ * absence in the store.
42
+ *
43
+ * A section the client's capabilities ruled out counts as handled, having
44
+ * been settled by asking nothing.
45
+ */
46
+ protected readonly requestedSections = new Map<string, Promise<void>>();
47
+
48
+ override async initialized(params: ConfigurationInitializedParams): Promise<void> {
49
+ this.registerForSection = params.register;
50
+ this.fetchSections = params.fetchConfiguration;
51
+ await super.initialized(params);
52
+ }
53
+
54
+ /**
55
+ * The `ready` await precedes the emptiness check: the base resolves it at the
56
+ * end of its own initial fetch, so testing the store first calls every
57
+ * language section missing and re-requests what is already on its way.
58
+ */
59
+ override async getConfiguration(language: string, configuration: string): Promise<unknown> {
60
+ await this.ready;
61
+ const section = this.toSectionName(language);
62
+ if (this.settings[section] === undefined) {
63
+ await this.requestSection(section);
64
+ }
65
+ return super.getConfiguration(language, configuration);
66
+ }
67
+
68
+ /** Ask about `section` once, sharing the request with any concurrent reader. */
69
+ protected requestSection(section: string): Promise<void> {
70
+ const existing = this.requestedSections.get(section);
71
+ if (existing) {
72
+ return existing;
73
+ }
74
+ const request = this.doRequestSection(section);
75
+ this.requestedSections.set(section, request);
76
+ return request;
77
+ }
78
+
79
+ /**
80
+ * The hooks are gated on the client having declared `workspace.configuration`.
81
+ * A connection binds them whatever the client said, so their presence is not
82
+ * consent: calling them sends a `client/registerCapability` and a
83
+ * `workspace/configuration` to a client that advertised support for neither,
84
+ * which is entitled to answer either with a method-not-found and may drop the
85
+ * connection.
86
+ *
87
+ * That flag is exact for the fetch and KNOWINGLY coarse for the registration,
88
+ * which the spec governs by
89
+ * `workspace.didChangeConfiguration.dynamicRegistration`. The base registers
90
+ * under `workspace.configuration` alone before any of this runs, so gating
91
+ * the registration on the narrower flag here would withhold a request whose
92
+ * twin has already gone out, sparing the client nothing. A client declaring
93
+ * `workspace.configuration` without dynamic registration is still registered
94
+ * for, by the base and by this; closing that means changing the base's pass,
95
+ * not this gate.
96
+ *
97
+ * Registering before fetching, so an edit landing between the two arrives as
98
+ * a notification instead of being lost; the reverse order drops it.
99
+ *
100
+ * A `null` result is left out of the store rather than written, so the
101
+ * section reads as unset, and no `onConfigurationSectionUpdate` fires — the
102
+ * value goes to the caller that asked for it, and an event would announce a
103
+ * change that is only this provider catching up.
104
+ */
105
+ protected async doRequestSection(section: string): Promise<void> {
106
+ if (!this.workspaceConfig) {
107
+ return;
108
+ }
109
+ this.registerForSection?.({ section });
110
+ if (!this.fetchSections) {
111
+ return;
112
+ }
113
+ const fetched = await this.fetchSections([{ section }]);
114
+ const value = Array.isArray(fetched) ? fetched[0] : fetched;
115
+ if (value !== null && value !== undefined) {
116
+ this.updateSectionConfiguration(section, value);
117
+ }
118
+ }
119
+ }
@@ -7,4 +7,5 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
+ export * from './configuration-provider.js';
10
11
  export * from './settings.js';
@@ -92,14 +92,17 @@ export class LspLogger extends AbstractLogger implements Logger {
92
92
  // a Node concern; skip it cleanly there.
93
93
  const env = processEnv();
94
94
  if (env) {
95
- const envLevel = parseLogLevel(env[DEFAULT_LOG_LEVEL_ENV]);
96
- if (envLevel) {
97
- Logger.setLevel(envLevel);
98
- }
95
+ // The file-tee target FIRST: the other order writes the level
96
+ // announcement before the sink exists, losing the one line that says
97
+ // which threshold the rest of the file was written at.
99
98
  const envLogFile = env[DEFAULT_LOG_FILE_ENV];
100
99
  if (envLogFile) {
101
100
  setLogFilePath(envLogFile);
102
101
  }
102
+ const envLevel = parseLogLevel(env[DEFAULT_LOG_LEVEL_ENV]);
103
+ if (envLevel) {
104
+ this.applyLevel(envLevel, DEFAULT_LOG_LEVEL_ENV);
105
+ }
103
106
  }
104
107
  }
105
108
  // Apply the configured threshold from the root logger only — `derive`
@@ -109,16 +112,48 @@ export class LspLogger extends AbstractLogger implements Logger {
109
112
  if (options.logThreshold !== undefined) {
110
113
  const level = ObservableValue.from(options.logThreshold);
111
114
  if (level.value) {
112
- Logger.setLevel(level.value);
115
+ this.applyLevel(level.value, 'setting');
113
116
  }
114
117
  level.onChange(next => {
115
118
  if (next) {
116
- Logger.setLevel(next);
119
+ this.applyLevel(next, 'setting');
117
120
  }
118
121
  });
119
122
  }
120
123
  }
121
124
 
125
+ /**
126
+ * Set the process-wide threshold and announce the transition under whichever
127
+ * of the two thresholds can carry the notice, so a reader sees every change
128
+ * and its absence means the threshold never moved.
129
+ *
130
+ * `error` and `off` admit no notice of their own — `off` nothing at all, and
131
+ * an error-severity line is read as a fault, which a client that marks its
132
+ * log as having seen an error keeps for the session. Entering either is
133
+ * therefore announced BEFORE the switch, under the outgoing threshold; every
134
+ * other change is announced after it, at the level being entered. Leaving
135
+ * `error` or `off` for one of those two says nothing, both thresholds being
136
+ * unable to carry it.
137
+ *
138
+ * Only on a real transition: the bound setting re-fires for configuration
139
+ * changes that leave the value alone.
140
+ */
141
+ protected applyLevel(next: LogThreshold, source: string): void {
142
+ const previous = Logger.getLevel();
143
+ if (previous === next) {
144
+ return;
145
+ }
146
+ const message = `Log level ${previous} → ${next} (${source})`;
147
+ const announceFirst = next === 'error' || next === 'off';
148
+ if (announceFirst && previous !== 'error') {
149
+ this.logAt(previous, message);
150
+ }
151
+ Logger.setLevel(next);
152
+ if (!announceFirst) {
153
+ this.logAt(next, message);
154
+ }
155
+ }
156
+
122
157
  /**
123
158
  * Route logger timing and timestamps through the injected framework
124
159
  * {@link Clock} (the dedicated `shared.Clock` slot) so server-side log time
@@ -65,6 +65,19 @@ export interface BuildPipelineIntegrationOptions {
65
65
  readonly integrityPriority?: number;
66
66
  }
67
67
 
68
+ /**
69
+ * The build-pipeline slot. Empty because nothing calls this service — it wires
70
+ * itself in its constructor and every seam it offers is `protected`.
71
+ *
72
+ * Typing the slot by the class instead would stop an adopter's subclass
73
+ * satisfying it, since a class carries its `protected` members into the check
74
+ * and they compare by declaration rather than by shape. The cost of the empty
75
+ * shape is that any non-null value satisfies it, so a mis-bound slot compiles
76
+ * and the framework's phase listeners are then never attached.
77
+ */
78
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- the slot has no public member to name
79
+ export interface BuildPipelineIntegration {}
80
+
68
81
  /**
69
82
  * Wires the framework's build-time features (integrity rules, AST
70
83
  * enrichment) into Langium's document-build pipeline. The single shared
@@ -90,7 +103,7 @@ export interface BuildPipelineIntegrationOptions {
90
103
  * Eagerly constructed (via `DEFAULT_EAGER_SERVICES`) so the listeners are
91
104
  * attached before the first build cycle.
92
105
  */
93
- export class BuildPipelineIntegration {
106
+ export class DefaultBuildPipelineIntegration implements BuildPipelineIntegration {
94
107
  constructor(
95
108
  protected readonly services: ServerSharedServices,
96
109
  options: BuildPipelineIntegrationOptions = {}
@@ -0,0 +1,87 @@
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 URI } from '@hydranium/langium';
11
+
12
+ /** What a build is about, handed to `HydraniumDocumentBuilder.createBuildSession`. */
13
+ export interface BuildSessionContext {
14
+ /**
15
+ * Which entry point opened the build: `'build'` is the workspace-wide pass
16
+ * (initialization), `'update'` an incremental rebuild. A subclass deriving
17
+ * cascade state skips the work for `'build'`, where every document is in the
18
+ * set already and `shouldRelink` is never consulted.
19
+ */
20
+ readonly kind: 'build' | 'update';
21
+ /** Formatted description of what caused the build — a URI, a count summary. */
22
+ readonly trigger: string;
23
+ /** True when {@link trigger} already states the document count. */
24
+ readonly triggerCountsDocs: boolean;
25
+ /** URIs the build was asked to rebuild, already flattened and canonicalized. */
26
+ readonly changed: readonly URI[];
27
+ /** URIs the build was asked to delete, already expanded by `collectDeletedURIs`. Empty for a `build` call. */
28
+ readonly deleted: readonly URI[];
29
+ }
30
+
31
+ /**
32
+ * One rebuild, treated as a correlated unit.
33
+ *
34
+ * Everything a build phase needs to know about the build it belongs to — what
35
+ * triggered it, when it started, whether it was cancelled, and the log lines it
36
+ * has produced so far. A `HydraniumDocumentBuilder` opens one per `update` /
37
+ * `build` call and holds it for the duration, so a per-phase hook can reach
38
+ * build-wide facts that no per-line formatting hook could.
39
+ *
40
+ * **Identity is the object, not {@link traceId}.** `Tracer.time` short-circuits
41
+ * when its log level is suppressed and never calls `captureId`, so `traceId` is
42
+ * `undefined` on every build in a production configuration that logs above
43
+ * `debug`. A teardown guard keyed on the id would then compare `undefined`
44
+ * against `undefined` and let a preempted build clear its successor's state.
45
+ * Reference equality against `HydraniumDocumentBuilder.activeSession` holds in
46
+ * both configurations, which is why nothing here is keyed on the id.
47
+ *
48
+ * Adopters carrying build-scoped state of their own subclass this and return it
49
+ * from `HydraniumDocumentBuilder.createBuildSession`, narrowing with an
50
+ * `instanceof` where they read it back. That buys them the framework's
51
+ * preemption-correct teardown rather than a second copy of it.
52
+ */
53
+ export class BuildSession {
54
+ /**
55
+ * `Tracer.time` correlation id, so a phase line and the build line share a
56
+ * `#N`. `undefined` until the timer starts, and permanently `undefined` when
57
+ * the timing line is suppressed.
58
+ */
59
+ traceId?: number;
60
+ /** Set when the build ended in `OperationCancelled`, so the successor can tag itself "cancels #N". */
61
+ cancelled = false;
62
+ /** Phase-reached lines produced so far; the first one measures from {@link startMs} rather than from the previous phase. */
63
+ phasesLogged = 0;
64
+ /** Lines held back by {@link detailThresholdMs}; flushed or dropped when the build ends. */
65
+ readonly bufferedLines: string[] = [];
66
+
67
+ constructor(
68
+ /** Millisecond timestamp the build started at, on the same clock as `performance.now()`. */
69
+ readonly startMs: number,
70
+ /** What caused this build — a formatted URI, a count summary, or an adopter label. */
71
+ readonly trigger: string,
72
+ /** True when {@link trigger} already states the document count, so a phase line must not repeat it. */
73
+ readonly triggerCountsDocs: boolean,
74
+ /**
75
+ * Build duration at or above which the buffered lines are emitted; `0`
76
+ * disables buffering entirely. Captured per session so the flush decision
77
+ * uses the same value the buffering decision did, even if the underlying
78
+ * observable changed mid-build.
79
+ */
80
+ readonly detailThresholdMs: number
81
+ ) {}
82
+
83
+ /** True while lines are being held rather than emitted as they are produced. */
84
+ get buffers(): boolean {
85
+ return this.detailThresholdMs > 0;
86
+ }
87
+ }