@hydranium/cli 1.0.0-next.42 → 1.0.0-next.421

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 (371) hide show
  1. package/README.md +125 -204
  2. package/bin/hydranium-cli.js +13 -0
  3. package/heap-analysis/heap/core/load.mjs +2 -2
  4. package/heap-analysis/heap/langium/langium.classifier.mjs +22 -11
  5. package/lib/cli-version.js.map +1 -1
  6. package/lib/cli.js +11 -2
  7. package/lib/cli.js.map +1 -1
  8. package/lib/commands/analyze-heap-args.d.ts.map +1 -1
  9. package/lib/commands/analyze-heap-args.js +1 -2
  10. package/lib/commands/analyze-heap-args.js.map +1 -1
  11. package/lib/commands/analyze-heap.d.ts +3 -0
  12. package/lib/commands/analyze-heap.d.ts.map +1 -1
  13. package/lib/commands/analyze-heap.js +2 -1
  14. package/lib/commands/analyze-heap.js.map +1 -1
  15. package/lib/commands/ast-ground-truth-args.d.ts +3 -2
  16. package/lib/commands/ast-ground-truth-args.d.ts.map +1 -1
  17. package/lib/commands/ast-ground-truth-args.js +12 -10
  18. package/lib/commands/ast-ground-truth-args.js.map +1 -1
  19. package/lib/commands/ast-ground-truth-driver.js +4 -2
  20. package/lib/commands/ast-ground-truth-driver.js.map +1 -1
  21. package/lib/commands/ast-ground-truth.js +1 -1
  22. package/lib/commands/ast-ground-truth.js.map +1 -1
  23. package/lib/commands/check-skills-args.d.ts +11 -0
  24. package/lib/commands/check-skills-args.d.ts.map +1 -0
  25. package/lib/commands/check-skills-args.js +71 -0
  26. package/lib/commands/check-skills-args.js.map +1 -0
  27. package/lib/commands/client-session.d.ts +17 -0
  28. package/lib/commands/client-session.d.ts.map +1 -0
  29. package/lib/commands/client-session.js +27 -0
  30. package/lib/commands/client-session.js.map +1 -0
  31. package/lib/commands/create-args.d.ts +17 -0
  32. package/lib/commands/create-args.d.ts.map +1 -0
  33. package/lib/commands/create-args.js +53 -0
  34. package/lib/commands/create-args.js.map +1 -0
  35. package/lib/commands/create.d.ts +38 -0
  36. package/lib/commands/create.d.ts.map +1 -0
  37. package/lib/commands/create.js +43 -0
  38. package/lib/commands/create.js.map +1 -0
  39. package/lib/commands/delete-args.d.ts +17 -0
  40. package/lib/commands/delete-args.d.ts.map +1 -0
  41. package/lib/commands/delete-args.js +49 -0
  42. package/lib/commands/delete-args.js.map +1 -0
  43. package/lib/commands/delete.d.ts +30 -0
  44. package/lib/commands/delete.d.ts.map +1 -0
  45. package/lib/commands/delete.js +32 -0
  46. package/lib/commands/delete.js.map +1 -0
  47. package/lib/commands/generate-ast-builder.d.ts +1 -1
  48. package/lib/commands/generate-ast-builder.d.ts.map +1 -1
  49. package/lib/commands/generate-ast-builder.js +2 -11
  50. package/lib/commands/generate-ast-builder.js.map +1 -1
  51. package/lib/commands/generate-languages.d.ts +35 -0
  52. package/lib/commands/generate-languages.d.ts.map +1 -0
  53. package/lib/commands/generate-languages.js +105 -0
  54. package/lib/commands/generate-languages.js.map +1 -0
  55. package/lib/commands/generate-transfer-builder.d.ts +33 -0
  56. package/lib/commands/generate-transfer-builder.d.ts.map +1 -0
  57. package/lib/commands/generate-transfer-builder.js +86 -0
  58. package/lib/commands/generate-transfer-builder.js.map +1 -0
  59. package/lib/commands/generate-transfer-model-args.d.ts.map +1 -1
  60. package/lib/commands/generate-transfer-model-args.js +20 -5
  61. package/lib/commands/generate-transfer-model-args.js.map +1 -1
  62. package/lib/commands/generate-transfer-model-config.d.ts.map +1 -1
  63. package/lib/commands/generate-transfer-model-config.js +8 -3
  64. package/lib/commands/generate-transfer-model-config.js.map +1 -1
  65. package/lib/commands/generate-transfer-model.d.ts +41 -9
  66. package/lib/commands/generate-transfer-model.d.ts.map +1 -1
  67. package/lib/commands/generate-transfer-model.js +225 -68
  68. package/lib/commands/generate-transfer-model.js.map +1 -1
  69. package/lib/commands/generated-module-path.d.ts +16 -0
  70. package/lib/commands/generated-module-path.d.ts.map +1 -0
  71. package/lib/commands/generated-module-path.js +21 -0
  72. package/lib/commands/generated-module-path.js.map +1 -0
  73. package/lib/commands/harness-args.d.ts +46 -43
  74. package/lib/commands/harness-args.d.ts.map +1 -1
  75. package/lib/commands/harness-args.js +170 -29
  76. package/lib/commands/harness-args.js.map +1 -1
  77. package/lib/commands/headless-harness.d.ts +37 -20
  78. package/lib/commands/headless-harness.d.ts.map +1 -1
  79. package/lib/commands/headless-harness.js +71 -19
  80. package/lib/commands/headless-harness.js.map +1 -1
  81. package/lib/commands/init-args.d.ts +5 -18
  82. package/lib/commands/init-args.d.ts.map +1 -1
  83. package/lib/commands/init-args.js +131 -35
  84. package/lib/commands/init-args.js.map +1 -1
  85. package/lib/commands/init-hosts.d.ts +24 -0
  86. package/lib/commands/init-hosts.d.ts.map +1 -0
  87. package/lib/commands/init-hosts.js +37 -0
  88. package/lib/commands/init-hosts.js.map +1 -0
  89. package/lib/commands/init-prompt.d.ts +3 -3
  90. package/lib/commands/init-prompt.d.ts.map +1 -1
  91. package/lib/commands/init-prompt.js +4 -5
  92. package/lib/commands/init-prompt.js.map +1 -1
  93. package/lib/commands/init-skills.d.ts +41 -0
  94. package/lib/commands/init-skills.d.ts.map +1 -0
  95. package/lib/commands/init-skills.js +110 -0
  96. package/lib/commands/init-skills.js.map +1 -0
  97. package/lib/commands/init-templates.d.ts +20 -14
  98. package/lib/commands/init-templates.d.ts.map +1 -1
  99. package/lib/commands/init-templates.js +3134 -763
  100. package/lib/commands/init-templates.js.map +1 -1
  101. package/lib/commands/init-wizard.d.ts +10 -0
  102. package/lib/commands/init-wizard.d.ts.map +1 -1
  103. package/lib/commands/init-wizard.js +76 -31
  104. package/lib/commands/init-wizard.js.map +1 -1
  105. package/lib/commands/init-workspace.d.ts +13 -11
  106. package/lib/commands/init-workspace.d.ts.map +1 -1
  107. package/lib/commands/init-workspace.js +21 -7
  108. package/lib/commands/init-workspace.js.map +1 -1
  109. package/lib/commands/init.d.ts +61 -24
  110. package/lib/commands/init.d.ts.map +1 -1
  111. package/lib/commands/init.js +244 -50
  112. package/lib/commands/init.js.map +1 -1
  113. package/lib/commands/kebab.d.ts +15 -0
  114. package/lib/commands/kebab.d.ts.map +1 -0
  115. package/lib/commands/kebab.js +17 -0
  116. package/lib/commands/kebab.js.map +1 -0
  117. package/lib/commands/lint-grammar-args.d.ts +3 -2
  118. package/lib/commands/lint-grammar-args.d.ts.map +1 -1
  119. package/lib/commands/lint-grammar-args.js +14 -11
  120. package/lib/commands/lint-grammar-args.js.map +1 -1
  121. package/lib/commands/lint-grammar-driver.js +2 -1
  122. package/lib/commands/lint-grammar-driver.js.map +1 -1
  123. package/lib/commands/lint-grammar-report.js.map +1 -1
  124. package/lib/commands/lint-grammar.js +1 -1
  125. package/lib/commands/lint-grammar.js.map +1 -1
  126. package/lib/commands/measure-memory-args.d.ts +3 -2
  127. package/lib/commands/measure-memory-args.d.ts.map +1 -1
  128. package/lib/commands/measure-memory-args.js +26 -14
  129. package/lib/commands/measure-memory-args.js.map +1 -1
  130. package/lib/commands/measure-memory-driver.js +5 -3
  131. package/lib/commands/measure-memory-driver.js.map +1 -1
  132. package/lib/commands/measure-memory.d.ts +1 -1
  133. package/lib/commands/measure-memory.d.ts.map +1 -1
  134. package/lib/commands/measure-memory.js +6 -3
  135. package/lib/commands/measure-memory.js.map +1 -1
  136. package/lib/commands/message-catalogue-args.d.ts +16 -0
  137. package/lib/commands/message-catalogue-args.d.ts.map +1 -0
  138. package/lib/commands/message-catalogue-args.js +53 -0
  139. package/lib/commands/message-catalogue-args.js.map +1 -0
  140. package/lib/commands/message-catalogue.d.ts +21 -0
  141. package/lib/commands/message-catalogue.d.ts.map +1 -0
  142. package/lib/commands/message-catalogue.js +48 -0
  143. package/lib/commands/message-catalogue.js.map +1 -0
  144. package/lib/commands/model-docs-args.d.ts +4 -3
  145. package/lib/commands/model-docs-args.d.ts.map +1 -1
  146. package/lib/commands/model-docs-args.js +13 -11
  147. package/lib/commands/model-docs-args.js.map +1 -1
  148. package/lib/commands/model-docs-driver.js +2 -1
  149. package/lib/commands/model-docs-driver.js.map +1 -1
  150. package/lib/commands/model-docs.js +1 -1
  151. package/lib/commands/model-docs.js.map +1 -1
  152. package/lib/commands/projects-args.d.ts.map +1 -1
  153. package/lib/commands/projects-args.js +5 -5
  154. package/lib/commands/projects-args.js.map +1 -1
  155. package/lib/commands/projects.d.ts +3 -4
  156. package/lib/commands/projects.d.ts.map +1 -1
  157. package/lib/commands/projects.js +2 -2
  158. package/lib/commands/projects.js.map +1 -1
  159. package/lib/commands/query-args.d.ts.map +1 -1
  160. package/lib/commands/query-args.js +6 -5
  161. package/lib/commands/query-args.js.map +1 -1
  162. package/lib/commands/query.d.ts +6 -5
  163. package/lib/commands/query.d.ts.map +1 -1
  164. package/lib/commands/query.js +5 -4
  165. package/lib/commands/query.js.map +1 -1
  166. package/lib/commands/reflect-args.d.ts +3 -2
  167. package/lib/commands/reflect-args.d.ts.map +1 -1
  168. package/lib/commands/reflect-args.js +14 -11
  169. package/lib/commands/reflect-args.js.map +1 -1
  170. package/lib/commands/reflect-driver.js +2 -1
  171. package/lib/commands/reflect-driver.js.map +1 -1
  172. package/lib/commands/reflect-report.js.map +1 -1
  173. package/lib/commands/reflect.js +1 -1
  174. package/lib/commands/reflect.js.map +1 -1
  175. package/lib/commands/save-args.d.ts.map +1 -1
  176. package/lib/commands/save-args.js +7 -4
  177. package/lib/commands/save-args.js.map +1 -1
  178. package/lib/commands/save.d.ts +8 -4
  179. package/lib/commands/save.d.ts.map +1 -1
  180. package/lib/commands/save.js +12 -23
  181. package/lib/commands/save.js.map +1 -1
  182. package/lib/commands/skills.d.ts +22 -0
  183. package/lib/commands/skills.d.ts.map +1 -0
  184. package/lib/commands/skills.js +129 -0
  185. package/lib/commands/skills.js.map +1 -0
  186. package/lib/commands/validate-args.d.ts +3 -2
  187. package/lib/commands/validate-args.d.ts.map +1 -1
  188. package/lib/commands/validate-args.js +14 -11
  189. package/lib/commands/validate-args.js.map +1 -1
  190. package/lib/commands/validate-driver.js +2 -1
  191. package/lib/commands/validate-driver.js.map +1 -1
  192. package/lib/commands/validate-report.js.map +1 -1
  193. package/lib/commands/validate.js +1 -1
  194. package/lib/commands/validate.js.map +1 -1
  195. package/lib/commands/watch-args.d.ts.map +1 -1
  196. package/lib/commands/watch-args.js +6 -4
  197. package/lib/commands/watch-args.js.map +1 -1
  198. package/lib/commands/watch.d.ts +3 -4
  199. package/lib/commands/watch.d.ts.map +1 -1
  200. package/lib/commands/watch.js +5 -2
  201. package/lib/commands/watch.js.map +1 -1
  202. package/lib/driver-heap.d.ts +52 -0
  203. package/lib/driver-heap.d.ts.map +1 -0
  204. package/lib/driver-heap.js +51 -0
  205. package/lib/driver-heap.js.map +1 -0
  206. package/lib/index.d.ts +3 -0
  207. package/lib/index.d.ts.map +1 -1
  208. package/lib/index.js +3 -0
  209. package/lib/index.js.map +1 -1
  210. package/lib/log-level.d.ts +21 -11
  211. package/lib/log-level.d.ts.map +1 -1
  212. package/lib/log-level.js +25 -14
  213. package/lib/log-level.js.map +1 -1
  214. package/lib/spawn-data-server.d.ts +9 -4
  215. package/lib/spawn-data-server.d.ts.map +1 -1
  216. package/lib/spawn-data-server.js +5 -4
  217. package/lib/spawn-data-server.js.map +1 -1
  218. package/lib/stderr-connection-logger.d.ts +12 -0
  219. package/lib/stderr-connection-logger.d.ts.map +1 -0
  220. package/lib/stderr-connection-logger.js +16 -0
  221. package/lib/stderr-connection-logger.js.map +1 -0
  222. package/lib/testing/echo-server.js +9 -1
  223. package/lib/testing/echo-server.js.map +1 -1
  224. package/package.json +99 -41
  225. package/src/cli.ts +11 -2
  226. package/src/commands/analyze-heap-args.ts +1 -2
  227. package/src/commands/analyze-heap.ts +4 -1
  228. package/src/commands/ast-ground-truth-args.ts +16 -13
  229. package/src/commands/ast-ground-truth-driver.ts +4 -2
  230. package/src/commands/ast-ground-truth.ts +1 -1
  231. package/src/commands/check-skills-args.ts +70 -0
  232. package/src/commands/client-session.ts +33 -0
  233. package/src/commands/create-args.ts +68 -0
  234. package/src/commands/create.ts +83 -0
  235. package/src/commands/delete-args.ts +64 -0
  236. package/src/commands/delete.ts +61 -0
  237. package/src/commands/generate-ast-builder.ts +2 -12
  238. package/src/commands/generate-languages.ts +132 -0
  239. package/src/commands/generate-transfer-builder.ts +101 -0
  240. package/src/commands/generate-transfer-model-args.ts +20 -5
  241. package/src/commands/generate-transfer-model-config.ts +8 -3
  242. package/src/commands/generate-transfer-model.ts +297 -66
  243. package/src/commands/generated-module-path.ts +22 -0
  244. package/src/commands/harness-args.ts +163 -33
  245. package/src/commands/headless-harness.ts +89 -28
  246. package/src/commands/init-args.ts +135 -35
  247. package/src/commands/init-hosts.ts +51 -0
  248. package/src/commands/init-prompt.ts +5 -6
  249. package/src/commands/init-skills.ts +129 -0
  250. package/src/commands/init-templates.ts +3326 -785
  251. package/src/commands/init-wizard.ts +97 -32
  252. package/src/commands/init-workspace.ts +31 -16
  253. package/src/commands/init.ts +311 -65
  254. package/src/commands/kebab.ts +17 -0
  255. package/src/commands/lint-grammar-args.ts +18 -14
  256. package/src/commands/lint-grammar-driver.ts +2 -1
  257. package/src/commands/lint-grammar.ts +1 -1
  258. package/src/commands/measure-memory-args.ts +31 -17
  259. package/src/commands/measure-memory-driver.ts +5 -3
  260. package/src/commands/measure-memory.ts +6 -3
  261. package/src/commands/message-catalogue-args.ts +55 -0
  262. package/src/commands/message-catalogue.ts +56 -0
  263. package/src/commands/model-docs-args.ts +17 -14
  264. package/src/commands/model-docs-driver.ts +2 -1
  265. package/src/commands/model-docs.ts +1 -1
  266. package/src/commands/projects-args.ts +5 -4
  267. package/src/commands/projects.ts +4 -6
  268. package/src/commands/query-args.ts +6 -4
  269. package/src/commands/query.ts +9 -9
  270. package/src/commands/reflect-args.ts +18 -14
  271. package/src/commands/reflect-driver.ts +2 -1
  272. package/src/commands/reflect.ts +1 -1
  273. package/src/commands/save-args.ts +7 -3
  274. package/src/commands/save.ts +24 -27
  275. package/src/commands/skills.ts +146 -0
  276. package/src/commands/validate-args.ts +18 -14
  277. package/src/commands/validate-driver.ts +2 -1
  278. package/src/commands/validate.ts +1 -1
  279. package/src/commands/watch-args.ts +6 -3
  280. package/src/commands/watch.ts +7 -6
  281. package/src/driver-heap.ts +65 -0
  282. package/src/index.ts +3 -0
  283. package/src/log-level.ts +42 -14
  284. package/src/spawn-data-server.ts +17 -5
  285. package/src/stderr-connection-logger.ts +18 -0
  286. package/src/testing/echo-server.ts +11 -2
  287. package/templates/client/gitignore +3 -0
  288. package/templates/client/src/__GRAMMAR_ID__-diagram-module.ts +82 -0
  289. package/templates/editorconfig +15 -0
  290. package/templates/gitignore +1 -0
  291. package/templates/host-browser/esbuild.mjs +82 -0
  292. package/templates/host-browser/gitignore +4 -0
  293. package/templates/host-browser/hydranium-mark.svg +40 -0
  294. package/templates/host-browser/index.html +287 -0
  295. package/templates/host-browser/playwright.config.mts +24 -0
  296. package/templates/host-browser/src/data-status.ts +15 -0
  297. package/templates/host-browser/src/diagram-save.ts +60 -0
  298. package/templates/host-browser/src/diagram.ts +121 -0
  299. package/templates/host-browser/src/editor.ts +116 -0
  300. package/templates/host-browser/src/log.ts +14 -0
  301. package/templates/host-browser/src/monaco-editor-core-worker.d.ts +7 -0
  302. package/templates/host-browser/src/monaco-editor-worker.ts +10 -0
  303. package/templates/host-browser/src/posix-path-shim.ts +23 -0
  304. package/templates/host-browser/src/shown-file.ts +61 -0
  305. package/templates/host-browser/src/workspace.ts +5 -0
  306. package/templates/host-browser/tsconfig.e2e.json +16 -0
  307. package/templates/host-browser/tsconfig.page.json +16 -0
  308. package/templates/host-browser/tsconfig.worker.json +16 -0
  309. package/templates/host-theia-browser/esbuild.mjs +24 -0
  310. package/templates/host-theia-browser/gitignore +9 -0
  311. package/templates/host-theia-browser/playwright.config.mts +42 -0
  312. package/templates/host-theia-browser/start.mjs +50 -0
  313. package/templates/host-theia-browser/tsconfig.e2e.json +16 -0
  314. package/templates/host-theia-electron/esbuild.mjs +27 -0
  315. package/templates/host-theia-electron/gitignore +8 -0
  316. package/templates/host-theia-electron/playwright.config.mts +12 -0
  317. package/templates/host-theia-electron/start.mjs +47 -0
  318. package/templates/host-theia-electron/tsconfig.e2e.json +16 -0
  319. package/templates/host-vscode/esbuild.mjs +38 -0
  320. package/templates/host-vscode/gitignore +7 -0
  321. package/templates/host-vscode/playwright.config.mts +14 -0
  322. package/templates/host-vscode/src/__GRAMMAR_ID__-diagram-editor.ts +19 -0
  323. package/templates/host-vscode/src/show-model-command.ts +24 -0
  324. package/templates/host-vscode/src/webview/__GRAMMAR_ID__-diagram.ts +18 -0
  325. package/templates/host-vscode/src/webview/diagram.css +40 -0
  326. package/templates/host-vscode/start.mjs +22 -0
  327. package/templates/host-vscode/test/content-server.cjs +40 -0
  328. package/templates/host-vscode/test/install-vscode.mts +21 -0
  329. package/templates/host-vscode/test/virtual-documents.spec.mts +28 -0
  330. package/templates/host-vscode/test/vscode-version.mts +10 -0
  331. package/templates/host-vscode/tsconfig.e2e.json +17 -0
  332. package/templates/host-vscode/tsconfig.json +19 -0
  333. package/templates/host-vscode/tsconfig.webview.json +17 -0
  334. package/templates/oxlintrc.json +4 -0
  335. package/templates/protocol/gitignore +3 -0
  336. package/templates/protocol/src/__GRAMMAR_ID__-diagram-types.ts +47 -0
  337. package/templates/server/gitignore +6 -0
  338. package/templates/server/src/glsp/__GRAMMAR_ID__/create-node-operation-handler.ts +62 -0
  339. package/templates/server/src/glsp/__GRAMMAR_ID__/delete-operation-handler.ts +63 -0
  340. package/templates/server/src/glsp/__GRAMMAR_ID__/gmodel-factory.ts +144 -0
  341. package/templates/server/src/glsp/__GRAMMAR_ID__/state.ts +18 -0
  342. package/templates/server/src/glsp/__GRAMMAR_ID__/storage.ts +16 -0
  343. package/templates/server/src/glsp/__GRAMMAR_ID__/submission-handler.ts +24 -0
  344. package/templates/server/src/grammar/common.langium +12 -0
  345. package/templates/server/src/language-server/__GRAMMAR_ID__-semantic-tokens.ts +10 -0
  346. package/templates/server/src/language-server/__GRAMMAR_ID__-serializer.ts +53 -0
  347. package/templates/server/src/language-server/__GRAMMAR_ID__-validation.ts +30 -0
  348. package/templates/server/src/language-server/ast.ts +23 -0
  349. package/templates/server/src/node/services.ts +16 -0
  350. package/templates/server/test/__GRAMMAR_ID__-diagram.test.ts +403 -0
  351. package/templates/server/test/__GRAMMAR_ID__-semantic-tokens.test.ts +34 -0
  352. package/templates/server/test/tsconfig.json +4 -0
  353. package/templates/server/tsconfig.test.json +10 -0
  354. package/templates/server/vitest.config.ts +14 -0
  355. package/templates/theia/gitignore +3 -0
  356. package/templates/theia/src/browser/__GRAMMAR_ID__-diagram-frontend-module.ts +65 -0
  357. package/templates/theia/src/browser/__PROJECT_ID__-connection-preload-module.ts +15 -0
  358. package/templates/theia/src/browser/__PROJECT_ID__-data-connection.ts +15 -0
  359. package/templates/theia/src/browser/__PROJECT_ID__-data-port.ts +11 -0
  360. package/templates/theia/src/browser/__PROJECT_ID__-glsp-client-contribution.ts +13 -0
  361. package/templates/theia/src/browser/__PROJECT_ID__-show-model-command.ts +51 -0
  362. package/templates/theia/src/common/__PROJECT_ID__-glsp-contribution.ts +3 -0
  363. package/templates/theia/src/node/__PROJECT_ID__-connection-backend-module.ts +12 -0
  364. package/templates/theia/src/node/__PROJECT_ID__-data-backend-module.ts +15 -0
  365. package/templates/theia/src/node/__PROJECT_ID__-glsp-backend-module.ts +16 -0
  366. package/templates/theia/style/diagram.css +20 -0
  367. package/templates/theia/tsconfig.json +24 -0
  368. package/templates/vscode-servers/gitignore +4 -0
  369. package/templates/vscode-servers/tsconfig.json +19 -0
  370. package/templates/workspace/nodes.__EXTENSION__ +5 -0
  371. package/templates/workspace/references.__EXTENSION__ +2 -0
