@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98

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 (397) hide show
  1. package/README.md +38 -15
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/_adapter.d.mts +50 -0
  4. package/api/build/_adapter.mjs +60 -0
  5. package/api/build/build.doc.mjs +14 -7
  6. package/api/build/build.test.mjs +184 -6
  7. package/api/build/build.type.d.mts +57 -2
  8. package/api/build/build.type.mjs +30 -7
  9. package/api/build/help/help.d.mts +4 -0
  10. package/api/build/help/help.mjs +24 -11
  11. package/api/build/kit/kit.d.mts +4 -1
  12. package/api/build/kit/kit.mjs +165 -49
  13. package/api/build/kit/rank.d.mts +44 -0
  14. package/api/build/kit/rank.mjs +432 -0
  15. package/api/build/kit/rank.test.mjs +196 -0
  16. package/api/component/_adapter.d.mts +6 -12
  17. package/api/component/_adapter.mjs +20 -10
  18. package/api/component/component.doc.mjs +1 -0
  19. package/api/component/component.mjs +60 -12
  20. package/api/component/list/list.mjs +3 -2
  21. package/api/discover/discover.doc.mjs +1 -0
  22. package/api/docs/_adapter.d.mts +252 -34
  23. package/api/docs/_adapter.mjs +923 -132
  24. package/api/docs/detail/detail.mjs +11 -3
  25. package/api/docs/detail/section/section.mjs +24 -13
  26. package/api/docs/detail/section/section.test.mjs +15 -6
  27. package/api/docs/docs.d.mts +5 -3
  28. package/api/docs/docs.doc.mjs +39 -17
  29. package/api/docs/docs.mjs +44 -8
  30. package/api/docs/docs.test.mjs +158 -4
  31. package/api/docs/docs.type.d.mts +181 -2
  32. package/api/docs/docs.type.mjs +117 -3
  33. package/api/docs/index/index.mjs +11 -3
  34. package/api/docs/index/index.test.mjs +1 -1
  35. package/api/docs/integration-tree.test.mjs +555 -0
  36. package/api/docs/integrationDocs.test.mjs +14 -14
  37. package/api/docs/list/list.mjs +28 -12
  38. package/api/docs/node/node.d.mts +43 -0
  39. package/api/docs/node/node.mjs +192 -0
  40. package/api/docs/reference-blocks.test.mjs +406 -0
  41. package/api/doctor/doctor.d.mts +54 -4
  42. package/api/doctor/doctor.doc.mjs +1 -0
  43. package/api/doctor/doctor.mjs +331 -16
  44. package/api/doctor/doctor.test.mjs +420 -7
  45. package/api/gap-report/gap-report.doc.mjs +1 -0
  46. package/api/hook/_adapter.mjs +19 -5
  47. package/api/hook/hook.doc.mjs +1 -0
  48. package/api/hook/list/list.d.mts +1 -1
  49. package/api/hook/list/list.mjs +69 -17
  50. package/api/index.d.mts +1 -1
  51. package/api/index.mjs +1 -0
  52. package/api/init/init.doc.mjs +2 -1
  53. package/api/integration/add-contribution.d.mts +2 -1
  54. package/api/integration/add-contribution.mjs +114 -12
  55. package/api/integration/add-contribution.test.mjs +174 -3
  56. package/api/integration/add-theme.mjs +34 -64
  57. package/api/integration/add-theme.test.mjs +105 -21
  58. package/api/integration/authoring-checks.mjs +138 -28
  59. package/api/integration/authoring-checks.test.mjs +179 -7
  60. package/api/integration/authoring-checks.type.mjs +6 -1
  61. package/api/integration/integration-authoring.type.d.mts +2 -0
  62. package/api/integration/integration-authoring.type.mjs +2 -0
  63. package/api/integration/integration-block-exports.test.mjs +10 -6
  64. package/api/integration/integrationAdd.doc.mjs +7 -0
  65. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  66. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  67. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  68. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  69. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  70. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  71. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  72. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  73. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  74. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  75. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  76. package/api/integration/pack-check.mjs +34 -0
  77. package/api/integration/pack-check.test.mjs +138 -47
  78. package/api/integration/pack-check.type.d.mts +26 -2
  79. package/api/integration/pack-check.type.mjs +14 -1
  80. package/api/integration/summarizeIssues.doc.mjs +1 -0
  81. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  82. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  83. package/api/integration/validate-integration.mjs +50 -102
  84. package/api/integration/validate-integration.test.mjs +88 -26
  85. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  86. package/api/integration/validateIntegration.doc.mjs +2 -1
  87. package/api/json/assertResponse.doc.mjs +1 -0
  88. package/api/json/index.ts +2 -0
  89. package/api/json/isError.doc.mjs +1 -0
  90. package/api/json/parseResponse.doc.mjs +1 -0
  91. package/api/layout/_adapter.mjs +20 -5
  92. package/api/layout/layoutCheck.doc.mjs +1 -0
  93. package/api/layout/layoutExpand.doc.mjs +1 -0
  94. package/api/layout/layoutGrammar.doc.mjs +1 -0
  95. package/api/search/search.d.mts +59 -1
  96. package/api/search/search.doc.mjs +5 -3
  97. package/api/search/search.mjs +458 -77
  98. package/api/search/search.test.mjs +91 -2
  99. package/api/search/search.type.d.mts +13 -1
  100. package/api/search/search.type.mjs +4 -1
  101. package/api/swizzle/swizzle.doc.mjs +1 -0
  102. package/api/template/list/list.mjs +1 -0
  103. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  104. package/api/template/template-integration.test.mjs +1072 -3
  105. package/api/template/template-suffix.test.mjs +41 -21
  106. package/api/template/template.doc.mjs +28 -7
  107. package/api/template/template.mjs +45 -8
  108. package/api/template/template.type.d.mts +6 -8
  109. package/api/template/template.type.mjs +3 -2
  110. package/api/theme/_adapter.d.mts +2 -3
  111. package/api/theme/_adapter.mjs +4 -5
  112. package/api/theme/add/add.binary.test.mjs +10 -17
  113. package/api/theme/add/add.test.mjs +14 -1
  114. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  115. package/api/theme/integration-themes.test.mjs +39 -28
  116. package/api/theme/list/list.test.mjs +19 -20
  117. package/api/theme/listThemes.doc.mjs +6 -5
  118. package/api/theme/template/template.test.mjs +5 -0
  119. package/api/theme/themeAdd.doc.mjs +4 -3
  120. package/api/theme/themeBuild.doc.mjs +1 -0
  121. package/api/theme/themeList.doc.mjs +6 -3
  122. package/api/theme/themeListAvailable.doc.mjs +5 -3
  123. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  124. package/api/theme/themeTargets.doc.mjs +1 -0
  125. package/api/theme/themeTemplate.doc.mjs +2 -1
  126. package/api/upgrade/_adapter.d.mts +32 -5
  127. package/api/upgrade/_adapter.mjs +97 -69
  128. package/api/upgrade/provider-agreement.test.mjs +152 -0
  129. package/api/upgrade/run/run.mjs +356 -59
  130. package/api/upgrade/upgrade.doc.mjs +6 -1
  131. package/api/upgrade/upgrade.type.d.mts +34 -0
  132. package/api/upgrade/upgrade.type.mjs +15 -0
  133. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  134. package/assets/codemods/integration-discovery.mjs +8 -2
  135. package/assets/codemods/integration-discovery.test.mjs +15 -0
  136. package/assets/codemods/integration-runner.mjs +56 -4
  137. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  138. package/assets/codemods/run-codemod.mjs +177 -34
  139. package/assets/codemods/runner.mjs +350 -102
  140. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  141. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  142. package/assets/codemods/transforms/next/index.mjs +19 -1
  143. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  144. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  145. package/assets/docs/getting-started.doc.mjs +2 -2
  146. package/assets/docs/layout.doc.dense.mjs +2 -2
  147. package/assets/docs/layout.doc.mjs +1 -1
  148. package/assets/docs/principles.doc.mjs +6 -6
  149. package/assets/docs/styling-libraries.doc.mjs +3 -3
  150. package/assets/docs/styling.doc.mjs +4 -4
  151. package/assets/docs/theme.doc.mjs +5 -5
  152. package/assets/docs/tokens.doc.mjs +1 -1
  153. package/assets/docs/tree/api.doc.mjs +30 -0
  154. package/assets/docs/tree/cli.doc.mjs +23 -0
  155. package/assets/docs/tree/commands.doc.mjs +25 -0
  156. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +66 -38
  157. package/assets/docs/tree/integrations.test.mjs +62 -0
  158. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  159. package/assets/docs/working-with-ai.doc.mjs +3 -3
  160. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  161. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  162. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  163. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  164. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  165. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  166. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  167. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  168. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  169. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  170. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  171. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  172. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  173. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  174. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  175. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  176. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  177. package/authoring/codemod/codemod.doc.mjs +1 -1
  178. package/authoring/codemod/type.ts +12 -0
  179. package/authoring/config/config.doc.mjs +1 -1
  180. package/authoring/debug/debug.doc.d.mts +11 -0
  181. package/authoring/debug/debug.doc.mjs +182 -0
  182. package/authoring/debug/parse.d.mts +3 -3
  183. package/authoring/doctypes/_schema.d.mts +119 -117
  184. package/authoring/doctypes/_schema.mjs +54 -3
  185. package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
  186. package/authoring/doctypes/base/type.ts +6 -5
  187. package/authoring/doctypes/command/command.doc.mjs +1 -1
  188. package/authoring/doctypes/command/type.ts +2 -2
  189. package/authoring/doctypes/component/type.ts +2 -2
  190. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  191. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  192. package/authoring/doctypes/enum/type.ts +1 -1
  193. package/authoring/doctypes/function/function.doc.mjs +3 -2
  194. package/authoring/doctypes/function/type.ts +3 -2
  195. package/authoring/doctypes/hook/type.ts +2 -2
  196. package/authoring/doctypes/load-contract.test.mjs +28 -2
  197. package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
  198. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  199. package/authoring/doctypes/namespace/type.ts +5 -2
  200. package/authoring/doctypes/parse.d.mts +4 -2
  201. package/authoring/doctypes/parse.mjs +10 -5
  202. package/authoring/doctypes/reference/reference.doc.mjs +35 -6
  203. package/authoring/doctypes/reference/type.ts +30 -13
  204. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  205. package/authoring/doctypes/schema/type.ts +1 -2
  206. package/authoring/doctypes/template/parse.d.mts +2 -0
  207. package/authoring/doctypes/template/parse.mjs +4 -0
  208. package/authoring/doctypes/template/parse.test.mjs +18 -0
  209. package/authoring/doctypes/template/template.doc.mjs +9 -3
  210. package/authoring/doctypes/template/type.ts +8 -0
  211. package/authoring/doctypes/theme/parse.d.mts +35 -0
  212. package/authoring/doctypes/theme/parse.mjs +76 -0
  213. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  214. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  215. package/authoring/doctypes/theme/type.ts +42 -0
  216. package/authoring/doctypes/types.ts +2 -1
  217. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  218. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  219. package/authoring/identity/identity.doc.mjs +2 -2
  220. package/authoring/index.d.mts +1 -0
  221. package/authoring/index.d.ts +7 -4
  222. package/authoring/index.mjs +2 -1
  223. package/authoring/integration/integration.doc.mjs +2 -2
  224. package/authoring/integration/type.ts +4 -10
  225. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  226. package/clients/cli/commands/blog.doc.mjs +1 -1
  227. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  228. package/clients/cli/commands/build-theme.mjs +11 -45
  229. package/clients/cli/commands/build.doc.mjs +12 -7
  230. package/clients/cli/commands/build.mjs +105 -67
  231. package/clients/cli/commands/build.text-fields.test.mjs +41 -0
  232. package/clients/cli/commands/component/index.mjs +1 -1
  233. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  234. package/clients/cli/commands/component.doc.mjs +1 -1
  235. package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
  236. package/clients/cli/commands/discover.doc.mjs +1 -1
  237. package/clients/cli/commands/discover.mjs +1 -1
  238. package/clients/cli/commands/docs.doc.mjs +21 -9
  239. package/clients/cli/commands/docs.mjs +184 -70
  240. package/clients/cli/commands/docs.test.mjs +120 -16
  241. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  242. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  243. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  244. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  245. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  246. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  247. package/clients/cli/commands/doctor.doc.mjs +1 -1
  248. package/clients/cli/commands/doctor.mjs +53 -16
  249. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  250. package/clients/cli/commands/hook.doc.mjs +1 -1
  251. package/clients/cli/commands/init.doc.mjs +1 -1
  252. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  253. package/clients/cli/commands/integration-add.doc.mjs +11 -1
  254. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  255. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  256. package/clients/cli/commands/integration.doc.mjs +1 -1
  257. package/clients/cli/commands/integration.mjs +1 -0
  258. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  259. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  260. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  261. package/clients/cli/commands/layout.doc.mjs +1 -1
  262. package/clients/cli/commands/manifest.doc.mjs +1 -1
  263. package/clients/cli/commands/search.doc.mjs +1 -1
  264. package/clients/cli/commands/search.mjs +11 -2
  265. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  266. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  267. package/clients/cli/commands/template.doc.mjs +24 -7
  268. package/clients/cli/commands/template.mjs +4 -91
  269. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  270. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  271. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  272. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  273. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  274. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  275. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  276. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  277. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  278. package/clients/cli/commands/theme.doc.mjs +1 -1
  279. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  280. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  281. package/clients/cli/commands/upgrade.mjs +29 -7
  282. package/clients/cli/formatters/index.mjs +2 -0
  283. package/clients/cli/formatters/index.test.mjs +6 -0
  284. package/clients/cli/index.mjs +8 -0
  285. package/clients/cli/lib/hook-format.mjs +14 -5
  286. package/clients/cli/lib/manifest.mjs +9 -2
  287. package/foundation/agent-docs/agent-docs.d.mts +3 -2
  288. package/foundation/agent-docs/agent-docs.mjs +13 -9
  289. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  290. package/foundation/config/project-themes.test.mjs +11 -19
  291. package/foundation/config/project.d.mts +20 -11
  292. package/foundation/config/project.mjs +123 -80
  293. package/foundation/config/project.test.mjs +144 -21
  294. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  295. package/foundation/discovery/authoring-self-docs.mjs +37 -15
  296. package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
  297. package/foundation/discovery/authoring-surface.d.mts +74 -0
  298. package/foundation/discovery/authoring-surface.mjs +525 -0
  299. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  300. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  301. package/foundation/discovery/cli-self-docs.mjs +490 -0
  302. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  303. package/foundation/discovery/component-discovery.d.mts +38 -0
  304. package/foundation/discovery/component-discovery.mjs +48 -0
  305. package/foundation/discovery/component-loader.d.mts +35 -38
  306. package/foundation/discovery/component-loader.mjs +53 -222
  307. package/foundation/discovery/docs-discovery.d.mts +112 -11
  308. package/foundation/discovery/docs-discovery.mjs +231 -36
  309. package/foundation/discovery/docs-discovery.test.mjs +111 -32
  310. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  311. package/foundation/discovery/docs-output-budget.mjs +1 -1
  312. package/foundation/discovery/docs-section-key.d.mts +28 -10
  313. package/foundation/discovery/docs-section-key.mjs +134 -33
  314. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  315. package/foundation/discovery/template-adapter.d.mts +99 -6
  316. package/foundation/discovery/template-adapter.mjs +480 -57
  317. package/foundation/discovery/template-adapter.test.mjs +57 -0
  318. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  319. package/foundation/discovery/template-conflict-release.mjs +40 -0
  320. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  321. package/foundation/discovery/theme-discovery.d.mts +67 -7
  322. package/foundation/discovery/theme-discovery.mjs +916 -186
  323. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  324. package/foundation/doc-compiler/bundle.d.mts +47 -0
  325. package/foundation/doc-compiler/bundle.mjs +278 -0
  326. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  327. package/foundation/doc-compiler/compile.d.mts +220 -39
  328. package/foundation/doc-compiler/compile.mjs +311 -15
  329. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  330. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  331. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  332. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  333. package/foundation/doc-compiler/import.d.mts +24 -0
  334. package/foundation/doc-compiler/import.mjs +59 -0
  335. package/foundation/doc-compiler/inputs.d.mts +102 -0
  336. package/foundation/doc-compiler/inputs.mjs +291 -0
  337. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  338. package/foundation/doc-compiler/ir.d.mts +13 -0
  339. package/foundation/doc-compiler/ir.mjs +196 -12
  340. package/foundation/doc-compiler/lenses.d.mts +8 -5
  341. package/foundation/doc-compiler/lenses.mjs +50 -4
  342. package/foundation/doc-compiler/links.d.mts +162 -0
  343. package/foundation/doc-compiler/links.mjs +294 -0
  344. package/foundation/doc-compiler/links.test.mjs +192 -0
  345. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  346. package/foundation/doc-compiler/overlays.d.mts +37 -0
  347. package/foundation/doc-compiler/overlays.mjs +206 -0
  348. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  349. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  350. package/foundation/doc-compiler/read.d.mts +127 -0
  351. package/foundation/doc-compiler/read.mjs +325 -0
  352. package/foundation/doc-compiler/read.test.mjs +313 -0
  353. package/foundation/doc-compiler/source.d.mts +33 -0
  354. package/foundation/doc-compiler/source.mjs +128 -0
  355. package/foundation/doc-compiler/tree.d.mts +288 -0
  356. package/foundation/doc-compiler/tree.mjs +876 -0
  357. package/foundation/doc-compiler/tree.test.mjs +598 -0
  358. package/foundation/fs/file-protection.d.mts +33 -0
  359. package/foundation/fs/file-protection.mjs +825 -0
  360. package/foundation/fs/file-protection.test.mjs +250 -0
  361. package/foundation/integrations/autolink.d.mts +58 -1
  362. package/foundation/integrations/autolink.mjs +143 -57
  363. package/foundation/integrations/autolink.test.mjs +1 -1
  364. package/foundation/integrations/cli-requirement.d.mts +45 -0
  365. package/foundation/integrations/cli-requirement.mjs +154 -0
  366. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  367. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  368. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  369. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  370. package/foundation/integrations/contribution-inventory.mjs +27 -24
  371. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  372. package/foundation/integrations/integration-warnings.d.mts +9 -2
  373. package/foundation/integrations/integration-warnings.mjs +51 -26
  374. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  375. package/foundation/integrations/integrations.d.mts +3 -0
  376. package/foundation/integrations/integrations.mjs +11 -97
  377. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  378. package/foundation/integrations/provider-resolution.d.mts +152 -0
  379. package/foundation/integrations/provider-resolution.mjs +576 -0
  380. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  381. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  382. package/foundation/integrations/theme-descriptor.mjs +44 -0
  383. package/foundation/integrations/validate-contributions.mjs +121 -29
  384. package/foundation/response/error-codes.d.mts +3 -1
  385. package/foundation/response/error-codes.d.ts +2 -0
  386. package/foundation/response/error-codes.doc.mjs +12 -2
  387. package/foundation/response/error-codes.mjs +7 -1
  388. package/foundation/response/error-codes.test.mjs +55 -11
  389. package/foundation/response/response-types.doc.d.mts +5 -1
  390. package/foundation/response/response-types.doc.mjs +20 -11
  391. package/foundation/response/response.doc.mjs +1 -1
  392. package/foundation/text/string-utils.d.mts +8 -0
  393. package/foundation/text/string-utils.mjs +40 -10
  394. package/foundation/xle/expand.mjs +1 -1
  395. package/foundation/xle/xle.test.mjs +13 -0
  396. package/package.json +10 -9
  397. package/assets/templates/themes/manifest.json +0 -95
