@hydranium/core 1.0.0-next.11 → 1.0.0-next.112

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 (424) 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 +132 -42
  10. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  11. package/lib/documents/hydranium-text-documents.js +199 -57
  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 +3 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +6 -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 +21 -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 +32 -5
  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 +16 -0
  65. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  66. package/lib/langium/integrity/integrity-service.js +110 -2
  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/language-module.d.ts +25 -0
  81. package/lib/langium/language-module.d.ts.map +1 -1
  82. package/lib/langium/language-module.js +14 -0
  83. package/lib/langium/language-module.js.map +1 -1
  84. package/lib/langium/model-service/model-service.d.ts +161 -10
  85. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  86. package/lib/langium/model-service/model-service.js +176 -97
  87. package/lib/langium/model-service/model-service.js.map +1 -1
  88. package/lib/langium/module.d.ts +84 -15
  89. package/lib/langium/module.d.ts.map +1 -1
  90. package/lib/langium/module.js +26 -12
  91. package/lib/langium/module.js.map +1 -1
  92. package/lib/langium/naming/name-provider.d.ts +25 -4
  93. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  94. package/lib/langium/naming/name-provider.js +2 -1
  95. package/lib/langium/naming/name-provider.js.map +1 -1
  96. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  97. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  98. package/lib/langium/naming/name-separator-validation.js +30 -1
  99. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  100. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  101. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  102. package/lib/langium/residency/cst-residency-service.js +13 -56
  103. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  104. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  105. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  106. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  107. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  108. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  109. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  110. package/lib/langium/serialization/abstract-serializer.js +12 -21
  111. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  112. package/lib/langium/service-registry.d.ts +15 -1
  113. package/lib/langium/service-registry.d.ts.map +1 -1
  114. package/lib/langium/service-registry.js +16 -0
  115. package/lib/langium/service-registry.js.map +1 -1
  116. package/lib/langium/shared-services.d.ts +15 -2
  117. package/lib/langium/shared-services.d.ts.map +1 -1
  118. package/lib/langium/shared-services.js.map +1 -1
  119. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  120. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  121. package/lib/langium/transfer/transfer-encoder.js +38 -16
  122. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  123. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  124. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  125. package/lib/langium/trivia/comment-preserver.js +906 -0
  126. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  127. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  128. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  129. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  130. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  131. package/lib/langium/trivia/index.d.ts +14 -0
  132. package/lib/langium/trivia/index.d.ts.map +1 -0
  133. package/lib/langium/trivia/index.js +14 -0
  134. package/lib/langium/trivia/index.js.map +1 -0
  135. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  136. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  137. package/lib/langium/trivia/trivia-contribution.js +10 -0
  138. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  139. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  140. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  141. package/lib/langium/trivia/trivia-preserver.js +10 -0
  142. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  143. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  144. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  145. package/lib/langium/trivia/trivia-service.js +56 -0
  146. package/lib/langium/trivia/trivia-service.js.map +1 -0
  147. package/lib/langium/validation/document-validator.d.ts +294 -10
  148. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  149. package/lib/langium/validation/document-validator.js +448 -5
  150. package/lib/langium/validation/document-validator.js.map +1 -1
  151. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  152. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  153. package/lib/langium/workspace/document-uri-policy.js +3 -4
  154. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  155. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  156. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  157. package/lib/langium/workspace/file-system-provider.js +40 -7
  158. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  159. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  160. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  161. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  162. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  163. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  164. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  165. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  166. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  167. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  168. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  169. package/lib/langium/workspace/in-memory-file-system-provider.js +47 -9
  170. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  171. package/lib/langium/workspace/initialize-workspace.d.ts +23 -2
  172. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  173. package/lib/langium/workspace/initialize-workspace.js +13 -5
  174. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  175. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  176. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  177. package/lib/langium/workspace/langium-documents.js +83 -28
  178. package/lib/langium/workspace/langium-documents.js.map +1 -1
  179. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  180. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  181. package/lib/langium/workspace/virtual-document.js +19 -2
  182. package/lib/langium/workspace/virtual-document.js.map +1 -1
  183. package/lib/locale/index.d.ts +10 -0
  184. package/lib/locale/index.d.ts.map +1 -0
  185. package/lib/locale/index.js +10 -0
  186. package/lib/locale/index.js.map +1 -0
  187. package/lib/locale/server-locale.d.ts +73 -0
  188. package/lib/locale/server-locale.d.ts.map +1 -0
  189. package/lib/locale/server-locale.js +61 -0
  190. package/lib/locale/server-locale.js.map +1 -0
  191. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  192. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  193. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  194. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  195. package/lib/lsp/connection-features.d.ts +23 -0
  196. package/lib/lsp/connection-features.d.ts.map +1 -0
  197. package/lib/lsp/connection-features.js +60 -0
  198. package/lib/lsp/connection-features.js.map +1 -0
  199. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  200. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  201. package/lib/lsp/diagnostics-connection.js +53 -0
  202. package/lib/lsp/diagnostics-connection.js.map +1 -0
  203. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  204. package/lib/lsp/hydranium-document-update-handler.js +10 -1
  205. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  206. package/lib/lsp/index.d.ts +3 -1
  207. package/lib/lsp/index.d.ts.map +1 -1
  208. package/lib/lsp/index.js +3 -1
  209. package/lib/lsp/index.js.map +1 -1
  210. package/lib/lsp/lsp-latency.d.ts +39 -0
  211. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  212. package/lib/lsp/lsp-latency.js +58 -0
  213. package/lib/lsp/lsp-latency.js.map +1 -0
  214. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  215. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  216. package/lib/lsp/semantic-token-provider.js +55 -8
  217. package/lib/lsp/semantic-token-provider.js.map +1 -1
  218. package/lib/lsp/start-language-server.d.ts +5 -3
  219. package/lib/lsp/start-language-server.d.ts.map +1 -1
  220. package/lib/lsp/start-language-server.js +10 -3
  221. package/lib/lsp/start-language-server.js.map +1 -1
  222. package/lib/messages/carriers.d.ts +54 -0
  223. package/lib/messages/carriers.d.ts.map +1 -0
  224. package/lib/messages/carriers.js +62 -0
  225. package/lib/messages/carriers.js.map +1 -0
  226. package/lib/messages/index.d.ts +25 -0
  227. package/lib/messages/index.d.ts.map +1 -0
  228. package/lib/messages/index.js +25 -0
  229. package/lib/messages/index.js.map +1 -0
  230. package/lib/messages/renderer.d.ts +136 -0
  231. package/lib/messages/renderer.d.ts.map +1 -0
  232. package/lib/messages/renderer.js +159 -0
  233. package/lib/messages/renderer.js.map +1 -0
  234. package/lib/node/heap-ceiling.d.ts +61 -0
  235. package/lib/node/heap-ceiling.d.ts.map +1 -0
  236. package/lib/node/heap-ceiling.js +72 -0
  237. package/lib/node/heap-ceiling.js.map +1 -0
  238. package/lib/node/index.d.ts +1 -0
  239. package/lib/node/index.d.ts.map +1 -1
  240. package/lib/node/index.js +1 -0
  241. package/lib/node/index.js.map +1 -1
  242. package/lib/node/latency-from-env.d.ts +1 -1
  243. package/lib/node/latency-from-env.js +1 -1
  244. package/lib/node/measure-memory.d.ts.map +1 -1
  245. package/lib/node/measure-memory.js +1 -0
  246. package/lib/node/measure-memory.js.map +1 -1
  247. package/lib/node/node-file-system-provider.d.ts +35 -1
  248. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  249. package/lib/node/node-file-system-provider.js +140 -45
  250. package/lib/node/node-file-system-provider.js.map +1 -1
  251. package/lib/node/profile-capture.d.ts +8 -2
  252. package/lib/node/profile-capture.d.ts.map +1 -1
  253. package/lib/node/profile-capture.js.map +1 -1
  254. package/lib/node/profiling-run.d.ts +2 -2
  255. package/lib/node/profiling-run.js +1 -1
  256. package/lib/node/profiling-run.js.map +1 -1
  257. package/lib/node/rename-over-open-readers.d.ts +52 -0
  258. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  259. package/lib/node/rename-over-open-readers.js +66 -0
  260. package/lib/node/rename-over-open-readers.js.map +1 -0
  261. package/lib/node/socket-launcher.d.ts +15 -0
  262. package/lib/node/socket-launcher.d.ts.map +1 -1
  263. package/lib/node/socket-launcher.js +3 -2
  264. package/lib/node/socket-launcher.js.map +1 -1
  265. package/lib/testing/fake-document.d.ts +16 -4
  266. package/lib/testing/fake-document.d.ts.map +1 -1
  267. package/lib/testing/fake-document.js +18 -3
  268. package/lib/testing/fake-document.js.map +1 -1
  269. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  270. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  271. package/lib/testing/make-noop-shared-services.js +28 -2
  272. package/lib/testing/make-noop-shared-services.js.map +1 -1
  273. package/lib/testing/make-test-services.d.ts +45 -9
  274. package/lib/testing/make-test-services.d.ts.map +1 -1
  275. package/lib/testing/make-test-services.js +19 -3
  276. package/lib/testing/make-test-services.js.map +1 -1
  277. package/lib/testing/node/index.d.ts +1 -0
  278. package/lib/testing/node/index.d.ts.map +1 -1
  279. package/lib/testing/node/index.js +1 -0
  280. package/lib/testing/node/index.js.map +1 -1
  281. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  282. package/lib/testing/node/lsp-harness.js +2 -0
  283. package/lib/testing/node/lsp-harness.js.map +1 -1
  284. package/lib/testing/node/lsp-server-connection.d.ts +10 -2
  285. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  286. package/lib/testing/node/lsp-server-connection.js +42 -9
  287. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  288. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  289. package/lib/testing/node/scratch-workspace.js +9 -4
  290. package/lib/testing/node/scratch-workspace.js.map +1 -1
  291. package/lib/testing/node/spawned-server.d.ts +14 -0
  292. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  293. package/lib/testing/node/spawned-server.js +33 -1
  294. package/lib/testing/node/spawned-server.js.map +1 -1
  295. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  296. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  297. package/lib/testing/node/unhandled-rejections.js +32 -0
  298. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  299. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  300. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  301. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  302. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  303. package/lib/testing/playwright/index.d.ts +1 -0
  304. package/lib/testing/playwright/index.d.ts.map +1 -1
  305. package/lib/testing/playwright/index.js +1 -0
  306. package/lib/testing/playwright/index.js.map +1 -1
  307. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  308. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  309. package/lib/testing/playwright/server-log-capture.js +21 -4
  310. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  311. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  312. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  313. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  314. package/lib/testing/stub-ast-document-manager.d.ts +5 -5
  315. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  316. package/lib/testing/stub-ast-document-manager.js +4 -6
  317. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  318. package/lib/testing/stub-document-builder.d.ts +8 -0
  319. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  320. package/lib/testing/stub-document-builder.js +22 -3
  321. package/lib/testing/stub-document-builder.js.map +1 -1
  322. package/lib/testing/stub-langium-documents.d.ts +3 -2
  323. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  324. package/lib/testing/stub-langium-documents.js.map +1 -1
  325. package/lib/testing/stub-model-service.d.ts +5 -4
  326. package/lib/testing/stub-model-service.d.ts.map +1 -1
  327. package/lib/testing/stub-model-service.js +2 -2
  328. package/lib/testing/stub-model-service.js.map +1 -1
  329. package/lib/util/connection-liveness.d.ts +7 -6
  330. package/lib/util/connection-liveness.d.ts.map +1 -1
  331. package/lib/util/connection-liveness.js +35 -14
  332. package/lib/util/connection-liveness.js.map +1 -1
  333. package/package.json +17 -8
  334. package/src/documents/ast-document-manager.ts +129 -36
  335. package/src/documents/client-ids.ts +5 -22
  336. package/src/documents/hydranium-text-documents.ts +256 -81
  337. package/src/documents/language-client-text-shadow.ts +59 -11
  338. package/src/documents/self-save-registry.ts +52 -13
  339. package/src/index.ts +6 -0
  340. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  341. package/src/langium/bootstrap.ts +16 -9
  342. package/src/langium/config/configuration-provider.ts +119 -0
  343. package/src/langium/config/index.ts +1 -0
  344. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  345. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  346. package/src/langium/document-builder/build-session.ts +87 -0
  347. package/src/langium/document-builder/document-builder.ts +339 -11
  348. package/src/langium/document-builder/index.ts +1 -0
  349. package/src/langium/integration-services.ts +67 -6
  350. package/src/langium/integrity/integrity-service.ts +116 -2
  351. package/src/langium/keys/containment.ts +87 -0
  352. package/src/langium/keys/index.ts +1 -0
  353. package/src/langium/keys/name-based-key-provider.ts +4 -0
  354. package/src/langium/language-module.ts +38 -0
  355. package/src/langium/model-service/model-service.ts +266 -34
  356. package/src/langium/module.ts +106 -21
  357. package/src/langium/naming/name-provider.ts +27 -5
  358. package/src/langium/naming/name-separator-validation.ts +34 -5
  359. package/src/langium/residency/cst-residency-service.ts +36 -14
  360. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  361. package/src/langium/serialization/abstract-serializer.ts +13 -32
  362. package/src/langium/service-registry.ts +19 -2
  363. package/src/langium/shared-services.ts +19 -2
  364. package/src/langium/transfer/transfer-encoder.ts +85 -24
  365. package/src/langium/trivia/comment-preserver.ts +1100 -0
  366. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  367. package/src/langium/trivia/index.ts +14 -0
  368. package/src/langium/trivia/trivia-contribution.ts +39 -0
  369. package/src/langium/trivia/trivia-preserver.ts +54 -0
  370. package/src/langium/trivia/trivia-service.ts +99 -0
  371. package/src/langium/validation/document-validator.ts +537 -11
  372. package/src/langium/workspace/document-uri-policy.ts +3 -4
  373. package/src/langium/workspace/file-system-provider.ts +48 -8
  374. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  375. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  376. package/src/langium/workspace/in-memory-file-system-provider.ts +53 -10
  377. package/src/langium/workspace/initialize-workspace.ts +35 -5
  378. package/src/langium/workspace/langium-documents.ts +106 -33
  379. package/src/langium/workspace/virtual-document.ts +21 -3
  380. package/src/locale/index.ts +10 -0
  381. package/src/locale/server-locale.ts +86 -0
  382. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  383. package/src/lsp/connection-features.ts +67 -0
  384. package/src/lsp/diagnostics-connection.ts +56 -0
  385. package/src/lsp/hydranium-document-update-handler.ts +10 -1
  386. package/src/lsp/index.ts +3 -1
  387. package/src/lsp/lsp-latency.ts +62 -0
  388. package/src/lsp/semantic-token-provider.ts +74 -7
  389. package/src/lsp/start-language-server.ts +10 -3
  390. package/src/messages/carriers.ts +78 -0
  391. package/src/messages/index.ts +36 -0
  392. package/src/messages/renderer.ts +206 -0
  393. package/src/node/heap-ceiling.ts +108 -0
  394. package/src/node/index.ts +1 -0
  395. package/src/node/latency-from-env.ts +1 -1
  396. package/src/node/measure-memory.ts +1 -0
  397. package/src/node/node-file-system-provider.ts +154 -47
  398. package/src/node/profile-capture.ts +8 -2
  399. package/src/node/profiling-run.ts +3 -3
  400. package/src/node/rename-over-open-readers.ts +78 -0
  401. package/src/node/socket-launcher.ts +18 -2
  402. package/src/testing/fake-document.ts +24 -5
  403. package/src/testing/make-noop-shared-services.ts +50 -2
  404. package/src/testing/make-test-services.ts +74 -18
  405. package/src/testing/node/index.ts +1 -0
  406. package/src/testing/node/lsp-harness.ts +2 -0
  407. package/src/testing/node/lsp-server-connection.ts +60 -10
  408. package/src/testing/node/scratch-workspace.ts +9 -4
  409. package/src/testing/node/spawned-server.ts +35 -1
  410. package/src/testing/node/unhandled-rejections.ts +31 -0
  411. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  412. package/src/testing/playwright/index.ts +1 -0
  413. package/src/testing/playwright/server-log-capture.ts +24 -5
  414. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  415. package/src/testing/stub-ast-document-manager.ts +10 -14
  416. package/src/testing/stub-document-builder.ts +32 -3
  417. package/src/testing/stub-langium-documents.ts +3 -2
  418. package/src/testing/stub-model-service.ts +7 -6
  419. package/src/util/connection-liveness.ts +37 -14
  420. package/lib/lsp/instrument-connection.d.ts +0 -36
  421. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  422. package/lib/lsp/instrument-connection.js +0 -62
  423. package/lib/lsp/instrument-connection.js.map +0 -1
  424. package/src/lsp/instrument-connection.ts +0 -66
