@hydranium/core 1.0.0-next.39 → 1.0.0-next.391

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 (805) hide show
  1. package/README.md +78 -82
  2. package/lib/browser/host-heads-in-worker.d.ts +77 -0
  3. package/lib/browser/host-heads-in-worker.d.ts.map +1 -0
  4. package/lib/browser/host-heads-in-worker.js +169 -0
  5. package/lib/browser/host-heads-in-worker.js.map +1 -0
  6. package/lib/browser/index.d.ts +12 -0
  7. package/lib/browser/index.d.ts.map +1 -0
  8. package/lib/browser/index.js +17 -0
  9. package/lib/browser/index.js.map +1 -0
  10. package/lib/browser/indexeddb-file-system-store.d.ts +82 -0
  11. package/lib/browser/indexeddb-file-system-store.d.ts.map +1 -0
  12. package/lib/browser/indexeddb-file-system-store.js +181 -0
  13. package/lib/browser/indexeddb-file-system-store.js.map +1 -0
  14. package/lib/browser/start-language-server-in-worker.d.ts +32 -0
  15. package/lib/browser/start-language-server-in-worker.d.ts.map +1 -0
  16. package/lib/browser/start-language-server-in-worker.js +40 -0
  17. package/lib/browser/start-language-server-in-worker.js.map +1 -0
  18. package/lib/documents/ast-document-manager.d.ts +254 -140
  19. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  20. package/lib/documents/ast-document-manager.js +311 -128
  21. package/lib/documents/ast-document-manager.js.map +1 -1
  22. package/lib/documents/client-ids.d.ts +5 -19
  23. package/lib/documents/client-ids.d.ts.map +1 -1
  24. package/lib/documents/client-ids.js +5 -19
  25. package/lib/documents/client-ids.js.map +1 -1
  26. package/lib/documents/client-session-errors.d.ts +15 -0
  27. package/lib/documents/client-session-errors.d.ts.map +1 -0
  28. package/lib/documents/client-session-errors.js +15 -0
  29. package/lib/documents/client-session-errors.js.map +1 -0
  30. package/lib/documents/client-session-registry.d.ts +146 -0
  31. package/lib/documents/client-session-registry.d.ts.map +1 -0
  32. package/lib/documents/client-session-registry.js +207 -0
  33. package/lib/documents/client-session-registry.js.map +1 -0
  34. package/lib/documents/dirty-state-tracker.d.ts +101 -0
  35. package/lib/documents/dirty-state-tracker.d.ts.map +1 -0
  36. package/lib/documents/dirty-state-tracker.js +85 -0
  37. package/lib/documents/dirty-state-tracker.js.map +1 -0
  38. package/lib/documents/document-release-handler.d.ts +204 -0
  39. package/lib/documents/document-release-handler.d.ts.map +1 -0
  40. package/lib/documents/document-release-handler.js +317 -0
  41. package/lib/documents/document-release-handler.js.map +1 -0
  42. package/lib/documents/document-release-scheduler.d.ts +67 -0
  43. package/lib/documents/document-release-scheduler.d.ts.map +1 -0
  44. package/lib/documents/document-release-scheduler.js +80 -0
  45. package/lib/documents/document-release-scheduler.js.map +1 -0
  46. package/lib/documents/file-system-task-queue.d.ts +52 -0
  47. package/lib/documents/file-system-task-queue.d.ts.map +1 -0
  48. package/lib/documents/file-system-task-queue.js +40 -0
  49. package/lib/documents/file-system-task-queue.js.map +1 -0
  50. package/lib/documents/hydranium-text-documents.d.ts +453 -282
  51. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  52. package/lib/documents/hydranium-text-documents.js +918 -504
  53. package/lib/documents/hydranium-text-documents.js.map +1 -1
  54. package/lib/documents/index.d.ts +10 -2
  55. package/lib/documents/index.d.ts.map +1 -1
  56. package/lib/documents/index.js +10 -2
  57. package/lib/documents/index.js.map +1 -1
  58. package/lib/documents/language-client-shadow.d.ts +234 -0
  59. package/lib/documents/language-client-shadow.d.ts.map +1 -0
  60. package/lib/documents/language-client-shadow.js +306 -0
  61. package/lib/documents/language-client-shadow.js.map +1 -0
  62. package/lib/documents/model-ledger.d.ts +47 -0
  63. package/lib/documents/model-ledger.d.ts.map +1 -0
  64. package/lib/documents/model-ledger.js +30 -0
  65. package/lib/documents/model-ledger.js.map +1 -0
  66. package/lib/documents/text-ledger.d.ts +64 -0
  67. package/lib/documents/text-ledger.d.ts.map +1 -0
  68. package/lib/documents/text-ledger.js +63 -0
  69. package/lib/documents/text-ledger.js.map +1 -0
  70. package/lib/documents/version-sync-service.d.ts +153 -0
  71. package/lib/documents/version-sync-service.d.ts.map +1 -0
  72. package/lib/documents/version-sync-service.js +186 -0
  73. package/lib/documents/version-sync-service.js.map +1 -0
  74. package/lib/index.d.ts +3 -0
  75. package/lib/index.d.ts.map +1 -1
  76. package/lib/index.js +3 -0
  77. package/lib/index.js.map +1 -1
  78. package/lib/langium/ast-extension/ast-extension-service.d.ts +42 -31
  79. package/lib/langium/ast-extension/ast-extension-service.d.ts.map +1 -1
  80. package/lib/langium/ast-extension/ast-extension-service.js +45 -19
  81. package/lib/langium/ast-extension/ast-extension-service.js.map +1 -1
  82. package/lib/langium/ast-extension/ast-node-builder.d.ts +12 -7
  83. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  84. package/lib/langium/ast-extension/ast-node-builder.js +25 -9
  85. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  86. package/lib/langium/bootstrap.d.ts +49 -36
  87. package/lib/langium/bootstrap.d.ts.map +1 -1
  88. package/lib/langium/bootstrap.js +75 -36
  89. package/lib/langium/bootstrap.js.map +1 -1
  90. package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts +9 -9
  91. package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts.map +1 -1
  92. package/lib/langium/build-phase-pass/build-phase-pass-service.js +25 -16
  93. package/lib/langium/build-phase-pass/build-phase-pass-service.js.map +1 -1
  94. package/lib/langium/build-phase-pass/build-phase-pass.d.ts +20 -8
  95. package/lib/langium/build-phase-pass/build-phase-pass.d.ts.map +1 -1
  96. package/lib/langium/build-phase-pass/build-phase-pass.js +20 -0
  97. package/lib/langium/build-phase-pass/build-phase-pass.js.map +1 -1
  98. package/lib/langium/composite-ast-reflection.d.ts +46 -0
  99. package/lib/langium/composite-ast-reflection.d.ts.map +1 -0
  100. package/lib/langium/composite-ast-reflection.js +97 -0
  101. package/lib/langium/composite-ast-reflection.js.map +1 -0
  102. package/lib/langium/config/configuration-provider.d.ts +81 -0
  103. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  104. package/lib/langium/config/configuration-provider.js +112 -0
  105. package/lib/langium/config/configuration-provider.js.map +1 -0
  106. package/lib/langium/config/index.d.ts +1 -0
  107. package/lib/langium/config/index.d.ts.map +1 -1
  108. package/lib/langium/config/index.js +1 -0
  109. package/lib/langium/config/index.js.map +1 -1
  110. package/lib/langium/config/settings.js.map +1 -1
  111. package/lib/langium/diagnostics/console-tee.d.ts +27 -0
  112. package/lib/langium/diagnostics/console-tee.d.ts.map +1 -0
  113. package/lib/langium/diagnostics/console-tee.js +141 -0
  114. package/lib/langium/diagnostics/console-tee.js.map +1 -0
  115. package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts +9 -20
  116. package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts.map +1 -1
  117. package/lib/langium/diagnostics/hydranium-langium-profiler.js +15 -24
  118. package/lib/langium/diagnostics/hydranium-langium-profiler.js.map +1 -1
  119. package/lib/langium/diagnostics/log-preamble.js +1 -1
  120. package/lib/langium/diagnostics/log-preamble.js.map +1 -1
  121. package/lib/langium/diagnostics/logger.d.ts +25 -6
  122. package/lib/langium/diagnostics/logger.d.ts.map +1 -1
  123. package/lib/langium/diagnostics/logger.js +49 -17
  124. package/lib/langium/diagnostics/logger.js.map +1 -1
  125. package/lib/langium/diagnostics/lsp-logger.d.ts +25 -0
  126. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  127. package/lib/langium/diagnostics/lsp-logger.js +81 -46
  128. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  129. package/lib/langium/diagnostics/server-tracer.d.ts.map +1 -1
  130. package/lib/langium/diagnostics/server-tracer.js.map +1 -1
  131. package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
  132. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  133. package/lib/langium/document-builder/build-pipeline-integration.js +43 -30
  134. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  135. package/lib/langium/document-builder/build-session.d.ts +6 -5
  136. package/lib/langium/document-builder/build-session.d.ts.map +1 -1
  137. package/lib/langium/document-builder/build-session.js +5 -5
  138. package/lib/langium/document-builder/build-session.js.map +1 -1
  139. package/lib/langium/document-builder/document-builder.d.ts +390 -40
  140. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  141. package/lib/langium/document-builder/document-builder.js +635 -102
  142. package/lib/langium/document-builder/document-builder.js.map +1 -1
  143. package/lib/langium/documentation/comment-provider.d.ts.map +1 -1
  144. package/lib/langium/documentation/comment-provider.js.map +1 -1
  145. package/lib/langium/integration-services.d.ts +20 -23
  146. package/lib/langium/integration-services.d.ts.map +1 -1
  147. package/lib/langium/integration-services.js.map +1 -1
  148. package/lib/langium/integrity/integrity-rule.d.ts +22 -8
  149. package/lib/langium/integrity/integrity-rule.d.ts.map +1 -1
  150. package/lib/langium/integrity/integrity-rule.js.map +1 -1
  151. package/lib/langium/integrity/integrity-service.d.ts +217 -29
  152. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  153. package/lib/langium/integrity/integrity-service.js +588 -84
  154. package/lib/langium/integrity/integrity-service.js.map +1 -1
  155. package/lib/langium/keys/containment.d.ts +3 -2
  156. package/lib/langium/keys/containment.d.ts.map +1 -1
  157. package/lib/langium/keys/containment.js.map +1 -1
  158. package/lib/langium/keys/name-based-key-provider.d.ts +17 -10
  159. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  160. package/lib/langium/keys/name-based-key-provider.js +24 -12
  161. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  162. package/lib/langium/keys/positional-key-provider.d.ts.map +1 -1
  163. package/lib/langium/keys/positional-key-provider.js.map +1 -1
  164. package/lib/langium/labeling/label-provider.d.ts.map +1 -1
  165. package/lib/langium/labeling/label-provider.js.map +1 -1
  166. package/lib/langium/language-module.d.ts +30 -5
  167. package/lib/langium/language-module.d.ts.map +1 -1
  168. package/lib/langium/language-module.js +21 -5
  169. package/lib/langium/language-module.js.map +1 -1
  170. package/lib/langium/model-service/client-session.d.ts +405 -0
  171. package/lib/langium/model-service/client-session.d.ts.map +1 -0
  172. package/lib/langium/model-service/client-session.js +479 -0
  173. package/lib/langium/model-service/client-session.js.map +1 -0
  174. package/lib/langium/model-service/index.d.ts +2 -0
  175. package/lib/langium/model-service/index.d.ts.map +1 -1
  176. package/lib/langium/model-service/index.js +2 -0
  177. package/lib/langium/model-service/index.js.map +1 -1
  178. package/lib/langium/model-service/model-events.d.ts +68 -0
  179. package/lib/langium/model-service/model-events.d.ts.map +1 -0
  180. package/lib/langium/model-service/model-events.js +10 -0
  181. package/lib/langium/model-service/model-events.js.map +1 -0
  182. package/lib/langium/model-service/model-service.d.ts +441 -323
  183. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  184. package/lib/langium/model-service/model-service.js +602 -407
  185. package/lib/langium/model-service/model-service.js.map +1 -1
  186. package/lib/langium/module.d.ts +96 -37
  187. package/lib/langium/module.d.ts.map +1 -1
  188. package/lib/langium/module.js +41 -29
  189. package/lib/langium/module.js.map +1 -1
  190. package/lib/langium/naming/name-provider.d.ts +32 -7
  191. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  192. package/lib/langium/naming/name-provider.js +10 -4
  193. package/lib/langium/naming/name-provider.js.map +1 -1
  194. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  195. package/lib/langium/project/abstract-project-manager.d.ts +184 -13
  196. package/lib/langium/project/abstract-project-manager.d.ts.map +1 -1
  197. package/lib/langium/project/abstract-project-manager.js +336 -16
  198. package/lib/langium/project/abstract-project-manager.js.map +1 -1
  199. package/lib/langium/project/project-change-event.d.ts +22 -3
  200. package/lib/langium/project/project-change-event.d.ts.map +1 -1
  201. package/lib/langium/project/project-manager.d.ts +28 -2
  202. package/lib/langium/project/project-manager.d.ts.map +1 -1
  203. package/lib/langium/project/single-project-manager.d.ts +7 -1
  204. package/lib/langium/project/single-project-manager.d.ts.map +1 -1
  205. package/lib/langium/project/single-project-manager.js +12 -0
  206. package/lib/langium/project/single-project-manager.js.map +1 -1
  207. package/lib/langium/residency/cst-residency-service.d.ts +92 -52
  208. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  209. package/lib/langium/residency/cst-residency-service.js +154 -58
  210. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  211. package/lib/langium/scope/ast-node-description-provider.d.ts +46 -10
  212. package/lib/langium/scope/ast-node-description-provider.d.ts.map +1 -1
  213. package/lib/langium/scope/ast-node-description-provider.js +88 -15
  214. package/lib/langium/scope/ast-node-description-provider.js.map +1 -1
  215. package/lib/langium/scope/built-local-symbols.d.ts +12 -0
  216. package/lib/langium/scope/built-local-symbols.d.ts.map +1 -0
  217. package/lib/langium/scope/built-local-symbols.js +11 -0
  218. package/lib/langium/scope/built-local-symbols.js.map +1 -0
  219. package/lib/langium/scope/fallback-scope.d.ts +27 -0
  220. package/lib/langium/scope/fallback-scope.d.ts.map +1 -0
  221. package/lib/langium/scope/fallback-scope.js +35 -0
  222. package/lib/langium/scope/fallback-scope.js.map +1 -0
  223. package/lib/langium/scope/hydranium-scope-computation.d.ts +21 -18
  224. package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -1
  225. package/lib/langium/scope/hydranium-scope-computation.js +38 -39
  226. package/lib/langium/scope/hydranium-scope-computation.js.map +1 -1
  227. package/lib/langium/scope/hydranium-scope-provider.d.ts +122 -32
  228. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  229. package/lib/langium/scope/hydranium-scope-provider.js +217 -39
  230. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  231. package/lib/langium/scope/index.d.ts +1 -0
  232. package/lib/langium/scope/index.d.ts.map +1 -1
  233. package/lib/langium/scope/index.js +1 -0
  234. package/lib/langium/scope/index.js.map +1 -1
  235. package/lib/langium/scope/reference-builder.d.ts.map +1 -1
  236. package/lib/langium/scope/reference-builder.js.map +1 -1
  237. package/lib/langium/scope/reference-candidate-provider.d.ts +6 -5
  238. package/lib/langium/scope/reference-candidate-provider.d.ts.map +1 -1
  239. package/lib/langium/scope/reference-candidate-provider.js +3 -2
  240. package/lib/langium/scope/reference-candidate-provider.js.map +1 -1
  241. package/lib/langium/scope/scope-extension-service.d.ts +69 -37
  242. package/lib/langium/scope/scope-extension-service.d.ts.map +1 -1
  243. package/lib/langium/scope/scope-extension-service.js +102 -29
  244. package/lib/langium/scope/scope-extension-service.js.map +1 -1
  245. package/lib/langium/scope/scoped-ast-node-description.d.ts +1 -3
  246. package/lib/langium/scope/scoped-ast-node-description.d.ts.map +1 -1
  247. package/lib/langium/scope/scoped-ast-node-description.js +1 -3
  248. package/lib/langium/scope/scoped-ast-node-description.js.map +1 -1
  249. package/lib/langium/serialization/abstract-serializer.d.ts +38 -27
  250. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  251. package/lib/langium/serialization/abstract-serializer.js +54 -30
  252. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  253. package/lib/langium/serialization/json-serializer.d.ts +8 -1
  254. package/lib/langium/serialization/json-serializer.d.ts.map +1 -1
  255. package/lib/langium/serialization/json-serializer.js +13 -3
  256. package/lib/langium/serialization/json-serializer.js.map +1 -1
  257. package/lib/langium/serialization/serializer.d.ts +1 -1
  258. package/lib/langium/serialization/yaml-serializer.d.ts +12 -0
  259. package/lib/langium/serialization/yaml-serializer.d.ts.map +1 -1
  260. package/lib/langium/serialization/yaml-serializer.js +24 -4
  261. package/lib/langium/serialization/yaml-serializer.js.map +1 -1
  262. package/lib/langium/service-registry.d.ts +29 -1
  263. package/lib/langium/service-registry.d.ts.map +1 -1
  264. package/lib/langium/service-registry.js +37 -0
  265. package/lib/langium/service-registry.js.map +1 -1
  266. package/lib/langium/shared-services.d.ts +10 -4
  267. package/lib/langium/shared-services.d.ts.map +1 -1
  268. package/lib/langium/shared-services.js.map +1 -1
  269. package/lib/langium/single-langium-copy.d.ts +22 -0
  270. package/lib/langium/single-langium-copy.d.ts.map +1 -0
  271. package/lib/langium/single-langium-copy.js +36 -0
  272. package/lib/langium/single-langium-copy.js.map +1 -0
  273. package/lib/langium/transfer/transfer-encoder.d.ts +60 -39
  274. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  275. package/lib/langium/transfer/transfer-encoder.js +35 -24
  276. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  277. package/lib/langium/trivia/comment-preserver.d.ts +543 -0
  278. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  279. package/lib/langium/trivia/comment-preserver.js +1165 -0
  280. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  281. package/lib/langium/trivia/document-ending-preserver.d.ts +51 -0
  282. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  283. package/lib/langium/trivia/document-ending-preserver.js +62 -0
  284. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  285. package/lib/langium/trivia/index.d.ts +14 -0
  286. package/lib/langium/trivia/index.d.ts.map +1 -0
  287. package/lib/langium/trivia/index.js +14 -0
  288. package/lib/langium/trivia/index.js.map +1 -0
  289. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  290. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  291. package/lib/langium/trivia/trivia-contribution.js +10 -0
  292. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  293. package/lib/langium/trivia/trivia-preserver.d.ts +52 -0
  294. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  295. package/lib/langium/trivia/trivia-preserver.js +10 -0
  296. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  297. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  298. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  299. package/lib/langium/trivia/trivia-service.js +56 -0
  300. package/lib/langium/trivia/trivia-service.js.map +1 -0
  301. package/lib/langium/update-rewrite/normalize-empty-strings.d.ts.map +1 -1
  302. package/lib/langium/update-rewrite/update-rewrite-service.d.ts +1 -1
  303. package/lib/langium/update-rewrite/update-rewrite-service.d.ts.map +1 -1
  304. package/lib/langium/update-rewrite/update-rewrite-service.js.map +1 -1
  305. package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
  306. package/lib/langium/validation/document-validator.d.ts +168 -23
  307. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  308. package/lib/langium/validation/document-validator.js +264 -26
  309. package/lib/langium/validation/document-validator.js.map +1 -1
  310. package/lib/langium/validation/validation-contribution-collector.d.ts +9 -1
  311. package/lib/langium/validation/validation-contribution-collector.d.ts.map +1 -1
  312. package/lib/langium/validation/validation-contribution-collector.js +1 -1
  313. package/lib/langium/validation/validation-contribution-collector.js.map +1 -1
  314. package/lib/langium/workspace/ast-node-locator.d.ts +24 -0
  315. package/lib/langium/workspace/ast-node-locator.d.ts.map +1 -0
  316. package/lib/langium/workspace/ast-node-locator.js +32 -0
  317. package/lib/langium/workspace/ast-node-locator.js.map +1 -0
  318. package/lib/langium/workspace/document-uri-policy.d.ts +24 -15
  319. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  320. package/lib/langium/workspace/document-uri-policy.js +22 -13
  321. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  322. package/lib/langium/workspace/file-not-found.d.ts +23 -0
  323. package/lib/langium/workspace/file-not-found.d.ts.map +1 -0
  324. package/lib/langium/workspace/file-not-found.js +31 -0
  325. package/lib/langium/workspace/file-not-found.js.map +1 -0
  326. package/lib/langium/workspace/file-system-provider.d.ts +167 -8
  327. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  328. package/lib/langium/workspace/file-system-provider.js +115 -13
  329. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  330. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +78 -4
  331. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  332. package/lib/langium/workspace/hydranium-langium-document-factory.js +188 -5
  333. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  334. package/lib/langium/workspace/hydranium-workspace-lock.d.ts +50 -1
  335. package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -1
  336. package/lib/langium/workspace/hydranium-workspace-lock.js +73 -1
  337. package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
  338. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +105 -21
  339. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  340. package/lib/langium/workspace/hydranium-workspace-manager.js +186 -31
  341. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  342. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +26 -9
  343. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  344. package/lib/langium/workspace/in-memory-file-system-provider.js +45 -22
  345. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  346. package/lib/langium/workspace/index-manager.d.ts.map +1 -1
  347. package/lib/langium/workspace/index-manager.js.map +1 -1
  348. package/lib/langium/workspace/index.d.ts +3 -0
  349. package/lib/langium/workspace/index.d.ts.map +1 -1
  350. package/lib/langium/workspace/index.js +3 -0
  351. package/lib/langium/workspace/index.js.map +1 -1
  352. package/lib/langium/workspace/initialize-workspace.d.ts +40 -3
  353. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  354. package/lib/langium/workspace/initialize-workspace.js +68 -3
  355. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  356. package/lib/langium/workspace/langium-documents.d.ts +7 -0
  357. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  358. package/lib/langium/workspace/langium-documents.js +18 -1
  359. package/lib/langium/workspace/langium-documents.js.map +1 -1
  360. package/lib/langium/workspace/persistent-file-system-provider.d.ts +16 -10
  361. package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
  362. package/lib/langium/workspace/persistent-file-system-provider.js +25 -13
  363. package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
  364. package/lib/{documents → langium/workspace}/self-save-registry.d.ts +7 -9
  365. package/lib/langium/workspace/self-save-registry.d.ts.map +1 -0
  366. package/lib/{documents → langium/workspace}/self-save-registry.js +9 -11
  367. package/lib/langium/workspace/self-save-registry.js.map +1 -0
  368. package/lib/langium/workspace/syntax-errors.d.ts +23 -0
  369. package/lib/langium/workspace/syntax-errors.d.ts.map +1 -0
  370. package/lib/langium/workspace/syntax-errors.js +29 -0
  371. package/lib/langium/workspace/syntax-errors.js.map +1 -0
  372. package/lib/langium/workspace/virtual-document.d.ts +55 -21
  373. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  374. package/lib/langium/workspace/virtual-document.js +87 -24
  375. package/lib/langium/workspace/virtual-document.js.map +1 -1
  376. package/lib/langium/workspace/write-lock-scope.d.ts +22 -10
  377. package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
  378. package/lib/langium/workspace/write-lock-scope.js +15 -5
  379. package/lib/langium/workspace/write-lock-scope.js.map +1 -1
  380. package/lib/launcher/integrated-server.d.ts +3 -1
  381. package/lib/launcher/integrated-server.d.ts.map +1 -1
  382. package/lib/locale/server-locale.d.ts +6 -6
  383. package/lib/locale/server-locale.d.ts.map +1 -1
  384. package/lib/locale/server-locale.js.map +1 -1
  385. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  386. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  387. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  388. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  389. package/lib/lsp/connection-features.d.ts +23 -0
  390. package/lib/lsp/connection-features.d.ts.map +1 -0
  391. package/lib/lsp/connection-features.js +60 -0
  392. package/lib/lsp/connection-features.js.map +1 -0
  393. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  394. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  395. package/lib/lsp/diagnostics-connection.js +53 -0
  396. package/lib/lsp/diagnostics-connection.js.map +1 -0
  397. package/lib/lsp/edit-requests-guard.d.ts +31 -0
  398. package/lib/lsp/edit-requests-guard.d.ts.map +1 -0
  399. package/lib/lsp/edit-requests-guard.js +125 -0
  400. package/lib/lsp/edit-requests-guard.js.map +1 -0
  401. package/lib/lsp/hydranium-document-update-handler.d.ts +126 -82
  402. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  403. package/lib/lsp/hydranium-document-update-handler.js +234 -101
  404. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  405. package/lib/lsp/index.d.ts +6 -2
  406. package/lib/lsp/index.d.ts.map +1 -1
  407. package/lib/lsp/index.js +6 -2
  408. package/lib/lsp/index.js.map +1 -1
  409. package/lib/lsp/lsp-latency.d.ts +39 -0
  410. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  411. package/lib/lsp/lsp-latency.js +58 -0
  412. package/lib/lsp/lsp-latency.js.map +1 -0
  413. package/lib/lsp/save-text-document.d.ts +31 -0
  414. package/lib/lsp/save-text-document.d.ts.map +1 -0
  415. package/lib/lsp/save-text-document.js +52 -0
  416. package/lib/lsp/save-text-document.js.map +1 -0
  417. package/lib/lsp/semantic-token-provider.d.ts +58 -2
  418. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  419. package/lib/lsp/semantic-token-provider.js +98 -9
  420. package/lib/lsp/semantic-token-provider.js.map +1 -1
  421. package/lib/lsp/shared-module.d.ts +4 -8
  422. package/lib/lsp/shared-module.d.ts.map +1 -1
  423. package/lib/lsp/shared-module.js.map +1 -1
  424. package/lib/lsp/start-language-server.d.ts +12 -7
  425. package/lib/lsp/start-language-server.d.ts.map +1 -1
  426. package/lib/lsp/start-language-server.js +21 -7
  427. package/lib/lsp/start-language-server.js.map +1 -1
  428. package/lib/messages/carriers.d.ts +4 -6
  429. package/lib/messages/carriers.d.ts.map +1 -1
  430. package/lib/messages/carriers.js +4 -6
  431. package/lib/messages/carriers.js.map +1 -1
  432. package/lib/messages/index.d.ts +2 -2
  433. package/lib/messages/index.d.ts.map +1 -1
  434. package/lib/messages/index.js +2 -2
  435. package/lib/messages/index.js.map +1 -1
  436. package/lib/messages/renderer.d.ts +10 -4
  437. package/lib/messages/renderer.d.ts.map +1 -1
  438. package/lib/messages/renderer.js +6 -0
  439. package/lib/messages/renderer.js.map +1 -1
  440. package/lib/node/event-loop-monitor.d.ts.map +1 -1
  441. package/lib/node/event-loop-monitor.js +3 -2
  442. package/lib/node/event-loop-monitor.js.map +1 -1
  443. package/lib/node/heap-ceiling.d.ts +61 -0
  444. package/lib/node/heap-ceiling.d.ts.map +1 -0
  445. package/lib/node/heap-ceiling.js +72 -0
  446. package/lib/node/heap-ceiling.js.map +1 -0
  447. package/lib/node/index.d.ts +1 -1
  448. package/lib/node/index.d.ts.map +1 -1
  449. package/lib/node/index.js +5 -1
  450. package/lib/node/index.js.map +1 -1
  451. package/lib/node/latency-from-env.d.ts +1 -1
  452. package/lib/node/latency-from-env.js +1 -1
  453. package/lib/node/lint-grammar.js.map +1 -1
  454. package/lib/node/log-file-sink.d.ts +3 -2
  455. package/lib/node/log-file-sink.d.ts.map +1 -1
  456. package/lib/node/log-file-sink.js +6 -2
  457. package/lib/node/log-file-sink.js.map +1 -1
  458. package/lib/node/log-preamble-node.d.ts +5 -3
  459. package/lib/node/log-preamble-node.d.ts.map +1 -1
  460. package/lib/node/log-preamble-node.js +13 -10
  461. package/lib/node/log-preamble-node.js.map +1 -1
  462. package/lib/node/measure-memory.d.ts.map +1 -1
  463. package/lib/node/measure-memory.js +5 -4
  464. package/lib/node/measure-memory.js.map +1 -1
  465. package/lib/node/memory-monitor.d.ts +2 -2
  466. package/lib/node/memory-monitor.d.ts.map +1 -1
  467. package/lib/node/memory-monitor.js +9 -10
  468. package/lib/node/memory-monitor.js.map +1 -1
  469. package/lib/node/node-file-system-provider.d.ts +67 -9
  470. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  471. package/lib/node/node-file-system-provider.js +198 -53
  472. package/lib/node/node-file-system-provider.js.map +1 -1
  473. package/lib/node/profile-capture.d.ts +12 -4
  474. package/lib/node/profile-capture.d.ts.map +1 -1
  475. package/lib/node/profile-capture.js +8 -6
  476. package/lib/node/profile-capture.js.map +1 -1
  477. package/lib/node/profile-digest.js.map +1 -1
  478. package/lib/node/profiling-run.d.ts +13 -3
  479. package/lib/node/profiling-run.d.ts.map +1 -1
  480. package/lib/node/profiling-run.js +17 -8
  481. package/lib/node/profiling-run.js.map +1 -1
  482. package/lib/node/rename-over-open-readers.d.ts +61 -0
  483. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  484. package/lib/node/rename-over-open-readers.js +80 -0
  485. package/lib/node/rename-over-open-readers.js.map +1 -0
  486. package/lib/node/server-diagnostics.js +1 -1
  487. package/lib/node/server-diagnostics.js.map +1 -1
  488. package/lib/node/server-state-snapshot.d.ts +6 -5
  489. package/lib/node/server-state-snapshot.d.ts.map +1 -1
  490. package/lib/node/server-state-snapshot.js +20 -17
  491. package/lib/node/server-state-snapshot.js.map +1 -1
  492. package/lib/node/socket-launcher.d.ts +43 -7
  493. package/lib/node/socket-launcher.d.ts.map +1 -1
  494. package/lib/node/socket-launcher.js +42 -7
  495. package/lib/node/socket-launcher.js.map +1 -1
  496. package/lib/node/startup-cpu-profile.d.ts +29 -0
  497. package/lib/node/startup-cpu-profile.d.ts.map +1 -0
  498. package/lib/node/startup-cpu-profile.js +91 -0
  499. package/lib/node/startup-cpu-profile.js.map +1 -0
  500. package/lib/node/stdio-launcher.d.ts +13 -13
  501. package/lib/node/stdio-launcher.d.ts.map +1 -1
  502. package/lib/node/stdio-launcher.js +21 -11
  503. package/lib/node/stdio-launcher.js.map +1 -1
  504. package/lib/node/validate-workspace.d.ts +1 -1
  505. package/lib/node/validate-workspace.d.ts.map +1 -1
  506. package/lib/node/validate-workspace.js +5 -1
  507. package/lib/node/validate-workspace.js.map +1 -1
  508. package/lib/testing/document-uri-policy-conformance.js +1 -1
  509. package/lib/testing/document-uri-policy-conformance.js.map +1 -1
  510. package/lib/testing/fake-description.js.map +1 -1
  511. package/lib/testing/fake-document.d.ts +2 -1
  512. package/lib/testing/fake-document.d.ts.map +1 -1
  513. package/lib/testing/fake-document.js.map +1 -1
  514. package/lib/testing/index.d.ts +1 -1
  515. package/lib/testing/index.d.ts.map +1 -1
  516. package/lib/testing/index.js +2 -1
  517. package/lib/testing/index.js.map +1 -1
  518. package/lib/testing/langium-test-helpers.d.ts.map +1 -1
  519. package/lib/testing/langium-test-helpers.js +1 -0
  520. package/lib/testing/langium-test-helpers.js.map +1 -1
  521. package/lib/testing/make-noop-language-services.js.map +1 -1
  522. package/lib/testing/make-noop-shared-services.js.map +1 -1
  523. package/lib/testing/make-test-services.d.ts +45 -14
  524. package/lib/testing/make-test-services.d.ts.map +1 -1
  525. package/lib/testing/make-test-services.js +23 -7
  526. package/lib/testing/make-test-services.js.map +1 -1
  527. package/lib/testing/make-test-tracer.d.ts +12 -0
  528. package/lib/testing/make-test-tracer.d.ts.map +1 -1
  529. package/lib/testing/make-test-tracer.js +13 -0
  530. package/lib/testing/make-test-tracer.js.map +1 -1
  531. package/lib/testing/node/applied-edit-capture.d.ts +29 -0
  532. package/lib/testing/node/applied-edit-capture.d.ts.map +1 -0
  533. package/lib/testing/node/applied-edit-capture.js +117 -0
  534. package/lib/testing/node/applied-edit-capture.js.map +1 -0
  535. package/lib/testing/node/golden-corpus.js.map +1 -1
  536. package/lib/testing/node/index.d.ts +4 -1
  537. package/lib/testing/node/index.d.ts.map +1 -1
  538. package/lib/testing/node/index.js +4 -1
  539. package/lib/testing/node/index.js.map +1 -1
  540. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  541. package/lib/testing/node/lsp-harness.js +2 -0
  542. package/lib/testing/node/lsp-harness.js.map +1 -1
  543. package/lib/testing/node/lsp-server-connection.d.ts +21 -6
  544. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  545. package/lib/testing/node/lsp-server-connection.js +49 -106
  546. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  547. package/lib/testing/node/scratch-workspace.d.ts +2 -1
  548. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  549. package/lib/testing/node/scratch-workspace.js +12 -6
  550. package/lib/testing/node/scratch-workspace.js.map +1 -1
  551. package/lib/testing/node/settle-server.d.ts +33 -0
  552. package/lib/testing/node/settle-server.d.ts.map +1 -0
  553. package/lib/testing/node/settle-server.js +84 -0
  554. package/lib/testing/node/settle-server.js.map +1 -0
  555. package/lib/testing/node/spawned-server.d.ts +30 -1
  556. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  557. package/lib/testing/node/spawned-server.js +39 -2
  558. package/lib/testing/node/spawned-server.js.map +1 -1
  559. package/lib/testing/playwright/browser-capture-bridge.js.map +1 -1
  560. package/lib/testing/playwright/e2e-profiling.js.map +1 -1
  561. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  562. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  563. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  564. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  565. package/lib/testing/playwright/index.d.ts +1 -0
  566. package/lib/testing/playwright/index.d.ts.map +1 -1
  567. package/lib/testing/playwright/index.js +1 -0
  568. package/lib/testing/playwright/index.js.map +1 -1
  569. package/lib/testing/playwright/server-log-capture.d.ts +46 -16
  570. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  571. package/lib/testing/playwright/server-log-capture.js +111 -40
  572. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  573. package/lib/testing/playwright/server-log-fixture.d.ts +16 -0
  574. package/lib/testing/playwright/server-log-fixture.d.ts.map +1 -1
  575. package/lib/testing/playwright/server-log-fixture.js +25 -6
  576. package/lib/testing/playwright/server-log-fixture.js.map +1 -1
  577. package/lib/testing/playwright/server-log-region.d.ts +15 -0
  578. package/lib/testing/playwright/server-log-region.d.ts.map +1 -0
  579. package/lib/testing/playwright/server-log-region.js +30 -0
  580. package/lib/testing/playwright/server-log-region.js.map +1 -0
  581. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  582. package/lib/testing/playwright/server-log-rename-reporter.js +4 -3
  583. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  584. package/lib/testing/run-update-pipeline.d.ts +8 -5
  585. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  586. package/lib/testing/run-update-pipeline.js.map +1 -1
  587. package/lib/testing/stub-ast-document-manager.d.ts +11 -51
  588. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  589. package/lib/testing/stub-ast-document-manager.js +31 -59
  590. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  591. package/lib/testing/stub-document-builder.d.ts +25 -11
  592. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  593. package/lib/testing/stub-document-builder.js +91 -2
  594. package/lib/testing/stub-document-builder.js.map +1 -1
  595. package/lib/testing/stub-hydranium-text-documents.d.ts +33 -10
  596. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  597. package/lib/testing/stub-hydranium-text-documents.js +132 -16
  598. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  599. package/lib/testing/stub-index-manager.js.map +1 -1
  600. package/lib/testing/stub-langium-documents.d.ts +3 -2
  601. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  602. package/lib/testing/stub-langium-documents.js.map +1 -1
  603. package/lib/testing/stub-model-service.d.ts +8 -7
  604. package/lib/testing/stub-model-service.d.ts.map +1 -1
  605. package/lib/testing/stub-model-service.js +4 -4
  606. package/lib/testing/stub-model-service.js.map +1 -1
  607. package/lib/testing/stub-project-manager.d.ts.map +1 -1
  608. package/lib/testing/stub-project-manager.js +10 -0
  609. package/lib/testing/stub-project-manager.js.map +1 -1
  610. package/lib/testing/stub-self-save-registry.d.ts +1 -1
  611. package/lib/testing/stub-self-save-registry.d.ts.map +1 -1
  612. package/lib/testing/stub-service-registry.d.ts +1 -1
  613. package/lib/testing/stub-service-registry.js.map +1 -1
  614. package/lib/testing/stub-writable-file-system.d.ts +1 -1
  615. package/lib/testing/stub-writable-file-system.d.ts.map +1 -1
  616. package/lib/testing/stub-writable-file-system.js +2 -1
  617. package/lib/testing/stub-writable-file-system.js.map +1 -1
  618. package/lib/util/connection-liveness.d.ts +22 -6
  619. package/lib/util/connection-liveness.d.ts.map +1 -1
  620. package/lib/util/connection-liveness.js +75 -15
  621. package/lib/util/connection-liveness.js.map +1 -1
  622. package/lib/util/environment.d.ts +7 -0
  623. package/lib/util/environment.d.ts.map +1 -1
  624. package/lib/util/environment.js +15 -1
  625. package/lib/util/environment.js.map +1 -1
  626. package/lib/util/registry.d.ts.map +1 -1
  627. package/package.json +41 -60
  628. package/src/browser/host-heads-in-worker.ts +218 -0
  629. package/src/browser/index.ts +17 -0
  630. package/src/browser/indexeddb-file-system-store.ts +192 -0
  631. package/src/browser/start-language-server-in-worker.ts +56 -0
  632. package/src/documents/ast-document-manager.ts +477 -250
  633. package/src/documents/client-ids.ts +5 -22
  634. package/src/documents/client-session-errors.ts +26 -0
  635. package/src/documents/client-session-registry.ts +262 -0
  636. package/src/documents/dirty-state-tracker.ts +167 -0
  637. package/src/documents/document-release-handler.ts +404 -0
  638. package/src/documents/document-release-scheduler.ts +121 -0
  639. package/src/documents/file-system-task-queue.ts +77 -0
  640. package/src/documents/hydranium-text-documents.ts +1044 -615
  641. package/src/documents/index.ts +10 -2
  642. package/src/documents/language-client-shadow.ts +501 -0
  643. package/src/documents/model-ledger.ts +66 -0
  644. package/src/documents/text-ledger.ts +112 -0
  645. package/src/documents/version-sync-service.ts +304 -0
  646. package/src/index.ts +3 -0
  647. package/src/langium/ast-extension/ast-extension-service.ts +77 -41
  648. package/src/langium/ast-extension/ast-node-builder.ts +35 -13
  649. package/src/langium/bootstrap.ts +79 -43
  650. package/src/langium/build-phase-pass/build-phase-pass-service.ts +29 -19
  651. package/src/langium/build-phase-pass/build-phase-pass.ts +37 -8
  652. package/src/langium/composite-ast-reflection.ts +112 -0
  653. package/src/langium/config/configuration-provider.ts +119 -0
  654. package/src/langium/config/index.ts +1 -0
  655. package/src/langium/diagnostics/console-tee.ts +150 -0
  656. package/src/langium/diagnostics/hydranium-langium-profiler.ts +15 -39
  657. package/src/langium/diagnostics/log-preamble.ts +1 -1
  658. package/src/langium/diagnostics/logger.ts +51 -17
  659. package/src/langium/diagnostics/lsp-logger.ts +83 -45
  660. package/src/langium/document-builder/build-pipeline-integration.ts +57 -32
  661. package/src/langium/document-builder/build-session.ts +4 -3
  662. package/src/langium/document-builder/document-builder.ts +773 -106
  663. package/src/langium/integration-services.ts +20 -23
  664. package/src/langium/integrity/integrity-rule.ts +22 -8
  665. package/src/langium/integrity/integrity-service.ts +656 -90
  666. package/src/langium/keys/containment.ts +3 -2
  667. package/src/langium/keys/name-based-key-provider.ts +24 -12
  668. package/src/langium/language-module.ts +52 -9
  669. package/src/langium/model-service/client-session.ts +769 -0
  670. package/src/langium/model-service/index.ts +2 -0
  671. package/src/langium/model-service/model-events.ts +82 -0
  672. package/src/langium/model-service/model-service.ts +885 -532
  673. package/src/langium/module.ts +141 -65
  674. package/src/langium/naming/name-provider.ts +34 -8
  675. package/src/langium/project/abstract-project-manager.ts +409 -20
  676. package/src/langium/project/project-change-event.ts +25 -3
  677. package/src/langium/project/project-manager.ts +31 -2
  678. package/src/langium/project/single-project-manager.ts +16 -1
  679. package/src/langium/residency/cst-residency-service.ts +210 -87
  680. package/src/langium/scope/ast-node-description-provider.ts +111 -16
  681. package/src/langium/scope/built-local-symbols.ts +13 -0
  682. package/src/langium/scope/fallback-scope.ts +38 -0
  683. package/src/langium/scope/hydranium-scope-computation.ts +45 -44
  684. package/src/langium/scope/hydranium-scope-provider.ts +245 -41
  685. package/src/langium/scope/index.ts +1 -0
  686. package/src/langium/scope/reference-candidate-provider.ts +6 -5
  687. package/src/langium/scope/scope-extension-service.ts +148 -56
  688. package/src/langium/scope/scoped-ast-node-description.ts +1 -3
  689. package/src/langium/serialization/abstract-serializer.ts +57 -41
  690. package/src/langium/serialization/json-serializer.ts +14 -3
  691. package/src/langium/serialization/serializer.ts +1 -1
  692. package/src/langium/serialization/yaml-serializer.ts +26 -4
  693. package/src/langium/service-registry.ts +41 -2
  694. package/src/langium/shared-services.ts +13 -4
  695. package/src/langium/single-langium-copy.ts +42 -0
  696. package/src/langium/transfer/transfer-encoder.ts +76 -50
  697. package/src/langium/trivia/comment-preserver.ts +1410 -0
  698. package/src/langium/trivia/document-ending-preserver.ts +71 -0
  699. package/src/langium/trivia/index.ts +14 -0
  700. package/src/langium/trivia/trivia-contribution.ts +39 -0
  701. package/src/langium/trivia/trivia-preserver.ts +56 -0
  702. package/src/langium/trivia/trivia-service.ts +99 -0
  703. package/src/langium/update-rewrite/update-rewrite-service.ts +1 -1
  704. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  705. package/src/langium/validation/document-validator.ts +312 -31
  706. package/src/langium/validation/validation-contribution-collector.ts +10 -1
  707. package/src/langium/workspace/ast-node-locator.ts +33 -0
  708. package/src/langium/workspace/document-uri-policy.ts +32 -18
  709. package/src/langium/workspace/file-not-found.ts +35 -0
  710. package/src/langium/workspace/file-system-provider.ts +258 -16
  711. package/src/langium/workspace/hydranium-langium-document-factory.ts +239 -4
  712. package/src/langium/workspace/hydranium-workspace-lock.ts +101 -2
  713. package/src/langium/workspace/hydranium-workspace-manager.ts +214 -35
  714. package/src/langium/workspace/in-memory-file-system-provider.ts +54 -23
  715. package/src/langium/workspace/index.ts +3 -0
  716. package/src/langium/workspace/initialize-workspace.ts +96 -4
  717. package/src/langium/workspace/langium-documents.ts +19 -1
  718. package/src/langium/workspace/persistent-file-system-provider.ts +27 -15
  719. package/src/{documents → langium/workspace}/self-save-registry.ts +12 -14
  720. package/src/langium/workspace/syntax-errors.ts +34 -0
  721. package/src/langium/workspace/virtual-document.ts +106 -25
  722. package/src/langium/workspace/write-lock-scope.ts +27 -12
  723. package/src/launcher/integrated-server.ts +3 -1
  724. package/src/locale/server-locale.ts +6 -6
  725. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  726. package/src/lsp/connection-features.ts +67 -0
  727. package/src/lsp/diagnostics-connection.ts +56 -0
  728. package/src/lsp/edit-requests-guard.ts +152 -0
  729. package/src/lsp/hydranium-document-update-handler.ts +284 -110
  730. package/src/lsp/index.ts +6 -2
  731. package/src/lsp/lsp-latency.ts +62 -0
  732. package/src/lsp/save-text-document.ts +63 -0
  733. package/src/lsp/semantic-token-provider.ts +146 -8
  734. package/src/lsp/shared-module.ts +4 -8
  735. package/src/lsp/start-language-server.ts +21 -7
  736. package/src/messages/carriers.ts +4 -6
  737. package/src/messages/index.ts +11 -2
  738. package/src/messages/renderer.ts +11 -3
  739. package/src/node/event-loop-monitor.ts +3 -4
  740. package/src/node/heap-ceiling.ts +108 -0
  741. package/src/node/index.ts +5 -1
  742. package/src/node/latency-from-env.ts +1 -1
  743. package/src/node/log-file-sink.ts +6 -2
  744. package/src/node/log-preamble-node.ts +20 -10
  745. package/src/node/measure-memory.ts +5 -4
  746. package/src/node/memory-monitor.ts +10 -10
  747. package/src/node/node-file-system-provider.ts +214 -57
  748. package/src/node/profile-capture.ts +16 -15
  749. package/src/node/profiling-run.ts +30 -8
  750. package/src/node/rename-over-open-readers.ts +94 -0
  751. package/src/node/server-diagnostics.ts +1 -1
  752. package/src/node/server-state-snapshot.ts +28 -20
  753. package/src/node/socket-launcher.ts +78 -14
  754. package/src/node/startup-cpu-profile.ts +94 -0
  755. package/src/node/stdio-launcher.ts +29 -19
  756. package/src/node/validate-workspace.ts +8 -2
  757. package/src/testing/document-uri-policy-conformance.ts +1 -1
  758. package/src/testing/fake-document.ts +2 -1
  759. package/src/testing/index.ts +2 -1
  760. package/src/testing/langium-test-helpers.ts +1 -0
  761. package/src/testing/make-test-services.ts +83 -31
  762. package/src/testing/make-test-tracer.ts +26 -0
  763. package/src/testing/node/applied-edit-capture.ts +141 -0
  764. package/src/testing/node/index.ts +4 -1
  765. package/src/testing/node/lsp-harness.ts +2 -0
  766. package/src/testing/node/lsp-server-connection.ts +83 -119
  767. package/src/testing/node/scratch-workspace.ts +12 -6
  768. package/src/testing/node/settle-server.ts +89 -0
  769. package/src/testing/node/spawned-server.ts +57 -3
  770. package/src/testing/parse-semantic-root.ts +1 -1
  771. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  772. package/src/testing/playwright/index.ts +1 -0
  773. package/src/testing/playwright/server-log-capture.ts +137 -47
  774. package/src/testing/playwright/server-log-fixture.ts +46 -6
  775. package/src/testing/playwright/server-log-region.ts +32 -0
  776. package/src/testing/playwright/server-log-rename-reporter.ts +4 -3
  777. package/src/testing/run-update-pipeline.ts +8 -5
  778. package/src/testing/stub-ast-document-manager.ts +47 -123
  779. package/src/testing/stub-document-builder.ts +128 -15
  780. package/src/testing/stub-hydranium-text-documents.ts +184 -27
  781. package/src/testing/stub-langium-documents.ts +3 -2
  782. package/src/testing/stub-model-service.ts +9 -8
  783. package/src/testing/stub-project-manager.ts +10 -0
  784. package/src/testing/stub-self-save-registry.ts +1 -1
  785. package/src/testing/stub-service-registry.ts +1 -1
  786. package/src/testing/stub-writable-file-system.ts +3 -2
  787. package/src/util/connection-liveness.ts +81 -15
  788. package/src/util/environment.ts +24 -2
  789. package/lib/documents/language-client-text-shadow.d.ts +0 -124
  790. package/lib/documents/language-client-text-shadow.d.ts.map +0 -1
  791. package/lib/documents/language-client-text-shadow.js +0 -190
  792. package/lib/documents/language-client-text-shadow.js.map +0 -1
  793. package/lib/documents/self-save-registry.d.ts.map +0 -1
  794. package/lib/documents/self-save-registry.js.map +0 -1
  795. package/lib/lsp/instrument-connection.d.ts +0 -36
  796. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  797. package/lib/lsp/instrument-connection.js +0 -62
  798. package/lib/lsp/instrument-connection.js.map +0 -1
  799. package/lib/node/process-memory.d.ts +0 -66
  800. package/lib/node/process-memory.d.ts.map +0 -1
  801. package/lib/node/process-memory.js +0 -252
  802. package/lib/node/process-memory.js.map +0 -1
  803. package/src/documents/language-client-text-shadow.ts +0 -211
  804. package/src/lsp/instrument-connection.ts +0 -66
  805. package/src/node/process-memory.ts +0 -299
