@hydranium/core 1.0.0-next.12 → 1.0.0-next.127

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 (482) hide show
  1. package/README.md +5 -1
  2. package/lib/documents/ast-document-manager.d.ts +92 -19
  3. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  4. package/lib/documents/ast-document-manager.js +114 -26
  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 +184 -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 +701 -114
  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 +165 -9
  70. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  71. package/lib/langium/document-builder/document-builder.js +313 -13
  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 +299 -0
  105. package/lib/langium/model-service/client-session.d.ts.map +1 -0
  106. package/lib/langium/model-service/client-session.js +379 -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 +226 -162
  113. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  114. package/lib/langium/model-service/model-service.js +203 -309
  115. package/lib/langium/model-service/model-service.js.map +1 -1
  116. package/lib/langium/module.d.ts +95 -16
  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-manager.d.ts +21 -1
  193. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  194. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  195. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  196. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  197. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  198. package/lib/langium/workspace/in-memory-file-system-provider.js +55 -9
  199. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  200. package/lib/langium/workspace/initialize-workspace.d.ts +31 -2
  201. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  202. package/lib/langium/workspace/initialize-workspace.js +13 -5
  203. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  204. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  205. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  206. package/lib/langium/workspace/langium-documents.js +83 -28
  207. package/lib/langium/workspace/langium-documents.js.map +1 -1
  208. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  209. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  210. package/lib/langium/workspace/virtual-document.js +19 -2
  211. package/lib/langium/workspace/virtual-document.js.map +1 -1
  212. package/lib/langium/workspace/write-lock-scope.d.ts +1 -1
  213. package/lib/langium/workspace/write-lock-scope.js +1 -1
  214. package/lib/locale/index.d.ts +10 -0
  215. package/lib/locale/index.d.ts.map +1 -0
  216. package/lib/locale/index.js +10 -0
  217. package/lib/locale/index.js.map +1 -0
  218. package/lib/locale/server-locale.d.ts +73 -0
  219. package/lib/locale/server-locale.d.ts.map +1 -0
  220. package/lib/locale/server-locale.js +61 -0
  221. package/lib/locale/server-locale.js.map +1 -0
  222. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  223. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  224. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  225. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  226. package/lib/lsp/connection-features.d.ts +23 -0
  227. package/lib/lsp/connection-features.d.ts.map +1 -0
  228. package/lib/lsp/connection-features.js +60 -0
  229. package/lib/lsp/connection-features.js.map +1 -0
  230. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  231. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  232. package/lib/lsp/diagnostics-connection.js +53 -0
  233. package/lib/lsp/diagnostics-connection.js.map +1 -0
  234. package/lib/lsp/hydranium-document-update-handler.d.ts +70 -62
  235. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  236. package/lib/lsp/hydranium-document-update-handler.js +130 -75
  237. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  238. package/lib/lsp/index.d.ts +3 -1
  239. package/lib/lsp/index.d.ts.map +1 -1
  240. package/lib/lsp/index.js +3 -1
  241. package/lib/lsp/index.js.map +1 -1
  242. package/lib/lsp/lsp-latency.d.ts +39 -0
  243. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  244. package/lib/lsp/lsp-latency.js +58 -0
  245. package/lib/lsp/lsp-latency.js.map +1 -0
  246. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  247. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  248. package/lib/lsp/semantic-token-provider.js +55 -8
  249. package/lib/lsp/semantic-token-provider.js.map +1 -1
  250. package/lib/lsp/shared-module.d.ts +4 -8
  251. package/lib/lsp/shared-module.d.ts.map +1 -1
  252. package/lib/lsp/shared-module.js.map +1 -1
  253. package/lib/lsp/start-language-server.d.ts +8 -7
  254. package/lib/lsp/start-language-server.d.ts.map +1 -1
  255. package/lib/lsp/start-language-server.js +13 -7
  256. package/lib/lsp/start-language-server.js.map +1 -1
  257. package/lib/messages/carriers.d.ts +54 -0
  258. package/lib/messages/carriers.d.ts.map +1 -0
  259. package/lib/messages/carriers.js +62 -0
  260. package/lib/messages/carriers.js.map +1 -0
  261. package/lib/messages/index.d.ts +25 -0
  262. package/lib/messages/index.d.ts.map +1 -0
  263. package/lib/messages/index.js +25 -0
  264. package/lib/messages/index.js.map +1 -0
  265. package/lib/messages/renderer.d.ts +142 -0
  266. package/lib/messages/renderer.d.ts.map +1 -0
  267. package/lib/messages/renderer.js +165 -0
  268. package/lib/messages/renderer.js.map +1 -0
  269. package/lib/node/heap-ceiling.d.ts +61 -0
  270. package/lib/node/heap-ceiling.d.ts.map +1 -0
  271. package/lib/node/heap-ceiling.js +72 -0
  272. package/lib/node/heap-ceiling.js.map +1 -0
  273. package/lib/node/index.d.ts +1 -0
  274. package/lib/node/index.d.ts.map +1 -1
  275. package/lib/node/index.js +1 -0
  276. package/lib/node/index.js.map +1 -1
  277. package/lib/node/latency-from-env.d.ts +1 -1
  278. package/lib/node/latency-from-env.js +1 -1
  279. package/lib/node/measure-memory.d.ts.map +1 -1
  280. package/lib/node/measure-memory.js +1 -0
  281. package/lib/node/measure-memory.js.map +1 -1
  282. package/lib/node/node-file-system-provider.d.ts +35 -1
  283. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  284. package/lib/node/node-file-system-provider.js +140 -45
  285. package/lib/node/node-file-system-provider.js.map +1 -1
  286. package/lib/node/profile-capture.d.ts +8 -2
  287. package/lib/node/profile-capture.d.ts.map +1 -1
  288. package/lib/node/profile-capture.js.map +1 -1
  289. package/lib/node/profiling-run.d.ts +2 -2
  290. package/lib/node/profiling-run.js +1 -1
  291. package/lib/node/profiling-run.js.map +1 -1
  292. package/lib/node/rename-over-open-readers.d.ts +52 -0
  293. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  294. package/lib/node/rename-over-open-readers.js +66 -0
  295. package/lib/node/rename-over-open-readers.js.map +1 -0
  296. package/lib/node/socket-launcher.d.ts +16 -1
  297. package/lib/node/socket-launcher.d.ts.map +1 -1
  298. package/lib/node/socket-launcher.js +4 -3
  299. package/lib/node/socket-launcher.js.map +1 -1
  300. package/lib/node/stdio-launcher.d.ts +1 -1
  301. package/lib/node/stdio-launcher.d.ts.map +1 -1
  302. package/lib/node/stdio-launcher.js +1 -1
  303. package/lib/node/stdio-launcher.js.map +1 -1
  304. package/lib/testing/fake-document.d.ts +16 -4
  305. package/lib/testing/fake-document.d.ts.map +1 -1
  306. package/lib/testing/fake-document.js +18 -3
  307. package/lib/testing/fake-document.js.map +1 -1
  308. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  309. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  310. package/lib/testing/make-noop-shared-services.js +28 -2
  311. package/lib/testing/make-noop-shared-services.js.map +1 -1
  312. package/lib/testing/make-test-services.d.ts +61 -13
  313. package/lib/testing/make-test-services.d.ts.map +1 -1
  314. package/lib/testing/make-test-services.js +25 -5
  315. package/lib/testing/make-test-services.js.map +1 -1
  316. package/lib/testing/node/index.d.ts +1 -0
  317. package/lib/testing/node/index.d.ts.map +1 -1
  318. package/lib/testing/node/index.js +1 -0
  319. package/lib/testing/node/index.js.map +1 -1
  320. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  321. package/lib/testing/node/lsp-harness.js +2 -0
  322. package/lib/testing/node/lsp-harness.js.map +1 -1
  323. package/lib/testing/node/lsp-server-connection.d.ts +11 -3
  324. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  325. package/lib/testing/node/lsp-server-connection.js +42 -9
  326. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  327. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  328. package/lib/testing/node/scratch-workspace.js +10 -5
  329. package/lib/testing/node/scratch-workspace.js.map +1 -1
  330. package/lib/testing/node/spawned-server.d.ts +14 -0
  331. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  332. package/lib/testing/node/spawned-server.js +33 -1
  333. package/lib/testing/node/spawned-server.js.map +1 -1
  334. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  335. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  336. package/lib/testing/node/unhandled-rejections.js +32 -0
  337. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  338. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  339. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  340. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  341. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  342. package/lib/testing/playwright/index.d.ts +1 -0
  343. package/lib/testing/playwright/index.d.ts.map +1 -1
  344. package/lib/testing/playwright/index.js +1 -0
  345. package/lib/testing/playwright/index.js.map +1 -1
  346. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  347. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  348. package/lib/testing/playwright/server-log-capture.js +21 -4
  349. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  350. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  351. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  352. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  353. package/lib/testing/run-update-pipeline.d.ts +4 -4
  354. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  355. package/lib/testing/stub-ast-document-manager.d.ts +10 -9
  356. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  357. package/lib/testing/stub-ast-document-manager.js +8 -9
  358. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  359. package/lib/testing/stub-document-builder.d.ts +8 -0
  360. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  361. package/lib/testing/stub-document-builder.js +22 -3
  362. package/lib/testing/stub-document-builder.js.map +1 -1
  363. package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
  364. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  365. package/lib/testing/stub-hydranium-text-documents.js +92 -15
  366. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  367. package/lib/testing/stub-langium-documents.d.ts +3 -2
  368. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  369. package/lib/testing/stub-langium-documents.js.map +1 -1
  370. package/lib/testing/stub-model-service.d.ts +7 -6
  371. package/lib/testing/stub-model-service.d.ts.map +1 -1
  372. package/lib/testing/stub-model-service.js +4 -4
  373. package/lib/testing/stub-model-service.js.map +1 -1
  374. package/lib/util/connection-liveness.d.ts +7 -6
  375. package/lib/util/connection-liveness.d.ts.map +1 -1
  376. package/lib/util/connection-liveness.js +35 -14
  377. package/lib/util/connection-liveness.js.map +1 -1
  378. package/package.json +17 -8
  379. package/src/documents/ast-document-manager.ts +195 -45
  380. package/src/documents/client-ids.ts +5 -22
  381. package/src/documents/client-session-errors.ts +22 -0
  382. package/src/documents/client-session-registry.ts +237 -0
  383. package/src/documents/file-system-task-queue.ts +68 -0
  384. package/src/documents/hydranium-text-documents.ts +856 -142
  385. package/src/documents/index.ts +3 -0
  386. package/src/documents/language-client-text-shadow.ts +59 -11
  387. package/src/documents/self-save-registry.ts +52 -13
  388. package/src/index.ts +6 -0
  389. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  390. package/src/langium/bootstrap.ts +16 -9
  391. package/src/langium/config/configuration-provider.ts +119 -0
  392. package/src/langium/config/index.ts +1 -0
  393. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  394. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  395. package/src/langium/document-builder/build-session.ts +87 -0
  396. package/src/langium/document-builder/document-builder.ts +355 -13
  397. package/src/langium/document-builder/index.ts +1 -0
  398. package/src/langium/integration-services.ts +67 -6
  399. package/src/langium/integrity/integrity-rule.ts +13 -5
  400. package/src/langium/integrity/integrity-service.ts +202 -17
  401. package/src/langium/keys/containment.ts +87 -0
  402. package/src/langium/keys/index.ts +1 -0
  403. package/src/langium/keys/name-based-key-provider.ts +4 -0
  404. package/src/langium/language-module.ts +42 -4
  405. package/src/langium/model-service/client-session.ts +581 -0
  406. package/src/langium/model-service/index.ts +1 -0
  407. package/src/langium/model-service/model-service.ts +347 -298
  408. package/src/langium/module.ts +119 -22
  409. package/src/langium/naming/name-provider.ts +27 -5
  410. package/src/langium/naming/name-separator-validation.ts +34 -5
  411. package/src/langium/residency/cst-residency-service.ts +36 -14
  412. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  413. package/src/langium/serialization/abstract-serializer.ts +13 -32
  414. package/src/langium/service-registry.ts +19 -2
  415. package/src/langium/shared-services.ts +19 -2
  416. package/src/langium/transfer/transfer-encoder.ts +85 -24
  417. package/src/langium/trivia/comment-preserver.ts +1100 -0
  418. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  419. package/src/langium/trivia/index.ts +14 -0
  420. package/src/langium/trivia/trivia-contribution.ts +39 -0
  421. package/src/langium/trivia/trivia-preserver.ts +54 -0
  422. package/src/langium/trivia/trivia-service.ts +99 -0
  423. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  424. package/src/langium/validation/document-validator.ts +537 -11
  425. package/src/langium/workspace/document-uri-policy.ts +3 -4
  426. package/src/langium/workspace/file-system-provider.ts +48 -8
  427. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  428. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  429. package/src/langium/workspace/in-memory-file-system-provider.ts +62 -10
  430. package/src/langium/workspace/initialize-workspace.ts +43 -5
  431. package/src/langium/workspace/langium-documents.ts +106 -33
  432. package/src/langium/workspace/virtual-document.ts +21 -3
  433. package/src/langium/workspace/write-lock-scope.ts +1 -1
  434. package/src/locale/index.ts +10 -0
  435. package/src/locale/server-locale.ts +86 -0
  436. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  437. package/src/lsp/connection-features.ts +67 -0
  438. package/src/lsp/diagnostics-connection.ts +56 -0
  439. package/src/lsp/hydranium-document-update-handler.ts +159 -79
  440. package/src/lsp/index.ts +3 -1
  441. package/src/lsp/lsp-latency.ts +62 -0
  442. package/src/lsp/semantic-token-provider.ts +74 -7
  443. package/src/lsp/shared-module.ts +4 -8
  444. package/src/lsp/start-language-server.ts +13 -7
  445. package/src/messages/carriers.ts +78 -0
  446. package/src/messages/index.ts +36 -0
  447. package/src/messages/renderer.ts +212 -0
  448. package/src/node/heap-ceiling.ts +108 -0
  449. package/src/node/index.ts +1 -0
  450. package/src/node/latency-from-env.ts +1 -1
  451. package/src/node/measure-memory.ts +1 -0
  452. package/src/node/node-file-system-provider.ts +154 -47
  453. package/src/node/profile-capture.ts +8 -2
  454. package/src/node/profiling-run.ts +3 -3
  455. package/src/node/rename-over-open-readers.ts +78 -0
  456. package/src/node/socket-launcher.ts +19 -4
  457. package/src/node/stdio-launcher.ts +1 -2
  458. package/src/testing/fake-document.ts +24 -5
  459. package/src/testing/make-noop-shared-services.ts +50 -2
  460. package/src/testing/make-test-services.ts +99 -24
  461. package/src/testing/node/index.ts +1 -0
  462. package/src/testing/node/lsp-harness.ts +2 -0
  463. package/src/testing/node/lsp-server-connection.ts +61 -11
  464. package/src/testing/node/scratch-workspace.ts +10 -5
  465. package/src/testing/node/spawned-server.ts +35 -1
  466. package/src/testing/node/unhandled-rejections.ts +31 -0
  467. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  468. package/src/testing/playwright/index.ts +1 -0
  469. package/src/testing/playwright/server-log-capture.ts +24 -5
  470. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  471. package/src/testing/run-update-pipeline.ts +4 -4
  472. package/src/testing/stub-ast-document-manager.ts +18 -31
  473. package/src/testing/stub-document-builder.ts +32 -3
  474. package/src/testing/stub-hydranium-text-documents.ts +127 -22
  475. package/src/testing/stub-langium-documents.ts +3 -2
  476. package/src/testing/stub-model-service.ts +9 -8
  477. package/src/util/connection-liveness.ts +37 -14
  478. package/lib/lsp/instrument-connection.d.ts +0 -36
  479. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  480. package/lib/lsp/instrument-connection.js +0 -62
  481. package/lib/lsp/instrument-connection.js.map +0 -1
  482. package/src/lsp/instrument-connection.ts +0 -66
