@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.0e1fbdb

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 (254) hide show
  1. package/README.md +49 -40
  2. package/api/build/build.doc.mjs +6 -1
  3. package/api/build/build.test.mjs +22 -0
  4. package/api/build/kit/kit.mjs +44 -5
  5. package/api/component/component.doc.mjs +14 -7
  6. package/api/docs/_adapter.d.mts +8 -3
  7. package/api/docs/_adapter.mjs +14 -6
  8. package/api/docs/docOverlays.test.mjs +27 -1
  9. package/api/docs/docs.doc.mjs +2 -2
  10. package/api/doctor/doctor.doc.mjs +17 -8
  11. package/api/doctor/doctor.type.d.mts +1 -1
  12. package/api/doctor/doctor.type.mjs +1 -1
  13. package/api/gap-report/gap-report.doc.mjs +19 -10
  14. package/api/hook/hook.doc.mjs +6 -3
  15. package/api/index.d.mts +2 -0
  16. package/api/index.mjs +3 -1
  17. package/api/init/init.doc.mjs +17 -12
  18. package/api/integration/add-theme.mjs +22 -1
  19. package/api/integration/add-theme.test.mjs +34 -0
  20. package/api/integration/authoring-checks.mjs +2 -2
  21. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  22. package/api/integration/pack-check.lifecycle-output.test.mjs +2 -0
  23. package/api/integration/pack-check.mjs +54 -6
  24. package/api/integration/pack-check.test.mjs +90 -0
  25. package/api/integration/pack-check.type.mjs +1 -1
  26. package/api/json/assertResponse.doc.mjs +1 -1
  27. package/api/json/index.ts +1 -0
  28. package/api/json/isError.doc.mjs +1 -1
  29. package/api/layout/_adapter.d.mts +34 -0
  30. package/api/layout/_adapter.mjs +148 -0
  31. package/api/layout/check/check.d.mts +16 -0
  32. package/api/layout/check/check.mjs +40 -0
  33. package/api/layout/expand/expand.d.mts +22 -0
  34. package/api/layout/expand/expand.mjs +155 -0
  35. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  36. package/api/layout/grammar/grammar.d.mts +13 -0
  37. package/api/layout/grammar/grammar.mjs +87 -0
  38. package/api/layout/layout.d.mts +6 -0
  39. package/api/layout/layout.mjs +17 -0
  40. package/api/layout/layout.test.mjs +297 -0
  41. package/api/layout/layout.type.d.mts +89 -0
  42. package/api/layout/layout.type.mjs +103 -0
  43. package/api/layout/layoutCheck.doc.d.mts +11 -0
  44. package/api/layout/layoutCheck.doc.mjs +85 -0
  45. package/api/layout/layoutExpand.doc.d.mts +11 -0
  46. package/api/layout/layoutExpand.doc.mjs +107 -0
  47. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  48. package/api/layout/layoutGrammar.doc.mjs +57 -0
  49. package/api/search/search.d.mts +27 -1
  50. package/api/search/search.doc.mjs +2 -2
  51. package/api/search/search.mjs +228 -16
  52. package/api/swizzle/swizzle.doc.mjs +7 -5
  53. package/api/template/copy/copy.mjs +1 -1
  54. package/api/template/copy/copy.test.mjs +9 -0
  55. package/api/template/template-integration.test.mjs +65 -1
  56. package/api/template/template.doc.mjs +2 -1
  57. package/api/template/template.mjs +1 -1
  58. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  59. package/api/theme/listThemes.doc.mjs +1 -1
  60. package/api/theme/themeAdd.doc.mjs +9 -10
  61. package/api/theme/themeBuild.doc.mjs +13 -13
  62. package/api/theme/themeList.doc.mjs +1 -1
  63. package/api/theme/themeListAvailable.doc.mjs +2 -1
  64. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  65. package/api/theme/themeTargets.doc.mjs +3 -2
  66. package/api/theme/themeTemplate.doc.mjs +2 -1
  67. package/api/upgrade/run/run.mjs +1 -1
  68. package/api/upgrade/upgrade.doc.mjs +24 -22
  69. package/assets/docs/README.md +4 -2
  70. package/assets/docs/browser-support.doc.mjs +11 -11
  71. package/assets/docs/color.doc.mjs +8 -2
  72. package/assets/docs/elevation.doc.mjs +6 -4
  73. package/assets/docs/getting-started.doc.mjs +5 -16
  74. package/assets/docs/icons.doc.mjs +2 -21
  75. package/assets/docs/illustrations.doc.mjs +7 -15
  76. package/assets/docs/layout.doc.dense.mjs +130 -82
  77. package/assets/docs/layout.doc.mjs +133 -77
  78. package/assets/docs/migration.doc.mjs +19 -21
  79. package/assets/docs/motion.doc.mjs +16 -3
  80. package/assets/docs/principles.doc.dense.mjs +5 -5
  81. package/assets/docs/principles.doc.mjs +8 -0
  82. package/assets/docs/principles.doc.zh.mjs +6 -6
  83. package/assets/docs/shape.doc.mjs +8 -3
  84. package/assets/docs/spacing.doc.mjs +7 -2
  85. package/assets/docs/styling-libraries.doc.mjs +6 -2
  86. package/assets/docs/styling.doc.mjs +19 -23
  87. package/assets/docs/theme.doc.dense.mjs +58 -18
  88. package/assets/docs/theme.doc.mjs +56 -46
  89. package/assets/docs/theme.doc.zh.mjs +9 -8
  90. package/assets/docs/tokens.doc.dense.mjs +2 -2
  91. package/assets/docs/tokens.doc.mjs +389 -8
  92. package/assets/docs/tokens.doc.zh.mjs +2 -2
  93. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  94. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  95. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  96. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  97. package/assets/docs/tree/block-template.doc.mjs +130 -0
  98. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  99. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  100. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  101. package/assets/docs/tree/checks.doc.mjs +119 -0
  102. package/assets/docs/tree/codemods.doc.mjs +147 -0
  103. package/assets/docs/tree/component-family.doc.mjs +113 -0
  104. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  105. package/assets/docs/tree/components.doc.mjs +23 -0
  106. package/assets/docs/tree/configuration.doc.mjs +23 -0
  107. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  108. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  109. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  110. package/assets/docs/tree/docs.doc.mjs +21 -0
  111. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  112. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  113. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  114. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  115. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  116. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  117. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  118. package/assets/docs/tree/help.doc.mjs +16 -0
  119. package/assets/docs/tree/integrations.doc.mjs +25 -470
  120. package/assets/docs/tree/links.doc.mjs +98 -0
  121. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  122. package/assets/docs/tree/page-template.doc.mjs +71 -0
  123. package/assets/docs/tree/publishing.doc.mjs +111 -0
  124. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  125. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  126. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  127. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  128. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  129. package/assets/docs/tree/ship.doc.mjs +16 -0
  130. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  131. package/assets/docs/tree/single-component.doc.mjs +165 -0
  132. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  133. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  134. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  135. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  136. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  137. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  138. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  139. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  140. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  141. package/assets/docs/tree/templates.doc.mjs +34 -0
  142. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  143. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  144. package/assets/docs/tree/themes.doc.mjs +39 -0
  145. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  146. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  147. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  148. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  149. package/assets/docs/tree/versioning.doc.mjs +161 -0
  150. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  151. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  152. package/assets/docs/typography.doc.mjs +24 -4
  153. package/assets/docs/working-with-ai.doc.mjs +30 -22
  154. package/authoring/config/config.doc.mjs +2 -2
  155. package/authoring/config/type.ts +2 -2
  156. package/authoring/doctypes/_schema.d.mts +3 -2
  157. package/authoring/doctypes/_schema.mjs +6 -0
  158. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  159. package/authoring/doctypes/base/type.ts +4 -2
  160. package/authoring/doctypes/command/command.doc.mjs +1 -1
  161. package/authoring/doctypes/command/type.ts +1 -1
  162. package/authoring/doctypes/component/component.doc.mjs +6 -0
  163. package/authoring/doctypes/component/type.ts +8 -0
  164. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  165. package/authoring/doctypes/reference/type.ts +5 -0
  166. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  167. package/authoring/doctypes/template/template.doc.mjs +1 -1
  168. package/authoring/doctypes/template/type.ts +2 -2
  169. package/authoring/integration/integration.doc.mjs +12 -10
  170. package/clients/cli/command-result-coverage.test.mjs +7 -7
  171. package/clients/cli/commands/component.doc.mjs +4 -3
  172. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  173. package/clients/cli/commands/docs.doc.mjs +1 -1
  174. package/clients/cli/commands/docs.mjs +60 -17
  175. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  176. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  177. package/clients/cli/commands/doctor.doc.mjs +3 -1
  178. package/clients/cli/commands/doctor.mjs +49 -5
  179. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  180. package/clients/cli/commands/init.doc.mjs +9 -6
  181. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  182. package/clients/cli/commands/integration-authoring.test.mjs +61 -10
  183. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  184. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  185. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  186. package/clients/cli/commands/integration.doc.mjs +4 -4
  187. package/clients/cli/commands/integration.mjs +74 -43
  188. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  189. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  190. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  191. package/clients/cli/commands/layout.doc.mjs +34 -0
  192. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  193. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  194. package/clients/cli/commands/layout.mjs +275 -0
  195. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  196. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  197. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  198. package/clients/cli/commands/manifest.doc.mjs +1 -1
  199. package/clients/cli/commands/search.doc.mjs +10 -3
  200. package/clients/cli/commands/search.mjs +21 -2
  201. package/clients/cli/commands/search.test.mjs +21 -4
  202. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  203. package/clients/cli/commands/template.doc.mjs +1 -1
  204. package/clients/cli/commands/text-json-parity.test.mjs +24 -1
  205. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  206. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  207. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  208. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  209. package/clients/cli/commands/theme.doc.mjs +2 -1
  210. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  211. package/clients/cli/index.mjs +32 -6
  212. package/clients/cli/lib/define-command.mjs +28 -4
  213. package/clients/cli/lib/define-command.test.mjs +54 -0
  214. package/clients/cli/lib/exit-codes.test.mjs +25 -2
  215. package/clients/cli/lib/json-shim.test.mjs +20 -6
  216. package/clients/cli/lib/manifest.mjs +23 -5
  217. package/foundation/agent-docs/agent-docs.mjs +1 -1
  218. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  219. package/foundation/discovery/cli-self-docs.mjs +16 -2
  220. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  221. package/foundation/discovery/docs-discovery.mjs +5 -1
  222. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  223. package/foundation/discovery/docs-section-key.d.mts +1 -1
  224. package/foundation/discovery/docs-section-key.mjs +1 -1
  225. package/foundation/discovery/template-adapter.mjs +1 -1
  226. package/foundation/doc-compiler/doc-loads.test.mjs +15 -2
  227. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  228. package/foundation/doc-compiler/tree.d.mts +4 -0
  229. package/foundation/doc-compiler/tree.mjs +6 -1
  230. package/foundation/integrations/cli-requirement.d.mts +26 -6
  231. package/foundation/integrations/cli-requirement.mjs +46 -11
  232. package/foundation/integrations/cli-requirement.test.mjs +7 -2
  233. package/foundation/integrations/contribution-inventory.mjs +1 -1
  234. package/foundation/response/error-codes.doc.mjs +6 -8
  235. package/foundation/response/error-codes.test.mjs +30 -5
  236. package/foundation/response/response-types.doc.d.mts +4 -3
  237. package/foundation/response/response-types.doc.mjs +42 -6
  238. package/foundation/response/response.doc.mjs +11 -10
  239. package/foundation/xle/browser.d.mts +3 -3
  240. package/foundation/xle/browser.mjs +3 -3
  241. package/foundation/xle/expand.mjs +2 -2
  242. package/foundation/xle/parse.mjs +1 -1
  243. package/foundation/xle/print.mjs +2 -2
  244. package/foundation/xle/splice.mjs +1 -1
  245. package/package.json +9 -9
  246. package/api/docs/docs.test.mjs +0 -245
  247. package/api/docs/integration-tree.test.mjs +0 -555
  248. package/api/docs/integrationDocs.test.mjs +0 -314
  249. package/api/search/search.test.mjs +0 -530
  250. package/assets/docs/tree/integrations.test.mjs +0 -62
  251. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
  252. package/clients/cli/commands/docs.test.mjs +0 -323
  253. package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
  254. package/foundation/doc-compiler/tree.test.mjs +0 -606