@@ -6,9 +6,12 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import { isPromiseLike, Logger } from '@hydranium/protocol';
10
- import { AstUtils, DocumentState, interruptAndCheck, UriUtils } from '@hydranium/langium';
9
+ import { describeErrorWithStack, isPromiseLike, Logger, textHash } from '@hydranium/protocol';
10
+ import { AstUtils, DocumentState, interruptAndCheck, isMultiReference, isOperationCancelled, UriUtils } from '@hydranium/langium';
11
+ import { CancellationToken } from 'vscode-languageserver';
11
12
  import { Registry } from '../../util/registry.js';
13
+ import { hasSkippedText } from '../workspace/syntax-errors.js';
14
+ import { isVirtualUri } from '../workspace/virtual-document.js';
12
15
  import { IntegrityPhase } from './integrity-rule.js';
13
16
  export var IntegrityService;
14
17
  (function (IntegrityService) {
@@ -23,11 +26,21 @@ export var IntegrityService;
23
26
  * at `DocumentState.Validated` and delivered separately via `publishDiagnostics`
24
27
  * or the `onModelUpdated` event.
25
28
  *
29
+ * A document whose parse lacks some of its text reaches this state
30
+ * unrepaired: no rule runs on it until it parses.
31
+ *
26
32
  * The AST is what this guarantees, not the text. For a closed document whose
27
33
  * repair was STAGED rather than written (`'editor'` sync mode),
28
34
  * `textDocument` still mirrors disk — read the repair off the AST, or from
29
35
  * the staged content the next open consumes.
30
36
  *
37
+ * A `Linked`-phase repair settles the documents that reference the repaired
38
+ * one in the same build against what the repair replaced. A build after it
39
+ * relinks them, and they settle again. Past `maxRelinkChain` such builds in
40
+ * a row they keep stale links. A closed `'editor'` document whose CST was
41
+ * shed re-reads its unrepaired text in that build and repeats the repair,
42
+ * so they resolve a renamed element by its old name until the next change.
43
+ *
31
44
  * Invariant: integrity rules only register at Parsed or Linked, so the build
32
45
  * is guaranteed post-integrity once it advances past `onBuildPhase(Linked)`
33
46
  * into `IndexedReferences`. {@link IntegrityService.register} enforces this at
@@ -36,6 +49,15 @@ export var IntegrityService;
36
49
  */
37
50
  IntegrityService.SettledState = DocumentState.IndexedReferences;
38
51
  })(IntegrityService || (IntegrityService = {}));