@@ -24,9 +24,8 @@
24
24
  import * as fs from 'node:fs';
25
25
  import * as path from 'node:path';
26
26
  import {createRequire} from 'node:module';
27
- import {createJiti} from 'jiti';
28
- import {loadModuleWithParser} from '../fs/module-loader.mjs';
29
- import {parseTemplate} from '../../authoring/doctypes/template/parse.mjs';
27
+ import {readDocView} from '../doc-compiler/read.mjs';
28
+ import {importDocModule} from '../doc-compiler/import.mjs';
30
29
  import {CLI_ROOT, discoverExternalPackages} from '../fs/paths.mjs';
31
30
  import {CORE_PROVIDER_ID} from '../identity/providers.mjs';
32
31
  import {Project} from '../config/project.mjs';
@@ -46,6 +45,53 @@ export function pkgOf(t) {
46
45
  return t.package ?? CORE_PACKAGE;
47
46
  }
48
47
 
48
+ /**
49
+ * Every id that should resolve to a discovered template. Replacements keep
50
+ * their own integration id and also own the Core id they replace.
51
+ * @param {{dirName: string, replaces?: string}} template
52
+ * @returns {string[]}
53
+ */
54
+ export function templateLookupIds(template) {
55
+ if (template.replaces && template.replaces !== template.dirName) {
56
+ return [template.dirName, template.replaces];
57
+ }
58
+ return [template.dirName];
59
+ }
60
+
61
+ /**
62
+ * Remove entries shadowed in the default discovery view while retaining them in
63
+ * package-scoped views. An active replacement owns its target id. A rejected
64
+ * same-id declaration falls back to Core when a Core template of the same kind
65
+ * owns that id; otherwise the integration template remains available by kind.
66
+ * @param {DiscoveredTemplate[]} templates
67
+ * @returns {DiscoveredTemplate[]}
68
+ */
69
+ export function effectiveTemplateDiscovery(templates) {
70
+ const activeTargets = new Set(
71
+ templates
72
+ .filter(template => template.replaces != null)
73
+ .map(template => `${template.type}:${template.replaces}`),
74
+ );
75
+ const coreIds = new Set(
76
+ templates
77
+ .filter(template => pkgOf(template) === CORE_PACKAGE)
78
+ .map(template => `${template.type}:${template.dirName}`),
79
+ );
80
+ return templates.filter(template => {
81
+ if (
82
+ template.replacementRejected &&
83
+ template.replacementTarget === template.dirName &&
84
+ coreIds.has(`${template.type}:${template.dirName}`)
85
+ ) {
86
+ return false;
87
+ }
88
+ return (
89
+ !activeTargets.has(`${template.type}:${template.dirName}`) ||
90
+ template.replaces != null
91
+ );
92
+ });
93
+ }
94
+
49
95
  /**
50
96
  * A discovered template (page or block), normalized across core, external, and
51
97
  * integration sources. Not every source populates every field, so
@@ -68,6 +114,11 @@ export function pkgOf(t) {
68
114
  * @property {string} filePath
69
115
  * @property {string} docPath
70
116
  * @property {string} [package]
117
+ * @property {boolean} [autolinked] whether the owning integration was discovered
118
+ * from package.json rather than named in astryx.config
119
+ * @property {string} [replaces] active Core template id this integration template replaces
120
+ * @property {boolean} [replacementRejected] whether an invalid declaration was disabled
121
+ * @property {string} [replacementTarget] disabled declaration target
71
122
  */
