@hydranium/core 1.0.0-next.14 → 1.0.0-next.148

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 (489) hide show
  1. package/README.md +5 -1
  2. package/lib/documents/ast-document-manager.d.ts +187 -27
  3. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  4. package/lib/documents/ast-document-manager.js +235 -38
  5. package/lib/documents/ast-document-manager.js.map +1 -1
  6. package/lib/documents/client-ids.d.ts +5 -19
  7. package/lib/documents/client-ids.d.ts.map +1 -1
  8. package/lib/documents/client-ids.js +5 -19
  9. package/lib/documents/client-ids.js.map +1 -1
  10. package/lib/documents/client-session-errors.d.ts +15 -0
  11. package/lib/documents/client-session-errors.d.ts.map +1 -0
  12. package/lib/documents/client-session-errors.js +15 -0
  13. package/lib/documents/client-session-errors.js.map +1 -0
  14. package/lib/documents/client-session-registry.d.ts +138 -0
  15. package/lib/documents/client-session-registry.d.ts.map +1 -0
  16. package/lib/documents/client-session-registry.js +187 -0
  17. package/lib/documents/client-session-registry.js.map +1 -0
  18. package/lib/documents/file-system-task-queue.d.ts +49 -0
  19. package/lib/documents/file-system-task-queue.d.ts.map +1 -0
  20. package/lib/documents/file-system-task-queue.js +34 -0
  21. package/lib/documents/file-system-task-queue.js.map +1 -0
  22. package/lib/documents/hydranium-text-documents.d.ts +386 -57
  23. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  24. package/lib/documents/hydranium-text-documents.js +700 -126
  25. package/lib/documents/hydranium-text-documents.js.map +1 -1
  26. package/lib/documents/index.d.ts +3 -0
  27. package/lib/documents/index.d.ts.map +1 -1
  28. package/lib/documents/index.js +3 -0
  29. package/lib/documents/index.js.map +1 -1
  30. package/lib/documents/language-client-text-shadow.d.ts +41 -9
  31. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  32. package/lib/documents/language-client-text-shadow.js +56 -11
  33. package/lib/documents/language-client-text-shadow.js.map +1 -1
  34. package/lib/documents/self-save-registry.d.ts +37 -6
  35. package/lib/documents/self-save-registry.d.ts.map +1 -1
  36. package/lib/documents/self-save-registry.js +28 -15
  37. package/lib/documents/self-save-registry.js.map +1 -1
  38. package/lib/index.d.ts +3 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +6 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
  43. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  44. package/lib/langium/ast-extension/ast-node-builder.js +60 -22
  45. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  46. package/lib/langium/bootstrap.d.ts.map +1 -1
  47. package/lib/langium/bootstrap.js +16 -9
  48. package/lib/langium/bootstrap.js.map +1 -1
  49. package/lib/langium/config/configuration-provider.d.ts +81 -0
  50. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  51. package/lib/langium/config/configuration-provider.js +112 -0
  52. package/lib/langium/config/configuration-provider.js.map +1 -0
  53. package/lib/langium/config/index.d.ts +1 -0
  54. package/lib/langium/config/index.d.ts.map +1 -1
  55. package/lib/langium/config/index.js +1 -0
  56. package/lib/langium/config/index.js.map +1 -1
  57. package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
  58. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  59. package/lib/langium/diagnostics/lsp-logger.js +40 -6
  60. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  61. package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
  62. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  63. package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
  64. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  65. package/lib/langium/document-builder/build-session.d.ts +93 -0
  66. package/lib/langium/document-builder/build-session.d.ts.map +1 -0
  67. package/lib/langium/document-builder/build-session.js +72 -0
  68. package/lib/langium/document-builder/build-session.js.map +1 -0
  69. package/lib/langium/document-builder/document-builder.d.ts +352 -16
  70. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  71. package/lib/langium/document-builder/document-builder.js +616 -40
  72. package/lib/langium/document-builder/document-builder.js.map +1 -1
  73. package/lib/langium/document-builder/index.d.ts +1 -0
  74. package/lib/langium/document-builder/index.d.ts.map +1 -1
  75. package/lib/langium/document-builder/index.js +1 -0
  76. package/lib/langium/document-builder/index.js.map +1 -1
  77. package/lib/langium/integration-services.d.ts +45 -1
  78. package/lib/langium/integration-services.d.ts.map +1 -1
  79. package/lib/langium/integration-services.js +10 -5
  80. package/lib/langium/integration-services.js.map +1 -1
  81. package/lib/langium/integrity/integrity-rule.d.ts +13 -5
  82. package/lib/langium/integrity/integrity-rule.d.ts.map +1 -1
  83. package/lib/langium/integrity/integrity-rule.js.map +1 -1
  84. package/lib/langium/integrity/integrity-service.d.ts +50 -6
  85. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  86. package/lib/langium/integrity/integrity-service.js +193 -17
  87. package/lib/langium/integrity/integrity-service.js.map +1 -1
  88. package/lib/langium/keys/containment.d.ts +64 -0
  89. package/lib/langium/keys/containment.d.ts.map +1 -0
  90. package/lib/langium/keys/containment.js +53 -0
  91. package/lib/langium/keys/containment.js.map +1 -0
  92. package/lib/langium/keys/index.d.ts +1 -0
  93. package/lib/langium/keys/index.d.ts.map +1 -1
  94. package/lib/langium/keys/index.js +1 -0
  95. package/lib/langium/keys/index.js.map +1 -1
  96. package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
  97. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  98. package/lib/langium/keys/name-based-key-provider.js +4 -0
  99. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  100. package/lib/langium/language-module.d.ts +27 -2
  101. package/lib/langium/language-module.d.ts.map +1 -1
  102. package/lib/langium/language-module.js +16 -2
  103. package/lib/langium/language-module.js.map +1 -1
  104. package/lib/langium/model-service/client-session.d.ts +307 -0
  105. package/lib/langium/model-service/client-session.d.ts.map +1 -0
  106. package/lib/langium/model-service/client-session.js +395 -0
  107. package/lib/langium/model-service/client-session.js.map +1 -0
  108. package/lib/langium/model-service/index.d.ts +1 -0
  109. package/lib/langium/model-service/index.d.ts.map +1 -1
  110. package/lib/langium/model-service/index.js +1 -0
  111. package/lib/langium/model-service/index.js.map +1 -1
  112. package/lib/langium/model-service/model-service.d.ts +261 -198
  113. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  114. package/lib/langium/model-service/model-service.js +232 -344
  115. package/lib/langium/model-service/model-service.js.map +1 -1
  116. package/lib/langium/module.d.ts +107 -24
  117. package/lib/langium/module.d.ts.map +1 -1
  118. package/lib/langium/module.js +30 -12
  119. package/lib/langium/module.js.map +1 -1
  120. package/lib/langium/naming/name-provider.d.ts +25 -4
  121. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  122. package/lib/langium/naming/name-provider.js +2 -1
  123. package/lib/langium/naming/name-provider.js.map +1 -1
  124. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  125. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  126. package/lib/langium/naming/name-separator-validation.js +30 -1
  127. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  128. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  129. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  130. package/lib/langium/residency/cst-residency-service.js +13 -56
  131. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  132. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  133. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  134. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  135. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  136. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  137. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  138. package/lib/langium/serialization/abstract-serializer.js +12 -21
  139. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  140. package/lib/langium/service-registry.d.ts +15 -1
  141. package/lib/langium/service-registry.d.ts.map +1 -1
  142. package/lib/langium/service-registry.js +16 -0
  143. package/lib/langium/service-registry.js.map +1 -1
  144. package/lib/langium/shared-services.d.ts +15 -2
  145. package/lib/langium/shared-services.d.ts.map +1 -1
  146. package/lib/langium/shared-services.js.map +1 -1
  147. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  148. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  149. package/lib/langium/transfer/transfer-encoder.js +38 -16
  150. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  151. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  152. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  153. package/lib/langium/trivia/comment-preserver.js +906 -0
  154. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  155. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  156. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  157. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  158. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  159. package/lib/langium/trivia/index.d.ts +14 -0
  160. package/lib/langium/trivia/index.d.ts.map +1 -0
  161. package/lib/langium/trivia/index.js +14 -0
  162. package/lib/langium/trivia/index.js.map +1 -0
  163. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  164. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  165. package/lib/langium/trivia/trivia-contribution.js +10 -0
  166. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  167. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  168. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  169. package/lib/langium/trivia/trivia-preserver.js +10 -0
  170. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  171. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  172. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  173. package/lib/langium/trivia/trivia-service.js +56 -0
  174. package/lib/langium/trivia/trivia-service.js.map +1 -0
  175. package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
  176. package/lib/langium/validation/document-validator.d.ts +294 -10
  177. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  178. package/lib/langium/validation/document-validator.js +448 -5
  179. package/lib/langium/validation/document-validator.js.map +1 -1
  180. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  181. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  182. package/lib/langium/workspace/document-uri-policy.js +3 -4
  183. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  184. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  185. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  186. package/lib/langium/workspace/file-system-provider.js +40 -7
  187. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  188. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  189. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  190. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  191. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  192. package/lib/langium/workspace/hydranium-workspace-lock.d.ts +13 -0
  193. package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -1
  194. package/lib/langium/workspace/hydranium-workspace-lock.js +18 -0
  195. package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
  196. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  197. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  198. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  199. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  200. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  201. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  202. package/lib/langium/workspace/in-memory-file-system-provider.js +55 -9
  203. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  204. package/lib/langium/workspace/initialize-workspace.d.ts +31 -2
  205. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  206. package/lib/langium/workspace/initialize-workspace.js +13 -5
  207. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  208. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  209. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  210. package/lib/langium/workspace/langium-documents.js +83 -28
  211. package/lib/langium/workspace/langium-documents.js.map +1 -1
  212. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  213. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  214. package/lib/langium/workspace/virtual-document.js +19 -2
  215. package/lib/langium/workspace/virtual-document.js.map +1 -1
  216. package/lib/langium/workspace/write-lock-scope.d.ts +4 -3
  217. package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
  218. package/lib/langium/workspace/write-lock-scope.js +4 -3
  219. package/lib/langium/workspace/write-lock-scope.js.map +1 -1
  220. package/lib/locale/index.d.ts +10 -0
  221. package/lib/locale/index.d.ts.map +1 -0
  222. package/lib/locale/index.js +10 -0
  223. package/lib/locale/index.js.map +1 -0
  224. package/lib/locale/server-locale.d.ts +73 -0
  225. package/lib/locale/server-locale.d.ts.map +1 -0
  226. package/lib/locale/server-locale.js +61 -0
  227. package/lib/locale/server-locale.js.map +1 -0
  228. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  229. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  230. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  231. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  232. package/lib/lsp/connection-features.d.ts +23 -0
  233. package/lib/lsp/connection-features.d.ts.map +1 -0
  234. package/lib/lsp/connection-features.js +60 -0
  235. package/lib/lsp/connection-features.js.map +1 -0
  236. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  237. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  238. package/lib/lsp/diagnostics-connection.js +53 -0
  239. package/lib/lsp/diagnostics-connection.js.map +1 -0
  240. package/lib/lsp/hydranium-document-update-handler.d.ts +90 -79
  241. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  242. package/lib/lsp/hydranium-document-update-handler.js +152 -99
  243. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  244. package/lib/lsp/index.d.ts +3 -1
  245. package/lib/lsp/index.d.ts.map +1 -1
  246. package/lib/lsp/index.js +3 -1
  247. package/lib/lsp/index.js.map +1 -1
  248. package/lib/lsp/lsp-latency.d.ts +39 -0
  249. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  250. package/lib/lsp/lsp-latency.js +58 -0
  251. package/lib/lsp/lsp-latency.js.map +1 -0
  252. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  253. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  254. package/lib/lsp/semantic-token-provider.js +55 -8
  255. package/lib/lsp/semantic-token-provider.js.map +1 -1
  256. package/lib/lsp/shared-module.d.ts +4 -8
  257. package/lib/lsp/shared-module.d.ts.map +1 -1
  258. package/lib/lsp/shared-module.js.map +1 -1
  259. package/lib/lsp/start-language-server.d.ts +8 -7
  260. package/lib/lsp/start-language-server.d.ts.map +1 -1
  261. package/lib/lsp/start-language-server.js +13 -7
  262. package/lib/lsp/start-language-server.js.map +1 -1
  263. package/lib/messages/carriers.d.ts +54 -0
  264. package/lib/messages/carriers.d.ts.map +1 -0
  265. package/lib/messages/carriers.js +62 -0
  266. package/lib/messages/carriers.js.map +1 -0
  267. package/lib/messages/index.d.ts +25 -0
  268. package/lib/messages/index.d.ts.map +1 -0
  269. package/lib/messages/index.js +25 -0
  270. package/lib/messages/index.js.map +1 -0
  271. package/lib/messages/renderer.d.ts +142 -0
  272. package/lib/messages/renderer.d.ts.map +1 -0
  273. package/lib/messages/renderer.js +165 -0
  274. package/lib/messages/renderer.js.map +1 -0
  275. package/lib/node/heap-ceiling.d.ts +61 -0
  276. package/lib/node/heap-ceiling.d.ts.map +1 -0
  277. package/lib/node/heap-ceiling.js +72 -0
  278. package/lib/node/heap-ceiling.js.map +1 -0
  279. package/lib/node/index.d.ts +1 -0
  280. package/lib/node/index.d.ts.map +1 -1
  281. package/lib/node/index.js +1 -0
  282. package/lib/node/index.js.map +1 -1
  283. package/lib/node/latency-from-env.d.ts +1 -1
  284. package/lib/node/latency-from-env.js +1 -1
  285. package/lib/node/measure-memory.d.ts.map +1 -1
  286. package/lib/node/measure-memory.js +1 -0
  287. package/lib/node/measure-memory.js.map +1 -1
  288. package/lib/node/node-file-system-provider.d.ts +35 -1
  289. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  290. package/lib/node/node-file-system-provider.js +140 -45
  291. package/lib/node/node-file-system-provider.js.map +1 -1
  292. package/lib/node/profile-capture.d.ts +8 -2
  293. package/lib/node/profile-capture.d.ts.map +1 -1
  294. package/lib/node/profile-capture.js.map +1 -1
  295. package/lib/node/profiling-run.d.ts +2 -2
  296. package/lib/node/profiling-run.js +1 -1
  297. package/lib/node/profiling-run.js.map +1 -1
  298. package/lib/node/rename-over-open-readers.d.ts +52 -0
  299. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  300. package/lib/node/rename-over-open-readers.js +66 -0
  301. package/lib/node/rename-over-open-readers.js.map +1 -0
  302. package/lib/node/socket-launcher.d.ts +16 -1
  303. package/lib/node/socket-launcher.d.ts.map +1 -1
  304. package/lib/node/socket-launcher.js +4 -3
  305. package/lib/node/socket-launcher.js.map +1 -1
  306. package/lib/node/stdio-launcher.d.ts +1 -1
  307. package/lib/node/stdio-launcher.d.ts.map +1 -1
  308. package/lib/node/stdio-launcher.js +1 -1
  309. package/lib/node/stdio-launcher.js.map +1 -1
  310. package/lib/testing/fake-document.d.ts +16 -4
  311. package/lib/testing/fake-document.d.ts.map +1 -1
  312. package/lib/testing/fake-document.js +18 -3
  313. package/lib/testing/fake-document.js.map +1 -1
  314. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  315. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  316. package/lib/testing/make-noop-shared-services.js +28 -2
  317. package/lib/testing/make-noop-shared-services.js.map +1 -1
  318. package/lib/testing/make-test-services.d.ts +62 -14
  319. package/lib/testing/make-test-services.d.ts.map +1 -1
  320. package/lib/testing/make-test-services.js +27 -6
  321. package/lib/testing/make-test-services.js.map +1 -1
  322. package/lib/testing/node/index.d.ts +1 -0
  323. package/lib/testing/node/index.d.ts.map +1 -1
  324. package/lib/testing/node/index.js +1 -0
  325. package/lib/testing/node/index.js.map +1 -1
  326. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  327. package/lib/testing/node/lsp-harness.js +2 -0
  328. package/lib/testing/node/lsp-harness.js.map +1 -1
  329. package/lib/testing/node/lsp-server-connection.d.ts +11 -3
  330. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  331. package/lib/testing/node/lsp-server-connection.js +42 -9
  332. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  333. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  334. package/lib/testing/node/scratch-workspace.js +10 -5
  335. package/lib/testing/node/scratch-workspace.js.map +1 -1
  336. package/lib/testing/node/spawned-server.d.ts +14 -0
  337. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  338. package/lib/testing/node/spawned-server.js +33 -1
  339. package/lib/testing/node/spawned-server.js.map +1 -1
  340. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  341. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  342. package/lib/testing/node/unhandled-rejections.js +32 -0
  343. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  344. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  345. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  346. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  347. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  348. package/lib/testing/playwright/index.d.ts +1 -0
  349. package/lib/testing/playwright/index.d.ts.map +1 -1
  350. package/lib/testing/playwright/index.js +1 -0
  351. package/lib/testing/playwright/index.js.map +1 -1
  352. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  353. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  354. package/lib/testing/playwright/server-log-capture.js +21 -4
  355. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  356. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  357. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  358. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  359. package/lib/testing/run-update-pipeline.d.ts +4 -4
  360. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  361. package/lib/testing/stub-ast-document-manager.d.ts +10 -9
  362. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  363. package/lib/testing/stub-ast-document-manager.js +17 -9
  364. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  365. package/lib/testing/stub-document-builder.d.ts +26 -11
  366. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  367. package/lib/testing/stub-document-builder.js +57 -4
  368. package/lib/testing/stub-document-builder.js.map +1 -1
  369. package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
  370. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  371. package/lib/testing/stub-hydranium-text-documents.js +92 -15
  372. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  373. package/lib/testing/stub-langium-documents.d.ts +3 -2
  374. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  375. package/lib/testing/stub-langium-documents.js.map +1 -1
  376. package/lib/testing/stub-model-service.d.ts +7 -6
  377. package/lib/testing/stub-model-service.d.ts.map +1 -1
  378. package/lib/testing/stub-model-service.js +4 -4
  379. package/lib/testing/stub-model-service.js.map +1 -1
  380. package/lib/util/connection-liveness.d.ts +7 -6
  381. package/lib/util/connection-liveness.d.ts.map +1 -1
  382. package/lib/util/connection-liveness.js +35 -14
  383. package/lib/util/connection-liveness.js.map +1 -1
  384. package/package.json +17 -8
  385. package/src/documents/ast-document-manager.ts +345 -62
  386. package/src/documents/client-ids.ts +5 -22
  387. package/src/documents/client-session-errors.ts +24 -0
  388. package/src/documents/client-session-registry.ts +240 -0
  389. package/src/documents/file-system-task-queue.ts +68 -0
  390. package/src/documents/hydranium-text-documents.ts +856 -154
  391. package/src/documents/index.ts +3 -0
  392. package/src/documents/language-client-text-shadow.ts +59 -11
  393. package/src/documents/self-save-registry.ts +52 -13
  394. package/src/index.ts +6 -0
  395. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  396. package/src/langium/bootstrap.ts +16 -9
  397. package/src/langium/config/configuration-provider.ts +119 -0
  398. package/src/langium/config/index.ts +1 -0
  399. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  400. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  401. package/src/langium/document-builder/build-session.ts +87 -0
  402. package/src/langium/document-builder/document-builder.ts +698 -39
  403. package/src/langium/document-builder/index.ts +1 -0
  404. package/src/langium/integration-services.ts +67 -6
  405. package/src/langium/integrity/integrity-rule.ts +13 -5
  406. package/src/langium/integrity/integrity-service.ts +202 -17
  407. package/src/langium/keys/containment.ts +87 -0
  408. package/src/langium/keys/index.ts +1 -0
  409. package/src/langium/keys/name-based-key-provider.ts +4 -0
  410. package/src/langium/language-module.ts +42 -4
  411. package/src/langium/model-service/client-session.ts +606 -0
  412. package/src/langium/model-service/index.ts +1 -0
  413. package/src/langium/model-service/model-service.ts +393 -349
  414. package/src/langium/module.ts +137 -30
  415. package/src/langium/naming/name-provider.ts +27 -5
  416. package/src/langium/naming/name-separator-validation.ts +34 -5
  417. package/src/langium/residency/cst-residency-service.ts +36 -14
  418. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  419. package/src/langium/serialization/abstract-serializer.ts +13 -32
  420. package/src/langium/service-registry.ts +19 -2
  421. package/src/langium/shared-services.ts +19 -2
  422. package/src/langium/transfer/transfer-encoder.ts +85 -24
  423. package/src/langium/trivia/comment-preserver.ts +1100 -0
  424. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  425. package/src/langium/trivia/index.ts +14 -0
  426. package/src/langium/trivia/trivia-contribution.ts +39 -0
  427. package/src/langium/trivia/trivia-preserver.ts +54 -0
  428. package/src/langium/trivia/trivia-service.ts +99 -0
  429. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  430. package/src/langium/validation/document-validator.ts +537 -11
  431. package/src/langium/workspace/document-uri-policy.ts +3 -4
  432. package/src/langium/workspace/file-system-provider.ts +48 -8
  433. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  434. package/src/langium/workspace/hydranium-workspace-lock.ts +21 -0
  435. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  436. package/src/langium/workspace/in-memory-file-system-provider.ts +62 -10
  437. package/src/langium/workspace/initialize-workspace.ts +43 -5
  438. package/src/langium/workspace/langium-documents.ts +106 -33
  439. package/src/langium/workspace/virtual-document.ts +21 -3
  440. package/src/langium/workspace/write-lock-scope.ts +4 -3
  441. package/src/locale/index.ts +10 -0
  442. package/src/locale/server-locale.ts +86 -0
  443. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  444. package/src/lsp/connection-features.ts +67 -0
  445. package/src/lsp/diagnostics-connection.ts +56 -0
  446. package/src/lsp/hydranium-document-update-handler.ts +182 -106
  447. package/src/lsp/index.ts +3 -1
  448. package/src/lsp/lsp-latency.ts +62 -0
  449. package/src/lsp/semantic-token-provider.ts +74 -7
  450. package/src/lsp/shared-module.ts +4 -8
  451. package/src/lsp/start-language-server.ts +13 -7
  452. package/src/messages/carriers.ts +78 -0
  453. package/src/messages/index.ts +36 -0
  454. package/src/messages/renderer.ts +212 -0
  455. package/src/node/heap-ceiling.ts +108 -0
  456. package/src/node/index.ts +1 -0
  457. package/src/node/latency-from-env.ts +1 -1
  458. package/src/node/measure-memory.ts +1 -0
  459. package/src/node/node-file-system-provider.ts +154 -47
  460. package/src/node/profile-capture.ts +8 -2
  461. package/src/node/profiling-run.ts +3 -3
  462. package/src/node/rename-over-open-readers.ts +78 -0
  463. package/src/node/socket-launcher.ts +19 -4
  464. package/src/node/stdio-launcher.ts +1 -2
  465. package/src/testing/fake-document.ts +24 -5
  466. package/src/testing/make-noop-shared-services.ts +50 -2
  467. package/src/testing/make-test-services.ts +103 -27
  468. package/src/testing/node/index.ts +1 -0
  469. package/src/testing/node/lsp-harness.ts +2 -0
  470. package/src/testing/node/lsp-server-connection.ts +61 -11
  471. package/src/testing/node/scratch-workspace.ts +10 -5
  472. package/src/testing/node/spawned-server.ts +35 -1
  473. package/src/testing/node/unhandled-rejections.ts +31 -0
  474. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  475. package/src/testing/playwright/index.ts +1 -0
  476. package/src/testing/playwright/server-log-capture.ts +24 -5
  477. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  478. package/src/testing/run-update-pipeline.ts +4 -4
  479. package/src/testing/stub-ast-document-manager.ts +28 -21
  480. package/src/testing/stub-document-builder.ts +83 -17
  481. package/src/testing/stub-hydranium-text-documents.ts +127 -22
  482. package/src/testing/stub-langium-documents.ts +3 -2
  483. package/src/testing/stub-model-service.ts +9 -8
  484. package/src/util/connection-liveness.ts +37 -14
  485. package/lib/lsp/instrument-connection.d.ts +0 -36
  486. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  487. package/lib/lsp/instrument-connection.js +0 -62
  488. package/lib/lsp/instrument-connection.js.map +0 -1
  489. package/src/lsp/instrument-connection.ts +0 -66