52
+ /**
53
+ * The shared services of each server that has logged its first silent
54
+ * write-back. Every language of a server holds the same shared services, so
55
+ * keying on them makes {@link DefaultIntegrityService.noteSilentWrite} report once
56
+ * per server rather than once per language.
57
+ */
58
+ const silentWriteReported = new WeakSet();
59
+ /** The running builds that relink dependents, per server, because a repair in one language can extend a chain in another. */
60
+ const runningRelinksByServer = new WeakMap();
39
61
  /**
40
62
  * Default {@link IntegrityService} implementation. Enforces rules,
41
63
  * resyncs document text after mutations, and propagates corrections.
@@ -60,13 +82,14 @@ export class DefaultIntegrityService {
60
82
  /** One logger per rule, created at registration and dropped on unregister. */
61
83
  ruleLoggers = new Map();
62
84
  /**
63
- * Rules bucketed by the phase they target, preserving the registry's
64
- * priority order within each bucket. Rebuilt lazily on the first
65
- * `enforceIntegrity` call after a registry mutation — staleness is detected
66
- * via reference identity of the registry's cached array (see
67
- * {@link Registry.all}). Filtering the rule list per call would instead
68
- * allocate one transient array per document per phase, which a workspace
69
- * whose documents all settle at the same phase pays on every build.
85
+ * Rules bucketed by the phase they target, then by each `$type` from
86
+ * {@link nodeTypesFor}, preserving the registry's priority order within
87
+ * each bucket. Rebuilt lazily on the first `enforceIntegrity` call after a
88
+ * registry mutation — staleness is detected via reference identity of the
89
+ * registry's cached array (see {@link Registry.all}). Filtering the rule
90
+ * list per call would instead allocate one transient array per document per
91
+ * phase, which a workspace whose documents all settle at the same phase pays
92
+ * on every build.
70
93
  */