72
123
 
73
124
  /**
@@ -76,6 +127,15 @@ export function pkgOf(t) {
76
127
  * @property {string} package
77
128
  * @property {string} [template]
78
129
  * @property {string} message
130
+ * @property {string} [code]
131
+ * @property {'warning' | 'error'} [severity]
132
+ * @property {string} [replacementTarget] Core target whose replacement set is invalid
133
+ * @property {boolean} [autolinked] whether the declaration came from an autolinked integration
134
+ */
135
+
136
+ /**
137
+ * A semantic error in an integration template replacement declaration.
138
+ * @typedef {TemplateDiscoveryError & {code: 'missing_template_replacement_target' | 'ambiguous_template_replacement' | 'invalid_template_replacement', severity: 'warning' | 'error'}} TemplateReplacementError
79
139
  */
80
140
 
81
141
  /**
@@ -84,31 +144,28 @@ export function pkgOf(t) {
84
144
  */
85
145
 
86
146
  /**
87
- * Canonical basename suffixes for template-spec files, in precedence order.
88
- * A template spec is a scaffoldable TEMPLATE (a plain object stamped with a
89
- * `type` of `'page'` or `'block'`), so `.template.*` is the descriptive family
90
- * name.
147
+ * Released compatibility suffixes, in precedence order. Stable 0.6.0
148
+ * documented `.template.*`, so discovery keeps reading those files while all
149
+ * new authoring uses `.doc.mjs`.
91
150
  */