@@ -8,16 +8,18 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import { type Tracer } from '@hydranium/protocol';
11
- import { URI, UriUtils } from '@hydranium/langium';
11
+ import { type FileSystemNode, URI, UriUtils } from '@hydranium/langium';
12
12
  import { NodeFileSystemProvider } from '@hydranium/langium/node';
13
13
  import { randomUUID } from 'node:crypto';
14
14
  import { realpathSync } from 'node:fs';
15
15
  import * as fsp from 'node:fs/promises';
16
16
  import { type WritableFileSystemProvider } from '../documents/ast-document-manager.js';
17
17
  import { type SelfSaveRegistry } from '../documents/self-save-registry.js';
18
+ import { renameOverOpenReaders } from './rename-over-open-readers.js';
18
19
  import { type LogNameOptions } from '../langium/diagnostics/logger.js';
19
20
  import { serverSharedFactory, type ServerSharedServicesMinimal } from '../langium/shared-services.js';
20
- import { serveVirtualDocument } from '../langium/workspace/virtual-document.js';
21
+ import { NO_SUCH_FILE, NO_SUCH_PATH, UNSUPPORTED_WRITE } from '../langium/workspace/in-memory-file-system-provider.js';
22
+ import { serveVirtualDocument, serveVirtualNode } from '../langium/workspace/virtual-document.js';
21
23
 