@@ -55,8 +55,8 @@ export interface ProfilingRunOptions {
55
55
  /**
56
56
  * Optional latency collector. When present, {@link ProfilingRun.finish} writes
57
57
  * its report to `server-latency.json` and records a `server-latency` manifest artefact. A
58
- * head feeds it via the RPC binding / `instrumentLspConnection`; a pure
59
- * headless build with no request traffic leaves it empty.
58
+ * head feeds it via the RPC binding / `lspLatencyOptions`; a pure headless
59
+ * build with no request traffic leaves it empty.
60
60
  */
61
61
  latency?: LatencyCollector;
62
62
  }
@@ -97,7 +97,7 @@ export class ProfilingRun {
97
97
  // `HYDRANIUM_LOG_FILE` env, which every `@hydranium/core` copy reads when
98
98
  // its logger is CONSTRUCTED — so `ProfilingRun.start` must run before the
99
99
  // head's services are created (as `measureModelMemory` does). This is the
100
- // only mechanism that crosses a yalc'd adopter's separate core copy; a
100
+ // only mechanism that crosses an adopter's separate core copy; a
101
101
  // process-local `setLogFilePath` would only reach this module's own copy.
102
102
  this.previousLogFileEnv = process.env[DEFAULT_LOG_FILE_ENV];
103
103
  process.env[DEFAULT_LOG_FILE_ENV] = this.serverLogPath;
@@ -0,0 +1,78 @@
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 * as fsp from 'node:fs/promises';
11
+
12
+ /**
13
+ * Errnos Windows raises when a replacing `rename` meets a handle already on the
14
+ * destination open. POSIX renames are unaffected by open handles, so these
15
+ * cannot fire there and the retry below costs nothing.
16
+ */
17
+ export const RENAME_CONTENTION_CODES = new Set(['EPERM', 'EACCES', 'EBUSY']);
18
+
19
+ /**
20
+ * How long a replacing rename may keep retrying before it gives up.
21
+ *
22
+ * BOUNDED, not open-ended: a genuine permission fault raises the same errno as
23
+ * contention and is distinguishable from it only by never clearing, so an
24
+ * unbounded wait would convert a hard error into a hang.
25
+ *
26
+ * The figure is a judgement, not a measurement, and the honest bound on it is
27
+ * that the previous 2s was observed EXHAUSTED on a loaded Windows CI runner
28
+ * while no upper bound has been established. What would settle it is recording
29
+ * how long a real contended rename takes to clear. graceful-fs tolerates 60s
30
+ * for Windows rename contention, but that is weaker support than it looks: it
31
+ * retries only where the destination has GONE, which is the opposite of
32
+ * replacing one a reader holds open.
33
+ */
34
+ export const RENAME_BUDGET_MS = 10_000;
35
+
36
+ /** Seams for the test, which cannot make a real rename fail on demand. */
37
+ export interface RenameOverOpenReadersOptions {
38
+ readonly rename?: (from: string, to: string) => Promise<void>;
39
+ readonly budgetMs?: number;
40
+ }
41
+
42
+ /**
43
+ * `rename` the staged content over `destination`, retrying while Windows
44
+ * reports the destination as held open.
45
+ *
46
+ * The staging file exists so a reader never sees a half-written file, and on
47
+ * Windows the very presence of that reader is what makes the replacing rename
48
+ * fail — so the indivisible write breaks under exactly the contention it was
49
+ * built for, and it breaks intermittently, which is worse than never working.
50
+ * A reader's handle is released in milliseconds, so waiting turns a spurious
51
+ * failure into a slightly later success.
52
+ *
53
+ * Exhausting the budget rethrows the last failure, leaving the caller's cleanup
54
+ * and error contract unchanged. An errno OUTSIDE the contention set is rethrown
55
+ * without waiting at all: retrying a fault that will not clear only delays it.
56
+ */
57
+ export async function renameOverOpenReaders(
58
+ staging: string,
59
+ destination: string,
60
+ options: RenameOverOpenReadersOptions = {}
61
+ ): Promise<void> {
62
+ const rename = options.rename ?? fsp.rename;
63
+ const deadline = Date.now() + (options.budgetMs ?? RENAME_BUDGET_MS);
64
+ for (let attempt = 0; ; attempt++) {
65
+ try {
66
+ await rename(staging, destination);
67
+ return;
68
+ } catch (err: unknown) {
69
+ const code = err instanceof Error && 'code' in err ? String((err as NodeJS.ErrnoException).code) : undefined;
70
+ if (code === undefined || !RENAME_CONTENTION_CODES.has(code) || Date.now() >= deadline) {
71
+ throw err;
72
+ }
73
+ // Backs off to keep a long contention window from spinning, capped so
74
+ // a late attempt still lands promptly once the handle is released.
75
+ await new Promise(resolve => setTimeout(resolve, Math.min(2 ** attempt, 50)));
76
+ }
77
+ }
78
+ }
@@ -24,6 +24,21 @@ export interface SocketServerOptions {
24
24
  * resolves (only then is the address info available).
25
25
  */
26
26
  readonly port?: number;
27
+ /**
28
+ * Interface to bind. Default `'127.0.0.1'` (LOCAL ONLY) — a head whose
29
+ * port is OS-assigned and announced over the LSP connection is
30
+ * reachable only by a client on the same machine anyway, so a wider
31
+ * binding exposes a port nothing off-box can address. Matches
32
+ * `startGlspServer`'s default, so the two socket heads agree.
33
+ *
34
+ * Pass `'::'` for every interface: that is the dual-stack any-address,
35
+ * so both IPv4 and IPv6 clients connect. `'0.0.0.0'` is NOT the same —
36
+ * it takes IPv4 only and refuses `::1`.
37
+ *
38
+ * An address no local interface holds fails the bind, so `started`
39
+ * rejects with `EADDRNOTAVAIL` rather than falling back to a wider one.
40
+ */
41
+ readonly host?: string;
27
42
  /**
28
43
  * Optional logger. If omitted, the launcher runs silently — the
29
44
  * adopter is expected to surface lifecycle events through their own
@@ -88,6 +103,7 @@ export interface StartedSocketServer extends IntegratedServer {
88
103
  */
89
104
  export function startSocketServer(options: SocketServerOptions, onClientConnection: OnClientConnection): StartedSocketServer {
90
105
  const port = options.port ?? 0;
106
+ const host = options.host ?? '127.0.0.1';
91
107
  const logger = options.logger;
92
108
  const tag = options.logTag ?? 'SocketServer';
93
109
  const started = new Deferred<void>();
@@ -128,7 +144,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
128
144
  connection.listen();
129
145
  });
130
146
 
131
- netServer.listen(port);
147
+ netServer.listen(port, host);
132
148
  netServer.on('listening', () => {
133
149
  const addressInfo = netServer.address();
134
150
  if (!addressInfo) {
@@ -144,7 +160,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
144
160
  return;
145
161
  }
146
162
  handle.port = addressInfo.port;
147
- logger?.info(`[${tag}] Ready to accept new client requests on port: ${addressInfo.port}`);
163
+ logger?.info(`[${tag}] Ready to accept new client requests on ${addressInfo.address} port: ${addressInfo.port}`);
148
164
  started.resolve();
149
165
  });
150
166
  netServer.on('error', err => {
@@ -7,7 +7,9 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { DocumentState, type AstNode, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
10
+ import { DocumentState, type AstNode, type AstReflection, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
11
+ import { buildAstNode } from '../langium/ast-extension/ast-node-builder.js';
12
+ import { type AstDiagnostic } from '../langium/validation/document-validator.js';
11
13
 
12
14
  /**
13
15
  * Build a typed AST-node fixture for unit tests.
@@ -23,12 +25,29 @@ import { DocumentState, type AstNode, type LangiumDocument, UriUtils, type URI }
23
25
  * framework synthetic node, and marking it would change scope/index behaviour.
24
26
  * A test that wants a genuine synthetic node calls `markSynthetic` explicitly.
25
27
  *
28
+ * **Without a `reflection`, the fixture carries only what the test spells out**,
29
+ * which is a shape production never produces: a parsed or built node always has
30
+ * its grammar-declared containment arrays, so a fixture that omits one lets a
31
+ * test pass against an impossible node, and lets code that walks a list crash
32
+ * only under test. Pass the grammar's reflection whenever the type is a real
33
+ * one and the code under test reads a collection off it. Fixtures typed against
34
+ * ad-hoc names have no reflection to pass and keep the plain form.
35
+ *
26
36
  * @param node the node shape — `$type` is required; every other property is a
27
37
  * value the test cares about (`$container`, cross-reference values, internal
28
- * `_id` fields, …). The object is returned by reference, not copied.
38
+ * `_id` fields, …).
39
+ * @param reflection when given, pre-fills the grammar's declared defaults the
40
+ * same way {@link buildAstNode} does, and the result is a fresh object rather
41
+ * than `node` itself. Omitted, `node` is returned by reference.
29
42
  */
30
- export function makeFakeAstNode<TAst extends AstNode = AstNode>(node: { $type: string } & Record<string, unknown>): TAst {
31
- return node as unknown as TAst;
43
+ export function makeFakeAstNode<TAst extends AstNode = AstNode>(
44
+ node: { $type: string } & Record<string, unknown>,
45
+ reflection?: AstReflection
46
+ ): TAst {
47
+ if (!reflection) {
48
+ return node as unknown as TAst;
49
+ }
50
+ return buildAstNode<TAst>(reflection, node.$type, node);
32
51
  }
33
52
 
34
53
  /**
@@ -86,7 +105,7 @@ function stringifyFakeRoot(root: unknown): string {
86
105
  * staging. Override via `options.state` for tests that gate on earlier
87
106
  * phases.
88
107
  */
89
- export function makeFakeDocument<TAst extends AstNode = AstNode, TDiagnostic = unknown>(
108
+ export function makeFakeDocument<TAst extends AstNode = AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic>(
90
109
  uri: string | URI,
91
110
  root: TAst,
92
111
  options: FakeDocumentOptions<TAst, TDiagnostic> = {}
@@ -9,6 +9,8 @@
9
9
 
10
10
  import { type Clock, DefaultTracer, type Logger, NoopLogger, SystemClock, type Tracer } from '@hydranium/protocol';
11
11
  import type { ServerSharedServicesMinimal } from '../langium/shared-services.js';
12
+ import { DefaultServerLocale, type ServerLocale } from '../locale/server-locale.js';
13
+ import { DefaultMessageRenderer, type MessageRenderer } from '../messages/renderer.js';
12
14
 
13
15
  /**
14
16
  * Overrides for {@link makeNoopSharedServices}. The three observability slots
@@ -29,6 +31,18 @@ export interface NoopSharedServicesOverrides {
29
31
  Logger?: Logger;
30
32
  /** Bound on the top-level `Tracer` slot. Default: a `DefaultTracer` over `Logger` + `Clock`. */
31
33
  Tracer?: Tracer;
34
+ /** Factory for the `ServerLocale` slot. Default: a real one, reporting no locale. */
35
+ ServerLocale?: (services: ServerSharedServicesMinimal) => ServerLocale;
36
+ /**
37
+ * Factory for the `MessageRenderer` slot. Default: the real
38
+ * {@link DefaultMessageRenderer}, whose no-catalogue behaviour is a
39
+ * pass-through — so the default is the framework's own behaviour, not a stub.
40
+ *
41
+ * A factory rather than an instance, because a renderer reads the tree it is
42
+ * bound into. Both slots are resolved lazily, so an override is honoured
43
+ * however late the caller reads them.
44
+ */
45
+ MessageRenderer?: (services: ServerSharedServicesMinimal) => MessageRenderer;
32
46
  /** Per-slot `workspace` overrides. Slots left out resolve to `undefined`. */
33
47
  workspace?: Record<string, unknown>;
34
48
  /** Any other minimal slot (`ServiceRegistry`, `AstReflection`, `additionalDocuments`, …). */
@@ -63,11 +77,21 @@ export interface NoopSharedServicesOverrides {
63
77
  export function makeNoopSharedServices<T extends ServerSharedServicesMinimal = ServerSharedServicesMinimal>(
64
78
  overrides: NoopSharedServicesOverrides = {}
65
79
  ): T {
66
- const { Clock: clock, Logger: logger, Tracer: tracer, workspace, additionalDocuments, ...restTop } = overrides;
80
+ const {
81
+ Clock: clock,
82
+ Logger: logger,
83
+ Tracer: tracer,
84
+ ServerLocale: serverLocale,
85
+ MessageRenderer: messageRenderer,
86
+ workspace,
87
+ additionalDocuments,
88
+ ...restTop
89
+ } = overrides;
67
90
  const resolvedClock = clock ?? new SystemClock();
68
91
  const resolvedLogger = logger ?? new NoopLogger();
69
92
  const resolvedTracer = tracer ?? new DefaultTracer(resolvedLogger, resolvedClock);
70
- return {
93
+ // Assembled in two steps, because both defaults read the tree they belong to.
94
+ const services = {
71
95
  Clock: resolvedClock,
72
96
  Logger: resolvedLogger,
73
97
  Tracer: resolvedTracer,
@@ -75,4 +99,28 @@ export function makeNoopSharedServices<T extends ServerSharedServicesMinimal = S
75
99
  ...restTop,
76
100
  workspace: { ...(workspace ?? {}) }
77
101
  } as unknown as T;
102
+ // Lazy, like Langium's own per-slot construction — and load-bearing here:
103
+ // both services emit an `instantiated` trace, so building them eagerly puts
104
+ // two lines into the capture of every test that passes a capturing logger
105
+ // and asserts on emptiness.
106
+ defineLazySlot(services, 'ServerLocale', () => serverLocale?.(services) ?? new DefaultServerLocale(services));
107
+ defineLazySlot(services, 'MessageRenderer', () => messageRenderer?.(services) ?? new DefaultMessageRenderer(services));
108
+ return services;
109
+ }
110
+
111
+ /** Install `name` as a memoized getter, so nothing is constructed until it is read. */
112
+ function defineLazySlot(target: object, name: string, create: () => unknown): void {
113
+ let resolved: unknown;
114
+ let built = false;
115
+ Object.defineProperty(target, name, {
116
+ configurable: true,
117
+ enumerable: true,
118
+ get: () => {
119
+ if (!built) {
120
+ resolved = create();
121
+ built = true;
122
+ }
123
+ return resolved;
124
+ }
125
+ });
78
126
  }
@@ -10,6 +10,7 @@
10
10
  import {
11
11
  type Clock,
12
12
  DefaultTracer,
13
+ type Locale,
13
14
  type Logger,
14
15
  NoopLogger,
15
16
  type Project,
@@ -18,11 +19,14 @@ import {
18
19
  type TransferDiagnostic,
19
20
  type TransferElement
20
21
  } from '@hydranium/protocol';
22
+ import { DefaultServerLocale, type ServerLocale } from '../locale/server-locale.js';
23
+ import { DefaultMessageRenderer, type MessageRenderer } from '../messages/renderer.js';
21
24
  import type { Harness } from '@hydranium/protocol/testing';
22
25
  import { type AstNode, type AstNodeDescription, type WorkspaceLock } from '@hydranium/langium';
23
26
  import type { ModelService, ModelServiceOptions } from '../langium/model-service/model-service.js';
24
27
  import type { ServerSharedServices } from '../langium/module.js';
25
- import { TransferEncoder } from '../langium/transfer/transfer-encoder.js';
28
+ import type { AstDiagnostic } from '../langium/validation/document-validator.js';
29
+ import { DefaultTransferEncoder, type TransferEncoder } from '../langium/transfer/transfer-encoder.js';
26
30
  import { DefaultDocumentUriPolicy, type DocumentUriPolicy } from '../langium/workspace/document-uri-policy.js';
27
31
  import { HydraniumWorkspaceLock } from '../langium/workspace/hydranium-workspace-lock.js';
28
32
  import type { FakeDocumentOptions } from './fake-document.js';
@@ -46,12 +50,21 @@ import { makeStubWritableFileSystem, type StubWritableFileSystem } from './stub-
46
50
  * to `ServerSharedServices` in {@link makeTestServices} is therefore the
47
51
  * single named place where the "stub stands in for the full service tree"
48
52
  * assertion happens.
53
+ *
54
+ * **`TDiagnostic` and `TTransferDiagnostic` are the two ends of a conversion
55
+ * and must stay separate.** The first is what the build left on
56
+ * `LangiumDocument.diagnostics` and what `ModelService` hands back on an
57
+ * `AstDocument`; the second is what `TransferEncoder.toTransferDiagnostic`
58
+ * produces for the wire. One parameter serving both makes the tree
59
+ * unbindable for an adopter who extends either shape, since extending one
60
+ * says nothing about the other.
49
61
  */
50
62
  export interface TestSharedServices<
51
63
  TAst extends AstNode = AstNode,
52
- TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
64
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
53
65
  TTransfer extends TransferElement = TransferElement,
54
- TProject extends Project = Project
66
+ TProject extends Project = Project,
67
+ TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
55
68
  > {
56
69
  readonly Clock: Clock;
57
70
  readonly Logger: Logger;
@@ -90,17 +103,27 @@ export interface TestSharedServices<
90
103
  WorkspaceLock: WorkspaceLock;
91
104
  };
92
105
  readonly model: {
93
- TransferEncoder: TransferEncoder<unknown, TDiagnostic>;
106
+ TransferEncoder: TransferEncoder<TTransferDiagnostic>;
94
107
  ModelService: ModelService<TAst, TDiagnostic, TTransfer>;
95
108
  };
109
+ /**
110
+ * The REAL services, not stubs — the framework renderer's no-catalogue
111
+ * behaviour is a pass-through, so the stub tree agrees with a production one
112
+ * unless a test installs a catalogue. Bound rather than omitted because
113
+ * `HydraniumDocumentBuilder` renders through the renderer on every
114
+ * `Validated` phase, where an omitted slot is a `TypeError`.
115
+ */
116
+ readonly ServerLocale: ServerLocale;
117
+ readonly MessageRenderer: MessageRenderer;
96
118
  }
97
119
 
98
120
  /** Optional configuration for {@link makeTestServices}. */
99
121
  export interface MakeTestServicesOptions<
100
122
  TAst extends AstNode = AstNode,
101
- TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
123
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
102
124
  TTransfer extends TransferElement = TransferElement,
103
- TProject extends Project = Project
125
+ TProject extends Project = Project,
126
+ TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
104
127
  > {
105
128
  /**
106
129
  * Serialiser used by the bundled `StubModelService`. The stub tree
@@ -112,6 +135,14 @@ export interface MakeTestServicesOptions<
112
135
  * for tests that don't assert on the produced text.
113
136
  */
114
137
  serialize?: (uri: string, root: TTransfer) => string;
138
+ /**
139
+ * Renderer bound on `MessageRenderer`. Default: the framework's own, which
140
+ * passes every sentence through unchanged. Supply one to install a catalogue
141
+ * or to drive the throwing path.
142
+ */
143
+ messageRenderer?: (services: ServerSharedServices<TProject>) => MessageRenderer;
144
+ /** Locale handed to the bundle's {@link ServerLocale}. Default: none, i.e. the framework's English. */
145
+ locale?: Locale;
115
146
  /**
116
147
  * Languages to register on a {@link StubServiceRegistry} bound on the
117
148
  * `ServiceRegistry` slot. This is how a test gets multi-language routing:
@@ -187,7 +218,7 @@ export interface MakeTestServicesOptions<
187
218
  * Override the {@link TransferEncoder} factory. Default: framework
188
219
  * {@link TransferEncoder} with no overrides.
189
220
  */
190
- transferEncoder?: (services: ServerSharedServices<TProject>) => TransferEncoder<unknown, TDiagnostic>;
221
+ transferEncoder?: (services: ServerSharedServices<TProject>) => TransferEncoder<TTransferDiagnostic>;
191
222
  }
192
223
 
193
224
  /**
@@ -205,9 +236,10 @@ export interface MakeTestServicesOptions<
205
236
  */
206
237
  export interface TestServicesBundle<
207
238
  TAst extends AstNode = AstNode,
208
- TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
239
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
209
240
  TTransfer extends TransferElement = TransferElement,
210
- TProject extends Project = Project
241
+ TProject extends Project = Project,
242
+ TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
211
243
  > extends Harness {
212
244
  readonly services: ServerSharedServices<TProject>;
213
245
  readonly documents: StubLangiumDocuments<TAst, TDiagnostic>;
@@ -229,10 +261,18 @@ export interface TestServicesBundle<
229
261
  */
230
262
  readonly indexManager: StubIndexManager | undefined;
231
263
  readonly modelService: ModelService<TAst, TDiagnostic, TTransfer>;
232
- readonly transferEncoder: TransferEncoder<unknown, TDiagnostic>;
264
+ readonly transferEncoder: TransferEncoder<TTransferDiagnostic>;
233
265
  readonly logger: Logger;
234
266
  /** The clock bound on the `Clock` slot — a `makeFakeClock()` if one was passed. */
235
267
  readonly clock: Clock;
268
+ /**
269
+ * The service bound on `ServerLocale`, so a test can hand over a locale
270
+ * mid-run — which is also how it verifies the renderer reads the locale per
271
+ * render rather than caching it at construction.
272
+ */
273
+ readonly serverLocale: ServerLocale;
274
+ /** The renderer bound on `MessageRenderer` — the framework's own unless one was supplied. */
275
+ readonly messageRenderer: MessageRenderer;
236
276
  }
237
277
 
238
278
  /**
@@ -247,12 +287,13 @@ export interface TestServicesBundle<
247
287
  */
248
288
  export function makeTestServices<
249
289
  TAst extends AstNode = AstNode,
250
- TDiagnostic extends TransferDiagnostic = TransferDiagnostic,
290
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
251
291
  TTransfer extends TransferElement = TransferElement,
252
- TProject extends Project = Project
292
+ TProject extends Project = Project,
293
+ TTransferDiagnostic extends TransferDiagnostic = TransferDiagnostic
253
294
  >(
254
- options: MakeTestServicesOptions<TAst, TDiagnostic, TTransfer, TProject> = {}
255
- ): TestServicesBundle<TAst, TDiagnostic, TTransfer, TProject> {
295
+ options: MakeTestServicesOptions<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> = {}
296
+ ): TestServicesBundle<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> {
256
297
  const documents = makeStubLangiumDocuments<TAst, TDiagnostic>(options.seedDocuments);
257
298
  const textDocuments = makeStubHydraniumTextDocuments();
258
299
  const documentBuilder = makeStubDocumentBuilder();
@@ -289,7 +330,7 @@ export function makeTestServices<
289
330
  // (after the factories run); the literal uses unsafe casts to
290
331
  // partially-built objects to satisfy TestSharedServices, then patches
291
332
  // them in.
292
- const services: TestSharedServices<TAst, TDiagnostic, TTransfer, TProject> = {
333
+ const services: TestSharedServices<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic> = {
293
334
  Clock: clock,
294
335
  Logger: logger,
295
336
  Tracer: tracer,
@@ -311,20 +352,33 @@ export function makeTestServices<
311
352
  ...(indexManager ? { IndexManager: indexManager } : {}),
312
353
  WorkspaceLock: new HydraniumWorkspaceLock()
313
354
  },
314
- model: {} as TestSharedServices<TAst, TDiagnostic, TTransfer, TProject>['model']
355
+ model: {} as TestSharedServices<TAst, TDiagnostic, TTransfer, TProject, TTransferDiagnostic>['model'],
356
+ ServerLocale: {} as ServerLocale,
357
+ MessageRenderer: {} as DefaultMessageRenderer
315
358
  };
316
359
  const sharedServices = services as unknown as ServerSharedServices<TProject>;
317
360
 
361
+ // Patched in after the literal, like `model` below: both read the tree they
362
+ // belong to, and the renderer reads the locale service.
363
+ const mutableMessages = services as { ServerLocale: ServerLocale; MessageRenderer: MessageRenderer };
364
+ const serverLocale = new DefaultServerLocale(sharedServices);
365
+ if (options.locale) {
366
+ serverLocale.accept(options.locale);
367
+ }
368
+ mutableMessages.ServerLocale = serverLocale;
369
+ const messageRenderer = options.messageRenderer?.(sharedServices) ?? new DefaultMessageRenderer(sharedServices);
370
+ mutableMessages.MessageRenderer = messageRenderer;
371
+
318
372
  const serialize = options.serialize ?? ((_uri: string, root: TTransfer) => JSON.stringify(root));
319
373
  const transferEncoder = options.transferEncoder
320
374
  ? options.transferEncoder(sharedServices)
321
- : new TransferEncoder<unknown, TDiagnostic>(sharedServices);
375
+ : new DefaultTransferEncoder<unknown, TTransferDiagnostic>(sharedServices);
322
376
  const modelService = options.modelService
323
377
  ? options.modelService(sharedServices)
324
378
  : makeStubModelService<TAst, TDiagnostic, TTransfer>(sharedServices, serialize, options.modelServiceOptions);
325
379
 
326
380
  const mutableModel = services.model as {
327
- TransferEncoder: TransferEncoder<unknown, TDiagnostic>;
381
+ TransferEncoder: TransferEncoder<TTransferDiagnostic>;
328
382
  ModelService: ModelService<TAst, TDiagnostic, TTransfer>;
329
383
  };
330
384
  mutableModel.TransferEncoder = transferEncoder;
@@ -346,6 +400,8 @@ export function makeTestServices<
346
400
  transferEncoder,
347
401
  logger,
348
402
  clock,
403
+ serverLocale,
404
+ messageRenderer,
349
405
  dispose: () => {
350
406
  // No-op: the bundled stubs hold only in-memory state (maps, arrays),
351
407
  // released with the bundle when it goes out of scope. The hook exists
@@ -27,3 +27,4 @@ export * from './lsp-harness.js';
27
27
  export * from './lsp-server-connection.js';
28
28
  export * from './scratch-workspace.js';
29
29
  export * from './spawned-server.js';
30
+ export * from './unhandled-rejections.js';
@@ -216,12 +216,14 @@ export function makeLspHarness(options: MakeLspHarnessOptions): LspHarness {
216
216
  },
217
217
  shutdown: transport.shutdown,
218
218
  openDocument: transport.openDocument,
219
+ closeDocument: transport.closeDocument,
219
220
  changeDocument: transport.changeDocument,
220
221
  completion: transport.completion,
221
222
  hover: transport.hover,
222
223
  semanticTokens: transport.semanticTokens,
223
224
  nextDiagnostics: transport.nextDiagnostics,
224
225
  appliedEdits: transport.appliedEdits,
226
+ wireBytes: transport.wireBytes,
225
227
  nextAppliedEdit: transport.nextAppliedEdit,
226
228
  setApplyEditHandler: transport.setApplyEditHandler,
227
229
  buildWorkspace: folders => buildWorkspaceProgrammatically(services, folders),
@@ -8,17 +8,16 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import { type Harness } from '@hydranium/protocol/testing';
11
- import { makeDuplexStreamPair } from '@hydranium/protocol/testing/node';
11
+ import { PassThrough, type TransformCallback } from 'node:stream';
12
12
  import { StreamMessageReader, StreamMessageWriter } from 'vscode-jsonrpc/node';
13
13
  import { type Connection, ProposedFeatures } from 'vscode-languageserver/node';
14
+ import { withHydraniumLspFeatures } from '../../lsp/connection-features.js';
14
15
  // The node `createConnection` from `vscode-languageserver/node` is built for a
15
16
  // standalone server PROCESS: it wires `process.exit()` onto the input stream's
16
17
  // `end`/`close` events, onto the `exit` notification (via its watchDog), and onto
17
- // a parent-pid liveness timer, and it calls `endProtocolConnection()` (a write)
18
- // on stream close. In-process every one of those is fatal — destroying the
19
- // streams at teardown trips the `close` handler, which writes into the destroyed
20
- // stream (an uncatchable synchronous `ERR_STREAM_DESTROYED`) and/or exits the host
21
- // test runner. The lower-level common `createConnection(connectionFactory,
18
+ // a parent-pid liveness timer. In-process every one of those is fatal —
19
+ // destroying the streams at teardown trips the `close` handler, which exits the
20
+ // host test runner. The lower-level common `createConnection(connectionFactory,
22
21
  // watchDog, features)` form does none of that: it lets us inject a no-op-`exit`
23
22
  // WatchDog and build the connection over the in-memory streams, so teardown is the
24
23
  // primitive's responsibility alone.
@@ -30,6 +29,7 @@ import {
30
29
  ApplyWorkspaceEditRequest,
31
30
  CompletionRequest,
32
31
  DidChangeTextDocumentNotification,
32
+ DidCloseTextDocumentNotification,
33
33
  DidOpenTextDocumentNotification,
34
34
  HoverRequest,
35
35
  InitializeRequest,
@@ -170,8 +170,8 @@ function insertedText(edit: WorkspaceEdit): string {
170
170
  }
171
171
 
172
172
  /**
173
- * The in-process LSP transport seam — a real `Connection` over the in-memory
174
- * {@link makeDuplexStreamPair}, plus the client side, the diagnostics capture,
173
+ * The in-process LSP transport seam — a real `Connection` over a crossed pair
174
+ * of in-memory streams, plus the client side, the diagnostics capture,
175
175
  * and the typed request facades — with NO services attached. It is the lower
176
176
  * half of `makeLspHarness`, factored out so a caller can OWN the services
177
177
  * tree: build the tree around {@link serverConnection} (`createXxxServices({
@@ -226,6 +226,12 @@ export interface LspServerConnection extends Harness {
226
226
  * unhandled method.
227
227
  */
228
228
  readonly appliedEdits: ReadonlyArray<AppliedEdit>;
229
+ /** Bytes written in each direction of the in-process JSON-RPC wire. */
230
+ readonly wireBytes: () => {
231
+ readonly clientToServer: number;
232
+ readonly serverToClient: number;
233
+ readonly total: number;
234
+ };
229
235
 
230
236
  /**
231
237
  * Drive the real LSP `initialize` → `initialized` handshake and return the
@@ -247,6 +253,8 @@ export interface LspServerConnection extends Harness {
247
253
  * associates with a language (silent no-diagnostics). `version` defaults to 1.
248
254
  */
249
255
  openDocument(uri: string, text: string, languageId: string, version?: number): void;
256
+ /** Send `textDocument/didClose` for a document opened through this harness. */
257
+ closeDocument(uri: string): Promise<void>;
250
258
  /** Send `textDocument/didChange` for `uri` as a single full-text replacement at `version`. */
251
259
  changeDocument(uri: string, text: string, version: number): void;
252
260
 
@@ -319,6 +327,39 @@ export interface LspServerConnection extends Harness {
319
327
  dispose(): void;
320
328
  }
321
329
 
330
+ /**
331
+ * A `PassThrough` that counts the bytes written through it. Counting here
332
+ * rather than in a `'data'` listener is what keeps the wire buffering: a
333
+ * listener switches the stream to flowing mode, and a message written before
334
+ * the server calls `listen()` is then delivered to the counter alone and lost.
335
+ */
336
+ class CountingPassThrough extends PassThrough {
337
+ bytes = 0;
338
+
339
+ override _transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void {
340
+ this.bytes += chunk.length;
341
+ callback(null, chunk);
342
+ }
343
+ }
344
+
345
+ /** A crossed pair of {@link CountingPassThrough}s, disposed as one. */
346
+ function makeCountingStreamPair(): {
347
+ readonly clientToServer: CountingPassThrough;
348
+ readonly serverToClient: CountingPassThrough;
349
+ dispose(): void;
350
+ } {
351
+ const clientToServer = new CountingPassThrough();
352
+ const serverToClient = new CountingPassThrough();
353
+ return {
354
+ clientToServer,
355
+ serverToClient,
356
+ dispose(): void {
357
+ clientToServer.destroy();
358
+ serverToClient.destroy();
359
+ }
360
+ };
361
+ }
362
+
322
363
  /**
323
364
  * Build the in-process LSP transport seam (server `Connection` + client + wire +
324
365
  * diagnostics capture + typed facades) WITHOUT attaching a server. The caller
@@ -331,7 +372,7 @@ export interface LspServerConnection extends Harness {
331
372
  * additional protocol heads (data-server, GLSP) onto the same `shared` services.
332
373
  */
333
374
  export function makeLspServerConnection(): LspServerConnection {
334
- const pair = makeDuplexStreamPair();
375
+ const pair = makeCountingStreamPair();
335
376
 
336
377
  // No-op watchDog: in-process, no LSP lifecycle event (parent-pid liveness, the
337
378
  // `exit` notification, …) may exit the host test runner — `dispose()` owns
@@ -347,7 +388,7 @@ export function makeLspServerConnection(): LspServerConnection {
347
388
  logger =>
348
389
  createProtocolConnection(new StreamMessageReader(pair.clientToServer), new StreamMessageWriter(pair.serverToClient), logger),
349
390
  watchDog,
350
- ProposedFeatures.all
391
+ withHydraniumLspFeatures(ProposedFeatures.all)
351
392
  );
352
393
 
353
394
  // Client side mirrors the directions: reads serverToClient, writes clientToServer.
@@ -415,6 +456,11 @@ export function makeLspServerConnection(): LspServerConnection {
415
456
  client,
416
457
  diagnostics,
417
458
  appliedEdits,
459
+ wireBytes: () => ({
460
+ clientToServer: pair.clientToServer.bytes,
461
+ serverToClient: pair.serverToClient.bytes,
462
+ total: pair.clientToServer.bytes + pair.serverToClient.bytes
463
+ }),
418
464
 
419
465
  async initialize(params?: Partial<InitializeParams>): Promise<InitializeResult> {
420
466
  const initializeParams: InitializeParams = {
@@ -455,6 +501,10 @@ export function makeLspServerConnection(): LspServerConnection {
455
501
  });
456
502
  },
457
503
 
504
+ async closeDocument(uri: string): Promise<void> {
505
+ await client.sendNotification(DidCloseTextDocumentNotification.type, { textDocument: { uri } });
506
+ },
507
+
458
508
  changeDocument(uri: string, text: string, version: number): void {
459
509
  client.sendNotification(DidChangeTextDocumentNotification.type, {
460
510
  textDocument: { uri, version },