92
151
  const TEMPLATE_SUFFIXES = ['.template.ts', '.template.mjs', '.template.js'];
93
152
 
94
153
  /**
95
- * Legacy basename suffixes for template-spec files, in precedence order.
96
- * `.doc.*` was inherited from the component-doc convention before templates
97
- * had their own name; it is still accepted during the transition window.
154
+ * Descriptor suffixes for templates, in precedence order. New authoring always
155
+ * emits `.doc.mjs`; the TypeScript and JavaScript variants remain readable.
98
156
  */
99
157
  const DOC_SUFFIXES = ['.doc.ts', '.doc.mjs', '.doc.js'];
100
158
 
101
159
  /**
102
- * The union of canonical + legacy template-spec suffixes, canonical first so
103
- * `.template.*` wins over `.doc.*` when both stems exist. All template
104
- * discovery matches this union so a `Foo.template.ts` file is treated exactly
105
- * like a legacy `Foo.doc.mjs`.
160
+ * Every template-spec suffix, in the released precedence a core page directory
161
+ * uses to pick one file. Integration discovery never picks: every match is a
162
+ * template, so two specs for one stem list twice and read as ambiguous.
106
163
  */
107
164
  const ALL_TEMPLATE_SUFFIXES = [...TEMPLATE_SUFFIXES, ...DOC_SUFFIXES];