@@ -32,6 +32,28 @@ export interface GenerateTransferModelOptions {
32
32
  * and not the other has the two disagreeing about the same grammar.
33
33
  */
34
34
  astBuilderFile?: string;
35
+ /**
36
+ * Destination for a generated test module binding `makeTransferNodeBuilder`
37
+ * from `@hydranium/protocol/testing` to the transfer model, as `transferNode`,
38
+ * the way {@link astBuilderFile} binds `astNode`. Belongs in a test tree:
39
+ * production code has no use for a builder that fills grammar defaults.
40
+ *
41
+ * When it sits in another package than {@link outFile}, it imports the model
42
+ * by that package's name, so the package's main entry must re-export the
43
+ * generated model, and with it each type's constant: an interface the entry
44
+ * narrows hides the generated constant of the same name unless the entry
45
+ * re-exports that constant too, and the builder keeps the generated
46
+ * interface unless the entry declares `<LanguageId>TransferTypes` with the
47
+ * narrowed entry.
48
+ */
49
+ transferBuilderFile?: string;
50
+ /**
51
+ * Destination for a generated module stating each grammar's language id and
52
+ * file extensions, read from the `module.ts` beside {@link astFile}. Lets a
53
+ * package without Langium, such as one shared with clients, name the ids
54
+ * without copying them by hand.
55
+ */
56
+ languagesFile?: string;
35
57
  /** Name used for the base element type in the output. Defaults to `TransferElement`. */
36
58
  elementTypeName?: string;
37
59
  /** Name used for the terminal-patterns const in the output. Defaults to `ModelTerminals`. */
@@ -63,10 +85,18 @@ export interface GenerateTransferModelOptions {
63
85
  regenCommand?: string;
64
86
  }
65
87
  /**
66
- * Generate once, then re-generate whenever the AST or augmentation file changes,
88
+ * Generate once, then re-generate whenever an input file changes (the AST, the
89
+ * augmentation file, and `module.ts` when a languages file is written),
67
90
  * until `signal` aborts (the CLI wires this to SIGINT). Regeneration is debounced
68
91
  * so an editor's multi-event save fires a single run, and a generation error is
69
92
  * logged without tearing down the watch. Resolves when the signal aborts.
93
+ * An already-aborted signal resolves immediately without generating or watching.
94
+ *
95
+ * Polled by path rather than watched through OS events: langium-cli removes
96
+ * its output directory and writes the files back, which an event watch, Node's
97
+ * and chokidar's alike, stops hearing after the first run. A run that finds an
98
+ * input missing is skipped, since the file's return is a change of its own, so
99
+ * nothing generates from a half-written output.
70
100
  */
71
101
  export declare function watchTransferModel(options: GenerateTransferModelOptions, context: {
72
102
  signal: AbortSignal;
@@ -94,10 +124,11 @@ export declare function parseTerminals(astSource: SourceFile, terminalsSourceNam
94
124
  }>;
95
125
  /**
96
126
  * Type-guard expression (against an `item: unknown`) for one member of a non-literal type-alias
97
- * union. Covers the full set of Langium data-type primitives: the `typeof`-checkable ones
98
- * (`string`/`number`/`boolean`/`bigint`) use `typeof`; `Date` — the one Langium primitive `typeof`
99
- * reports as `'object'` — uses `instanceof`; any other member is a named type whose generated
100
- * `is<Member>` guard is called.
127
+ * union. A string literal is compared as written, so the escapes Langium wrote stay valid.
128
+ * Covers the full set of Langium data-type primitives. The `typeof`-checkable ones
129
+ * (`string`/`number`/`boolean`/`bigint`) use `typeof`, and `Date`, the one Langium primitive
130
+ * `typeof` reports as `'object'`, uses `instanceof`. Any other member is a named type whose
131
+ * generated `is<Member>` guard is called.
101
132
  */
102
133
  export declare function typeAliasMemberGuard(member: string): string;
103
134
  /**
@@ -110,10 +141,11 @@ export declare function typeAliasMemberGuard(member: string): string;
110
141
  * name.
111
142
  *
112
143
  * Each referenced member is inlined and de-duplicated, preserving first-seen
113
- * order. The definition is returned UNCHANGED when it holds no
114
- * namespace-qualified reference (the single-language case) or when any referenced
115
- * member is not a pure string-literal union — passing it through rather than
116
- * emitting a half-resolved union.
144
+ * order; when no member holds a literal, the union is `never`. The definition
145
+ * is returned UNCHANGED when it holds no namespace-qualified reference (the
146
+ * single-language case) or when any referenced member is not a pure
147
+ * string-literal union — passing it through rather than emitting a
148
+ * half-resolved union.
117
149
  */
118
150
  export declare function resolveNamespacedStringUnion(astSource: SourceFile, definition: string): string;
119
151
  //# sourceMappingURL=generate-transfer-model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-transfer-model.d.ts","sourceRoot":"","sources":["../../src/commands/generate-transfer-model.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAKlF,OAAO,EAOJ,KAAK,UAAU,EACjB,MAAM,UAAU,CAAC;AAElB;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA4B;IAC1C,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACxB;AAwBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCzH;AAED,wCAAwC;AACxC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,CAwRjF;AAwFD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC3B,SAAS,EAAE,UAAU,EACrB,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B1C;AAsFD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAY3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB9F"}
1
+ {"version":3,"file":"generate-transfer-model.d.ts","sourceRoot":"","sources":["../../src/commands/generate-transfer-model.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAOlF,OAAO,EAQJ,KAAK,UAAU,EACjB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA4B;IAC1C,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACxB;AAqCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDzH;AAOD,wCAAwC;AACxC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,CAsSjF;AA2LD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC3B,SAAS,EAAE,UAAU,EACrB,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B1C;AAsFD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAe3D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB9F"}
@@ -6,10 +6,13 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import * as fs from 'fs';
9
+ import * as fs from 'node:fs';
10
10
  import * as path from 'path';
11
11
  import { emitAstBuilder } from './generate-ast-builder.js';
12
- import { Project, SyntaxKind } from 'ts-morph';
12
+ import { emitLanguages } from './generate-languages.js';
13
+ import { emitTransferBuilder } from './generate-transfer-builder.js';
14
+ import { Project, SyntaxKind, ts } from 'ts-morph';
15
+ import { describeError } from '@hydranium/protocol';
13
16
  const DEFAULT_ELEMENT_TYPE_NAME = 'TransferElement';
14
17
  const DEFAULT_TERMINALS_NAME = 'ModelTerminals';
15
18
  const DEFAULT_REGEN_COMMAND = 'Run the hydranium-cli generate-transfer-model command to regenerate.';
@@ -19,35 +22,68 @@ const DEFAULT_REGEN_COMMAND = 'Run the hydranium-cli generate-transfer-model com
19
22
  * `<LanguageId>Terminals` const then spreads these.
20
23
  */
21
24
  const NAMESPACED_TERMINALS_NAME = 'Terminals';
25
+ /** How often `--watch` polls its input files. */
26
+ const WATCH_POLL_INTERVAL_MS = 250;
27
+ const WATCH_DEBOUNCE_MS = 150;
22
28
  /**
23
- * Generate once, then re-generate whenever the AST or augmentation file changes,
29
+ * Generate once, then re-generate whenever an input file changes (the AST, the
30
+ * augmentation file, and `module.ts` when a languages file is written),
24
31
  * until `signal` aborts (the CLI wires this to SIGINT). Regeneration is debounced
25
32
  * so an editor's multi-event save fires a single run, and a generation error is
26
33
  * logged without tearing down the watch. Resolves when the signal aborts.
34
+ * An already-aborted signal resolves immediately without generating or watching.
35
+ *
36
+ * Polled by path rather than watched through OS events: langium-cli removes
37
+ * its output directory and writes the files back, which an event watch, Node's
38
+ * and chokidar's alike, stops hearing after the first run. A run that finds an
39
+ * input missing is skipped, since the file's return is a change of its own, so
40
+ * nothing generates from a half-written output.
27
41
  */
28
42
  export function watchTransferModel(options, context) {
43
+ if (context.signal.aborted) {
44
+ return Promise.resolve();
45
+ }
29
46
  const runSafely = () => {
30
47
  try {
31
48
  generateTransferModel(options);
32
49
  }
33
50
  catch (error) {
34
- console.error(`Transfer-model generation failed: ${error instanceof Error ? error.message : String(error)}`);
51
+ console.error(`Transfer-model generation failed: ${describeError(error)}`);
35
52
  }
36
53
  };
37
54
  runSafely();
38
- console.log(`Watching ${options.astFile} and ${options.augmentationFile} for changes. Press Ctrl-C to stop.`);
55
+ // A grammar's language id and extensions reach only `module.ts`, so the
56
+ // languages module would go stale without it.
57
+ const watched = [
58
+ options.astFile,
59
+ options.augmentationFile,
60
+ ...(options.languagesFile !== undefined ? [languagesModuleFile(options)] : [])
61
+ ];
39
62
  let timer;
40
- const scheduleRerun = () => {
63
+ const rerun = () => {
64
+ if (watched.every(file => fs.existsSync(file))) {
65
+ runSafely();
66
+ }
67
+ };
68
+ // The poll reports every stat it takes; a replaced file has a new inode, a
69
+ // rewritten one a new mtime, a removed or returning one zeroed stats.
70
+ const onPoll = (current, previous) => {
71
+ if (current.ino === previous.ino && current.mtimeMs === previous.mtimeMs && current.size === previous.size) {
72
+ return;
73
+ }
41
74
  if (timer) {
42
75
  clearTimeout(timer);
43
76
  }
44
- timer = setTimeout(runSafely, 150);
77
+ timer = setTimeout(rerun, WATCH_DEBOUNCE_MS);
45
78
  };
46
- const watchers = [options.astFile, options.augmentationFile].map(file => fs.watch(file, scheduleRerun));
79
+ for (const file of watched) {
80
+ fs.watchFile(file, { interval: WATCH_POLL_INTERVAL_MS }, onPoll);
81
+ }
82
+ console.log(`Watching ${watched.join(', ')} for changes. Press Ctrl-C to stop.`);
47
83
  return new Promise(resolve => {
48
84
  context.signal.addEventListener('abort', () => {
49
- for (const watcher of watchers) {
50
- watcher.close();
85
+ for (const file of watched) {
86
+ fs.unwatchFile(file, onPoll);
51
87
  }
52
88
  if (timer) {
53
89
  clearTimeout(timer);
@@ -56,6 +92,10 @@ export function watchTransferModel(options, context) {
56
92
  }, { once: true });
57
93
  });
58
94
  }
95
+ /** The langium-cli `module.ts` beside the AST file, which the languages module is read from. */
96
+ function languagesModuleFile(options) {
97
+ return path.join(path.dirname(options.astFile), 'module.ts');
98
+ }
59
99
  /** Run the transfer-model generator. */
60
100
  export function generateTransferModel(options) {
61
101
  const elementTypeName = options.elementTypeName ?? DEFAULT_ELEMENT_TYPE_NAME;
@@ -93,9 +133,13 @@ export function generateTransferModel(options) {
93
133
  if (skipTypeAliases.has(name)) {
94
134
  continue;
95
135
  }
96
- const typeText = alias.getTypeNode()?.getText();
97
- if (typeText) {
98
- typeAliases.push({ name, definition: mapType(resolveNamespacedStringUnion(astSource, typeText), elementTypeName) });
136
+ const typeNode = alias.getTypeNode();
137
+ if (typeNode) {
138
+ typeAliases.push({
139
+ name,
140
+ definition: mapType(resolveNamespacedStringUnion(astSource, typeNode.getText()), elementTypeName),
141
+ members: unionMembers(typeNode).map(member => mapType(member, elementTypeName))
142
+ });
99
143
  }
100
144
  }
101
145
  // 5. Parse augmentations from the consumer's `declare module` blocks.
@@ -109,7 +153,6 @@ export function generateTransferModel(options) {
109
153
  // 7. Classify interfaces: abstract (union $type) vs concrete (literal $type).
110
154
  const interfaceNames = [];
111
155
  const abstractTypes = new Set();
112
- const concreteTypes = new Set();
113
156
  for (const name of Object.keys(reflectionTypes)) {
114
157
  const iface = astInterfaces.get(name);
115
158
  if (!iface) {
@@ -121,9 +164,6 @@ export function generateTransferModel(options) {
121
164
  if (typeText.includes('|')) {
122
165
  abstractTypes.add(name);
123
166
  }
124
- else {
125
- concreteTypes.add(name);
126
- }
127
167
  }
128
168
  // 8. Resolve extends-chains. Filter out type-alias supertypes (Langium reflection
129
169
  // includes them for the grammar hierarchy but TypeScript can't extend a union
@@ -158,12 +198,13 @@ export function generateTransferModel(options) {
158
198
  out.push(' readonly $type: string;');
159
199
  out.push('}');
160
200
  out.push('');
161
- // Type constants for concrete types.
201
+ // Langium's per-type constant under the same name, for every reflection type
202
+ // the model emits, abstract ones and unions included.
203
+ const reflectedTypes = Object.keys(reflectionTypes).filter(name => astInterfaces.has(name) || typeAliasNames.has(name));
162
204
  out.push('// --- Type Constants ---');
163
- for (const name of sorted) {
164
- if (concreteTypes.has(name)) {
165
- out.push(`export const ${name}Type = '${name}';`);
166
- }
205
+ for (const name of reflectedTypes) {
206
+ const keys = reflectionTypes[name].properties.map(property => `${property.name}: '${property.name}'`);
207
+ out.push(`export const ${name} = { ${[`$type: '${name}'`, ...keys].join(', ')} } as const;`);
167
208
  }
168
209
  out.push('');
169
210
  // Type aliases.
@@ -173,7 +214,7 @@ export function generateTransferModel(options) {
173
214
  out.push(`export type ${name} = ${definition};`);
174
215
  const literals = extractStringLiterals(definition);
175
216
  if (literals) {
176
- const items = literals.map(l => `'${l}'`).join(', ');
217
+ const items = literals.map(quote).join(', ');
177
218
  out.push(`export const ${name}Values = [${items}] as const;`);
178
219
  }
179
220
  }
@@ -201,7 +242,7 @@ export function generateTransferModel(options) {
201
242
  out.push(` readonly $type: ${typeValue};`);
202
243
  }
203
244
  else {
204
- out.push(` readonly $type: typeof ${name}Type;`);
245
+ out.push(` readonly $type: '${name}';`);
205
246
  }
206
247
  const emitted = new Set(['$type']);
207
248
  for (const prop of iface.getProperties()) {
@@ -257,24 +298,24 @@ export function generateTransferModel(options) {
257
298
  out.push(` return is${elementTypeName}(item) && (${checks});`);
258
299
  }
259
300
  else {
260
- out.push(` return is${elementTypeName}(item) && item.$type === ${name}Type;`);
301
+ out.push(` return is${elementTypeName}(item) && item.$type === '${name}';`);
261
302
  }
262
303
  out.push('}');
263
304
  out.push('');
264
305
  }
265
- for (const { name, definition } of typeAliases) {
306
+ for (const { name, definition, members } of typeAliases) {
266
307
  const literals = extractStringLiterals(definition);
267
308
  if (literals) {
268
- const checks = literals.map(l => `item === '${l}'`).join(' || ');
309
+ const checks = literals.map(l => `item === ${quote(l)}`).join(' || ') || 'false';
269
310
  out.push(`export function is${name}(item: unknown): item is ${name} {`);
270
311
  out.push(` return ${checks};`);
271
312
  out.push('}');
272
313
  out.push('');
273
314
  }
274
315
  else {
275
- // Union (or single) of named members and/or Langium data-type primitives. Each member maps
276
- // to its own check (typeof / instanceof / named-guard) via typeAliasMemberGuard.
277
- const members = definition.split('|').map(s => s.trim());
316
+ // Union (or single) of named members, Langium data-type primitives and the string
317
+ // literals a data-type rule's keywords add beside them. Each member maps to its own
318
+ // check via typeAliasMemberGuard.
278
319
  const checks = members.map(typeAliasMemberGuard).join(' || ');
279
320
  out.push(`export function is${name}(item: unknown): item is ${name} {`);
280
321
  out.push(` return ${checks};`);
@@ -282,6 +323,8 @@ export function generateTransferModel(options) {
282
323
  out.push('');
283
324
  }
284
325
  }
326
+ out.push(...renderTypeMap(languageId, reflectedTypes));
327
+ out.push(...renderReflection(languageId, elementTypeName, reflectedTypes, reflectionTypes));
285
328
  // Coverage check: warn on reflection types not emitted.
286
329
  const processedSet = new Set(sorted);
287
330
  for (const name of Object.keys(reflectionTypes)) {
@@ -310,6 +353,73 @@ export function generateTransferModel(options) {
310
353
  console.warn(`Warning: no '${languageId}AstType' alias in ${options.astFile}; skipping the AST builder.`);
311
354
  }
312
355
  }
356
+ if (options.transferBuilderFile !== undefined) {
357
+ emitTransferBuilder({ outFile: options.transferBuilderFile, transferModelFile: options.outFile, languageId, regenCommand });
358
+ }
359
+ if (options.languagesFile !== undefined) {
360
+ const moduleSource = project.addSourceFileAtPath(languagesModuleFile(options));
361
+ emitLanguages(moduleSource, { outFile: options.languagesFile, regenCommand });
362
+ }
363
+ }
364
+ /**
365
+ * `<LanguageId>TransferTypes`, each `$type` to its interface, so an encoder or a
366
+ * builder can type a node by the grammar the way Langium's `<LanguageId>AstType`
367
+ * does on the AST side.
368
+ */
369
+ function renderTypeMap(languageId, types) {
370
+ return ['// --- Type Map ---', `export type ${languageId}TransferTypes = {`, ...types.map(name => ` ${name}: ${name};`), '};', ''];
371
+ }
372
+ /**
373
+ * `<LanguageId>TransferReflection`, the grammar metadata with the method names
374
+ * Langium's `AstReflection` uses. A plain object with no framework import, so
375
+ * the file stays self-contained, and nothing references it but its importers:
376
+ * the `is` guards test `$type` directly, so a guard never pulls it in. `types`
377
+ * keeps its literal type, which is how the transfer-node builder tells which
378
+ * fields the grammar defaults.
379
+ */
380
+ function renderReflection(languageId, elementTypeName, types, reflectionTypes) {
381
+ const metaData = `${languageId}TransferTypeMetaData`;
382
+ // Index signatures, not `Readonly<Record<…>>`: a grammar type may be named
383
+ // `Record` or `Readonly`, which Langium does not reserve as it does `Object`.
384
+ const out = [
385
+ '// --- Reflection ---',
386
+ `export interface ${metaData} {`,
387
+ ' readonly name: string;',
388
+ ' readonly properties: {',
389
+ ' readonly [name: string]: { readonly name: string; readonly defaultValue?: unknown; readonly optional?: boolean; readonly referenceType?: string };',
390
+ ' };',
391
+ ' readonly superTypes: readonly string[];',
392
+ '}',
393
+ '',
394
+ `export const ${languageId}TransferReflection = {`,
395
+ ' types: {'
396
+ ];
397
+ for (const name of types) {
398
+ const { properties, superTypes } = reflectionTypes[name];
399
+ const propertyEntries = properties.map(property => {
400
+ const fields = [`name: '${property.name}'`];
401
+ if (property.defaultValue !== undefined) {
402
+ fields.push(`defaultValue: ${property.defaultValue}`);
403
+ }
404
+ if (property.optional) {
405
+ fields.push('optional: true');
406
+ }
407
+ if (property.referenceType !== undefined) {
408
+ fields.push(`referenceType: '${property.referenceType}'`);
409
+ }
410
+ return `${property.name}: { ${fields.join(', ')} }`;
411
+ });
412
+ const supers = superTypes.map(superType => `'${superType}'`).join(', ');
413
+ const propertiesText = propertyEntries.length > 0 ? `{ ${propertyEntries.join(', ')} }` : '{}';
414
+ out.push(` ${name}: { name: '${name}', properties: ${propertiesText}, superTypes: [${supers}] },`);
415
+ }
416
+ out.push(` } as const satisfies { readonly [type: string]: ${metaData} },`, '', ' getAllTypes(): string[] {', ' return Object.keys(this.types);', ' },', '',
417
+ // An own-property check, not `types[type] ?? …`: a name the object
418
+ // prototype carries, `toString` say, would read the inherited member and
419
+ // make `isSubtype` throw. `hasOwnProperty.call` rather than `Object.hasOwn`
420
+ // because the adopter's protocol package may target a lib without it.
421
+ ` getTypeMetaData(type: string): ${metaData} {`, ' return Object.prototype.hasOwnProperty.call(this.types, type)', ` ? (this.types as { readonly [type: string]: ${metaData} })[type]`, ' : { name: type, properties: {}, superTypes: [] };', ' },', '', ' isSubtype(subtype: string, supertype: string): boolean {', ' return subtype === supertype || this.getTypeMetaData(subtype).superTypes.some(type => this.isSubtype(type, supertype));', ' },', '', ' getAllSubTypes(type: string): string[] {', ' return this.getAllTypes().filter(candidate => this.isSubtype(candidate, type));', ' },', '', ' isInstance(item: unknown, type: string): boolean {', ` return is${elementTypeName}(item) && this.isSubtype(item.$type, type);`, ' }', '};', '');
422
+ return out;
313
423
  }
314
424
  // ---------------------------------------------------------------------------
315
425
  // AST source parsing helpers
@@ -348,10 +458,11 @@ function findAstReflectionClass(source) {
348
458
  throw new Error('Could not find a class extending AbstractAstReflection in the AST source.');
349
459
  }
350
460
  /**
351
- * Extract `superTypes` for each entry of the reflection class's `types` property
352
- * initializer. We rely on the Langium convention that `X.$type === 'X'`, so a
353
- * supertype expression resolves to the literal type-name string by stripping
354
- * `.$type`.
461
+ * Extract `superTypes` and the grammar properties for each entry of the
462
+ * reflection class's `types` property initializer. We rely on the Langium
463
+ * convention that `X.$type === 'X'` and `X.p === 'p'`, so a type expression
464
+ * resolves to the literal type-name string by stripping `.$type`, and a
465
+ * property's name is its key.
355
466
  */
356
467
  function parseReflectionTypes(reflectionClass) {
357
468
  const result = {};
@@ -372,29 +483,31 @@ function parseReflectionTypes(reflectionClass) {
372
483
  const value = entry.getLastChildByKind(SyntaxKind.ObjectLiteralExpression);
373
484
  const supersProp = value?.getProperty('superTypes');
374
485
  const supersInit = supersProp?.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression);
375
- const superTypes = [];
376
- if (supersInit) {
377
- for (const element of supersInit.getElements()) {
378
- const text = element.getText();
379
- const dotMatch = /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text);
380
- if (dotMatch) {
381
- superTypes.push(dotMatch[1]);
382
- continue;
383
- }
384
- // Bare identifier or quoted string fall-back.
385
- const stringMatch = /^['"](.+)['"]$/.exec(text);
386
- if (stringMatch) {
387
- superTypes.push(stringMatch[1]);
388
- }
389
- else {
390
- superTypes.push(text);
391
- }
392
- }
393
- }
394
- result[name] = { superTypes };
486
+ const superTypes = supersInit?.getElements().map(element => typeNameOf(element.getText())) ?? [];
487
+ result[name] = { superTypes, properties: parseReflectionProperties(value) };
395
488
  }
396
489
  return result;
397
490
  }
491
+ function parseReflectionProperties(entry) {
492
+ const propertiesInit = entry?.getProperty('properties')?.getFirstChildByKind(SyntaxKind.ObjectLiteralExpression);
493
+ const properties = [];
494
+ for (const property of propertiesInit?.getChildrenOfKind(SyntaxKind.PropertyAssignment) ?? []) {
495
+ const meta = property.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
496
+ const field = (key) => meta?.getProperty(key)?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer()?.getText();
497
+ const referenceType = field('referenceType');
498
+ properties.push({
499
+ name: property.getName(),
500
+ defaultValue: field('defaultValue'),
501
+ optional: field('optional') === 'true',
502
+ referenceType: referenceType === undefined ? undefined : typeNameOf(referenceType)
503
+ });
504
+ }
505
+ return properties;
506
+ }
507
+ /** `X.$type` or a quoted `'X'` to `X`; any other text passes through. */
508
+ function typeNameOf(text) {
509
+ return /^([A-Za-z_$][\w$]*)\.\$type$/.exec(text)?.[1] ?? /^['"](.+)['"]$/.exec(text)?.[1] ?? text;
510
+ }
398
511
  /**
399
512
  * Collect terminal regex literals from the AST file.
400
513
  *
@@ -513,12 +626,16 @@ function mapType(text, elementTypeName) {
513
626
  }
514
627
  /**
515
628
  * Type-guard expression (against an `item: unknown`) for one member of a non-literal type-alias
516
- * union. Covers the full set of Langium data-type primitives: the `typeof`-checkable ones
517
- * (`string`/`number`/`boolean`/`bigint`) use `typeof`; `Date` — the one Langium primitive `typeof`
518
- * reports as `'object'` — uses `instanceof`; any other member is a named type whose generated
519
- * `is<Member>` guard is called.
629
+ * union. A string literal is compared as written, so the escapes Langium wrote stay valid.
630
+ * Covers the full set of Langium data-type primitives. The `typeof`-checkable ones
631
+ * (`string`/`number`/`boolean`/`bigint`) use `typeof`, and `Date`, the one Langium primitive
632
+ * `typeof` reports as `'object'`, uses `instanceof`. Any other member is a named type whose
633
+ * generated `is<Member>` guard is called.
520
634
  */
521
635
  export function typeAliasMemberGuard(member) {
636
+ if (member.startsWith("'") || member.startsWith('"')) {
637
+ return `item === ${member}`;
638
+ }
522
639
  switch (member) {
523
640
  case 'string':
524
641
  case 'number':
@@ -541,10 +658,11 @@ export function typeAliasMemberGuard(member) {
541
658
  * name.
542
659
  *
543
660
  * Each referenced member is inlined and de-duplicated, preserving first-seen
544
- * order. The definition is returned UNCHANGED when it holds no
545
- * namespace-qualified reference (the single-language case) or when any referenced
546
- * member is not a pure string-literal union — passing it through rather than
547
- * emitting a half-resolved union.
661
+ * order; when no member holds a literal, the union is `never`. The definition
662
+ * is returned UNCHANGED when it holds no namespace-qualified reference (the
663
+ * single-language case) or when any referenced member is not a pure
664
+ * string-literal union — passing it through rather than emitting a
665
+ * half-resolved union.
548
666
  */
549
667
  export function resolveNamespacedStringUnion(astSource, definition) {
550
668
  const references = [...definition.matchAll(/\b([A-Za-z_]\w*)\.([A-Za-z_]\w*)\b/g)];
@@ -566,15 +684,54 @@ export function resolveNamespacedStringUnion(astSource, definition) {
566
684
  }
567
685
  }
568
686
  }
569
- return literals.map(literal => `'${literal}'`).join(' | ');
687
+ return literals.length > 0 ? literals.map(quote).join(' | ') : 'never';
688
+ }
689
+ /**
690
+ * The members of a type-alias union as TypeScript parses them. If a data-type rule groups
691
+ * alternatives in parentheses, the alias Langium generates contains the same group, as in
692
+ * `'a' | ('b' | Keyword)`, so each group is flattened into its members. Splitting the text on
693
+ * `|` breaks such a group and any keyword that contains `|`, such as `'||'`, and the
694
+ * resulting guard does not compile.
695
+ */
696
+ function unionMembers(typeNode) {
697
+ if (typeNode.isKind(SyntaxKind.UnionType)) {
698
+ return typeNode.getTypeNodes().flatMap(member => unionMembers(member));
699
+ }
700
+ if (typeNode.isKind(SyntaxKind.ParenthesizedType)) {
701
+ return unionMembers(typeNode.getTypeNode());
702
+ }
703
+ return [typeNode.getText()];
570
704
  }
571
- /** Extract string literals from a union definition, or undefined if not pure literals. */
705
+ /**
706
+ * The values of a union of string literals, or undefined when the definition holds anything
707
+ * else. The TypeScript scanner decodes each literal, so a keyword with a quote in it reads the
708
+ * same whichever quotes and escapes Langium wrote it with. Langium writes the keyword names of
709
+ * a language without keywords as `never`, which adds no value.
710
+ */
572
711
  function extractStringLiterals(definition) {
573
- const stringLiteral = '(?:\'[^\']*\'|"[^"]*")';
574
- if (!new RegExp(`^\\|?\\s*${stringLiteral}(\\s*\\|\\s*${stringLiteral})*$`).test(definition.trim())) {
575
- return undefined;
712
+ const scanner = ts.createScanner(ts.ScriptTarget.Latest, true, ts.LanguageVariant.Standard, definition);
713
+ const literals = [];
714
+ let members = 0;
715
+ for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFileToken; token = scanner.scan()) {
716
+ if (token === ts.SyntaxKind.StringLiteral) {
717
+ literals.push(scanner.getTokenValue());
718
+ members++;
719
+ }
720
+ else if (token === ts.SyntaxKind.NeverKeyword) {
721
+ members++;
722
+ }
723
+ else if (token !== ts.SyntaxKind.BarToken) {
724
+ return undefined;
725
+ }
576
726
  }
577
- return [...definition.matchAll(/'([^']*)'|"([^"]*)"/g)].map(m => m[1] ?? m[2]);
727
+ return members > 0 ? literals : undefined;
728
+ }
729
+ /**
730
+ * `value` as a string literal in the single quotes the generated file uses. A keyword can
731
+ * contain a quote or a backslash, and either one ends or escapes the literal unless escaped.
732
+ */
733
+ function quote(value) {
734
+ return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
578
735
  }
579
736
  function topologicalSort(names, getDeps) {
580
737
  const visited = new Set();