22
24
  /**
23
25
  * Node-based default {@link WritableFileSystemProvider}. Extends Langium's
@@ -27,6 +29,13 @@ import { serveVirtualDocument } from '../langium/workspace/virtual-document.js';
27
29
  * {@link SelfSaveRegistry} bound at `services.workspace.SelfSaveRegistry` — so
28
30
  * consumers can suppress the `didChangeWatchedFiles` echo for their own writes.
29
31
  *
32
+ * Its read surface answers for registered virtual documents and refuses to
33
+ * derive a disk path from a URI that names no disk location — see
34
+ * {@link servesFromDisk}, which is the one place that judgement is made. Every
35
+ * method that derives an OS path is held to it, writes most of all: a read that
36
+ * skipped it would only mis-report, while a write would CREATE a file at a path
37
+ * the URI never named.
38
+ *
30
39
  * Server-only (`@hydranium/core/node`): pulls `node:fs`. The portable `.`
31
40
  * entry binds `DefaultEmptyFileSystemProvider` by default, so a Node host
32
41
  * must rebind this slot — through `context.fileSystemProvider` or directly — to
@@ -45,15 +54,114 @@ export class DefaultFileSystemProvider extends NodeFileSystemProvider implements
45
54
  this.tracer = services.Tracer.for(options.logName ?? this.constructor.name).trace('instantiated');
46
55
  }
47
56
 
48
- // Serve virtual documents from the registered document before touching disk
49
- // (a virtual URI has no `fsPath` backing); delegate everything else to Node.
57
+ /**
58
+ * Whether `uri` names something this provider can reach on disk.
59
+ *
60
+ * Only a `file:` URI does. `URI.fsPath` yields a path for ANY scheme — for a
61
+ * hierarchical one it is the path component, which for a schemed URI with no
62
+ * authority comes out RELATIVE — so delegating an unrecognised scheme to Node
63
+ * does not fail, it probes `<cwd>/<path>` and answers about whatever
64
+ * unrelated file happens to sit there. A server whose working directory is
65
+ * the workspace root makes that collision ordinary rather than exotic.
66
+ *
67
+ * Note the deliberate disagreement with {@link realpath}, which treats a
68
+ * non-`file:` URI as PRESENT. The two degrade in opposite directions because
69
+ * they answer different questions: identity has to hand back a usable URI, so
70
+ * its safe answer is the one it was given, while occupancy and content must
71
+ * not be invented from an unrelated path, so their safe answer is "nothing
72
+ * here". A subclass adding a second on-disk scheme widens this and nothing
73
+ * else.
74
+ */
75
+ protected servesFromDisk(uri: URI): boolean {
76
+ return uri.scheme === 'file';
77
+ }
78
+
79
+ // Each read resolves in the same order: a registered virtual document first
80
+ // (it has no `fsPath` backing), then disk for a path this provider can
81
+ // reach, then the filesystem's own "nothing here" answer — never a probe
82
+ // against a path derived from a URI that names no disk location.
50
83
  override readFile(uri: URI): Promise<string> {
51
84
  const served = serveVirtualDocument(this.services, uri);
52
- return served !== undefined ? Promise.resolve(served) : super.readFile(uri);
85
+ if (served !== undefined) {
86
+ return Promise.resolve(served);
87
+ }
88
+ return this.servesFromDisk(uri) ? super.readFile(uri) : Promise.reject(noSuchFile(uri));
53
89
  }