108
165
 
109
166
  /**
110
167
  * The template-spec suffix present on `file`, or null if none matches.
111
- * Recognizes both the canonical `.template.*` and legacy `.doc.*` families.
168
+ * Recognizes both the canonical `.doc.*` and released `.template.*` families.
112
169
  * @param {string} file
113
170
  * @returns {string | null}
114
171
  */
@@ -122,16 +179,6 @@ function matchedTemplateSuffix(file) {
122
179
  */
123
180
  const TEMPLATE_SUFFIX_RE = /\.(template|doc)\.(ts|mjs|js)$/;
124
181
 
125
- /** @type {ReturnType<typeof createJiti> | undefined} */
126
- let jitiInstance;
127
- /** Lazily-created jiti for loading `.ts` template specs (JSX-capable). */
128
- function getJiti() {
129
- if (!jitiInstance) {
130
- jitiInstance = createJiti(import.meta.url, {jsx: true});
131
- }
132
- return jitiInstance;
133
- }
134
-
135
182
  /**
136
183
  * Load an integration template doc module and validate it against the template
137
184
  * envelope at the load boundary. Default export only — `.ts` via jiti,
@@ -144,7 +191,13 @@ function getJiti() {
144
191
  * @param {string} [label]
145
192
  */
146
193
  async function loadIntegrationDoc(file, label) {
147
- return loadModuleWithParser(file, parseTemplate, {label});
194
+ return readDocView(file, {
195
+ root: 'templates',
196
+ exports: ['default'],
197
+ label: label ?? file,
198
+ strict: true,
199
+ value: 'parsed',
200
+ });
148
201
  }
149
202
 
150
203
  const TEMPLATES_DIR = path.join(CLI_ROOT, 'assets', 'templates');
@@ -492,12 +545,11 @@ function unsafeFixtureReference(source, at, reason) {
492
545
  /**
493
546
  * Load a template-spec module and return its metadata object. Supports both
494
547
  * families of suffix:
495
- * - Legacy `.doc.*` core/external specs export `export const doc = {...}`.
496
- * - Canonical `.template.*` specs export the stamped object (`type: 'page' |
497
- * 'block'`) as the default export.
498
- * Prefers the default export, falling back to the named `doc` export, so a
499
- * `Foo.template.ts` (default export) is read identically to a legacy
500
- * `Foo.doc.mjs` (`doc` export). `.ts` is loaded via jiti; `.mjs`/`.js` via a
548
+ * - Canonical `.doc.*` specs may export the stamped object (`type: 'page' |
549
+ * 'block'`) as the default export or use the historical named `doc` export.
550
+ * - Released `.template.*` compatibility specs use the same object shape.
551
+ * Prefers the default export, falling back to the named `doc` export. `.ts` is
552
+ * loaded via jiti; `.mjs`/`.js` via a
501
553
  * native dynamic import. Returns null if the file does not exist.
502
554
  *
503
555
  * @param {string} docPath absolute path to the spec file
@@ -505,10 +557,7 @@ function unsafeFixtureReference(source, at, reason) {
505
557
  */
506
558
  async function loadDocModule(docPath) {
507
559
  if (!fs.existsSync(docPath)) return null;
508
- const docModule = docPath.endsWith('.ts')
509
- ? await getJiti().import(docPath)
510
- : await import(`file://${docPath}`);
511
- return docModule.default ?? docModule.doc;
560
+ return readDocView(docPath, {root: 'templates', loader: 'template'});
512
561
  }
513
562
 
514
563
  /**
@@ -553,11 +602,11 @@ function findDocFiles(dir, pattern) {
553
602
 
554
603
  /**
555
604
  * Resolve the template-spec file for a core page directory: the first existing
556
- * `template.<suffix>` in canonical-then-legacy precedence, or null.
605
+ * metadata file in {@link ALL_TEMPLATE_SUFFIXES} precedence, or null.
557
606
  * @param {string} dirPath
558
607
  * @returns {string | null}
559
608
  */
560
- function findPageDocFile(dirPath) {
609
+ export function findPageDocFile(dirPath) {
561
610
  for (const suffix of ALL_TEMPLATE_SUFFIXES) {
562
611
  const candidate = path.join(dirPath, `template${suffix}`);
563
612
  if (fs.existsSync(candidate)) return candidate;
@@ -696,44 +745,297 @@ export async function discoverCoreTemplates() {
696
745
  }
697
746
 
698
747
  /**
748
+ * Apply valid integration replacements to a raw template set.
749
+ *
750
+ * One declaration owns its Core target. When different configured packages
751
+ * replace the same target, the later package wins and discovery returns a
752
+ * warning, matching integration-doc replacement order. Multiple declarations
753
+ * inside one package are invalid and fail closed. Missing targets and kind
754
+ * mismatches also fail closed: Core stays selected and every integration
755
+ * template remains addressable by its own id.
756
+ *
757
+ * @param {DiscoveredTemplate[]} templates
758
+ * @param {TemplateDiscoveryError[]} [declarationErrors]
759
+ * @returns {{templates: DiscoveredTemplate[], errors: TemplateReplacementError[]}}
760
+ */
761
+ export function applyTemplateReplacements(templates, declarationErrors = []) {
762
+ /** @type {Map<string, DiscoveredTemplate[]>} */
763
+ const coreById = new Map();
764
+ /** @type {Map<string, DiscoveredTemplate[]>} */
765
+ const replacementsByTarget = new Map();
766
+
767
+ for (const template of templates) {
768
+ if (pkgOf(template) === CORE_PACKAGE) {
769
+ const matches = coreById.get(template.dirName) ?? [];
770
+ matches.push(template);
771
+ coreById.set(template.dirName, matches);
772
+ }
773
+ if (template.replaces != null) {
774
+ const replacements = replacementsByTarget.get(template.replaces) ?? [];
775
+ replacements.push(template);
776
+ replacementsByTarget.set(template.replaces, replacements);
777
+ }
778
+ }
779
+
780
+ const activeReplacements = new Set();
781
+ const replacedCore = new Set();
782
+ /** @type {TemplateReplacementError[]} */
783
+ const errors = declarationErrors.map(error => ({
784
+ ...error,
785
+ code: /** @type {TemplateReplacementError['code']} */ (
786
+ error.code ?? 'invalid_template_replacement'
787
+ ),
788
+ severity: error.severity ?? 'error',
789
+ }));
790
+ /** @param {TemplateReplacementError} error */
791
+ const pushError = error => {
792
+ if (
793
+ errors.some(
794
+ existing =>
795
+ existing.code === error.code &&
796
+ existing.package === error.package &&
797
+ existing.template === error.template &&
798
+ existing.message === error.message,
799
+ )
800
+ ) {
801
+ return;
802
+ }
803
+ errors.push(error);
804
+ };
805
+ for (const [target, replacements] of replacementsByTarget) {
806
+ const targetErrors = errors.filter(
807
+ error => error.replacementTarget === target,
808
+ );
809
+ const hasExplicitIntent =
810
+ replacements.some(replacement => !replacement.autolinked) ||
811
+ targetErrors.some(error => !error.autolinked);
812
+ const contenders = hasExplicitIntent
813
+ ? replacements.filter(replacement => !replacement.autolinked)
814
+ : replacements;
815
+ let targetInvalid = targetErrors.some(
816
+ error =>
817
+ error.severity === 'error' && (!hasExplicitIntent || !error.autolinked),
818
+ );
819
+
820
+ /** @type {Map<string, DiscoveredTemplate[]>} */
821
+ const byPackage = new Map();
822
+ for (const replacement of replacements) {
823
+ const pkg = pkgOf(replacement);
824
+ const fromPackage = byPackage.get(pkg) ?? [];
825
+ fromPackage.push(replacement);
826
+ byPackage.set(pkg, fromPackage);
827
+ }
828
+ for (const [pkg, declarations] of byPackage) {
829
+ if (declarations.length < 2) continue;
830
+ if (!hasExplicitIntent || !declarations[0].autolinked) {
831
+ targetInvalid = true;
832
+ }
833
+ const message =
834
+ `${pkg} declares ${declarations.length} templates as replacements for Core ` +
835
+ `template "${target}" (${declarations.map(template => template.dirName).join(', ')}). ` +
836
+ 'One package must declare at most one replacement for a Core target.';
837
+ for (const declaration of declarations) {
838
+ pushError({
839
+ code: 'ambiguous_template_replacement',
840
+ severity: 'error',
841
+ package: pkg,
842
+ template: declaration.dirName,
843
+ replacementTarget: target,
844
+ autolinked: declaration.autolinked,
845
+ message,
846
+ });
847
+ }
848
+ }
849
+
850
+ const coreMatches = coreById.get(target) ?? [];
851
+ /** @type {Map<DiscoveredTemplate, DiscoveredTemplate>} */
852
+ const coreMatchByReplacement = new Map();
853
+ for (const replacement of replacements) {
854
+ const invalidAffectsTarget =
855
+ !hasExplicitIntent || !replacement.autolinked;
856
+ if (coreMatches.length === 0) {
857
+ if (invalidAffectsTarget) targetInvalid = true;
858
+ pushError({
859
+ code: 'missing_template_replacement_target',
860
+ severity: 'error',
861
+ package: pkgOf(replacement),
862
+ template: replacement.dirName,
863
+ replacementTarget: target,
864
+ autolinked: replacement.autolinked,
865
+ message: `Template "${replacement.dirName}" replaces "${target}", which is not a Core template id.`,
866
+ });
867
+ continue;
868
+ }
869
+ const sameType = coreMatches.filter(
870
+ core => core.type === replacement.type,
871
+ );
872
+ if (sameType.length !== 1) {
873
+ if (invalidAffectsTarget) targetInvalid = true;
874
+ const kinds = coreMatches.map(core => core.type).join(', ');
875
+ pushError({
876
+ code: 'invalid_template_replacement',
877
+ severity: 'error',
878
+ package: pkgOf(replacement),
879
+ template: replacement.dirName,
880
+ replacementTarget: target,
881
+ autolinked: replacement.autolinked,
882
+ message:
883
+ `Template "${replacement.dirName}" is a ${replacement.type} template, but Core ` +
884
+ `template "${target}" is ${kinds || 'not available'}. A replacement must have the same type.`,
885
+ });
886
+ continue;
887
+ }
888
+ coreMatchByReplacement.set(replacement, sameType[0]);
889
+ }
890
+
891
+ const validContenders = contenders.filter(replacement =>
892
+ coreMatchByReplacement.has(replacement),
893
+ );
894
+ if (targetInvalid || validContenders.length === 0) continue;
895
+
896
+ const replacement = validContenders[validContenders.length - 1];
897
+ const validReplacements = replacements.filter(candidate =>
898
+ coreMatchByReplacement.has(candidate),
899
+ );
900
+ if (validReplacements.length > 1) {
901
+ const autolinkedLost =
902
+ hasExplicitIntent &&
903
+ validReplacements.some(candidate => candidate.autolinked);
904
+ const allAutolinked = validReplacements.every(
905
+ candidate => candidate.autolinked,
906
+ );
907
+ pushError({
908
+ code: 'ambiguous_template_replacement',
909
+ severity: 'warning',
910
+ package: pkgOf(replacement),
911
+ template: replacement.dirName,
912
+ replacementTarget: target,
913
+ autolinked: replacement.autolinked,
914
+ message: autolinkedLost
915
+ ? `Core template "${target}" is replaced by ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is explicitly configured, so it wins over autolinked integrations.`
916
+ : allAutolinked
917
+ ? `Core template "${target}" is replaced by autolinked dependencies ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is listed later in package.json dependencies, so it wins. Add the intended package to astryx.config integrations to make precedence explicit.`
918
+ : `Core template "${target}" is replaced by ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is configured later, so it wins.`,
919
+ });
920
+ }
921
+
922
+ activeReplacements.add(replacement);
923
+ const coreMatch = coreMatchByReplacement.get(replacement);
924
+ if (coreMatch) replacedCore.add(coreMatch);
925
+ }
926
+
927
+ const effective = templates.flatMap(template => {
928
+ if (replacedCore.has(template)) return [];
929
+ if (template.replaces != null && !activeReplacements.has(template)) {
930
+ const fallback = {
931
+ ...template,
932
+ replacementRejected: true,
933
+ replacementTarget: template.replaces,
934
+ };
935
+ delete fallback.replaces;
936
+ return [fallback];
937
+ }
938
+ return [template];
939
+ });
940
+
941
+ return {
942
+ templates: effective.sort((a, b) => a.name.localeCompare(b.name)),
943
+ errors,
944
+ };
945
+ }
946
+
947
+ /**
948
+ * Discover the raw Core, external, and integration template set before
949
+ * replacement declarations are applied.
699
950
  * @param {string} [cwd]
700
- * @returns {Promise<DiscoveredTemplate[]>}
951
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
701
952
  */
702
- export async function discoverAll(cwd = process.cwd()) {
953
+ async function discoverAllSources(cwd = process.cwd()) {
703
954
  const [core, external, integration] = await Promise.all([
704
955
  discoverCoreTemplates(),
705
956
  discoverExternalBlocks(cwd),
706
957
  discoverIntegrationTemplates(cwd),
707
958
  ]);
708
- return [...core, ...external, ...integration.templates].sort((a, b) =>
959
+ return {
960
+ templates: [...core, ...external, ...integration.templates],
961
+ errors: integration.errors,
962
+ };
963
+ }
964
+
965
+ /**
966
+ * Discover every template without hiding replaced Core originals. Internal
967
+ * package-qualified selection uses this view.
968
+ * @param {string} [cwd]
969
+ * @returns {Promise<DiscoveredTemplate[]>}
970
+ */
971
+ export async function discoverAllUnresolved(cwd = process.cwd()) {
972
+ return (await discoverAllSources(cwd)).templates.sort((a, b) =>
709
973
  a.name.localeCompare(b.name),
710
974
  );
711
975
  }
712
976
 
713
977
  /**
714
- * Like {@link discoverAll} but also returns integration-template discovery
715
- * errors (missing same-stem source, missing `type`, load failure). Use this
716
- * when the caller wants to warn about malformed integration templates.
978
+ * Resolve replacement declarations while retaining package-addressable entries
979
+ * that are shadowed or rejected in the default discovery view.
980
+ * @param {string} [cwd]
981
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: Array<TemplateDiscoveryError | TemplateReplacementError>}>}
982
+ */
983
+ async function resolveAllSources(cwd = process.cwd()) {
984
+ const discovered = await discoverAllSources(cwd);
985
+ const replacementErrors = discovered.errors.filter(
986
+ error => error.replacementTarget != null,
987
+ );
988
+ const resolved = applyTemplateReplacements(
989
+ discovered.templates,
990
+ replacementErrors,
991
+ );
992
+ return {
993
+ templates: resolved.templates,
994
+ errors: [
995
+ ...discovered.errors.filter(error => error.replacementTarget == null),
996
+ ...resolved.errors,
997
+ ],
998
+ };
999
+ }
1000
+
1001
+ /**
1002
+ * Discover the resolved catalog before default-view shadowing. Internal
1003
+ * package-qualified selection uses this view.
1004
+ * @param {string} [cwd]
1005
+ * @returns {Promise<DiscoveredTemplate[]>}
1006
+ */
1007
+ export async function discoverAllResolved(cwd = process.cwd()) {
1008
+ return (await resolveAllSources(cwd)).templates;
1009
+ }
1010
+
1011
+ /**
1012
+ * @param {string} [cwd]
1013
+ * @returns {Promise<DiscoveredTemplate[]>}
1014
+ */
1015
+ export async function discoverAll(cwd = process.cwd()) {
1016
+ return effectiveTemplateDiscovery(await discoverAllResolved(cwd));
1017
+ }
1018
+
1019
+ /**
1020
+ * Like {@link discoverAll} but also returns integration-template discovery and
1021
+ * replacement-declaration errors. Use this when the caller wants to warn about
1022
+ * malformed integration templates or inactive replacement declarations.
717
1023
  *
718
1024
  * @param {string} [cwd]
719
- * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
1025
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: Array<TemplateDiscoveryError | TemplateReplacementError>}>}
720
1026
  */
721
1027
  export async function discoverAllWithErrors(cwd = process.cwd()) {
722
- const [pages, blocks, integration] = await Promise.all([
723
- discoverPages(),
724
- discoverAllBlocks(cwd),
725
- discoverIntegrationTemplates(cwd),
726
- ]);
727
- const templates = [...pages, ...blocks, ...integration.templates].sort(
728
- (a, b) => a.name.localeCompare(b.name),
729
- );
730
- return {templates, errors: integration.errors};
1028
+ const resolved = await resolveAllSources(cwd);
1029
+ return {
1030
+ templates: effectiveTemplateDiscovery(resolved.templates),
1031
+ errors: resolved.errors,
1032
+ };
731
1033
  }
732
1034
 
733
1035
  /**
734
1036
  * Recursively collect integration template-spec files under `root`.
735
1037
  * Returns absolute paths to files ending in one of ALL_TEMPLATE_SUFFIXES
736
- * (canonical `.template.*` or legacy `.doc.*`).
1038
+ * (canonical `.doc.*` or released `.template.*` compatibility files).
737
1039
  *
738
1040
  * @param {string} root
739
1041
  * @returns {string[]}
@@ -763,7 +1065,7 @@ function findIntegrationDocFiles(root) {
763
1065
  * Discover templates contributed by configured integrations.
764
1066
  *
765
1067
  * For each integration with a resolved `templates` root, every
766
- * `<id>.template.{ts,mjs,js}` (or legacy `<id>.doc.{ts,mjs,js}`) file is a
1068
+ * canonical `<id>.doc.{mjs,ts,js}` (or released `<id>.template.{ts,mjs,js}`) file is a
767
1069
  * template whose id is its path relative to the templates root with the
768
1070
  * matched suffix stripped (kebab-case, may be nested). The doc's `type`
769
1071
  * (page|block) decides scaffolding — there is no `/pages` vs `/blocks`
@@ -820,13 +1122,51 @@ async function discoverIntegrationTemplates(cwd = process.cwd()) {
820
1122
  return {templates, errors};
821
1123
  }
822
1124
 
1125
+ /**
1126
+ * Report replacement declarations that cannot hold inside one package: two of
1127
+ * its templates replacing the same Core target. Cross-package precedence and
1128
+ * target checks happen in {@link applyTemplateReplacements}.
1129
+ * @param {string} pkg
1130
+ * @param {boolean | undefined} autolinked
1131
+ * @param {DiscoveredTemplate[]} templates
1132
+ * @param {TemplateDiscoveryError[]} errors
1133
+ */
1134
+ function reportSamePackageReplacements(pkg, autolinked, templates, errors) {
1135
+ /** @type {Map<string, string[]>} */
1136
+ const idsByTarget = new Map();
1137
+ for (const template of templates) {
1138
+ if (template.replaces == null) continue;
1139
+ const ids = idsByTarget.get(template.replaces) ?? [];
1140
+ ids.push(template.dirName);
1141
+ idsByTarget.set(template.replaces, ids);
1142
+ }
1143
+ for (const [target, ids] of idsByTarget) {
1144
+ if (ids.length < 2) continue;
1145
+ const message =
1146
+ `${pkg} declares ${ids.length} templates as replacements for Core template ` +
1147
+ `"${target}" (${ids.join(', ')}). One package must declare at most one ` +
1148
+ 'replacement for a Core target.';
1149
+ for (const template of ids) {
1150
+ errors.push({
1151
+ code: 'ambiguous_template_replacement',
1152
+ severity: 'error',
1153
+ package: pkg,
1154
+ autolinked,
1155
+ template,
1156
+ replacementTarget: target,
1157
+ message,
1158
+ });
1159
+ }
1160
+ }
1161
+ }
1162
+
823
1163
  /**
824
1164
  * Discover the templates contributed by a SINGLE integration. Same per-template
825
1165
  * rules as {@link discoverIntegrationTemplates} (same-stem source required,
826
1166
  * page|block type required); broken templates are recorded in `errors` rather
827
1167
  * than thrown. Exposed for `doctor integration validate` and template authoring checks.
828
1168
  *
829
- * @param {{name?: string, __spec?: string, templates?: string}} integration
1169
+ * @param {{name?: string, __spec?: string, __autolinked?: boolean, templates?: string}} integration
830
1170
  * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
831
1171
  */
832
1172
  export async function discoverIntegrationTemplatesForOne(integration) {
@@ -855,6 +1195,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
855
1195
  template: id,
856
1196
  message: `Template "${id}" is missing its same-stem source file ${path.basename(sourcePath)}.`,
857
1197
  });
1198
+ // A replacement this unusable template declares still counts, so its
1199
+ // target fails closed instead of going to a sibling.
1200
+ const declared = await declaredReplacement(docPath, id);
1201
+ if (declared != null) {
1202
+ errors.push({
1203
+ code: 'invalid_template_replacement',
1204
+ severity: 'error',
1205
+ package: pkgLabel,
1206
+ autolinked: integration.__autolinked,
1207
+ template: id,
1208
+ replacementTarget: declared,
1209
+ message: `Template "${id}" replaces "${declared}", but it cannot be used: it is missing its same-stem source file ${path.basename(sourcePath)}.`,
1210
+ });
1211
+ }
858
1212
  continue;
859
1213
  }
860
1214
 
@@ -867,6 +1221,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
867
1221
  template: id,
868
1222
  message: `Template "${id}" failed to load: ${/** @type {any} */ (err).message}`,
869
1223
  });
1224
+ // A replacement this unusable template declares still counts, so its
1225
+ // target fails closed instead of going to a sibling (spec:AST-035 FR4).
1226
+ const declared = await declaredReplacement(docPath, id);
1227
+ if (declared != null) {
1228
+ errors.push({
1229
+ code: 'invalid_template_replacement',
1230
+ severity: 'error',
1231
+ package: pkgLabel,
1232
+ autolinked: integration.__autolinked,
1233
+ template: id,
1234
+ replacementTarget: declared,
1235
+ message: `Template "${id}" replaces "${declared}", but it cannot be used: its metadata does not load.`,
1236
+ });
1237
+ }
870
1238
  continue;
871
1239
  }
872
1240
 
@@ -880,6 +1248,18 @@ export async function discoverIntegrationTemplatesForOne(integration) {
880
1248
  template: id,
881
1249
  message: `Template "${id}" is missing a "type" of "page" or "block". Stamp the default export with type: 'page' or type: 'block'.`,
882
1250
  });
1251
+ const declared = doc?.replaces ?? (await declaredReplacement(docPath, id));
1252
+ if (declared != null) {
1253
+ errors.push({
1254
+ code: 'invalid_template_replacement',
1255
+ severity: 'error',
1256
+ package: pkgLabel,
1257
+ autolinked: integration.__autolinked,
1258
+ template: id,
1259
+ replacementTarget: declared,
1260
+ message: `Template "${id}" replaces "${declared}", but it cannot be used: it has no "type".`,
1261
+ });
1262
+ }
883
1263
  continue;
884
1264
  }