@@ -8,11 +8,14 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  import {
11
+ type CanonicalUri,
11
12
  type CloseModelArgs,
12
13
  type Tracer,
13
14
  type TransferSavedEvent,
14
15
  type TransferUpdatedEvent,
15
- type OpenModelArgs
16
+ type OpenModelArgs,
17
+ asSnapshotVersion,
18
+ type SnapshotVersion
16
19
  } from '@hydranium/protocol';
17
20
  import {
18
21
  type AstNode,
@@ -24,6 +27,7 @@ import {
24
27
  UriUtils
25
28
  } from '@hydranium/langium';
26
29
  import { type LogNameOptions } from '../langium/diagnostics/logger.js';
30
+ import { type AstDiagnostic } from '../langium/validation/document-validator.js';
27
31
 
28
32
  /**
29
33
  * File-system provider extension that supports writes. Langium's standard
@@ -102,19 +106,18 @@ export interface UpdateInfo {
102
106
  * twins; the type-system distinction is the generic constraint, and the
103
107
  * conversion seam between them is the framework `TransferEncoder`.
104
108
  */
105
- export interface AstDocument<TAst extends AstNode, TDiagnostic> {
109
+ export interface AstDocument<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic> {
106
110
  root: TAst;
107
111
  diagnostics: TDiagnostic[];
108
112
  uri: string;
109
113
  /**
110
114
  * Text-document version this snapshot was taken at — read from
111
115
  * `LangiumDocument.textDocument.version`. Symmetric with
112
- * `TransferDocument.version` on the wire side: in-process callers
113
- * that hold an `AstDocument` and mutate it pass this value back as
114
- * `TransferUpdateArgs.baseVersion` / `TransferSaveArgs.baseVersion` to opt
115
- * into the conflict gate.
116
+ * `TransferDocument.version` on the wire side: an in-process caller that
117
+ * holds an `AstDocument` and mutates it sends this straight back as the
118
+ * `basedOn` of its session's write, and the conflict gate arms on it.
116
119
  */
117
- version: number;
120
+ version: SnapshotVersion;
118
121
  }
119
122
 
120
123
  export namespace AstDocument {
@@ -124,13 +127,13 @@ export namespace AstDocument {
124
127
  * don't need to repeat the empty array at every call site. Mirrors
125
128
  * `TransferDocument.create` on the wire side.
126
129
  */
127
- export function create<TAst extends AstNode, TDiagnostic = unknown>(
130
+ export function create<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic>(
128
131
  uri: string,
129
132
  version: number,
130
133
  root: TAst,
131
134
  diagnostics: TDiagnostic[] = []
132
135
  ): AstDocument<TAst, TDiagnostic> {
133
- return { uri, version, root, diagnostics };
136
+ return { uri, version: asSnapshotVersion(version), root, diagnostics };
134
137
  }
135
138
 
136
139
  /**
@@ -146,25 +149,85 @@ export namespace AstDocument {
146
149
  * carry that identity, not the subscriber's (possibly non-canonical) URI:
147
150
  * a subscriber already knows the URI it subscribed with, so the useful
148
151
  * thing to surface is the canonical one every other layer keys by.
152
+ *
153
+ * Narrowing `TDiagnostic` below the constraint asserts that the validators
154
+ * in play produce that shape, and nothing here checks it: the build fills the
155
+ * array, so a document carrying a diagnostic from elsewhere — a lexer error,
156
+ * another validator — satisfies the declared type and not the narrowed one.
149
157
  */
150
- export function from<TAst extends AstNode, TDiagnostic = unknown>(document: LangiumDocument): AstDocument<TAst, TDiagnostic> {
158
+ export function from<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic>(
159
+ document: LangiumDocument
160
+ ): AstDocument<TAst, TDiagnostic> {
151
161
  return create<TAst, TDiagnostic>(
152
162
  document.textDocument.uri,
153
163
  document.textDocument.version,
154
164
  document.parseResult.value as TAst,
155
- (document.diagnostics ?? []) as unknown as TDiagnostic[]
165
+ (document.diagnostics ?? []) as TDiagnostic[]
156
166
  );
157
167
  }
158
168
  }
159
169
 
160
170
  /** Update event delivered by {@link AstDocumentManager.onUpdate} — typed alias over the generic event wrapper. */
161
- export type AstDocumentUpdatedEvent<TAst extends AstNode, TDiagnostic> = TransferUpdatedEvent<AstDocument<TAst, TDiagnostic>>;
171
+ export type AstDocumentUpdatedEvent<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic> = TransferUpdatedEvent<
172
+ AstDocument<TAst, TDiagnostic>
173
+ >;
162
174
 
163
175
  /** Save event delivered by {@link AstDocumentManager.onSave} — typed alias over the generic event wrapper. */
164
- export type AstDocumentSavedEvent<TAst extends AstNode, TDiagnostic> = TransferSavedEvent<AstDocument<TAst, TDiagnostic>>;
176
+ export type AstDocumentSavedEvent<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic> = TransferSavedEvent<
177
+ AstDocument<TAst, TDiagnostic>
178
+ >;
165
179
 
166
180
  /** Construction options for {@link AstDocumentManager}. */
167
- export type AstDocumentManagerOptions = LogNameOptions;
181
+ export interface AstDocumentManagerOptions extends LogNameOptions {
182
+ /**
183
+ * Skip a queued save of a URI when a newer save of it is queued behind. The
184
+ * newer save took its text later, so disk ends the same with fewer writes.
185
+ * A skipped save announces nothing and takes the newer save's outcome: it
186
+ * resolves when that one lands and rejects with its error when it fails,
187
+ * since disk then holds neither text. Defaults to `false`: every save
188
+ * writes. The framework binds the manager without options, so turning this
189
+ * on means rebinding `AstDocumentManager` with them.
190
+ */
191
+ readonly coalesceSaves?: boolean;
192
+ }
193
+
194
+ /**
195
+ * The document slot the model server and the integrity service talk to:
196
+ * open/close/update/save plus the AST-typed event streams, with the LSP
197
+ * plumbing hidden.
198
+ *
199
+ * Generic over `<TAst extends AstNode, TDiagnostic>` so each consumer projects
200
+ * its own AST root type and diagnostic shape into the emitted
201
+ * {@link AstDocument} envelopes.
202
+ *
203
+ * **URI contract.** Every URI-accepting member takes a URI in *any* spelling
204
+ * (canonical, symlinked, `..`/case-divergent) and canonicalizes internally
205
+ * through the {@link DocumentUriPolicy}, so a caller never has to canonicalize
206
+ * first and an implementation may not require it.
207
+ */
208
+ export interface AstDocumentManager<TAst extends AstNode, TDiagnostic extends AstDiagnostic = AstDiagnostic> {
209
+ open(args: OpenModelArgs): Promise<Disposable>;
210
+ close(args: CloseModelArgs): Promise<void>;
211
+ isOpen(uri: string): boolean;
212
+
213
+ /** Apply `text` as `clientId`'s edit. Resolves to the resulting text-document version. */
214
+ update(uri: string, text: string, clientId: string): Promise<number>;
215
+ /**
216
+ * Write the document's current text to disk through `FileSystemTaskQueue`.
217
+ * The text is taken when this is called, so saves of one file land in the
218
+ * order they were called.
219
+ */
220
+ save(uri: string, clientId: string): Promise<void>;
221
+
222
+ onUpdate(uri: string, listener: (event: AstDocumentUpdatedEvent<TAst, TDiagnostic>) => void): Disposable;
223
+ onSave(uri: string, listener: (event: AstDocumentSavedEvent<TAst, TDiagnostic>) => void | Promise<void>): Disposable;
224
+ onClientClosed(uri: string, clientId: string, listener: () => void): Disposable;
225
+
226
+ getDocument(uri: string): LangiumDocument | undefined;
227
+
228
+ /** Client id that authored the document's current version, or `undefined` for a framework-internal build. */
229
+ getAuthor(document: LangiumDocument): string | undefined;
230
+ }
168
231
 
169
232
  /**
170
233
  * Higher-level facade over {@link HydraniumTextDocuments} that speaks the
@@ -186,9 +249,16 @@ export type AstDocumentManagerOptions = LogNameOptions;
186
249
  * resolution (the egress `applyEditToLanguageClient`) stay on
187
250
  * {@link HydraniumTextDocuments}, their owner.
188
251
  */
189
- export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = unknown> {
252
+ export class DefaultAstDocumentManager<
253
+ TAst extends AstNode,
254
+ TDiagnostic extends AstDiagnostic = AstDiagnostic
255
+ > implements AstDocumentManager<TAst, TDiagnostic> {
190
256
  protected lastUpdate?: UpdateInfo;
191
257
 
258
+ /** Per canonical URI, the newest save queued for it, which {@link coalesceSaves} lets older queued saves defer to. */
259
+ protected readonly newestSaves = new Map<CanonicalUri, Promise<void>>();
260
+ protected readonly coalesceSaves: boolean;
261
+
192
262
  protected readonly textDocuments: HydraniumTextDocuments<TextDocument>;
193
263
  protected readonly fileSystemProvider: WritableFileSystemProvider;
194
264
  protected readonly langiumDocs: LangiumDocuments;
@@ -206,12 +276,19 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
206
276
  this.documentBuilder = services.workspace.DocumentBuilder;
207
277
  this.uriPolicy = services.workspace.DocumentUriPolicy;
208
278
  this.tracer = services.Tracer.for(options.logName ?? 'AstDocumentManager').trace('instantiated');
279
+ this.coalesceSaves = options.coalesceSaves ?? false;
209
280
  this.textDocuments.onDidOpen(event =>
210
281
  this.open({ clientId: event.clientId, uri: event.document.uri, languageId: event.document.languageId })
211
282
  );
212
283
  this.textDocuments.onDidClose(event => this.close({ clientId: event.clientId, uri: event.document.uri }));
213
284
  this.documentBuilder.onUpdate((changed, deleted) => {
214
285
  this.lastUpdate = { changed, deleted };
286
+ // The only place a deletion is observable: `DocumentBuilder.update`
287
+ // removes a deleted document before it builds anything, so no phase
288
+ // listener ever sees it.
289
+ for (const uri of deleted) {
290
+ this.textDocuments.notifyDocumentDeleted(uri.toString());
291
+ }
215
292
  });
216
293
  // Content transitions for CLOSED documents bypass the text store: the
217
294
  // last-close revert and watched-file changes rebuild from disk into a
@@ -311,11 +388,12 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
311
388
  const event: TransferUpdatedEvent<AstDocument<TAst, TDiagnostic>> = {
312
389
  document: this.toAstDocument(document),
313
390
  sourceClientId,
314
- reason: this.lastUpdate?.changed.find(changed => UriUtils.equals(changed, document.uri))
315
- ? 'changed'
316
- : this.lastUpdate?.deleted.find(deleted => UriUtils.equals(deleted, document.uri))
317
- ? 'deleted'
318
- : 'rebuilt'
391
+ // No `'deleted'` arm: `DocumentBuilder.update` drops a deleted
392
+ // document from `LangiumDocuments` before deriving the rebuild set
393
+ // from it, so a deleted URI is never built and never reaches this
394
+ // phase listener. A subscriber needing deletions has to be told on
395
+ // a channel that does not require a built document.
396
+ reason: this.lastUpdate?.changed.some(changed => UriUtils.equals(changed, document.uri)) ? 'changed' : 'rebuilt'
319
397
  };
320
398
  this.tracer.with(uri).trace(`emitUpdate start: source=${sourceClientId}, reason=${event.reason}`);
321
399
  listener(event);
@@ -328,21 +406,21 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
328
406
  * The client that authored `document`'s current version, or `undefined` when
329
407
  * no client wrote it — a framework-internal rebuild (workspace startup, a
330
408
  * cascade relink, a `didClose`-reload) or a genuine author gap. Honest about
331
- * absence so routing consumers (`ModelService.isNonLanguageClientEdit`)
332
- * test `undefined` directly rather than against a sentinel; the presentation
409
+ * absence so a routing consumer tests `undefined` directly rather than
410
+ * against a sentinel; the presentation
333
411
  * default ({@link UNKNOWN_CLIENT_ID}) is applied at the boundary that needs a
334
412
  * concrete value (the `onUpdate` event's `sourceClientId`, the data-server's
335
413
  * `resolveSourceClientId`).
336
414
  */
337
- getAuthor(document: LangiumDocument<AstNode>): string | undefined {
415
+ getAuthor(document: LangiumDocument): string | undefined {
338
416
  return this.textDocuments.getAuthor(document.textDocument.uri, document.textDocument.version);
339
417
  }
340
418
 
341
419
  async open(args: OpenModelArgs): Promise<Disposable> {
342
420
  // `open()` is the RPC-level "ensure this document is loaded" call. It is issued
343
- // by a view attaching, but also internally by `update()` / `save()` and by every
344
- // additional client attaching to the same URI, including the several sub-editors
345
- // a composite view opens. When the document is already loaded there is nothing to
421
+ // by a session opening the document, including by every additional client
422
+ // attaching to the same URI and the several sub-editors a composite view
423
+ // opens. When the document is already loaded there is nothing to
346
424
  // do here but hand back the close-disposable — callers that want the current
347
425
  // state read it from this method's surrounding RPC response, not from a re-build.
348
426
  //
@@ -355,10 +433,28 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
355
433
  // transiently breaks cross-document linking mid-rebuild (a dependent briefly
356
434
  // seeing an `extends`/`type` reference unresolved). And because the constructor
357
435
  // wires `onDidOpen -> this.open`, it would fire a second, identical rebuild on
358
- // every first open. So an already-open `open()` is a no-op.
436
+ // every first open.
437
+ //
438
+ // So an already-open `open()` records the attaching client's open and nothing
439
+ // else. The open is per `(uri, clientId)` and the last-close revert counts down
440
+ // to it, so skipping it lets the first client's close tear down a document
441
+ // another client is still reading. A document waiting out the revert grace
442
+ // is still loaded: a client lost from it within its own grace attaches and
443
+ // keeps its text, and for any other client the attach releases it, so the
444
+ // open reads the file.
445
+ if (this.isOpen(args.uri)) {
446
+ this.textDocuments.attachClient(args.uri, args.clientId);
447
+ }
359
448
  if (!this.isOpen(args.uri)) {
360
449
  const textDocument = await this.createDocumentFromTextOrFileSystem(args.uri, args.languageId, args.version, args.text);
450
+ const creates = !this.isOpen(args.uri);
361
451
  this.textDocuments.notifyDidOpenTextDocument({ textDocument }, args.clientId);
452
+ if (creates && args.text !== undefined) {
453
+ // Text the caller supplies was not read from the file, so no file
454
+ // is assumed: the document is dirty until its first save, whatever
455
+ // the file holds.
456
+ this.textDocuments.updateDiskBaseline(args.uri, undefined);
457
+ }
362
458
  }
363
459
  return Disposable.create(() => this.close(args));
364
460
  }
@@ -393,31 +489,80 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
393
489
  * callers don't need to pass it — the manager owns content and version
394
490
  * sequencing.
395
491
  *
492
+ * The text is taken synchronously, before the save waits in the file's task
493
+ * queue; the compare and the write run in the queue. Taken later, a save
494
+ * that waits behind another writes whatever the store holds when its turn
495
+ * comes, edits made after the call included, and announces that text under
496
+ * its own client id.
497
+ *
498
+ * **The write is skipped when the file already holds that text; the
499
+ * notification is not.** A save announces that the content is on disk, which
500
+ * is true either way — so a subscriber clearing a dirty marker or reacting to
501
+ * a persist behaves the same, while an mtime is not moved for byte-identical
502
+ * content that a downstream mtime-keyed build would then read as new work.
503
+ *
396
504
  * Throws if no document is open for `uri`.
397
505
  */
398
506
  async save(uri: string, clientId: string): Promise<void> {
399
- const document = this.textDocuments.get(uri);
507
+ // Canonical throughout: the write and the read that gates it must address
508
+ // the same file the store was keyed by, or a divergent spelling compares
509
+ // one file and writes another.
510
+ const canonical = this.uriPolicy.canonicalUri(uri);
511
+ const document = this.textDocuments.get(canonical);
400
512
  if (!document) {
401
513
  throw new Error(`Document ${uri} hasn't been opened for saving yet`);
402
514
  }
403
515
  const text = document.getText();
404
- await this.tracer.with(uri).time(
405
- `Write file (${text.length} bytes, from ${clientId})`,
406
- // Async write so a slow disk does not block the event loop for the duration of the fs call.
407
- () => this.fileSystemProvider.writeFile(UriUtils.toUri(uri), text),
408
- 'debug'
409
- );
516
+ let supersededBy: Promise<void> | undefined;
517
+ const saved: Promise<void> = this.services.workspace.FileSystemTaskQueue.enqueue(canonical, async () => {
518
+ const newest = this.newestSaves.get(canonical);
519
+ if (this.coalesceSaves && newest !== saved) {
520
+ supersededBy = newest;
521
+ return;
522
+ }
523
+ await this.writeSave(canonical, text, clientId);
524
+ })
525
+ .then(() => supersededBy)
526
+ .finally(() => {
527
+ if (this.newestSaves.get(canonical) === saved) {
528
+ this.newestSaves.delete(canonical);
529
+ }
530
+ });
531
+ this.newestSaves.set(canonical, saved);
532
+ return saved;
533
+ }
534
+
535
+ /** Write `text` to `uri` unless the file already holds it, then announce the save. Runs inside the file's task queue. */
536
+ protected async writeSave(uri: CanonicalUri, text: string, clientId: string): Promise<void> {
537
+ if (!(await this.matchesDisk(uri, text))) {
538
+ await this.tracer.with(uri).time(
539
+ `Write file (${text.length} bytes, from ${clientId})`,
540
+ // Async write so a slow disk does not block the event loop for the duration of the fs call.
541
+ () => this.fileSystemProvider.writeFile(UriUtils.toUri(uri), text),
542
+ 'debug'
543
+ );
544
+ }
410
545
  this.textDocuments.notifyDidSaveTextDocument({ textDocument: TextDocumentIdentifier.create(uri), text }, clientId);
411
546
  }
412
547
 
413
- isOpen(uri: string): boolean {
414
- return !!this.textDocuments.get(uri);
548
+ /**
549
+ * Whether the file at `uri` already holds `text`.
550
+ *
551
+ * Compared by CONTENT rather than against a remembered version, so a write
552
+ * from outside this server is still corrected: a version this manager already
553
+ * saved says nothing about what is on disk now. `false` when the file cannot
554
+ * be read at all, which is the answer that writes — a document whose file does
555
+ * not exist yet is exactly the one a first save has to create.
556
+ */
557
+ protected async matchesDisk(uri: string, text: string): Promise<boolean> {
558
+ return this.fileSystemProvider
559
+ .readFile(UriUtils.toUri(uri))
560
+ .then(onDisk => onDisk === text)
561
+ .catch(() => false);
415
562
  }
416
563
 
417
- /** True if the URI was in the directly-changed set of the most recent build (not just affected). */
418
- isDirectChange(uri: string): boolean {
419
- const canonical = this.uriPolicy.canonicalUri(uri);
420
- return this.lastUpdate?.changed.some(changed => this.uriPolicy.canonicalUri(changed) === canonical) ?? false;
564
+ isOpen(uri: string): boolean {
565
+ return !!this.textDocuments.get(uri);
421
566
  }
422
567
 
423
568
  /**
@@ -437,11 +582,16 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
437
582
  version = 0,
438
583
  text?: string
439
584
  ): Promise<TextDocumentItem> {
440
- return { uri, languageId, version, text: text ?? (await this.readFile(uri)) };
441
- }
442
-
443
- async readFile(uri: string): Promise<string> {
444
- return this.fileSystemProvider.readFile(UriUtils.toUri(uri));
585
+ // Read in the file's task queue: a read beside a queued save of the file
586
+ // returns the text from before it, and the document then opens on that.
587
+ return {
588
+ uri,
589
+ languageId,
590
+ version,
591
+ text:
592
+ text ??
593
+ (await this.services.workspace.FileSystemTaskQueue.enqueue(uri, () => this.fileSystemProvider.readFile(UriUtils.toUri(uri))))
594
+ };
445
595
  }
446
596
 
447
597
  /**
@@ -8,26 +8,9 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  /**
11
- * Well-known client-kind identifiers used across the multi-client framework.
12
- *
13
- * The framework tracks each document as belonging to one or more "clients"
14
- * — typically an LSP-text-client (Monaco, VS Code, etc.), a GLSP diagram
15
- * client, and/or a structured-form client. Each kind carries a stable id so
16
- * the server can route outbound corrections, edits, and notifications to the
17
- * right transport.
18
- */
19
-
20
- /** Identifier for an LSP-text client (Monaco, VS Code, …). */
21
- export const LANGUAGE_CLIENT_ID = 'language-client';
22
-
23
- /** Fallback identifier when a document's author is unknown (e.g. cold workspace-init load). */
24
- export const UNKNOWN_CLIENT_ID = 'unknown';
25
-
26
- /**
27
- * Synthetic author id on the `onDocumentUpdated` broadcast a data-server
28
- * emits after the LAST client closed a document and the framework rebuilt it
29
- * from its disk content (discarding unsaved in-session edits). Not a real
30
- * client: it lets consumers distinguish the revert broadcast from
31
- * client-authored updates.
11
+ * Re-export, because these are WIRE values and the side that has to recognise
12
+ * them is the client. They are defined in `@hydranium/protocol` so a frontend
13
+ * can name them without depending on the server tier; this path stays so
14
+ * server-side code keeps importing them from where it already does.
32
15
  */
33
- export const REVERT_ON_CLOSE_CLIENT_ID = 'revert-on-close';
16
+ export { FRAMEWORK_CLIENT_IDS, LANGUAGE_CLIENT_ID, REVERT_ON_CLOSE_CLIENT_ID, UNKNOWN_CLIENT_ID } from '@hydranium/protocol';
@@ -0,0 +1,22 @@
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
+ /**
11
+ * Re-export, because these errors cross the wire and the side that has to
12
+ * recognise them is the client. They are defined in `@hydranium/protocol` so a
13
+ * frontend can name them without depending on the server tier.
14
+ */
15
+ export {
16
+ DocumentNotOpenError,
17
+ DuplicateClientIdError,
18
+ SessionClosedError,
19
+ isDocumentNotOpenError,
20
+ isDuplicateClientIdError,
21
+ isSessionClosedError
22
+ } from '@hydranium/protocol';