54
90
 
55
91
  override readFileSync(uri: URI): string {
56
- return serveVirtualDocument(this.services, uri) ?? super.readFileSync(uri);
92
+ const served = serveVirtualDocument(this.services, uri);
93
+ if (served !== undefined) {
94
+ return served;
95
+ }
96
+ if (!this.servesFromDisk(uri)) {
97
+ throw noSuchFile(uri);
98
+ }
99
+ return super.readFileSync(uri);
100
+ }
101
+
102
+ override readBinary(uri: URI): Promise<Uint8Array> {
103
+ const served = serveVirtualDocument(this.services, uri);
104
+ if (served !== undefined) {
105
+ return Promise.resolve(encode(served));
106
+ }
107
+ return this.servesFromDisk(uri) ? super.readBinary(uri) : Promise.reject(noSuchFile(uri));
108
+ }
109
+
110
+ override readBinarySync(uri: URI): Uint8Array {
111
+ const served = serveVirtualDocument(this.services, uri);
112
+ if (served !== undefined) {
113
+ return encode(served);
114
+ }
115
+ if (!this.servesFromDisk(uri)) {
116
+ throw noSuchFile(uri);
117
+ }
118
+ return super.readBinarySync(uri);
119
+ }
120
+
121
+ override stat(uri: URI): Promise<FileSystemNode> {
122
+ const served = serveVirtualNode(this.services, uri);
123
+ if (served !== undefined) {
124
+ return Promise.resolve(served);
125
+ }
126
+ return this.servesFromDisk(uri) ? super.stat(uri) : Promise.reject(noSuchPath(uri));
127
+ }
128
+
129
+ override statSync(uri: URI): FileSystemNode {
130
+ const served = serveVirtualNode(this.services, uri);
131
+ if (served !== undefined) {
132
+ return served;
133
+ }
134
+ if (!this.servesFromDisk(uri)) {
135
+ throw noSuchPath(uri);
136
+ }
137
+ return super.statSync(uri);
138
+ }
139
+
140
+ // Delegating to `existsSync` would be shorter and would put a blocking stat
141
+ // on the async path.
142
+ override async exists(uri: URI): Promise<boolean> {
143
+ if (serveVirtualDocument(this.services, uri) !== undefined) {
144
+ return true;
145
+ }
146
+ return this.servesFromDisk(uri) ? super.exists(uri) : false;
147
+ }
148
+
149
+ override existsSync(uri: URI): boolean {
150
+ if (serveVirtualDocument(this.services, uri) !== undefined) {
151
+ return true;
152
+ }
153
+ return this.servesFromDisk(uri) ? super.existsSync(uri) : false;
154
+ }
155
+
156
+ // A URI this provider cannot reach on disk has no children rather than an
157
+ // error: a workspace walk enumerates folders it was handed, and failing one
158
+ // aborts the walk where an empty listing simply contributes nothing.
159
+ override readDirectory(uri: URI): Promise<FileSystemNode[]> {
160
+ return this.servesFromDisk(uri) ? super.readDirectory(uri) : Promise.resolve([]);
161
+ }
162
+
163
+ override readDirectorySync(uri: URI): FileSystemNode[] {
164
+ return this.servesFromDisk(uri) ? super.readDirectorySync(uri) : [];
57
165
  }
