@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
@@ -9,11 +9,11 @@
9
9
 
10
10
  import { type Tracer } from '@hydranium/protocol';
11
11
  import { EmptyFileSystemProvider } from '@hydranium/langium';
12
- import type { URI } from '@hydranium/langium';
12
+ import type { FileSystemNode, URI } from '@hydranium/langium';
13
13
  import { type WritableFileSystemProvider } from '../../documents/ast-document-manager.js';
14
14
  import { type LogNameOptions } from '../diagnostics/logger.js';
15
15
  import { type ServerSharedServicesMinimal } from '../shared-services.js';
16
- import { serveVirtualDocument } from './virtual-document.js';
16
+ import { serveVirtualDocument, serveVirtualNode } from './virtual-document.js';
17
17
 
18
18
  /**
19
19
  * Empty-filesystem default {@link WritableFileSystemProvider}. For
@@ -38,15 +38,55 @@ export class DefaultEmptyFileSystemProvider extends EmptyFileSystemProvider impl
38
38
  }
39
39
 
40
40
  // Serve virtual documents from the registered document (there is no disk to
41
- // read); delegate everything else to the empty base (which throws). The
42
- // base declares these param-less, so the param is optional here.
43
- override readFile(uri?: URI): Promise<string> {
44
- const served = uri !== undefined ? serveVirtualDocument(this.services, uri) : undefined;
45
- return served !== undefined ? Promise.resolve(served) : super.readFile();
41
+ // read); delegate everything else to the empty base (which throws, or
42
+ // answers "absent"). The base declares these param-less, so the param is
43
+ // optional here.
44
+ //
45
+ // The async reads are `async` so the base's refusal becomes a rejection: it
46
+ // throws SYNCHRONOUSLY from a method declared to return a promise, and a
47
+ // synchronous throw escapes the promise chain — `.catch()` never attaches
48
+ // and `Promise.all` dies while its argument array is still being built, so
49
+ // a caller reading several URIs cannot handle the miss it asked about.
50
+ override async readFile(uri?: URI): Promise<string> {
51
+ const served = this.served(uri);
52
+ return served !== undefined ? served : super.readFile();
46
53
  }
47
54
 
48
55
  override readFileSync(uri?: URI): string {
49
- const served = uri !== undefined ? serveVirtualDocument(this.services, uri) : undefined;
56
+ const served = this.served(uri);
50
57
  return served !== undefined ? served : super.readFileSync();
51
58
  }
59
+
60
+ override async readBinary(uri?: URI): Promise<Uint8Array> {
61
+ const served = this.served(uri);
62
+ return served !== undefined ? new TextEncoder().encode(served) : super.readBinary();
63
+ }
64
+
65
+ override readBinarySync(uri?: URI): Uint8Array {
66
+ const served = this.served(uri);
67
+ return served !== undefined ? new TextEncoder().encode(served) : super.readBinarySync();
68
+ }
69
+
70
+ // The base declares these two WITH a URI parameter, so they stay required.
71
+ override async stat(uri: URI): Promise<FileSystemNode> {
72
+ const served = serveVirtualNode(this.services, uri);
73
+ return served !== undefined ? served : super.stat(uri);
74
+ }
75
+
76
+ override statSync(uri: URI): FileSystemNode {
77
+ return serveVirtualNode(this.services, uri) ?? super.statSync(uri);
78
+ }
79
+
80
+ override async exists(uri?: URI): Promise<boolean> {
81
+ return this.existsSync(uri);
82
+ }
83
+
84
+ override existsSync(uri?: URI): boolean {
85
+ return this.served(uri) !== undefined;
86
+ }
87
+
88
+ /** Text of the virtual document registered at `uri`, if there is one. */
89
+ protected served(uri: URI | undefined): string | undefined {
90
+ return uri !== undefined ? serveVirtualDocument(this.services, uri) : undefined;
91
+ }
52
92
  }
@@ -15,6 +15,7 @@ import {
15
15
  type ParseResult,
16
16
  type URI
17
17
  } from '@hydranium/langium';
18
+ import { type ExtendedServiceRegistry } from '../service-registry.js';
18
19
  import { type Serializer } from '../serialization/serializer.js';
19
20
 
20
21
  /** Structural view of the per-language serializer slot on the resolved services. */