885
1265
 
@@ -907,12 +1287,55 @@ export async function discoverIntegrationTemplatesForOne(integration) {
907
1287
  filePath: sourcePath,
908
1288
  docPath,
909
1289
  package: pkgLabel,
1290
+ autolinked: integration.__autolinked,
1291
+ replaces: doc?.replaces,
910
1292
  });
911
1293
  }
912
1294
 
1295
+ reportSamePackageReplacements(
1296
+ pkgLabel,
1297
+ integration.__autolinked,
1298
+ templates,
1299
+ errors,
1300
+ );
913
1301
  return {templates, errors};
914
1302
  }
915
1303
 
1304
+ /**
1305
+ * The replacement a template doc declares, even when the doc does not validate
1306
+ * or load: an unusable template's declaration still counts, so its target
1307
+ * fails closed (spec:AST-035 FR4). Null when it declares none.
1308
+ * @param {string} docPath
1309
+ * @param {string} id
1310
+ * @returns {Promise<string | null>}
1311
+ */
1312
+ async function declaredReplacement(docPath, id) {
1313
+ try {
1314
+ const doc = await loadIntegrationDoc(docPath, `Template "${id}"`);
1315
+ return doc?.replaces ?? null;
1316
+ } catch {
1317
+ // The doc does not validate; read what it declares without validating.
1318
+ }
1319
+ try {
1320
+ // The guarded importer: anything the doc writes to stdout as it loads
1321
+ // goes to stderr, so `--json` output stays clean.
1322
+ const raw = (await importDocModule(docPath))?.default;
1323
+ if (raw != null && typeof raw === 'object') {
1324
+ return typeof raw.replaces === 'string' ? raw.replaces : null;
1325
+ }
1326
+ } catch {
1327
+ // The module does not load at all; its text may still name a target.
1328
+ }
1329
+ try {
1330
+ const match = /\breplaces\s*:\s*['"]([^'"\n]+)['"]/u.exec(
1331
+ fs.readFileSync(docPath, 'utf8'),
1332
+ );
1333
+ return match ? match[1] : null;
1334
+ } catch {
1335
+ return null;
1336
+ }
1337
+ }
1338
+
916
1339
  /**
917
1340
  * @param {string} componentName
918
1341
  * @param {string} [cwd]