58
166
 
59
167
  /**
@@ -103,6 +211,17 @@ export class DefaultFileSystemProvider extends NodeFileSystemProvider implements
103
211
  * only possible, which is what settles the trade.
104
212
  */
105
213
  async writeFile(uri: URI, content: string): Promise<void> {
214
+ // BEFORE the `mkdir`, and before any other OS path is derived. The read
215
+ // surface degrades to "nothing here" for a URI naming no disk location;
216
+ // a write has no such harmless degradation — `fsPath` would hand it a
217
+ // real path regardless of scheme, so an unguarded write does not fail,
218
+ // it CREATES a file (and the directories above it) somewhere unrelated.
219
+ // Rejecting is the only safe answer: a caller holding a `memory:` or
220
+ // `untitled:` document has nowhere for it to go, and inventing a
221
+ // destination from the path component is never what it meant.
222
+ if (!this.servesFromDisk(uri)) {
223
+ throw unsupportedWrite(uri);
224
+ }
106
225
  await fsp.mkdir(UriUtils.dirname(uri).fsPath, { recursive: true });
107
226
  const destination = this.realpath(uri)?.fsPath ?? uri.fsPath;
108
227
  const replaced = await replacedFile(destination);
@@ -135,6 +254,15 @@ export class DefaultFileSystemProvider extends NodeFileSystemProvider implements
135
254
  }