@@ -49,6 +50,39 @@ interface WithSerializer {
49
50
  * node identity is preserved.
50
51
  */
51
52
  export class HydraniumLangiumDocumentFactory extends DefaultLangiumDocumentFactory {
53
+ /**
54
+ * Narrows the inherited Langium `ServiceRegistry` field to the framework's
55
+ * {@link ExtendedServiceRegistry}, so the by-id lookup below needs no
56
+ * per-callsite cast. The framework's shared module binds that class.
57
+ */
58
+ declare protected readonly serviceRegistry: ExtendedServiceRegistry;
59
+
60
+ /**
61
+ * Parse `text` for `uri` under the grammar `languageId` names, instead of
62
+ * the one `uri` routes to.
63
+ *
64
+ * For a URI that routes to no grammar at all: a directory, or any other URI
65
+ * that names no file and so carries no extension. Langium's ladder ends at
66
+ * the extension, so it reports `no services for the extension ''` from
67
+ * inside the parse — naming neither the URI nor the caller that could have
68
+ * said which grammar it meant.
69
+ *
70
+ * The caller supplies the language because nothing else can. A URI with no
71
+ * extension carries no routing signal, and choosing on its behalf would be a
72
+ * guess the moment a second grammar is registered — which is why this takes
73
+ * the id rather than falling back to a sole registered language.
74
+ *
75
+ * @throws when `languageId` names no registered grammar.
76
+ */
77
+ fromStringInLanguage<T extends AstNode = AstNode>(text: string, uri: URI, languageId: string): LangiumDocument<T> {
78
+ const services = this.serviceRegistry.getServicesById(languageId);
79
+ if (!services) {
80
+ throw new Error(`No grammar is registered for the language id '${languageId}' (parsing ${uri.toString()}).`);
81
+ }
82
+ const parseResult: ParseResult<T> = services.parser.LangiumParser.parse<T>(text);
83
+ return this.createLangiumDocument<T>(parseResult, uri, undefined, text);
84
+ }
85
+
52
86
  override fromModel<T extends AstNode = AstNode>(model: T, uri: URI): LangiumDocument<T> {
53
87
  // Make the code-built AST as structurally complete as a parsed one — the
54
88
  // parser links containers, `fromModel` does not. In place (identity kept).
@@ -29,7 +29,28 @@ import { runInWriteLockScope } from './write-lock-scope.js';
29
29
  * would turn ordinary read-then-write sequences into false positives.
30
30
  */
31
31
  export class HydraniumWorkspaceLock extends DefaultWorkspaceLock {
32
+ /** Backs {@link writeCancellations}. */
33
+ protected cancelledWriteCount = 0;
34
+
35
+ /**
36
+ * How many calls have cancelled the lock's latest write: every `write`,
37
+ * which cancels the write before it, and every `cancelWrite`. A caller that
38
+ * reads this right after queuing a write, and reads the same number later,
39
+ * holds a write nothing has cancelled, whether it is still queued or
40
+ * already running. Its token answers that only once the write runs, since
41
+ * a queued write has not been handed it yet.
42
+ */
43
+ get writeCancellations(): number {
44
+ return this.cancelledWriteCount;
45
+ }
46
+
32
47
  override write(action: (token: CancellationToken) => MaybePromise<void>): Promise<void> {
33
48
  return super.write(token => runInWriteLockScope(() => action(token)));
34
49
  }
50
+
51
+ /** Counted here rather than in {@link write}, because Langium's `write` cancels through this method too. */
52
+ override cancelWrite(): void {
53
+ this.cancelledWriteCount++;
54
+ super.cancelWrite();
55
+ }
35
56
  }
@@ -9,9 +9,10 @@
9
9
 
10
10
  import { Deferred, type Logger, type Tracer } from '@hydranium/protocol';
11
11
  import { DefaultWorkspaceManager, DocumentState, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
12
- import { type CancellationToken } from 'vscode-languageserver';
12
+ import { type CancellationToken, type InitializeParams } from 'vscode-languageserver';
13
13
  import type { WorkspaceFolder } from 'vscode-languageserver-types';
14
14
  import { type LogNameOptions, resolveLogFilePlaceholder, toLogFileWorkspaceToken } from '../diagnostics/logger.js';
15
+ import type { ServerLocale } from '../../locale/server-locale.js';
15
16
  import type { ProjectChangeEvent } from '../project/project-change-event.js';
16
17
  import type { ProjectManager } from '../project/project-manager.js';
17
18
  import type { ServerSharedServicesMinimal } from '../shared-services.js';
@@ -156,6 +157,8 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
156
157
  readonly workspaceInitialized: Promise<unknown> = this.workspaceInitializedDeferred.promise;
157
158
 
158
159
  protected readonly uriPolicy: DocumentUriPolicy;
160
+ /** Handed the client's locale at init; read by whoever renders in it. */
161
+ protected readonly serverLocale: ServerLocale;
159
162
  protected readonly writableFileSystemProvider: WritableFileSystemProvider;
160
163
  protected readonly additionalDocuments: Record<string, AdditionalDocumentContribution>;
161
164
  /** Registry consulted by {@link warnIfUnroutable} to check a seeded document routes. */
@@ -166,6 +169,7 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
166
169
  this.projectManager = services.workspace.ProjectManager;
167
170
  this.projectManager.onProjectsChanged(event => this.onProjectsChanged(event));
168
171
  this.uriPolicy = services.workspace.DocumentUriPolicy;
172
+ this.serverLocale = services.ServerLocale;
169
173
  this.writableFileSystemProvider = services.workspace.FileSystemProvider;
170
174
  this.additionalDocuments = services.additionalDocuments;
171
175
  this.languageRegistry = services.ServiceRegistry;
@@ -360,6 +364,31 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
360
364
  }
361
365
  }
362
366
 
367
+ /**
368
+ * Forward the locale the client declared, then Langium's own read of the
369
+ * params. `DefaultWorkspaceManager.initialize` reads `workspaceFolders`
370
+ * alone, so `params.locale` would otherwise be discarded — and this is the
371
+ * only place it arrives, which the headless init seams route through too.
372
+ *
373
+ * **The ABSENT case is reported from here rather than from `ServerLocale`,
374
+ * because this is the only end that can see it.** `accept` is not called
375
+ * when no locale was declared — nor should it be, since `''` is a claim
376
+ * about a language rather than the absence of one — so a log written there
377
+ * covers one of the two outcomes and leaves the other looking like a server
378
+ * that never reached init. Both lines are at `info` for the reason `accept`
379
+ * gives: the framework ships no catalogue, so an undeclared locale and an
380
+ * untranslated code produce the same English and the log is what separates
381
+ * them.
382
+ */
383
+ override initialize(params: InitializeParams): void {
384
+ if (params.locale) {
385
+ this.serverLocale.accept(params.locale);
386
+ } else {
387
+ this.tracer.info("no locale declared at init — rendering messages in the framework's English");
388
+ }
389
+ super.initialize(params);
390
+ }
391
+
363
392
  /**
364
393
  * Override of Langium's workspace setup to resolve / reject
365
394
  * {@link workspaceInitializedDeferred} once setup completes. Adopters with
@@ -8,11 +8,49 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import { type FileSystemNode, URI } from '@hydranium/langium';
11
- import { type Tracer } from '@hydranium/protocol';
11
+ import { defineMessage, type Tracer } from '@hydranium/protocol';
12
12
  import { type WritableFileSystemProvider } from '../../documents/ast-document-manager.js';
13
13
  import { type LogNameOptions } from '../diagnostics/logger.js';
14
14
  import { serverSharedFactory, type ServerSharedServicesMinimal } from '../shared-services.js';
15
- import { serveVirtualDocument } from './virtual-document.js';
15
+ import { serveVirtualDocument, serveVirtualNode } from './virtual-document.js';
16
+
17
+ /**
18
+ * A provider's spelling of "the document is not there", raised wherever a read
19
+ * finds no content — this filesystem, or a URI naming no location a disk-backed
20
+ * provider can reach. Distinct from `NO_LOADABLE_CONTENT`, which the URI policy
21
+ * raises when it can settle the question AHEAD of the read; a provider with no
22
+ * `realpath` cannot, so the miss surfaces here instead.
23
+ */
24
+ export const NO_SUCH_FILE = defineMessage('hydranium/core/no-such-file', 'No such file: {uri}');
25
+
26
+ /** The same for a node of any kind, so it also covers a directory. */
27
+ export const NO_SUCH_PATH = defineMessage('hydranium/core/no-such-path', 'No such file or directory: {uri}');
28
+
29
+ /**
30
+ * A write addressed a URI the provider cannot reach on disk.
31
+ *
32
+ * Deliberately not a not-found code: {@link NO_SUCH_FILE} reports something
33
+ * missing, which a caller may reasonably answer by creating it — exactly the
34
+ * wrong reading here. The destination is not missing; the URI names no disk
35
+ * location at all, and no retry makes it one.
36
+ *
37
+ * Declared here rather than beside the Node provider that raises it, so the
38
+ * message catalog stays reachable from the portable `.` entry — the Node
39
+ * module pulls `node:fs` and is server-only.
40
+ */
41
+ export const UNSUPPORTED_WRITE = defineMessage(
42
+ 'hydranium/core/unsupported-write',
43
+ 'Cannot write {uri}: the scheme names no location on disk'
44
+ );
45
+
46
+ /**
47
+ * A missing-file error shaped as a Node file system raises it, with code
48
+ * `ENOENT` and the path: callers tell a missing file from any other failure by
49
+ * those two fields, whichever provider is bound.
50
+ */
51
+ function notFound(message: string, uri: URI): Error {
52
+ return Object.assign(new Error(message), { code: 'ENOENT', path: uri.fsPath });
53
+ }
16
54
 
17
55
  /**
18
56
  * File content keyed by path, for seeding an {@link InMemoryFileSystemProvider}.
@@ -116,8 +154,14 @@ export class InMemoryFileSystemProvider implements WritableFileSystemProvider {
116
154
  this.setFile(uri, content);
117
155
  }
118
156
 
119
- readFile(uri: URI): Promise<string> {
120
- return Promise.resolve(this.readFileSync(uri));
157
+ // `async` on each read whose sync twin can throw, rather than
158
+ // `Promise.resolve(this.<x>Sync(uri))`. That form evaluates the sync call
159
+ // FIRST, so a miss throws before `Promise.resolve` is ever reached — and a
160
+ // synchronous throw escapes the promise chain: `.catch()` never attaches and
161
+ // `Promise.all` dies while its argument array is still being built, so a
162
+ // caller reading several URIs cannot handle the miss it asked about.
163
+ async readFile(uri: URI): Promise<string> {
164
+ return this.readFileSync(uri);
121
165
  }
122
166
 
123
167
  readFileSync(uri: URI): string {
@@ -127,24 +171,29 @@ export class InMemoryFileSystemProvider implements WritableFileSystemProvider {
127
171
  }
128
172
  const content = this.files.get(normalize(uri));
129
173
  if (content === undefined) {
130
- throw new Error(`No such file: ${uri.toString()}`);
174
+ throw notFound(NO_SUCH_FILE.format({ uri: uri.toString() }), uri);
131
175
  }
132
176
  return content;
133
177
  }
134
178
 
135
- readBinary(uri: URI): Promise<Uint8Array> {
136
- return Promise.resolve(this.readBinarySync(uri));
179
+ async readBinary(uri: URI): Promise<Uint8Array> {
180
+ return this.readBinarySync(uri);
137
181
  }
138
182
 
139
183
  readBinarySync(uri: URI): Uint8Array {
140
184
  return new TextEncoder().encode(this.readFileSync(uri));
141
185
  }
142
186
 
143
- stat(uri: URI): Promise<FileSystemNode> {
144
- return Promise.resolve(this.statSync(uri));
187
+ async stat(uri: URI): Promise<FileSystemNode> {
188
+ return this.statSync(uri);
145
189
  }
146
190
 
147
191
  statSync(uri: URI): FileSystemNode {
192
+ // Virtual first, on the same terms as `readFileSync`.
193
+ const served = serveVirtualNode(this.services, uri);
194
+ if (served !== undefined) {
195
+ return served;
196
+ }
148
197
  const path = normalize(uri);
149
198
  if (this.files.has(path)) {
150
199
  return { isFile: true, isDirectory: false, uri };
@@ -152,7 +201,7 @@ export class InMemoryFileSystemProvider implements WritableFileSystemProvider {
152
201
  if (this.hasChildren(path)) {
153
202
  return { isFile: false, isDirectory: true, uri };
154
203
  }
155
- throw new Error(`No such file or directory: ${uri.toString()}`);
204
+ throw notFound(NO_SUCH_PATH.format({ uri: uri.toString() }), uri);
156
205
  }
157
206
 
158
207
  exists(uri: URI): Promise<boolean> {
@@ -160,6 +209,9 @@ export class InMemoryFileSystemProvider implements WritableFileSystemProvider {
160
209
  }
161
210
 
162
211
  existsSync(uri: URI): boolean {
212
+ if (serveVirtualDocument(this.services, uri) !== undefined) {
213
+ return true;
214
+ }
163
215
  const path = normalize(uri);
164
216
  return this.files.has(path) || this.hasChildren(path);
165
217
  }
@@ -8,6 +8,7 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import { UriUtils, URI } from '@hydranium/langium';
11
+ import type { Locale } from '@hydranium/protocol';
11
12
  // Bare `'path'` (not `node:path`) so a browser bundle can alias it to a POSIX
12
13
  // shim — this is the only filesystem-path use left on the portable `.` entry,
13
14
  // and it's the headless/CLI string-input convenience of `toWorkspaceFolders`.
@@ -18,6 +19,33 @@ import type { ServerSharedServicesMinimal } from '../shared-services.js';
18
19
  /** A workspace folder root accepted by the headless init seams: a filesystem path or a {@link URI}. */
19
20
  export type WorkspaceFolderInput = string | URI;
20
21
 
22
+ /**
23
+ * Options for the headless init seams.
24
+ *
25
+ * **Optional, and the parameter is optional too**, so that no existing caller
26
+ * has to change to gain a field it does not set.
27
+ */
28
+ export interface ProgrammaticInitOptions {
29
+ /**
30
+ * The locale to render user-facing server messages in — the headless
31
+ * equivalent of LSP `initialize`'s `locale`, for a head with no LSP
32
+ * connection (`startStdioServer`, the CLI subcommands, a test harness).
33
+ *
34
+ * Omitted means no locale, which the default renderer resolves as the
35
+ * framework's English. That is the correct answer for a headless tool: it
36
+ * has no reading user to have a language.
37
+ *
38
+ * **An IETF language tag, hyphenated — `de`, `de-CH`, `zh-Hant-TW` — and
39
+ * not the POSIX or Java spelling.** An LSP client is held to that syntax by
40
+ * the protocol, while a headless caller supplies the string itself and the
41
+ * obvious source is an environment variable holding `de_DE` or
42
+ * `en_US.UTF-8`. Neither is a tag, and the string is passed through
43
+ * unchecked, so a renderer that parses it finds nothing to match and every
44
+ * message renders in English.
45
+ */
46
+ readonly locale?: Locale;
47
+ }
48
+
21
49
  /**
22
50
  * Normalize the seam's flexible folder argument to LSP {@link WorkspaceFolder}s.
23
51
  * A `string` is resolved to an absolute filesystem path and wrapped as a
@@ -58,17 +86,25 @@ function toWorkspaceFolders(folders: WorkspaceFolderInput | ReadonlyArray<Worksp
58
86
  *
59
87
  * @param services the shared services tree the workspace manager lives on
60
88
  * @param folders one or more workspace roots (filesystem paths or URIs)
89
+ * @param options the message locale, when the caller has one
61
90
  */
62
91
  export async function initializeWorkspaceProgrammatically(
63
92
  services: ServerSharedServicesMinimal,
64
- folders: WorkspaceFolderInput | ReadonlyArray<WorkspaceFolderInput>
93
+ folders: WorkspaceFolderInput | ReadonlyArray<WorkspaceFolderInput>,
94
+ options: ProgrammaticInitOptions = {}
65
95
  ): Promise<void> {
66
96
  const workspaceFolders = toWorkspaceFolders(folders);
67
97
  const manager = services.workspace.WorkspaceManager;
68
- // `initialize` records the folders (DefaultWorkspaceManager reads only this
98
+ // `locale` goes through the synthesized params rather than a direct
99
+ // `acceptLocale` call, so this seam and a real LSP `initialize` reach the
100
+ // slot by the SAME path — an adopter overriding the capture in
101
+ // `HydraniumWorkspaceManager.initialize` is honoured headlessly too, which a
102
+ // second write site here would silently bypass.
103
+ //
104
+ // `initialize` records the folders (DefaultWorkspaceManager reads only that
69
105
  // field); `initialized` runs `mutex.write(initializeWorkspace)` and resolves
70
106
  // once discovery + the init build settle, so awaiting it is the ready gate.
71
- manager.initialize({ workspaceFolders } as InitializeParams);
107
+ manager.initialize({ workspaceFolders, locale: options.locale } as InitializeParams);
72
108
  await manager.initialized({} as InitializedParams);
73
109
  }
74
110
 
@@ -91,12 +127,14 @@ export async function initializeWorkspaceProgrammatically(
91
127
  *
92
128
  * @param services the shared services tree the workspace manager lives on
93
129
  * @param folders one or more workspace roots (filesystem paths or URIs)
130
+ * @param options the message locale, when the caller has one
94
131
  */
95
132
  export async function buildWorkspaceProgrammatically(
96
133
  services: ServerSharedServicesMinimal,
97
- folders: WorkspaceFolderInput | ReadonlyArray<WorkspaceFolderInput>
134
+ folders: WorkspaceFolderInput | ReadonlyArray<WorkspaceFolderInput>,
135
+ options: ProgrammaticInitOptions = {}
98
136
  ): Promise<void> {
99
- await initializeWorkspaceProgrammatically(services, folders);
137
+ await initializeWorkspaceProgrammatically(services, folders, options);
100
138
  const documents = services.workspace.LangiumDocuments.all.toArray();
101
139
  await services.workspace.DocumentBuilder.build(documents, { validation: true });
102
140
  }
@@ -7,14 +7,49 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { type Tracer } from '@hydranium/protocol';
11
- import { type AstNode, DefaultLangiumDocuments, type LangiumDocument, type URI } from '@hydranium/langium';
10
+ import { type AstNode, DefaultLangiumDocuments, type LangiumDocument, type LangiumDocuments, type URI } from '@hydranium/langium';
11
+ import { defineMessage } from '@hydranium/protocol';
12
12
  import { type ServerSharedServicesMinimal } from '../shared-services.js';
13
13
  import { type DocumentUriPolicy } from './document-uri-policy.js';
14
+ import { type HydraniumLangiumDocumentFactory } from './hydranium-langium-document-factory.js';
14
15
 
15
16
  /**
16
- * Default `LangiumDocuments` for `@hydranium/core` consumers. Layers two
17
- * extensions on top of Langium's {@link DefaultLangiumDocuments}:
17
+ * The document a caller asked to load is not there.
18
+ *
19
+ * User-facing, and the Node host's spelling of it: `loadUri` returns `undefined`
20
+ * exactly when the URI policy's `realpath` reports the path absent. A host whose
21
+ * provider has no `realpath` never reaches this — the miss surfaces from the
22
+ * provider instead, so the same condition has one declaration per host.
23
+ */
24
+ export const NO_LOADABLE_CONTENT = defineMessage('hydranium/core/no-loadable-content', 'No loadable content for {uri}');
25
+
26
+ /**
27
+ * A stand-in was asked for at a URI that routes to no grammar, and no language
28
+ * id was given to route it instead.
29
+ *
30
+ * User-facing, and raised BEFORE the parse: left to Langium's ladder the same
31
+ * condition surfaces as `no services for the extension ''`, which names neither
32
+ * the URI nor the argument that would have answered.
33
+ */
34
+ export const NO_LANGUAGE_FOR_STAND_IN = defineMessage(
35
+ 'hydranium/core/no-language-for-stand-in',
36
+ 'No grammar routes {uri}; pass a languageId to say which one the stand-in should parse with'
37
+ );
38
+
39
+ /**
40
+ * The registry surface the framework adds on top of Langium's
41
+ * {@link LangiumDocuments}. Declared separately from the implementing class so
42
+ * the shared-services slot can narrow to it: the class takes the services tree
43
+ * as its constructor parameter, so naming the CLASS there would make that type
44
+ * depend on itself.
45
+ */
46
+ export interface HydraniumDocumentRegistry extends LangiumDocuments {
47
+ createEmptyDocument(uri: URI, languageId?: string): LangiumDocument<AstNode>;
48
+ }
49
+
50
+ /**
51
+ * Default `LangiumDocuments` for `@hydranium/core` consumers, extending
52
+ * Langium's {@link DefaultLangiumDocuments} with:
18
53
  *
19
54
  * 1. **Identity via the {@link DocumentUriPolicy} seam.**
20
55
  * `getOrCreateDocument` resolves the requested URI through the seam — the
@@ -24,31 +59,78 @@ import { type DocumentUriPolicy } from './document-uri-policy.js';
24
59
  * `LangiumDocuments` override. The default resolves to the URI unchanged
25
60
  * (`DefaultDocumentUriPolicy`), matching Langium's own keying.
26
61
  *
27
- * 2. **`createEmptyDocument(uri)` — synchronous** factory for a transient
28
- * document with no loadable on-disk content. `getOrCreateDocument` falls
29
- * back to it when the seam reports no loadable URI, or when `super` cannot
30
- * load it, so callers always receive a document. Abstract because the
31
- * parse-result root is grammar-specific (each grammar returns its own
32
- * root node type). The fallback document is deliberately
33
- * **not** registered: a transient placeholder must not mask the real
34
- * document once it appears.
62
+ * 2. **A load that finds nothing rejects**, rather than answering with a
63
+ * transient empty document. An empty AST validates clean, so a placeholder
64
+ * hands the caller something that silently is not the file, and a caller
65
+ * that wants a document for a URI with no content on disk cannot use one
66
+ * anyway: it is unregistered, so no build, index or save can see it, and
67
+ * `LangiumDocumentFactory.update` reads from disk. Content that legitimately
68
+ * lives outside the filesystem belongs in a virtual document, which carries
69
+ * real source text.
35
70
  */
36
- export abstract class AbstractHydraniumLangiumDocuments extends DefaultLangiumDocuments {
71
+ export class HydraniumLangiumDocuments extends DefaultLangiumDocuments implements HydraniumDocumentRegistry {
37
72
  /** Document-identity seam, shared with the text store, event filters, and builder. */
38
73
  protected readonly uriPolicy: DocumentUriPolicy;
39
- protected readonly tracer: Tracer;
74
+
75
+ /**
76
+ * Narrows the inherited Langium factory field to the framework's, so the
77
+ * language-explicit `fromStringInLanguage` below needs no cast. The
78
+ * framework's shared module binds that class.
79
+ */
80
+ declare protected readonly langiumDocumentFactory: HydraniumLangiumDocumentFactory;
40
81
 
41
82
  constructor(protected override readonly services: ServerSharedServicesMinimal) {
42
83
  super(services);
43
84
  this.uriPolicy = services.workspace.DocumentUriPolicy;
44
- this.tracer = services.Tracer.for('LangiumDocuments');
45
85
  }
46
86
 
47
87
  /**
48
- * Build a transient empty document for `uri`. Implemented by consumers
49
- * because the parse-result root is grammar-specific.
88
+ * Build a transient document for `uri` by parsing empty text with the
89
+ * grammar `languageId` names, or the one `uri` routes to when it is
90
+ * omitted, so the root is that language's entry type with every containment
91
+ * list initialised.
92
+ *
93
+ * For callers that need a document at a URI with no content on disk — the
94
+ * scope provider querying before a file exists is the case it was added for.
95
+ * `getOrCreateDocument` deliberately does NOT fall back to this: fabricating
96
+ * a document behind a caller that asked to LOAD one hands back something
97
+ * that silently is not the file, whereas calling this is a caller saying it
98
+ * wants a stand-in.
99
+ *
100
+ * **Pass `languageId` whenever the URI names no file.** The create-element
101
+ * flow this exists for asks at the DIRECTORY a file is about to be written
102
+ * into, and a directory URI has no extension for the routing ladder to end
103
+ * on — so omitting the id there fails inside the parse, on an empty
104
+ * extension rather than on the URI. A caller in that position always knows
105
+ * its language: {@link HydraniumScopeProvider} is bound per grammar, and a
106
+ * request that names no document at all still carries the AST type
107
+ * `ExtendedServiceRegistry.soleServicesByType` routes on.
108
+ *
109
+ * The result is not registered, so nothing downstream can see it, and
110
+ * `LangiumDocumentFactory.update` would read from disk. It is a probe, not a
111
+ * document under construction; content that must survive belongs in a
112
+ * virtual document, which carries real source text.
113
+ *
114
+ * Building the root by hand instead needs the entry type name and a cast
115
+ * past the generated types, and leaves those lists `undefined`. A grammar
116
+ * whose entry rule opens with mandatory syntax yields a parse error here,
117
+ * and it is kept: `AstReflection.isComplete` is `false` for such a root
118
+ * however it is built, so the error states the same thing.
50
119
  */
51
- abstract createEmptyDocument(uri: URI): LangiumDocument<AstNode>;
120
+ createEmptyDocument(uri: URI, languageId?: string): LangiumDocument<AstNode> {
121
+ if (languageId !== undefined) {
122
+ return this.langiumDocumentFactory.fromStringInLanguage('', uri, languageId);
123
+ }
124
+ if (!this.services.ServiceRegistry.getServicesFor(uri)) {
125
+ // Reported here rather than left to the parse, which ends on the empty
126
+ // extension and so names neither the URI nor the id that would have
127
+ // answered.
128
+ throw new Error(NO_LANGUAGE_FOR_STAND_IN.format({ uri: uri.toString() }));
129
+ }
130
+ // The two-argument overload is synchronous; passing a cancellation token
131
+ // selects the promise-returning one, which this contract cannot await.
132
+ return this.langiumDocumentFactory.fromString('', uri);
133
+ }
52
134
 
53
135
  override async getOrCreateDocument(uri: URI): Promise<LangiumDocument<AstNode>> {
54
136
  const resolved = this.uriPolicy.loadUri(uri);
@@ -68,27 +150,18 @@ export abstract class AbstractHydraniumLangiumDocuments extends DefaultLangiumDo
68
150
  return await super.getOrCreateDocument(resolved);
69
151
  } catch (error: unknown) {
70
152
  // Load lost a race with a concurrent create — return that document.
153
+ // Checked before propagating, so a race is not reported as a
154
+ // missing file; cancellation carries no document and falls through.
71
155
  const reentrant = this.getDocument(resolved);
72
156
  if (reentrant) {
73
157
  this.services.workspace.CstResidencyService.rehydrate(reentrant);
74
158
  return reentrant;
75
159
  }
76
- // No concurrent document, so the load genuinely failed. The fallback
77
- // below still runs, because the file-not-found case is the ordinary
78
- // one here and no provider-independent way to recognise it exists:
79
- // Node's provider throws `ENOENT`, the in-memory one a bare `Error`,
80
- // and the provider is a seam an adopter rebinds. Discriminating on
81
- // message text across that seam would be a worse defect than the
82
- // one it fixes. So the failure is TRACED rather than propagated —
83
- // an unreadable-but-present file still degrades to an empty
84
- // document, but it stops doing so silently.
85
- this.tracer
86
- .with(resolved.toString())
87
- .debug(`Load failed, falling back to an empty document: ${error instanceof Error ? error.message : String(error)}`);
160
+ throw error;
88
161
  }
89
162
  }
90
- // No loadable content (missing / synthetic / not yet written): a transient
91
- // empty placeholder keyed canonically — not registered (see the class doc).
92
- return this.createEmptyDocument(resolved ?? uri);
163
+ // The seam reports no loadable content, so there is nothing to read and
164
+ // no error from a read to carry.
165
+ throw new Error(NO_LOADABLE_CONTENT.format({ uri: uri.toString() }));
93
166
  }
94
167
  }
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { URI } from '@hydranium/langium';
10
+ import { type FileSystemNode, URI } from '@hydranium/langium';
11
11
  import { type ServerSharedServicesMinimal } from '../shared-services.js';
12
12
 
13
13
  /**
@@ -76,14 +76,19 @@ export function isVirtualUri(uri: URI | string): boolean {
76
76
  * is not virtual or no document is registered for it (so a FileSystemProvider
77
77
  * delegates to its real backing).
78
78
  *
79
- * The framework's FileSystemProvider defaults call this at the top of
80
- * `readFile` / `readFileSync` so a virtual document survives a re-read:
79
+ * The framework's FileSystemProvider defaults consult this across their whole
80
+ * read surface, so a virtual document survives a re-read:
81
81
  * `DocumentBuilder.update` / `LangiumDocumentFactory.update` re-read a changed
82
82
  * URI from the FileSystemProvider, and nothing on disk backs a virtual URI. The
83
83
  * text comes from the registered document — the original source for a
84
84
  * `fromString` document, or the serialized form retained by the framework's
85
85
  * `fromModel` (which fills in the text Langium leaves empty). No serializer is
86
86
  * consulted here; the text is already on the document.
87
+ *
88
+ * **Every read method has to agree.** Serving only the text methods leaves one
89
+ * provider answering "here is the content" and "nothing is there" about one
90
+ * URI, which is a worse contract than answering neither: a caller cannot probe
91
+ * before reading.
87
92
  */
88
93
  export function serveVirtualDocument(services: ServerSharedServicesMinimal, uri: URI): string | undefined {
89
94
  if (!isVirtualUri(uri)) {
@@ -92,3 +97,16 @@ export function serveVirtualDocument(services: ServerSharedServicesMinimal, uri:
92
97
  const document = services.workspace.LangiumDocuments.getDocument(uri);
93
98
  return document ? document.textDocument.getText() : undefined;
94
99
  }
100
+
101
+ /**
102
+ * The filesystem node a registered virtual document presents as, or `undefined`
103
+ * on the same terms as {@link serveVirtualDocument}.
104
+ *
105
+ * Always a FILE: a virtual document is a leaf carrying text, and nothing is
106
+ * addressable beneath it. Reporting it as a directory instead would put it in
107
+ * front of a workspace walk that would then try to enumerate children it has
108
+ * none of.
109
+ */
110
+ export function serveVirtualNode(services: ServerSharedServicesMinimal, uri: URI): FileSystemNode | undefined {
111
+ return serveVirtualDocument(services, uri) === undefined ? undefined : { isFile: true, isDirectory: false, uri };
112
+ }