@@ -53,8 +53,8 @@ const RUNNERS = {
53
53
  sites: ['import(node:child_process)', 'worker_threads.Worker'],
54
54
  },
55
55
  'api/integration/pack-check.mjs': {
56
- runs: '`npm pack`, `tar`, and a Node script that resolves the packed specifiers, to check a package as published',
57
- sites: ['child_process.spawnSync ×3'],
56
+ runs: '`npm pack`, `tar`, and a Node script that resolves the packed specifiers, to check a package as published, and the package\'s doc modules, to see which need a newer CLI',
57
+ sites: ['child_process.spawnSync ×3', 'loadTopicModule'],
58
58
  },
59
59
  'api/swizzle/_github.mjs': {
60
60
  runs: '`gh auth status`, to check the GitHub CLI is signed in',
@@ -188,9 +188,22 @@ const RUNNERS = {
188
188
  'import ../../../api/integration/integrationPackCheck.doc.mjs',
189
189
  'import ./integration-add.doc.mjs',
190
190
  'import ./integration-pack.doc.mjs',
191
+ 'import ./integration-verify.doc.mjs',
191
192
  'import ./integration.doc.mjs',
192
193
  ],
193
194
  },
195
+ 'clients/cli/commands/layout.mjs': {
196
+ runs: SELF_DOCS,
197
+ sites: [
198
+ 'import ../../../api/layout/layoutCheck.doc.mjs',
199
+ 'import ../../../api/layout/layoutExpand.doc.mjs',
200
+ 'import ../../../api/layout/layoutGrammar.doc.mjs',
201
+ 'import ./layout-check.doc.mjs',
202
+ 'import ./layout-expand.doc.mjs',
203
+ 'import ./layout-grammar.doc.mjs',
204
+ 'import ./layout.doc.mjs',
205
+ ],
206
+ },
194
207
  'clients/cli/commands/search.mjs': {
195
208
  runs: SELF_DOCS,
196
209
  sites: [
@@ -90,7 +90,6 @@ describe('collectDocInputs over what this repo ships', () => {
90
90
  ).toEqual([
91
91
  'Chat',
92
92
  'ChatComposerTokenElement',
93
- 'Code',
94
93
  'ContextMenuItem',
95
94
  'Heading',
96
95
  'Indicator',
@@ -240,6 +240,10 @@ export type TreeNode = {
240
240
  name: string;
241
241
  title: string;
242
242
  summary: string;
243
+ /**
244
+ * an authored namespace's search keywords
245
+ */
246
+ keywords?: string[] | undefined;
243
247
  /**
244
248
  * the parent's route; null at the top
245
249
  */
@@ -58,7 +58,7 @@ export const KIND_GROUPS = Object.freeze({
58
58
  schema: {
59
59
  segment: 'schemas',
60
60
  title: 'Schemas',
61
- summary: 'Every shape the CLI reads or returns, field by field.',
61
+ summary: 'Data shapes, field by field.',
62
62
  },
63
63
  enum: {
64
64
  segment: 'enums',
@@ -140,6 +140,7 @@ export const UNORGANIZED = 'unorganized';
140
140
  * @property {string} name
141
141
  * @property {string} title
142
142
  * @property {string} summary
143
+ * @property {string[]} [keywords] an authored namespace's search keywords
143
144
  * @property {string | null} parent the parent's route; null at the top
144
145
  * @property {string | null} slot the parent slot this node sits in
145
146
  * @property {number | null} order
@@ -420,10 +421,14 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
420
421
  name: input.doc.name,
421
422
  title: input.doc.title,
422
423
  summary: input.doc.summary,
424
+ ...(Array.isArray(input.doc.keywords)
425
+ ? {keywords: input.doc.keywords}
426
+ : {}),
423
427
  parent: parentRoute,
424
428
  slot: home?.slot ?? null,
425
429
  order: home?.order ?? null,
426
430
  generated: false,
431
+ ref: {selfDoc: input.doc},
427
432
  slots: Object.entries(input.doc.slots).map(([name, slot]) => ({
428
433
  name,
429
434
  title: slot.title,
@@ -20,13 +20,31 @@ export function lowestAdmitted(range: unknown): string | null;
20
20
  */
21
21
  export function docsTreeCliProblem(pkg: any): string | null;
22
22
  /**
23
- * Why a package with a template that sets `replaces` would lose its templates
24
- * and doc topics on an older CLI (spec:AST-035), or null when its declared CLI
23
+ * Why a package with a template that sets `replaces` would lose templates and
24
+ * doc topics on an older CLI (spec:AST-035), or null when its declared CLI
25
25
  * range admits only CLIs that read the field.
26
26
  * @param {any} pkg package.json
27
27
  * @returns {string | null}
28
28
  */
29
29
  export function replacesCliProblem(pkg: any): string | null;
30
+ /**
31
+ * Why a package with a doc section that sets `id` would lose its doc topics on
32
+ * an older CLI, or null when its declared CLI range admits only CLIs that read
33
+ * the field. Published 0.6.3 rejects a section `id` and hides every doc topic
34
+ * the package ships.
35
+ * @param {any} pkg package.json
36
+ * @returns {string | null}
37
+ */
38
+ export function sectionIdsCliProblem(pkg: any): string | null;
39
+ /**
40
+ * Why a package that ships a theme would lose its themes and doc topics on an
41
+ * older CLI, or null when its declared CLI range admits only CLIs that read
42
+ * typed theme descriptors. Published 0.6.3 rejects a themes root with no
43
+ * `manifest.json` catalog, which `integration add theme` no longer writes.
44
+ * @param {any} pkg package.json
45
+ * @returns {string | null}
46
+ */
47
+ export function themesCliProblem(pkg: any): string | null;
30
48
  /**
31
49
  * The package.json with a CLI peer that reads namespace docs: optional, unless
32
50
  * the package already says otherwise.
@@ -37,9 +55,11 @@ export function withDocsTreeCli(pkg: any): any;
37
55
  export const CLI_PACKAGE: "@astryxdesign/cli";
38
56
  /**
39
57
  * The first CLI release that reads an integration's docs tree (namespace docs
40
- * and placed guides) and its templates' `replaces`. A release before it can
41
- * hide every doc topic a package with a namespace doc or a placed guide
42
- * ships, with no warning, and it rejects `replaces` and withholds the
43
- * package's templates and doc topics.
58
+ * and placed guides), its templates' `replaces`, and its typed theme
59
+ * descriptors. A release before it can hide every doc topic a package with a
60
+ * namespace doc or a placed guide ships, with no warning; it rejects
61
+ * `replaces`, drops that template, and hides the package's doc topics; and it
62
+ * rejects a themes root without the `manifest.json` catalog the CLI no longer
63
+ * writes, withholding the package's themes and doc topics.
44
64
  */
45
65
  export const DOCS_TREE_CLI: "0.7.0";
@@ -6,8 +6,9 @@
6
6
  * @input A package.json object.
7
7
  * @output Whether its `peerDependencies` range for `@astryxdesign/cli` admits
8
8
  * only CLIs that read namespace docs, and the package.json that declares it.
9
- * @position foundation/integrations; read by `integration add doc --parent`,
10
- * which declares the peer, and `integration pack --check`, which requires it.
9
+ * @position foundation/integrations; read by `integration add doc --parent`
10
+ * and `integration add theme`, which declare the peer, and
11
+ * `integration verify`, which requires it.
11
12
  */
12
13
 
13
14
  import {semverCompare} from '../env/semver.mjs';
@@ -16,10 +17,12 @@ export const CLI_PACKAGE = '@astryxdesign/cli';
16
17
 
17
18
  /**
18
19
  * The first CLI release that reads an integration's docs tree (namespace docs
19
- * and placed guides) and its templates' `replaces`. A release before it can
20
- * hide every doc topic a package with a namespace doc or a placed guide
21
- * ships, with no warning, and it rejects `replaces` and withholds the
22
- * package's templates and doc topics.
20
+ * and placed guides), its templates' `replaces`, and its typed theme
21
+ * descriptors. A release before it can hide every doc topic a package with a
22
+ * namespace doc or a placed guide ships, with no warning; it rejects
23
+ * `replaces`, drops that template, and hides the package's doc topics; and it
24
+ * rejects a themes root without the `manifest.json` catalog the CLI no longer
25
+ * writes, withholding the package's themes and doc topics.
23
26
  */
24
27
  export const DOCS_TREE_CLI = '0.7.0';
25
28
 
@@ -92,11 +95,11 @@ function cliRangeProblem(pkg, feature, loss) {
92
95
  const range = pkg?.peerDependencies?.[CLI_PACKAGE];
93
96
  const fix = `"${CLI_PACKAGE}": ">=${DOCS_TREE_CLI}" in peerDependencies (optional in peerDependenciesMeta, if the CLI is not required)`;
94
97
  if (typeof range !== 'string') {
95
- return `The package ${feature} but declares no ${CLI_PACKAGE} peer. A CLI older than ${DOCS_TREE_CLI} ${loss}. Declare ${fix}.`;
98
+ return `The package ${feature} but declares no ${CLI_PACKAGE} peer. A stable CLI before ${DOCS_TREE_CLI} ${loss}. Declare ${fix}.`;
96
99
  }
97
100
  const lowest = lowestAdmitted(range);
98
101
  if (lowest == null || semverCompare(lowest, DOCS_TREE_CLI) < 0) {
99
- return `The package ${feature}, but its ${CLI_PACKAGE} peer range "${range}" admits a CLI older than ${DOCS_TREE_CLI}, which ${loss}. Declare ${fix}.`;
102
+ return `The package ${feature}, but its ${CLI_PACKAGE} peer range "${range}" admits a stable CLI before ${DOCS_TREE_CLI}, which ${loss}. Declare ${fix}.`;
100
103
  }
101
104
  return null;
102
105
  }
@@ -119,8 +122,8 @@ export function docsTreeCliProblem(pkg) {
119
122
  }
120
123
 
121
124
  /**
122
- * Why a package with a template that sets `replaces` would lose its templates
123
- * and doc topics on an older CLI (spec:AST-035), or null when its declared CLI
125
+ * Why a package with a template that sets `replaces` would lose templates and
126
+ * doc topics on an older CLI (spec:AST-035), or null when its declared CLI
124
127
  * range admits only CLIs that read the field.
125
128
  * @param {any} pkg package.json
126
129
  * @returns {string | null}
@@ -129,7 +132,39 @@ export function replacesCliProblem(pkg) {
129
132
  return cliRangeProblem(
130
133
  pkg,
131
134
  'has a template that sets `replaces`',
132
- "rejects the field, and withholds the package's templates and doc topics",
135
+ "rejects the field, drops that template, and hides the package's doc topics",
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Why a package with a doc section that sets `id` would lose its doc topics on
141
+ * an older CLI, or null when its declared CLI range admits only CLIs that read
142
+ * the field. Published 0.6.3 rejects a section `id` and hides every doc topic
143
+ * the package ships.
144
+ * @param {any} pkg package.json
145
+ * @returns {string | null}
146
+ */
147
+ export function sectionIdsCliProblem(pkg) {
148
+ return cliRangeProblem(
149
+ pkg,
150
+ 'has a doc section that sets `id`',
151
+ 'rejects the field, and can hide every doc topic the package ships',
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Why a package that ships a theme would lose its themes and doc topics on an
157
+ * older CLI, or null when its declared CLI range admits only CLIs that read
158
+ * typed theme descriptors. Published 0.6.3 rejects a themes root with no
159
+ * `manifest.json` catalog, which `integration add theme` no longer writes.
160
+ * @param {any} pkg package.json
161
+ * @returns {string | null}
162
+ */
163
+ export function themesCliProblem(pkg) {
164
+ return cliRangeProblem(
165
+ pkg,
166
+ 'ships a theme',
167
+ "cannot read typed theme descriptors, and can drop the package's themes and hide its doc topics",
133
168
  );
134
169
  }
135
170
 
@@ -32,6 +32,11 @@ describe('docsTreeCliProblem', () => {
32
32
  expect(docsTreeCliProblem({name: '@acme/kit'})).toContain(
33
33
  'declares no @astryxdesign/cli peer',
34
34
  );
35
+ // Stable releases: a canary built from main before its release reports
36
+ // the older version and reads every feature.
37
+ expect(docsTreeCliProblem({name: '@acme/kit'})).toContain(
38
+ 'A stable CLI before 0.7.0 does not read the docs tree',
39
+ );
35
40
  });
36
41
 
37
42
  it.each(['^0.6.0', '^0.6.0 || >=0.7.0', '*', '>=0.6.3'])(
@@ -39,7 +44,7 @@ describe('docsTreeCliProblem', () => {
39
44
  range => {
40
45
  expect(
41
46
  docsTreeCliProblem({peerDependencies: {'@astryxdesign/cli': range}}),
42
- ).toContain('admits a CLI older than');
47
+ ).toContain('admits a stable CLI before');
43
48
  },
44
49
  );
45
50
 
@@ -76,7 +81,7 @@ describe('replacesCliProblem', () => {
76
81
  );
77
82
  expect(
78
83
  replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '^0.6.0'}}),
79
- ).toContain('admits a CLI older than');
84
+ ).toContain('admits a stable CLI before');
80
85
  expect(
81
86
  replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '>=0.7.0'}}),
82
87
  ).toBeNull();
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file Contribution inventory for integration pack-check.
4
+ * @file Contribution inventory for `astryx integration verify`.
5
5
  *
6
6
  * Enumerates the files and contribution identities an integration package
7
7
  * declares, for cross-referencing against the npm tarball. The file inventory
@@ -36,17 +36,16 @@ export const doc = {
36
36
  {
37
37
  value: 'ERR_INVALID_OPTION',
38
38
  description:
39
- 'An unknown flag/option was passed (Commander `unknownOption`).',
39
+ 'An unknown option was passed, --json was given to a command without JSON output, or layout --form got a value other than compact, outline, or auto.',
40
40
  },
41
41
  {
42
42
  value: 'ERR_INVALID_ARGUMENT',
43
43
  description:
44
- "An option/argument had a value Commander's parser rejected.",
44
+ 'An argument or option value is invalid: wrong type, out of range, an unknown choice, an extra argument, or a conflicting combination.',
45
45
  },
46
46
  {
47
47
  value: 'ERR_MISSING_ARGUMENT',
48
- description:
49
- 'A required positional argument was omitted (Commander `missingArgument`).',
48
+ description: 'A required argument or option value was omitted.',
50
49
  },
51
50
  {
52
51
  value: 'ERR_INVALID_LANG',
@@ -134,12 +133,11 @@ export const doc = {
134
133
  },
135
134
  {
136
135
  value: 'ERR_UNKNOWN_PACKAGE',
137
- description: 'No package matched the requested name (discover).',
136
+ description: 'No package matched the requested name.',
138
137
  },
139
138
  {
140
139
  value: 'ERR_UNKNOWN_AGENT',
141
- description:
142
- 'An unrecognized `--agent` value was passed to agent-docs/init.',
140
+ description: 'An unrecognized `--agent` value was passed to init.',
143
141
  },
144
142
  {
145
143
  value: 'ERR_UNKNOWN_FEATURE',
@@ -272,7 +270,7 @@ export const doc = {
272
270
  {
273
271
  value: 'ERR_UNCLASSIFIED_EXIT',
274
272
  description:
275
- 'Recorded in the debug log, never printed: a command exited non-zero without going through cliError/jsonError, so no stable code was available.',
273
+ 'Recorded in the debug log, never printed: a command exited non-zero without reporting an error code.',
276
274
  },
277
275
  {
278
276
  value: 'ERR_SIGNAL_TERMINATED',
@@ -195,9 +195,16 @@ describe('error codes: end-to-end JSON envelopes', () => {
195
195
  // is rejected at the preAction gate with a stable invalid-option code.
196
196
  {
197
197
  name: 'json not supported',
198
- args: ['theme', 'bogus-sub', '--json'],
198
+ args: ['theme', '--json'],
199
199
  code: 'ERR_INVALID_OPTION',
200
200
  },
201
+ // A group given a word it does not have names the unknown subcommand,
202
+ // in JSON as in text.
203
+ {
204
+ name: 'unknown subcommand of a group',
205
+ args: ['theme', 'bogus-sub', '--json'],
206
+ code: 'ERR_UNKNOWN_SUBCOMMAND',
207
+ },
201
208
  ];
202
209
 
203
210
  for (const {name, args, code} of cases) {
@@ -249,13 +256,31 @@ describe('error codes: human mode stays clean', () => {
249
256
  expect(stdout).not.toContain('ERR_UNKNOWN_COMPONENT');
250
257
  });
251
258
 
252
- it('unknown subcommand exits 1 in human mode (code carried internally)', async () => {
253
- // `theme` is not JSON-capable, so this path is human-only; we assert the
254
- // failure surfaces with exit 1 and a helpful message. The stable
255
- // ERR_UNKNOWN_SUBCOMMAND code rides along on the cliError call.
259
+ it('unknown subcommand exits 1, with the code in JSON and not in text', async () => {
260
+ // A group that is not JSON-capable itself still answers an unknown
261
+ // subcommand in JSON, so a JSON caller learns the subcommands it has.
256
262
  const {status, stderr} = await runCli(['theme', 'bogus-sub']);
257
263
  expect(status).toBe(1);
258
264
  expect(stderr).toContain("unknown subcommand 'theme bogus-sub'");
259
265
  expect(stderr).not.toContain('ERR_UNKNOWN_SUBCOMMAND');
266
+ for (const args of [
267
+ ['--json', 'theme', 'bogus-sub'],
268
+ ['theme', 'bogus-sub', '--json'],
269
+ ['--json', 'doctor', 'integration', 'bogus-sub'],
270
+ ]) {
271
+ const json = await runCli(args);
272
+ expect(json.status, args.join(' ')).toBe(1);
273
+ expect(JSON.parse(json.stdout), args.join(' ')).toMatchObject({
274
+ code: 'ERR_UNKNOWN_SUBCOMMAND',
275
+ suggestions: expect.arrayContaining([expect.objectContaining({reason: 'available subcommand'})]),
276
+ });
277
+ }
278
+ // Text and JSON agree for a group that shows help when run bare.
279
+ const text = await runCli(['doctor', 'integration', 'bogus-sub']);
280
+ expect(text.status).toBe(1);
281
+ expect(text.stderr).toContain("unknown subcommand 'doctor integration bogus-sub'");
282
+ const doctor = await runCli(['doctor', 'integrations']);
283
+ expect(doctor.status).toBe(1);
284
+ expect(doctor.stderr).toContain("unknown subcommand 'doctor integrations'");
260
285
  });
261
286
  });
@@ -4,9 +4,10 @@
4
4
  /**
5
5
  * @file EnumDoc for the `type` discriminant carried on every --json success
6
6
  * envelope. The vocabulary mirrors the RESPONSE_TYPES map (each command's
7
- * `jsonOut(...)` call sites) in `clients/cli/lib/manifest.mjs`; a consumer
8
- * switches on `type` to narrow the `data` payload. Descriptions follow the
9
- * currently published response projection, not a future package-version boundary.
7
+ * `jsonOut(...)` call sites) plus ROOT_RESPONSE_TYPES (help, version) in
8
+ * `clients/cli/lib/manifest.mjs`; a consumer switches on `type` to narrow the
9
+ * `data` payload. Descriptions follow the currently published response
10
+ * projection, not a future package-version boundary.
10
11
  *
11
12
  * @input Public response discriminants and their currently released payloads.
12
13
  * @output Generated consumer reference for every typed JSON success response.
@@ -3,9 +3,10 @@
3
3
  /**
4
4
  * @file EnumDoc for the `type` discriminant carried on every --json success
5
5
  * envelope. The vocabulary mirrors the RESPONSE_TYPES map (each command's
6
- * `jsonOut(...)` call sites) in `clients/cli/lib/manifest.mjs`; a consumer
7
- * switches on `type` to narrow the `data` payload. Descriptions follow the
8
- * currently published response projection, not a future package-version boundary.
6
+ * `jsonOut(...)` call sites) plus ROOT_RESPONSE_TYPES (help, version) in
7
+ * `clients/cli/lib/manifest.mjs`; a consumer switches on `type` to narrow the
8
+ * `data` payload. Descriptions follow the currently published response
9
+ * projection, not a future package-version boundary.
9
10
  *
10
11
  * @input Public response discriminants and their currently released payloads.
11
12
  * @output Generated consumer reference for every typed JSON success response.
@@ -35,7 +36,7 @@ export const doc = {
35
36
  {
36
37
  value: 'component.list',
37
38
  description:
38
- 'The component catalog grouped by category: `detail` (the level: names | compact | full) and `components`, the grouped map of names entries ({name, package, and optional canonical import for integrations}), brief entries, or a full ComponentDoc per entry.',
39
+ "The component catalog grouped by component group (each component's group field): `detail` (the level: names | compact | full) and `components`, the grouped map of names entries ({name, package, and optional canonical import for integrations}), brief entries, or a full ComponentDoc per entry.",
39
40
  },
40
41
  {
41
42
  value: 'component.batch',
@@ -160,7 +161,7 @@ export const doc = {
160
161
  {
161
162
  value: 'swizzle.copy',
162
163
  description:
163
- 'An eject receipt: component name, owning package, output directory, files-copied count, the written file names, whether any file uses StyleX, and an optional maintainer note.',
164
+ 'An eject receipt: component name, owning package, output directory, files-copied count, the written file names, whether any file uses StyleX, and, when the owner has an issues URL, feedback ({issuesUrl, ghCommand?}): where to report the gap that led to swizzling.',
164
165
  },
165
166
 
166
167
  // gap reports
@@ -262,6 +263,11 @@ export const doc = {
262
263
  description:
263
264
  'Every available codemod, oldest→newest, as {name, title, version, optional}; returned for --list without running anything.',
264
265
  },
266
+ {
267
+ value: 'upgrade.registry',
268
+ description:
269
+ 'The copied-composition receipt for --registry: applied, ok, the counts (found, current, wouldUpdate, updated, wouldMerge, merged, wouldRefreshReceipt, receiptsRefreshed, conflicts, missing, invalid, failed), and items.',
270
+ },
265
271
  {
266
272
  value: 'upgrade.status',
267
273
  description:
@@ -280,11 +286,24 @@ export const doc = {
280
286
  'The CLI capability manifest: name, version, apiVersion, description, globalOptions, commands (each name, description, arguments, options, json, aliases?, responseTypes?, examples?, exitCodes? as [{code, when}], subcommands?), jsonSupported, and the flat responseTypes index.',
281
287
  },
282
288
 
289
+ // help and version, which no single command owns
290
+ {
291
+ value: 'help',
292
+ description:
293
+ 'Help, in one of two shapes. A bare `astryx --json` returns the root manifest: name, version, commands (the command names), jsonSupported, and manifest (the full payload `astryx manifest --json` returns). ' +
294
+ "`--help --json` on any command, or `astryx help [command] --json`, returns that command's help: command, description, usage, options (each flags, description, and defaultValue and choices when set), and subcommands (each name and description). " +
295
+ 'data.manifest marks the first shape; data.usage marks the second.',
296
+ },
297
+ {
298
+ value: 'version',
299
+ description: 'The CLI version, for `astryx --version --json`: {version}.',
300
+ },
301
+
283
302
  // doctor
284
303
  {
285
304
  value: 'doctor',
286
305
  description:
287
- 'The health-check report: `checks` (each with id, label, status: pass | warn | fail | info, a message, and a fix when not passing) plus a `summary` of counts per status.',
306
+ 'The health-check report: `checks` (each with id, label, status: pass | warn | fail | info, a message, and an optional fix, always present on warn and fail) plus a `summary` of counts per status.',
288
307
  },
289
308
 
290
309
  // integration authoring
@@ -318,5 +337,22 @@ export const doc = {
318
337
  description:
319
338
  'The integration identity, structural issues, and Core doc overlaps. Each finding includes `severity` (`info` | `error`) and `relationship` (`replaces` | `extends` | `accidental`).',
320
339
  },
340
+
341
+ // layout (XLE/XLO)
342
+ {
343
+ value: 'layout.expand',
344
+ description:
345
+ 'The expansion: parsed form, generated TSX code, componentsUsed, states (count of useState hooks scaffolded), todos, blocksReferenced (each {name, mode}), warnings, and written (the output path, or null when nothing was written).',
346
+ },
347
+ {
348
+ value: 'layout.check',
349
+ description:
350
+ 'The validation result: a valid flag, the detected form, errors (each with line/col, message, formatted text, and suggestions), warnings, and the expression re-printed in both canonical surfaces (compact and outline).',
351
+ },
352
+ {
353
+ value: 'layout.grammar',
354
+ description:
355
+ "The XLE/XLO grammar cheatsheet: a text field with the full reference plus an aliases map (short name → canonical component) generated from this install's registry.",
356
+ },
321
357
  ],
322
358
  };
@@ -39,16 +39,13 @@ export const doc = {
39
39
  name: 'type',
40
40
  type: 'string',
41
41
  description:
42
- 'Discriminator naming the payload shape. Each command guarantees ' +
43
- 'its own `type` at its return; there is no central union.',
42
+ 'Names the payload shape. Every value is listed in the response-types enum.',
44
43
  required: true,
45
44
  },
46
45
  {
47
46
  name: 'data',
48
47
  type: 'unknown',
49
- description:
50
- "The command's payload. Structural by design, narrowed by the " +
51
- 'per-command return type, not by a map in the serializer.',
48
+ description: "The command's payload; its shape depends on `type`.",
52
49
  required: true,
53
50
  },
54
51
  {
@@ -64,7 +61,7 @@ export const doc = {
64
61
  type: '{ apiVersion: number; error: string; code: ErrorCode; suggestions?: Suggestion[] }',
65
62
  description:
66
63
  'Emitted for every failure in --json mode, including uncaught throws ' +
67
- 'and Commander parse errors, which are converted to this shape.',
64
+ 'and command-line parse errors, which are converted to this shape.',
68
65
  fields: [
69
66
  {
70
67
  name: 'apiVersion',
@@ -121,7 +118,7 @@ export const doc = {
121
118
  label: 'Success',
122
119
  code: `{
123
120
  "apiVersion": 1,
124
- "type": "component",
121
+ "type": "component.detail",
125
122
  "data": { "name": "Button" }
126
123
  }`,
127
124
  },
@@ -153,9 +150,13 @@ export const doc = {
153
150
  {
154
151
  type: 'prose',
155
152
  text:
156
- 'The process exit code is part of the contract too: successful commands ' +
157
- 'exit 0, and a JSON error envelope is accompanied by a non-zero exit ' +
158
- '(jsonError exits 1).',
153
+ 'The process exit code is part of the contract too. Exit 0 means success, ' +
154
+ 'and every error envelope exits 1. Some commands also exit 1 with a success ' +
155
+ 'envelope when what they report failed: a failed check (layout check, doctor ' +
156
+ 'and its integration subcommands, integration verify, theme build ' +
157
+ '--check), a gap report that was not delivered, an upgrade left incomplete, ' +
158
+ 'or init agent docs refused for a path outside the project. Read the ' +
159
+ 'envelope, not the exit code, to tell them apart.',
159
160
  },
160
161
  ],
161
162
  };
@@ -2,8 +2,8 @@
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
4
  /**
5
- * Validate an expression and return both canonical surfaces. Never throws on
6
- * invalid input; returns the errors.
5
+ * Validate an expression and return both canonical surfaces — the browser
6
+ * twin of `layout check`. Never throws on invalid input; returns the errors.
7
7
  *
8
8
  * @param {string} expression
9
9
  * @param {import('./xle-ast').Registry | import('./browser').SerializedRegistry | object} registry - serialized or hydrated
@@ -19,7 +19,7 @@ export function checkExpression(expression: string, registry: import("./xle-ast"
19
19
  loose?: boolean | undefined;
20
20
  }): import("./browser").CheckResult;
21
21
  /**
22
- * Validate + expand to TSX.
22
+ * Validate + expand to TSX — the browser twin of `layout expand`.
23
23
  * Returns either {code, ...} or {errors} (never writes files).
24
24
  *
25
25
  * @param {string} expression
@@ -44,8 +44,8 @@ function formatIssue(issue) {
44
44
  }
45
45
 
46
46
  /**
47
- * Validate an expression and return both canonical surfaces. Never throws on
48
- * invalid input; returns the errors.
47
+ * Validate an expression and return both canonical surfaces — the browser
48
+ * twin of `layout check`. Never throws on invalid input; returns the errors.
49
49
  *
50
50
  * @param {string} expression
51
51
  * @param {import('./xle-ast').Registry | import('./browser').SerializedRegistry | object} registry - serialized or hydrated
@@ -85,7 +85,7 @@ export function checkExpression(expression, registry, opts = {}) {
85
85
  }
86
86
 
87
87
  /**
88
- * Validate + expand to TSX.
88
+ * Validate + expand to TSX — the browser twin of `layout expand`.
89
89
  * Returns either {code, ...} or {errors} (never writes files).
90
90
  *
91
91
  * @param {string} expression
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * @input validated doc (nodes carry .bound) + registry
15
15
  * @output expand() → {code, componentsUsed, states, todos}
16
- * @position lib/xle — last stage; the browser barrel orchestrates
16
+ * @position lib/xle — last stage; api/layout.mjs orchestrates
17
17
  */
18
18
 
19
19
  import {PAYLOAD_PROPS} from './validate.mjs';
@@ -751,7 +751,7 @@ export function expand(doc, registry, options = {}) {
751
751
  : [];
752
752
 
753
753
  const code = [
754
- `// Generated by the Astryx XLE expander — this file is the artifact; edit freely.`,
754
+ `// Generated by \`astryx layout expand\` — this file is the artifact; edit freely.`,
755
755
  ...importLines,
756
756
  ...blockSection,
757
757
  '',
@@ -302,7 +302,7 @@ function parseAttrToken(token, line) {
302
302
  * Guard against unbounded recursion in the compact parser. A layout tree this
303
303
  * deep is never hand-authored; without the cap the recursive descent blows the
304
304
  * JS call stack and surfaces a raw RangeError (→ ERR_UNKNOWN, no line/col)
305
- * instead of a located parse error. The counter is module-level so it
305
+ * instead of a located ERR_LAYOUT_PARSE. The counter is module-level so it
306
306
  * spans group / attribute sub-streams; each parse entry point resets it.
307
307
  */
308
308
  const MAX_COMPACT_DEPTH = 512;
@@ -3,8 +3,8 @@
3
3
  /**
4
4
  * @file XLE canonical printers — AST → compact (XLE) or outline (XLO) text.
5
5
  *
6
- * Used by `checkExpression` so either party (agent or human) can request the
7
- * other surface, and by round-trip conformance tests.
6
+ * Used by `astryx layout check --form …` so either party (agent or human)
7
+ * can request the other surface, and by round-trip conformance tests.
8
8
  * Printing is best-effort canonical: spelling variants the lexer
9
9
  * normalized are emitted in canonical form.
10
10
  *
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * @input block TSX source (splice) or a name+importPath (import)
20
20
  * @output prepareSpliceModule / parseImportStatements + import-merge helpers
21
- * @position lib/xle — used by expand.mjs; sources gathered by the caller
21
+ * @position lib/xle — used by expand.mjs; sources gathered in api/layout.mjs
22
22
  */
23
23
 
24
24
  const IMPORT_RE =