136
255
 
137
256
  async mtimeMs(uri: URI): Promise<number | undefined> {
257
+ // Same rule as the read surface, and "unknown" is already the answer the
258
+ // caller handles — so a URI naming no disk location reports no mtime
259
+ // rather than the modification time of whatever occupies its `fsPath`.
260
+ // A stamp borrowed from an unrelated file is worse than none: the
261
+ // self-save registry keys on it to suppress a watcher echo, so a
262
+ // coincidental match would drop a real external change.
263
+ if (!this.servesFromDisk(uri)) {
264
+ return undefined;
265
+ }
138
266
  try {
139
267
  const stat = await fsp.stat(uri.fsPath);
140
268
  return stat.mtimeMs;
@@ -168,6 +296,26 @@ export class DefaultFileSystemProvider extends NodeFileSystemProvider implements
168
296
  }
169
297
  }
170
298
 
299
+ /** The "no content here" rejection, for a URI naming no readable file. */
300
+ function noSuchFile(uri: URI): Error {
301
+ return new Error(NO_SUCH_FILE.format({ uri: uri.toString() }));
302
+ }
303
+
304
+ /** The "nothing occupies this" rejection, for a URI naming no filesystem node. */
305
+ function noSuchPath(uri: URI): Error {
306
+ return new Error(NO_SUCH_PATH.format({ uri: uri.toString() }));
307
+ }
308
+
309
+ /** The "nowhere to put this" rejection, for a write to a URI with no disk backing. */
310
+ function unsupportedWrite(uri: URI): Error {
311
+ return new Error(UNSUPPORTED_WRITE.format({ uri: uri.toString() }));
312
+ }
313
+
314
+ /** A virtual document's text as the bytes a binary read returns. */
315
+ function encode(text: string): Uint8Array {
316
+ return new TextEncoder().encode(text);
317
+ }
318
+
171
319
  /**
172
320
  * What a write has to know about the regular file at `path` before replacing
173
321
  * it: the permission bits to carry over, and how many names share its inode.
@@ -195,47 +343,6 @@ async function replacedFile(path: string): Promise<ReplacedFile | undefined> {
195
343
  }
196
344
  }
197
345
 
198
- /**
199
- * Errno values a rename reports on Windows when another handle holds the
200
- * destination open. POSIX renames are unaffected by open handles, so this
201
- * cannot fire there and the retry below costs nothing.
202
- */
203
- const RENAME_CONTENTION_CODES = new Set(['EPERM', 'EACCES', 'EBUSY']);
204
-
205
- /**
206
- * `rename` the staged content over `destination`, retrying while Windows
207
- * reports the destination as held open.
208
- *
209
- * The staging file exists so a reader never sees a half-written file, and on
210
- * Windows the very presence of that reader is what makes the replacing rename
211
- * fail — so the indivisible write breaks under exactly the contention it was
212
- * built for, and it breaks intermittently, which is worse than never working.
213
- * A reader's handle is released in milliseconds, so waiting turns a spurious
214
- * failure into a slightly later success.
215
- *
216
- * BOUNDED, not open-ended: a genuine permission fault raises the same errno and
217
- * is distinguishable from contention only by never clearing, so an unbounded
218
- * wait would convert a hard error into a hang. Exhausting the budget rethrows
219
- * the last failure, leaving the caller's cleanup and error contract unchanged.
220
- */
221
- async function renameOverOpenReaders(staging: string, destination: string): Promise<void> {
222
- const deadline = Date.now() + 2_000;
223
- for (let attempt = 0; ; attempt++) {
224
- try {
225
- await fsp.rename(staging, destination);
226
- return;
227
- } catch (err: unknown) {
228
- const code = err instanceof Error && 'code' in err ? String((err as NodeJS.ErrnoException).code) : undefined;
229
- if (code === undefined || !RENAME_CONTENTION_CODES.has(code) || Date.now() >= deadline) {
230
- throw err;
231
- }
232
- // Backs off to keep a long contention window from spinning, capped so
233
- // a late attempt still lands promptly once the handle is released.
234
- await new Promise(resolve => setTimeout(resolve, Math.min(2 ** attempt, 50)));
235
- }
236
- }
237
- }
238
-
239
346
  /**
240
347
  * Module fragment that binds the writable Node {@link DefaultFileSystemProvider},
241
348
  * spread into the services context on a Node host.
@@ -147,8 +147,14 @@ export function formatProfileReport(report: ProfileReport, label = 'Profile repo
147
147
  return lines.join('\n');
148
148
  }
149
149
 
150
- /** Minimal structural view of a `node:inspector` `Session` — its typed per-method overloads can't be called generically. */
151
- interface InspectorSessionLike {
150
+ /**
151
+ * Minimal structural view of a `node:inspector` `Session` — its typed per-method
152
+ * overloads can't be called generically.
153
+ *
154
+ * Held by the `protected` {@link ProfileCapture.session}, so a subclass reaching
155
+ * the inspector names this rather than `Session`.
156
+ */
157
+ export interface InspectorSessionLike {
152
158
  connect(): void;
153
159
  disconnect(): void;
154
160
  post(method: string, params: Record<string, unknown>, callback: (error: Error | null, result?: unknown) => void): void;
@@ -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
+ }
@@ -7,9 +7,8 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { Deferred } from '@hydranium/langium';
11
10
  import * as net from 'node:net';
12
- import type { Disposable, Logger } from '@hydranium/protocol';
11
+ import { Deferred, type Disposable, type Logger } from '@hydranium/protocol';
13
12
  import { createMessageConnection, SocketMessageReader, SocketMessageWriter } from 'vscode-jsonrpc/node';
14
13
  import type { MessageConnection } from 'vscode-jsonrpc';
15
14
  import type { IntegratedServer } from '../launcher/integrated-server.js';
@@ -24,6 +23,21 @@ export interface SocketServerOptions {
24
23
  * resolves (only then is the address info available).
25
24
  */
26
25
  readonly port?: number;
26
+ /**
27
+ * Interface to bind. Default `'127.0.0.1'` (LOCAL ONLY) — a head whose
28
+ * port is OS-assigned and announced over the LSP connection is
29
+ * reachable only by a client on the same machine anyway, so a wider
30
+ * binding exposes a port nothing off-box can address. Matches
31
+ * `startGlspServer`'s default, so the two socket heads agree.
32
+ *
33
+ * Pass `'::'` for every interface: that is the dual-stack any-address,
34
+ * so both IPv4 and IPv6 clients connect. `'0.0.0.0'` is NOT the same —
35
+ * it takes IPv4 only and refuses `::1`.
36
+ *
37
+ * An address no local interface holds fails the bind, so `started`
38
+ * rejects with `EADDRNOTAVAIL` rather than falling back to a wider one.
39
+ */
40
+ readonly host?: string;
27
41
  /**
28
42
  * Optional logger. If omitted, the launcher runs silently — the
29
43
  * adopter is expected to surface lifecycle events through their own
@@ -88,6 +102,7 @@ export interface StartedSocketServer extends IntegratedServer {
88
102
  */
89
103
  export function startSocketServer(options: SocketServerOptions, onClientConnection: OnClientConnection): StartedSocketServer {
90
104
  const port = options.port ?? 0;
105
+ const host = options.host ?? '127.0.0.1';
91
106
  const logger = options.logger;
92
107
  const tag = options.logTag ?? 'SocketServer';
93
108
  const started = new Deferred<void>();
@@ -128,7 +143,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
128
143
  connection.listen();
129
144
  });
130
145
 
131
- netServer.listen(port);
146
+ netServer.listen(port, host);
132
147
  netServer.on('listening', () => {
133
148
  const addressInfo = netServer.address();
134
149
  if (!addressInfo) {
@@ -144,7 +159,7 @@ export function startSocketServer(options: SocketServerOptions, onClientConnecti
144
159
  return;
145
160
  }
146
161
  handle.port = addressInfo.port;
147
- logger?.info(`[${tag}] Ready to accept new client requests on port: ${addressInfo.port}`);
162
+ logger?.info(`[${tag}] Ready to accept new client requests on ${addressInfo.address} port: ${addressInfo.port}`);
148
163
  started.resolve();
149
164
  });
150
165
  netServer.on('error', err => {
@@ -7,8 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { Deferred } from '@hydranium/langium';
11
- import type { Disposable, Logger } from '@hydranium/protocol';
10
+ import { Deferred, type Disposable, type Logger } from '@hydranium/protocol';
12
11
  import type { MessageConnection } from 'vscode-jsonrpc';
13
12
  import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from 'vscode-jsonrpc/node';
14
13
  import type { IntegratedServer } from '../launcher/integrated-server.js';
@@ -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
  }