71
94
  phaseBuckets = new Map();
72
95
  phaseBucketsFor;
@@ -77,11 +100,28 @@ export class DefaultIntegrityService {
77
100
  tracer;
78
101
  /**
79
102
  * The framework document builder, used to re-parse + re-link a document in place after a
80
- * Linked-phase correction (see {@link resyncDocument}). The framework binds
81
- * `shared.workspace.DocumentBuilder` to {@link HydraniumDocumentBuilder}; the narrowing
82
- * cast surfaces its single-document phase runner.
103
+ * Linked-phase correction (see {@link resyncDocument}).
83
104
  */
84
105
  documentBuilder;
106
+ /** Each document's {@link DependentRelink}, from its request until it settles. See {@link relinkDependents}. */
107
+ relinkBuilds = new WeakMap();
108
+ /** The {@link replayKey} of the latest repair of each document that {@link stagesRepairs}, with the hash of the text it came from. */
109
+ replayKeys = new WeakMap();
110
+ /** The relinks the check inside the lock found needed, until their build starts. */
111
+ scheduledRelinks = new WeakSet();
112
+ /** The relinks of every language whose build runs, from its start until it settles or the next build starts. */
113
+ runningRelinks;
114
+ /** The rules of a repair while a relink's build waited or ran. Only one while it ran gets the next build in the chain. */
115
+ repairedAgain = new WeakMap();
116
+ /**
117
+ * Marks {@link scheduledRelinks} running as their builds start. Set on the
118
+ * first request only so it subscribes once, and never disposed.
119
+ */
120
+ relinkStarts;
121
+ /** See {@link IntegrityServiceOptions.maxRelinkChain}. */
122
+ maxRelinkChain;
123
+ /** The rules that mutated each document in its latest `Linked`-phase {@link enforceIntegrity} pass, so a warning can name them. */
124
+ mutatingRules = new WeakMap();
85
125
  constructor(services, options = {}) {
86
126
  this.services = services;
87
127
  this.textDocuments = services.shared.workspace.TextDocuments;
@@ -89,6 +129,12 @@ export class DefaultIntegrityService {
89
129
  this.tracer = services.shared.Tracer.for(options.logName ?? 'Integrity').trace('instantiated');
90
130
  this.documentBuilder = services.shared.workspace.DocumentBuilder;
91
131
  this.syncMode = options.syncMode ?? 'silent';
132
+ this.maxRelinkChain = options.maxRelinkChain ?? 3;
133
+ if (!Number.isInteger(this.maxRelinkChain) || this.maxRelinkChain < 1) {
134
+ throw new RangeError(`The integrity service's maxRelinkChain option must be an integer of at least 1, not ${this.maxRelinkChain}.`);
135
+ }
136
+ this.runningRelinks = runningRelinksByServer.get(services.shared) ?? new Set();
137
+ runningRelinksByServer.set(services.shared, this.runningRelinks);
92
138
  // Read the language's IntegrityRuleContribution group and let each
93
139
  // contribution register one or many rules through this service (the
94
140
  // declarative path; equivalent to imperative `register(rule)` calls).
@@ -114,24 +160,35 @@ export class DefaultIntegrityService {
114
160
  * rule set to completion against a doomed build.
115
161
  */
116
162
  async enforceBatch(documents, phase, cancelToken) {
117
- const phaseStart = performance.now();
163
+ const sincePhaseStart = this.services.shared.Clock.stopwatch();
118
164
  let mutatedCount = 0;
119
165
  // Per-rule self-time profiling is opt-in: only spin up a session when the
120
166
  // debug threshold is active, so the production `info` path allocates
121
167
  // nothing and the hot rule loop stays a single const-branch off `session`.
122
168
  const session = Logger.isLevelEnabled('debug') ? this.tracer.profile(`integrity ${DocumentState[phase]}`) : undefined;
169
+ // Before any skip or cancel, so text with parse errors also ends a replay.
170
+ for (const document of documents) {
171
+ this.forgetReplayOfOtherText(document);
172
+ }
123
173
  for (const document of documents) {
124
- const changed = await this.enforceIntegrity(document, phase, cancelToken, session);
125
- if (changed) {
174
+ if (this.isStructurallyBroken(document)) {
175
+ this.tracer.with(document.uri.toString()).debug('Parse errors: rules skipped until the document parses');
176
+ }
177
+ else if (await this.enforceIntegrity(document, phase, cancelToken, session)) {
126
178
  mutatedCount++;
127
- await this.resyncDocument(document, cancelToken);
179
+ // Not cancellable: see `resyncDocument`.
180
+ await this.resyncDocument(document, CancellationToken.None);
128
181
  }
129
182
  await interruptAndCheck(cancelToken);
130
183
  }
131
- const totalMs = Math.round(performance.now() - phaseStart);
132
- if (totalMs >= 25 || mutatedCount > 0) {
184
+ const totalMs = Math.round(sincePhaseStart.elapsedMs);
185
+ // A repair is the operator's to know of; the timing alone is diagnosis.
186
+ if (mutatedCount > 0) {
133
187
  this.tracer.info(`enforce ${DocumentState[phase]}: docs=${documents.length}, mutated=${mutatedCount} [${totalMs}ms]`);
134
188
  }
189
+ else if (totalMs >= 25) {
190
+ this.tracer.debug(`enforce ${DocumentState[phase]}: docs=${documents.length}, mutated=${mutatedCount} [${totalMs}ms]`);
191
+ }
135
192
  // One line per rule (count + self-% + self-ms), sorted by cost — only when profiling.
136
193
  session?.report('debug');
137
194
  }
@@ -146,6 +203,12 @@ export class DefaultIntegrityService {
146
203
  'Allowed phases are Parsed and Linked.');
147
204
  }
148
205
  const disposable = this.rules.register(rule);
206
+ // No parsed node carries a type the reflection does not know, so such a
207
+ // rule never runs. A warning, not a throw: a test reflection may omit the type.
208
+ if (!this.services.shared.AstReflection.getAllTypes().includes(rule.nodeType)) {
209
+ this.tracer.warn(`Integrity rule '${this.formatRule(rule)}' is keyed on node type '${rule.nodeType}', ` +
210
+ 'which the AST reflection does not know, so no parsed node matches it.');
211
+ }
149
212
  this.ruleLoggers.set(rule.id, this.tracer.sub(IntegrityPhase.toString(rule.phase)).sub(rule.label ?? rule.id));
150
213
  return {
151
214
  dispose: () => {
@@ -162,6 +225,14 @@ export class DefaultIntegrityService {
162
225
  }
163
226
  return removed;
164
227
  }
228
+ /**
229
+ * Seam: does the parse of `document` lack some of its text? {@link enforceBatch}
230
+ * runs no rule on such a document, because a repair is written back by
231
+ * serializing the AST, and a recovered AST lacks what the parser skipped.
232
+ */
233
+ isStructurallyBroken(document) {
234
+ return hasSkippedText(document);
235
+ }
165
236
  /**
166
237
  * Run all rules for the given phase against matching AST nodes. Returns
167
238
  * `true` if any rule mutated the AST.
@@ -180,37 +251,58 @@ export class DefaultIntegrityService {
180
251
  * full rule sweep) — coarser than per (node × rule) to avoid a microtask
181
252
  * per inner iteration, finer than per-document so a long-running rule set
182
253
  * on a large file can still interrupt mid-stream. Throws
183
- * `OperationCancelled` when the token is cancelled; Langium's build
184
- * pipeline absorbs the throw at the lock-holder.
254
+ * `OperationCancelled` when the token is cancelled before any rule mutated
255
+ * the AST; Langium's build pipeline absorbs the throw at the lock-holder.
256
+ * After a mutation it stops the sweep and returns `true` instead, so the
257
+ * caller can resync what was mutated before the cancellation unwinds.
185
258
  */
186
259
  async enforceIntegrity(document, phase, cancelToken, session) {
187
260
  if (cancelToken?.isCancellationRequested) {
188
261
  return false;
189
262
  }
190
263
  let changed = false;
191
- const rulesForPhase = this.getPhaseBucket(phase);
192
- if (rulesForPhase.length === 0) {
264
+ const rulesByType = this.getPhaseBucket(phase);
265
+ if (rulesByType.size === 0) {
193
266
  return false;
194
267
  }
268
+ // Read only after a Linked-phase repair. Cleared when such a pass starts, so
269
+ // one that records nothing leaves no older record to be named in its place.
270
+ const recorded = phase > DocumentState.Parsed;
271
+ if (recorded) {
272
+ this.mutatingRules.delete(document);
273
+ }
274
+ let mutating;
195
275
  const relativeUri = this.services.shared.workspace.WorkspaceManager.wsRelativePath(document.uri);
196
276
  for (const node of AstUtils.streamAst(document.parseResult.value)) {
197
- for (const rule of rulesForPhase) {
198
- if (node.$type === rule.nodeType) {
199
- const ruleLogger = this.ruleLoggers.get(rule.id).with(relativeUri);
200
- // Scope per rule so the session aggregates self-time by rule id across all
201
- // nodes; off the profiling path `session` is undefined and we call directly.
202
- const enforced = session
203
- ? session.scope(rule.id, () => rule.enforce(node, document, ruleLogger))
204
- : rule.enforce(node, document, ruleLogger);
205
- const mutated = isPromiseLike(enforced) ? await enforced : enforced;
206
- if (mutated) {
207
- ruleLogger.trace(`[${relativeUri}] Mutated ${this.formatNode(node)}`);
277
+ for (const rule of rulesByType.get(node.$type) ?? NO_RULES) {
278
+ const ruleLogger = this.ruleLoggers.get(rule.id).with(relativeUri);
279
+ // Scope per rule so the session aggregates self-time by rule id across all
280
+ // nodes; off the profiling path `session` is undefined and we call directly.
281
+ const enforced = session
282
+ ? session.scope(rule.id, () => rule.enforce(node, document, ruleLogger))
283
+ : rule.enforce(node, document, ruleLogger);
284
+ const mutated = isPromiseLike(enforced) ? await enforced : enforced;
285
+ if (mutated) {
286
+ if (recorded && mutating === undefined) {
287
+ // Only a mutating pass leads to a read, so a pass that mutates nothing allocates nothing.
288
+ mutating = new Set();
289
+ this.mutatingRules.set(document, mutating);
208
290
  }
209
- changed = mutated || changed;
291
+ mutating?.add(rule);
292
+ ruleLogger.trace(`[${relativeUri}] Mutated ${this.formatNode(node)}`);
210
293
  }
294
+ changed = mutated || changed;
211
295
  }
212
296
  if (cancelToken !== undefined) {
213
- await interruptAndCheck(cancelToken);
297
+ try {
298
+ await interruptAndCheck(cancelToken);
299
+ }
300
+ catch (err) {
301
+ if (changed && isOperationCancelled(err)) {
302
+ return true;
303
+ }
304
+ throw err;
305
+ }
214
306
  }
215
307
  }
216
308
  return changed;
@@ -219,27 +311,112 @@ export class DefaultIntegrityService {
219
311
  * Re-serialise the document text after AST mutations, propagate corrections,
220
312
  * and re-parse at Parsed phase (safe because linking hasn't occurred yet).
221
313
  *
222
- * `cancelToken` is checked at entry so a preempted build skips the
223
- * serialise / disk-write atomic; the steps inside resync are not further
224
- * subdivided because each is a single fast operation that does not yield
225
- * a natural interruption point.
314
+ * Runs to the end whatever `cancelToken` says: the AST is already mutated,
315
+ * and skipping the resync leaves it against unchanged text that the next
316
+ * build does not re-parse. `cancelToken` reaches only the re-parse.
226
317
  */
227
318
  async resyncDocument(document, cancelToken) {
228
- if (cancelToken?.isCancellationRequested) {
229
- return;
230
- }
231
319
  const root = document.parseResult.value;
232
- const newText = await this.services.serializer.Serializer.serializeAst(root);
320
+ // Extracted while `document` still holds the text being replaced — the
321
+ // resync that follows overwrites it with `newText`. Absent on a test
322
+ // stub that binds no trivia group; `createServerLanguageModule` always
323
+ // provides it, and an empty registry is already the no-op.
324
+ const trivia = this.services.trivia?.TriviaService;
325
+ const extracted = trivia?.extract(document);
326
+ const serialized = await this.services.serializer.Serializer.serializeAst(root);
327
+ const newText = extracted === undefined ? serialized : trivia.apply(serialized, extracted, UriUtils.toUri(document.textDocument.uri));
233
328
  const oldText = document.textDocument.getText();
234
329
  if (oldText === newText) {
235
330
  return;
236
331
  }
237
332
  const version = document.textDocument.version;
238
- // Keep the same version so HydraniumTextDocuments doesn't reject subsequent client edits.
239
- // Use manager.update so the `instanceof FullTextDocument` gate in the bare
240
- // `TextDocument.update` doesn't trip on adopter-custom text-document types.
241
- const textDocument = this.textDocuments.update(document.textDocument, [{ text: newText }], version);
242
- await this.syncCorrections(textDocument);
333
+ // Commit into the store's own document when the URI is open there, rather
334
+ // than into whichever text-document object this build is holding. The two
335
+ // are the same on the LSP path and are NOT when the document was built for
336
+ // an already-open URI through `fromString`: repairing only the build's copy
337
+ // leaves the store — the server's authority on what an open document says —
338
+ // on the unrepaired text, and the reconciling re-parse then redefines
339
+ // `textDocument` onto the store's object and discards the repair with it.
340
+ //
341
+ // Compared against the CST's own text, not against `document.textDocument`.
342
+ // That object IS the store's on the LSP path, so asking it whether the
343
+ // store still agrees compares the store with itself and can only ever say
344
+ // yes — the guard would be vacuous exactly where an editor race can happen.
345
+ // The CST's `fullText` is the text that actually produced this AST, which
346
+ // is the only thing a repair derived from it is valid against. A document
347
+ // with no CST (built from a model rather than parsed) has no better answer
348
+ // than the text it is carrying.
349
+ const parsedFrom = document.parseResult.value.$cstNode?.root.fullText ?? oldText;
350
+ const commit = this.textDocuments.commitRepair(document.textDocument.uri, parsedFrom, newText);
351
+ if (commit.status === 'stale') {
352
+ // An edit landed after the parse this repair came from, so the repair
353
+ // describes text the store has already replaced. Abandon it rather than
354
+ // hand it to `syncCorrections`: an override that persists from there
355
+ // would write a correction over the edit that superseded it. That edit
356
+ // drives a build of its own, which re-runs these rules against what the
357
+ // document actually says.
358
+ //
359
+ // Abandoning the WRITE is only half of it. The build carries on to its
360
+ // settled phase with whatever AST is registered, and every settled
361
+ // consumer reads that AST rather than the store — so leaving the rule's
362
+ // mutation in place would answer questions about this document from a
363
+ // parse of text nobody has. Reconciling discards it by re-reading the
364
+ // store, which is what the document now has to agree with.
365
+ //
366
+ // No dependents are relinked, because the edit's build relinks them.
367
+ await this.reconcileDocument(document, cancelToken);
368
+ return;
369
+ }
370
+ // A committed repair is a new version; a document no client has open keeps
371
+ // its number here, and the re-versioning below moves it once the build
372
+ // has reconciled. Through the store's `update`, so the `instanceof
373
+ // FullTextDocument` gate in the bare `TextDocument.update` does not trip
374
+ // on adopter-custom text-document types.
375
+ const textDocument = commit.status === 'committed' ? commit.document : this.textDocuments.update(document.textDocument, [{ text: newText }], version);
376
+ await this.syncCorrections(textDocument, parsedFrom);
377
+ if (document.state > DocumentState.Parsed) {
378
+ // Requested before the in-place relink, and an error logged, so neither skips the other.
379
+ try {
380
+ this.relinkDependents(document, { from: parsedFrom, to: newText });
381
+ }
382
+ catch (error) {
383
+ this.tracer
384
+ .with(document.uri.toString())
385
+ .error(`Relinking the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
386
+ }
387
+ }
388
+ await this.reconcileDocument(document, cancelToken);
389
+ // Re-version against the REPAIRED text, after either branch. The repair is
390
+ // a content change the store has not seen: whatever reconciliation ran
391
+ // during the re-parse saw the text on disk, which in `'editor'` sync mode
392
+ // is the PRE-repair text. Leave the sequence describing that and the next
393
+ // open hashes the repair, finds a mismatch and steps the version again, so
394
+ // every base version taken from this build is stale before it is used.
395
+ // An open document keeps the store's own version, which is not this
396
+ // method's to move.
397
+ //
398
+ // Deliberately NOT gated on cancellation, unlike the entry to this method:
399
+ // `syncCorrections` has already written or staged the repair by now, so a
400
+ // preempted build that skipped this would leave the sequence describing
401
+ // text that is no longer there.
402
+ if (!this.textDocuments.isOpen(document.textDocument.uri)) {
403
+ // The root describes the repair too; left at the re-parse's number, a
404
+ // syncing read waits for a build nothing requests. The CST is still
405
+ // the unrepaired text, so the repair is reported as the parsed text.
406
+ this.services.shared.workspace.VersionSyncService.modelProduced(document, { text: newText });
407
+ }
408
+ }
409
+ /**
410
+ * Bring `document` back into agreement with the text it is supposed to
411
+ * describe, after a rule has mutated its AST.
412
+ *
413
+ * Used for a correction that was applied and for one that was abandoned,
414
+ * because the document is left inconsistent either way: applied, the text
415
+ * moved under the CST; abandoned, the AST carries a mutation the text never
416
+ * had. Both are the same question — re-read and re-derive — and the phase is
417
+ * what decides how far that has to go.
418
+ */
419
+ async reconcileDocument(document, cancelToken) {
243
420
  if (document.state <= DocumentState.Parsed) {
244
421
  // Parsed-phase correction: re-parse only. The build pipeline still runs
245
422
  // IndexedContent → ComputedScopes → Linked → … on the fresh AST afterwards,
@@ -265,24 +442,216 @@ export class DefaultIntegrityService {
265
442
  // stays in the builder, its proper home, rather than being duplicated here.
266
443
  await this.documentBuilder.reparseAndRelink(document, cancelToken);
267
444
  }
268
- // Re-version against the REPAIRED text, after either branch. The repair is
269
- // a content change the store has not seen: whatever reconciliation ran
270
- // during the re-parse saw the text on disk, which in `'editor'` sync mode
271
- // is the PRE-repair text. Leave the sequence describing that and the next
272
- // open hashes the repair, finds a mismatch and steps the version again, so
273
- // every `baseVersion` taken from this build is stale before it is used.
274
- // Falls back to the pre-re-parse number for a URI the store never tracked,
275
- // where there is no sequence to advance; an OPEN document answers
276
- // `undefined` and keeps the store's own version, which is not this
277
- // method's to move.
278
- //
279
- // Deliberately NOT gated on cancellation, unlike the entry to this method:
280
- // `syncCorrections` has already written or staged the repair by now, so a
281
- // preempted build that skipped this would leave the sequence describing
282
- // text that is no longer there.
283
- const reconciled = this.textDocuments.reconcileExternalContent(document.textDocument.uri, newText) ?? version;
284
- if (document.textDocument.version !== reconciled) {
285
- this.textDocuments.update(document.textDocument, [], reconciled);
445
+ }
446
+ /**
447
+ * Relink the documents that reference `document` in a build after this
448
+ * one, where it counts as changed. They were linked before the repair, so
449
+ * they hold nodes a re-parse replaced, or names the repair changed.
450
+ *
451
+ * Each document has at most one such build, its {@link DependentRelink},
452
+ * which goes through four steps.
453
+ *
454
+ * 1. Requested here, and kept in {@link relinkBuilds}. A repair while it
455
+ * waits is relinked by it.
456
+ * 2. Checked inside the lock, which skips it unless {@link isRelinkNeeded}.
457
+ * A needed one joins {@link scheduledRelinks}. While a build outside the
458
+ * lock runs, it is requested again once that build ends.
459
+ * 3. Started, when `onUpdate` reports its build. An explicit `build` starts
460
+ * none. It is in {@link runningRelinks} until it settles or the next
461
+ * build starts that overlaps no other. A repair while it runs is kept in
462
+ * {@link repairedAgain} and gets the next build.
463
+ * 4. Settled, when its request resolves, which drops it from every record.
464
+ *
465
+ * A repair of a document that a running build relinked continues that
466
+ * build's chain, which {@link maxRelinkChain} bounds. A repair with the
467
+ * {@link replayKey} of the one before requests none.
468
+ */
469
+ relinkDependents(document, repair) {
470
+ const key = this.replayKey(document, repair);
471
+ if (key !== undefined && this.replayKeys.get(document)?.key === key) {
472
+ return;
473
+ }
474
+ const rules = [...(this.mutatingRules.get(document) ?? [])];
475
+ const pending = this.relinkBuilds.get(document);
476
+ let requested = true;
477
+ if (pending !== undefined) {
478
+ this.repairedAgain.set(pending, rules);
479
+ }
480
+ else {
481
+ const causes = [...this.runningRelinks].filter(relink => this.isRelinkedBy(document, relink.document));
482
+ requested = this.relinkInChain(document, Math.max(0, ...causes.map(relink => relink.chain)) + 1, rules);
483
+ }
484
+ // Recorded after the request, so one that throws or is past the bound leaves no key that skips the next repair.
485
+ if (key !== undefined && requested) {
486
+ this.replayKeys.set(document, { key, fromHash: textHash(repair.from) });
487
+ }
488
+ }
489
+ /**
490
+ * Whether a build where `changed` counts as changed relinks `document`, as
491
+ * Langium's `shouldRelink` decides. Read from the document's links, because
492
+ * its build has dropped its indexed references until it indexes again.
493
+ */
494
+ isRelinkedBy(document, changed) {
495
+ const target = changed.uri.toString();
496
+ return document.references.some(reference => {
497
+ const descriptions = isMultiReference(reference)
498
+ ? reference.items.map(item => item.$nodeDescription)
499
+ : [reference.$nodeDescription];
500
+ return reference.error !== undefined || descriptions.some(description => description?.documentUri.toString() === target);
501
+ });
502
+ }
503
+ /**
504
+ * Request the build at `chain`, or warn naming `rules` past
505
+ * {@link maxRelinkChain} and drop the {@link replayKey} of `document`.
506
+ * Returns whether it requested the build.
507
+ */
508
+ relinkInChain(document, chain, rules) {
509
+ if (chain > this.maxRelinkChain) {
510
+ this.replayKeys.delete(document);
511
+ const builds = this.maxRelinkChain === 1 ? '1 build in a row that relinks' : `${this.maxRelinkChain} builds in a row that relink`;
512
+ this.tracer
513
+ .with(document.uri.toString())
514
+ .warn(`${this.formatRules(rules)} repaired documents in ${builds} their dependents, ` +
515
+ 'which keep stale links until the next change. A rule that does not settle causes this, ' +
516
+ 'or documents with linking errors that rules repair one after another.');
517
+ return false;
518
+ }
519
+ this.requestRelink(document, chain);
520
+ return true;
521
+ }
522
+ /** Request the build {@link relinkInChain} decided on, skipped unless {@link isRelinkNeeded}. */
523
+ requestRelink(document, chain) {
524
+ const uri = document.uri;
525
+ this.relinkStarts ??= this.documentBuilder.onUpdate(changed => this.startRelinks(changed));
526
+ const relink = { document, chain };
527
+ let unlockedBuild;
528
+ const stillNeeded = () => {
529
+ // No locked build runs during the lock read, so one seen here runs outside the lock.
530
+ unlockedBuild = this.documentBuilder.activeBuild;
531
+ if (unlockedBuild !== undefined) {
532
+ return false;
533
+ }
534
+ // Marked before the deferral check, so a deferred relink stays scheduled until it settles.
535
+ const needed = this.isRelinkNeeded(document);
536
+ if (needed) {
537
+ this.scheduledRelinks.add(relink);
538
+ }
539
+ return needed;
540
+ };
541
+ const settled = () => {
542
+ this.relinkBuilds.delete(document);
543
+ this.scheduledRelinks.delete(relink);
544
+ this.runningRelinks.delete(relink);
545
+ };
546
+ const requestFailed = (error) => {
547
+ // No build relinks the dependents, so the same repair again needs one.
548
+ this.replayKeys.delete(document);
549
+ this.tracer
550
+ .with(uri.toString())
551
+ .error(`Requesting the next build that relinks the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
552
+ };
553
+ const request = this.services.shared.workspace.VersionSyncService.requestRecoveryBuild(uri, { stillNeeded });
554
+ // Recorded after the request, so one that throws leaves no relink that never settles.
555
+ this.relinkBuilds.set(document, relink);
556
+ request
557
+ .then(() => {
558
+ if (unlockedBuild !== undefined) {
559
+ // Requested again once that build ends, so the two never overlap. A repair meanwhile waits for it.
560
+ unlockedBuild
561
+ .then(() => {
562
+ settled();
563
+ this.requestRelink(document, chain);
564
+ })
565
+ .catch(requestFailed);
566
+ return;
567
+ }
568
+ // A build that never started, or was cancelled and not run again,
569
+ // leaves the repair to the build that held it back. Read before the
570
+ // next build's `onUpdate` clears it, which the lock's queue orders later.
571
+ const ran = this.runningRelinks.has(relink);
572
+ settled();
573
+ const again = this.repairedAgain.get(relink);
574
+ if (ran && again !== undefined) {
575
+ this.relinkInChain(document, chain + 1, again);
576
+ }
577
+ }, (error) => {
578
+ settled();
579
+ // The dependents were not relinked, so the same repair again needs its build.
580
+ this.replayKeys.delete(document);
581
+ this.tracer
582
+ .with(uri.toString())
583
+ .error(`The build that relinks the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
584
+ })
585
+ .catch(requestFailed);
586
+ }
587
+ /** Mark the {@link scheduledRelinks} of the `changed` documents running, as the build that carries them starts. */
588
+ startRelinks(changed) {
589
+ // An explicit build relinks no dependents and ends no build it overlaps.
590
+ if (this.documentBuilder.isInsideBuildUpdate()) {
591
+ return;
592
+ }
593
+ // The build before this one ended, done or cancelled, so its relinks run no more,
594
+ // unless this one overlaps it, as a reentrant build does.
595
+ // The set is shared by every language. Each service clears only its own,
596
+ // because another's listener may already have marked this build's relinks running.
597
+ if (!this.documentBuilder.overlapsRunningBuild()) {
598
+ for (const relink of this.runningRelinks) {
599
+ if (this.relinkBuilds.get(relink.document) === relink) {
600
+ this.runningRelinks.delete(relink);
601
+ }
602
+ }
603
+ }
604
+ const documents = this.services.shared.workspace.LangiumDocuments;
605
+ for (const uri of changed) {
606
+ const document = documents.getDocument(uri);
607
+ const relink = document === undefined ? undefined : this.relinkBuilds.get(document);
608
+ if (relink !== undefined && this.scheduledRelinks.delete(relink)) {
609
+ // The starting build relinks any repair while it waited.
610
+ this.repairedAgain.delete(relink);
611
+ this.runningRelinks.add(relink);
612
+ }
613
+ }
614
+ }
615
+ /**
616
+ * Whether a build where `document` counts as changed relinks another
617
+ * document, by the builder's own test, which counts a document a cancelled
618
+ * build left unindexed as relinked.
619
+ */
620
+ isRelinkNeeded(document) {
621
+ const documents = this.services.shared.workspace.LangiumDocuments;
622
+ const changed = new Set([document.uri.toString()]);
623
+ return (documents.getDocument(document.uri) === document &&
624
+ documents.all.some(other => other !== document && this.documentBuilder.shouldRelink(other, changed)));
625
+ }
626
+ /**
627
+ * Whether `document` stages its repairs rather than writing them, as a
628
+ * closed document does in `'editor'` mode, so its source keeps the
629
+ * unrepaired text.
630
+ */
631
+ stagesRepairs(document) {
632
+ return this.syncMode === 'editor' && !this.textDocuments.isOpen(document.textDocument.uri);
633
+ }
634
+ /**
635
+ * What tells `repair` made again from the same text apart from a new one,
636
+ * for a document that {@link stagesRepairs}, which reads the unrepaired
637
+ * text again in every build that re-parses it after its CST was shed. Such
638
+ * a repair requests no build, because the build that re-parsed the
639
+ * document relinked its dependents, and another would replay it again.
640
+ * Hashes, so the record keeps no text the shed freed.
641
+ */
642
+ replayKey(document, repair) {
643
+ return this.stagesRepairs(document) ? textHash(repair.from) + textHash(repair.to) : undefined;
644
+ }
645
+ /**
646
+ * Drop the {@link replayKey} of `document` once it was parsed from other
647
+ * text than its repair came from, such as a save and a revert. A repair of
648
+ * that text again is a new one, whose dependents need their build.
649
+ */
650
+ forgetReplayOfOtherText(document) {
651
+ const recorded = this.replayKeys.get(document);
652
+ const parsedFrom = document.parseResult.value.$cstNode?.root.fullText;
653
+ if (recorded !== undefined && parsedFrom !== undefined && recorded.fromHash !== textHash(parsedFrom)) {
654
+ this.replayKeys.delete(document);
286
655
  }
287
656
  }
288
657
  /**
@@ -290,40 +659,155 @@ export class DefaultIntegrityService {
290
659
  * already in the synced store (in-place in {@link resyncDocument}); this
291
660
  * method routes the out-of-band part by document registration:
292
661
  *
293
- * - **Open in the language client** → nothing to push here. The AST mutation
294
- * rode the current build, so `ModelService.syncToLanguageClient` (the
295
- * persistent integrity-settled listener) mirrors the corrected text to the
296
- * client by *content* (shadow diff) — no authorship rewrite, no second
297
- * rebuild.
662
+ * - **Open in the language client** → nothing to push here. The AST
663
+ * mutation rode the current build, so
664
+ * `DefaultModelService.syncToLanguageClient` (the persistent
665
+ * integrity-settled listener) mirrors it to the client by
666
+ * *content* (shadow diff), and the editor shows it as an unsaved change.
667
+ * - **Held only through the data or GLSP head**, or waiting out the release
668
+ * grace after its last client's connection was lost → the store is the
669
+ * authority, and those heads read the correction from the settled build.
670
+ * In silent mode it is also written to disk when the text it was computed
671
+ * from is what disk holds, since the repair is then the only difference;
672
+ * otherwise it waits for the next save. Editor mode always waits, and the
673
+ * repair leaves the document dirty until a save.
298
674
  * - **Closed, silent mode** → write to disk directly (disk is authoritative).
299
- * - **Closed, editor mode** → stage so the next open picks it up via applyEdit.
675
+ * - **Closed, editor mode** → stage, so the next open takes the correction
676
+ * over the disk text.
677
+ *
678
+ * A held URI is not closed. Writing it as one persists the holder's unsaved
679
+ * edits with the repair, and staging it leaves text no open reads, because
680
+ * only a first open consumes the stage.
681
+ *
682
+ * `parsedFrom` is the text the repaired AST was parsed from. Without it a
683
+ * held URI's repair is left to the next save.
300
684
  */
301
- async syncCorrections(document) {
302
- // Open files: delivery is the ModelService settled listener's job (by
303
- // content, via the shadow) — nothing to do here. Only the open/closed
304
- // question is asked, so no reverse address resolution is needed: a file
305
- // open under a symlinked path still answers `true`.
685
+ async syncCorrections(document, parsedFrom) {
686
+ // Only the open/closed question is asked, so no reverse address
687
+ // resolution is needed: a file open under a symlinked path still answers
688
+ // `true`.
306
689
  if (this.textDocuments.isOpenInLanguageClient(document.uri)) {
307
690
  return;
308
691
  }
692
+ // A virtual document has no disk backing, so neither persisting nor
693
+ // staging applies: there is no file to write and no editor that could open
694
+ // one. `URI.fsPath` yields a path for any scheme, so handing this to the
695
+ // provider would address `<cwd>/<contributor>/<name>` — an unrelated
696
+ // location a disk-backed provider refuses outright, turning a repair that
697
+ // was never persistable into a build failure. The correction is already on
698
+ // the AST and in the text store, which is the whole of where a virtual
699
+ // document lives, so there is nothing further to propagate.
700
+ if (isVirtualUri(document.uri)) {
701
+ this.tracer.with(document.uri).debug('Virtual document: correction kept in memory, nothing to persist');
702
+ return;
703
+ }
704
+ // A document waiting out the release grace still holds its lost client's
705
+ // unsaved text, so writing it as a closed file would persist that text.
706
+ if (this.textDocuments.isOpenInAnyClient(document.uri) || this.textDocuments.isReleaseDeferred(document.uri)) {
707
+ if (this.syncMode === 'silent' && parsedFrom !== undefined) {
708
+ await this.persistIfDiskMatches(document, parsedFrom);
709
+ }
710
+ return;
711
+ }
309
712
  // Closed files, silent mode: write to disk directly.
310
713
  if (this.syncMode === 'silent') {
714
+ const repaired = document.getText();
311
715
  // Async write so the integrity pass does not block the event loop on a slow filesystem.
312
- await this.fileSystemProvider.writeFile(UriUtils.toUri(document.uri), document.getText());
716
+ // In the file's task queue, behind any save already queued: written
717
+ // beside one, the older repair can land last and stay on disk.
718
+ await this.services.shared.workspace.FileSystemTaskQueue.enqueue(document.uri, () => this.fileSystemProvider.writeFile(UriUtils.toUri(document.uri), repaired));
719
+ this.noteSilentWrite(document.uri);
313
720
  return;
314
721
  }
315
- // Closed files, editor mode: stage content so the next open picks it up via applyEdit.
722
+ // Closed files, editor mode: stage content for the next open.
316
723
  this.textDocuments.stagePendingContent(document.uri, document.getText());
317
724
  }
725
+ /**
726
+ * Write the repair in `document` to disk when disk still holds `parsedFrom`,
727
+ * the text the repair was computed from — so the write carries the repair
728
+ * and nothing a holder could still discard.
729
+ *
730
+ * The read, the compare and the write run as one task in the file's task
731
+ * queue, so no save of the file lands between them. The store is compared
732
+ * again once the read returns, and the write is skipped if it no longer
733
+ * holds the repair: an edit does not wait for the queue, and writing the
734
+ * older repair then puts text on disk that no participant holds. The
735
+ * skipped repair loses nothing: the edit drives a build of its own, and a
736
+ * save writes the store's text. A file that cannot be read is left alone
737
+ * too: there is no evidence it matches. So is a file an editor opened in the
738
+ * meantime: the editor receives the repair as an unsaved change, and writing
739
+ * it would change the file under the editor.
740
+ */
741
+ async persistIfDiskMatches(document, parsedFrom) {
742
+ const uri = UriUtils.toUri(document.uri);
743
+ const repaired = document.getText();
744
+ await this.services.shared.workspace.FileSystemTaskQueue.enqueue(document.uri, async () => {
745
+ let onDisk;
746
+ try {
747
+ onDisk = await this.fileSystemProvider.readFile(uri);
748
+ }
749
+ catch (error) {
750
+ this.tracer.with(document.uri).debug(`Held document unreadable on disk, repair left to the next save: ${String(error)}`);
751
+ return;
752
+ }
753
+ // The file itself, not the store's disk baseline: the baseline moves
754
+ // only when the server hears of a change, so it can still hold the
755
+ // parsed text after another process rewrote the file and before the
756
+ // watcher reports it, and this write would then replace that change.
757
+ if (onDisk !== parsedFrom) {
758
+ return;
759
+ }
760
+ if (this.textDocuments.get(document.uri)?.getText() !== repaired) {
761
+ this.tracer.with(document.uri).debug('Held document changed while disk was read, repair left to that change');
762
+ return;
763
+ }
764
+ if (this.textDocuments.isOpenInLanguageClient(document.uri)) {
765
+ this.tracer.with(document.uri).debug('Editor attached while disk was read, repair left to its save');
766
+ return;
767
+ }
768
+ await this.fileSystemProvider.writeFile(uri, repaired);
769
+ this.textDocuments.setDiskBaseline(document.uri, repaired);
770
+ this.noteSilentWrite(document.uri);
771
+ });
772
+ }
773
+ /**
774
+ * Hand the first repair written to disk in `'silent'` sync mode to
775
+ * {@link reportSilentWrite}. Once per server, whichever language writes,
776
+ * rather than per file: a workspace-wide repair pass would otherwise log a
777
+ * line per document.
778
+ */
779
+ noteSilentWrite(uri) {
780
+ if (silentWriteReported.has(this.services.shared)) {
781
+ return;
782
+ }
783
+ silentWriteReported.add(this.services.shared);
784
+ this.reportSilentWrite(uri);
785
+ }
786
+ /**
787
+ * Log a server's first silent write-back, so an adopter learns that
788
+ * integrity rules edit their files and which setting controls it.
789
+ */
790
+ reportSilentWrite(uri) {
791
+ this.tracer.info(`Wrote an integrity repair to ${uri}. The integrity service's syncMode option is 'silent', ` +
792
+ "which writes repairs to disk; 'editor' leaves disk alone. Later repairs are written without this line.");
793
+ }
318
794
  /**
319
795
  * Format a rule's name for inclusion in user-facing diagnostic strings
320
- * (error messages thrown during registration). Defaults to
796
+ * (error messages thrown during registration, and log lines). Defaults to
321
797
  * `label ?? id`. Adopters override when their error reporting needs
322
798
  * richer rule context.
323
799
  */
324
800
  formatRule(rule) {
325
801
  return rule.label ?? rule.id;
326
802
  }
803
+ /** `rules` as the subject of a log line, or a rule left unnamed when an override recorded none. */
804
+ formatRules(rules) {
805
+ if (rules.length === 0) {
806
+ return 'An integrity rule';
807
+ }
808
+ const names = rules.map(rule => `'${this.formatRule(rule)}'`).join(', ');
809
+ return rules.length === 1 ? `Integrity rule ${names}` : `Integrity rules ${names}`;
810
+ }
327
811
  /**
328
812
  * Format an AST node for the per-mutation `trace` log line. Returns
329
813
  * `'TypeName'` or `"TypeName('label')"` when the node carries a string
@@ -338,6 +822,17 @@ export class DefaultIntegrityService {
338
822
  const label = fromName ?? fromId;
339
823
  return label ? `${node.$type}('${label}')` : node.$type;
340
824
  }
825
+ /**
826
+ * The `$type`s `rule` runs on. Defaults to its `nodeType` and every subtype,
827
+ * as Langium's validation registry does: no node carries an abstract or union
828
+ * type as its `$type`, so an exact match never runs a rule keyed on one.
829
+ * Read only when the phase buckets rebuild after a registration change, so an
830
+ * override must not depend on anything else.
831
+ */
832
+ nodeTypesFor(rule) {
833
+ // A type the reflection does not know still matches itself.
834
+ return [rule.nodeType, ...this.services.shared.AstReflection.getAllSubTypes(rule.nodeType)];
835
+ }
341
836
  getPhaseBucket(phase) {
342
837
  const current = this.rules.all();
343
838
  if (this.phaseBucketsFor !== current) {
@@ -345,10 +840,18 @@ export class DefaultIntegrityService {
345
840
  for (const rule of current) {
346
841
  let bucket = next.get(rule.phase);
347
842
  if (!bucket) {
348
- bucket = [];
843
+ bucket = new Map();
349
844
  next.set(rule.phase, bucket);
350
845
  }
351
- bucket.push(rule);
846
+ // Deduplicated, or a rule runs twice on a node of its own type.
847
+ for (const nodeType of new Set(this.nodeTypesFor(rule))) {
848
+ let rules = bucket.get(nodeType);
849
+ if (!rules) {
850
+ rules = [];
851
+ bucket.set(nodeType, rules);
852
+ }
853
+ rules.push(rule);
854
+ }
352
855
  }
353
856
  this.phaseBuckets = next;
354
857
  this.phaseBucketsFor = current;
@@ -356,6 +859,7 @@ export class DefaultIntegrityService {
356
859
  return this.phaseBuckets.get(phase) ?? EMPTY_PHASE_BUCKET;
357
860
  }
358
861
  }
359
- /** Shared empty result so `getPhaseBucket` doesn't allocate per call on the cold path. */
360
- const EMPTY_PHASE_BUCKET = Object.freeze([]);
862
+ // Shared empty results so neither the cold path nor a node no rule matches allocates.
863
+ const EMPTY_PHASE_BUCKET = new Map();
864
+ const NO_RULES = Object.freeze([]);
361
865
  //# sourceMappingURL=integrity-service.js.map