@astryxdesign/cli 0.6.3-canary.98a2e4e → 0.6.3-canary.9c4d44d

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 (612) hide show
  1. package/README.md +117 -79
  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 +16 -9
  6. package/api/build/build.test.mjs +197 -8
  7. package/api/build/build.type.d.mts +91 -2
  8. package/api/build/build.type.mjs +52 -8
  9. package/api/build/help/help.d.mts +12 -5
  10. package/api/build/help/help.mjs +69 -6
  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 +13 -3
  19. package/api/component/component.mjs +91 -14
  20. package/api/component/component.test.mjs +38 -0
  21. package/api/component/component.type.d.mts +22 -11
  22. package/api/component/component.type.mjs +32 -24
  23. package/api/component/detail/blocks/blocks.d.mts +2 -1
  24. package/api/component/detail/blocks/blocks.mjs +4 -3
  25. package/api/component/list/list.d.mts +0 -5
  26. package/api/component/list/list.mjs +40 -11
  27. package/api/discover/_adapter.d.mts +114 -6
  28. package/api/discover/_adapter.mjs +372 -17
  29. package/api/discover/_adapter.test.mjs +215 -0
  30. package/api/discover/_catalog-view.d.mts +115 -0
  31. package/api/discover/_catalog-view.mjs +203 -0
  32. package/api/discover/_catalog-view.test.mjs +128 -0
  33. package/api/discover/detail/detail.d.mts +18 -6
  34. package/api/discover/detail/detail.mjs +67 -13
  35. package/api/discover/detail/detail.test.mjs +85 -0
  36. package/api/discover/detail/item/item.d.mts +26 -0
  37. package/api/discover/detail/item/item.mjs +78 -0
  38. package/api/discover/detail/item/item.test.mjs +73 -0
  39. package/api/discover/discover.d.mts +3 -9
  40. package/api/discover/discover.doc.mjs +62 -18
  41. package/api/discover/discover.mjs +220 -36
  42. package/api/discover/discover.test.mjs +11 -2
  43. package/api/discover/discover.type.d.mts +150 -11
  44. package/api/discover/discover.type.mjs +107 -17
  45. package/api/discover/list/list.d.mts +20 -6
  46. package/api/discover/list/list.mjs +45 -12
  47. package/api/discover/list/list.test.mjs +46 -0
  48. package/api/discover/search/search.d.mts +18 -16
  49. package/api/discover/search/search.mjs +102 -56
  50. package/api/discover/search/search.test.mjs +144 -10
  51. package/api/docs/_adapter.d.mts +268 -47
  52. package/api/docs/_adapter.mjs +970 -149
  53. package/api/docs/compiled-topics.test.mjs +78 -0
  54. package/api/docs/detail/detail.d.mts +0 -15
  55. package/api/docs/detail/detail.mjs +22 -78
  56. package/api/docs/detail/section/section.mjs +39 -24
  57. package/api/docs/detail/section/section.test.mjs +19 -9
  58. package/api/docs/docs.d.mts +5 -3
  59. package/api/docs/docs.doc.mjs +39 -17
  60. package/api/docs/docs.mjs +44 -8
  61. package/api/docs/docs.test.mjs +158 -4
  62. package/api/docs/docs.type.d.mts +185 -6
  63. package/api/docs/docs.type.mjs +127 -13
  64. package/api/docs/index/index.mjs +15 -6
  65. package/api/docs/index/index.test.mjs +1 -1
  66. package/api/docs/integration-tree.test.mjs +555 -0
  67. package/api/docs/integrationDocs.test.mjs +14 -14
  68. package/api/docs/list/list.mjs +28 -12
  69. package/api/docs/node/node.d.mts +43 -0
  70. package/api/docs/node/node.mjs +192 -0
  71. package/api/docs/reference-blocks.test.mjs +406 -0
  72. package/api/doctor/doctor.d.mts +54 -4
  73. package/api/doctor/doctor.doc.mjs +1 -0
  74. package/api/doctor/doctor.mjs +332 -21
  75. package/api/doctor/doctor.test.mjs +420 -7
  76. package/api/gap-report/gap-report.doc.mjs +8 -4
  77. package/api/hook/_adapter.mjs +19 -5
  78. package/api/hook/hook.doc.mjs +1 -0
  79. package/api/hook/hook.type.d.mts +3 -3
  80. package/api/hook/hook.type.mjs +11 -11
  81. package/api/hook/list/list.d.mts +1 -1
  82. package/api/hook/list/list.mjs +69 -17
  83. package/api/index.d.mts +1 -1
  84. package/api/index.mjs +1 -0
  85. package/api/init/init.doc.mjs +6 -1
  86. package/api/init/init.test.mjs +41 -1
  87. package/api/init/remove/remove.mjs +1 -1
  88. package/api/init/run/run.mjs +20 -10
  89. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  90. package/api/integration/add-contribution.d.mts +2 -1
  91. package/api/integration/add-contribution.mjs +130 -15
  92. package/api/integration/add-contribution.test.mjs +258 -7
  93. package/api/integration/add-helpers.d.mts +5 -2
  94. package/api/integration/add-helpers.mjs +36 -9
  95. package/api/integration/add-theme.mjs +34 -64
  96. package/api/integration/add-theme.test.mjs +105 -21
  97. package/api/integration/authoring-checks.mjs +138 -28
  98. package/api/integration/authoring-checks.test.mjs +179 -7
  99. package/api/integration/authoring-checks.type.mjs +6 -1
  100. package/api/integration/integration-authoring.type.d.mts +2 -0
  101. package/api/integration/integration-authoring.type.mjs +2 -0
  102. package/api/integration/integration-block-exports.test.mjs +10 -6
  103. package/api/integration/integrationAdd.doc.mjs +14 -4
  104. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  105. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  106. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  107. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  108. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  109. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  110. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  111. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  112. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  113. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  114. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  115. package/api/integration/pack-check.mjs +83 -7
  116. package/api/integration/pack-check.test.mjs +387 -47
  117. package/api/integration/pack-check.type.d.mts +26 -2
  118. package/api/integration/pack-check.type.mjs +14 -1
  119. package/api/integration/summarizeIssues.doc.mjs +1 -0
  120. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  121. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  122. package/api/integration/validate-integration.mjs +52 -102
  123. package/api/integration/validate-integration.test.mjs +179 -26
  124. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  125. package/api/integration/validateIntegration.doc.mjs +3 -2
  126. package/api/json/assertResponse.doc.mjs +1 -0
  127. package/api/json/envelope-types.test.mjs +76 -0
  128. package/api/json/index.ts +2 -0
  129. package/api/json/isError.doc.mjs +1 -0
  130. package/api/json/parseResponse.doc.mjs +3 -2
  131. package/api/layout/_adapter.mjs +20 -5
  132. package/api/layout/expand/expand.mjs +7 -5
  133. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  134. package/api/layout/grammar/grammar.mjs +2 -1
  135. package/api/layout/layoutCheck.doc.mjs +1 -0
  136. package/api/layout/layoutExpand.doc.mjs +2 -1
  137. package/api/layout/layoutGrammar.doc.mjs +1 -0
  138. package/api/search/search-return-type.test.mjs +54 -0
  139. package/api/search/search.d.mts +61 -10
  140. package/api/search/search.doc.mjs +8 -2
  141. package/api/search/search.mjs +471 -83
  142. package/api/search/search.test.mjs +124 -1
  143. package/api/search/search.type.d.mts +14 -2
  144. package/api/search/search.type.mjs +5 -2
  145. package/api/swizzle/copy/copy.mjs +28 -11
  146. package/api/swizzle/swizzle.doc.mjs +2 -1
  147. package/api/swizzle/swizzle.type.d.mts +2 -2
  148. package/api/swizzle/swizzle.type.mjs +2 -2
  149. package/api/template/copy/copy.mjs +17 -23
  150. package/api/template/copy/copy.test.mjs +17 -0
  151. package/api/template/list/list.mjs +1 -0
  152. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  153. package/api/template/template-integration.test.mjs +1072 -3
  154. package/api/template/template-suffix.test.mjs +41 -21
  155. package/api/template/template.doc.mjs +30 -8
  156. package/api/template/template.mjs +45 -8
  157. package/api/template/template.type.d.mts +11 -13
  158. package/api/template/template.type.mjs +15 -14
  159. package/api/theme/_adapter.d.mts +2 -3
  160. package/api/theme/_adapter.mjs +4 -5
  161. package/api/theme/add/add.binary.test.mjs +84 -0
  162. package/api/theme/add/add.mjs +31 -22
  163. package/api/theme/add/add.rollback.test.mjs +158 -0
  164. package/api/theme/add/add.staging.test.mjs +83 -0
  165. package/api/theme/add/add.test.mjs +14 -1
  166. package/api/theme/build/build.family.test.mjs +7 -12
  167. package/api/theme/build/build.mjs +140 -59
  168. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  169. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  170. package/api/theme/build/build.rollback.test.mjs +148 -0
  171. package/api/theme/build/build.test.mjs +127 -0
  172. package/api/theme/build/font-warning.mjs +3 -3
  173. package/api/theme/build/font-warning.test.mjs +5 -2
  174. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  175. package/api/theme/integration-themes.test.mjs +39 -28
  176. package/api/theme/list/list.test.mjs +19 -20
  177. package/api/theme/listThemes.doc.mjs +6 -5
  178. package/api/theme/palette/generate/generate.mjs +8 -3
  179. package/api/theme/palette/generate/generate.test.mjs +96 -0
  180. package/api/theme/palette/generate/generator.d.mts +10 -13
  181. package/api/theme/palette/generate/generator.mjs +15 -4
  182. package/api/theme/palette/generate/generator.test.mjs +10 -0
  183. package/api/theme/template/template.mjs +11 -2
  184. package/api/theme/template/template.test.mjs +20 -0
  185. package/api/theme/theme.type.d.mts +170 -11
  186. package/api/theme/theme.type.mjs +94 -27
  187. package/api/theme/themeAdd.doc.mjs +4 -3
  188. package/api/theme/themeBuild.doc.mjs +8 -4
  189. package/api/theme/themeList.doc.mjs +6 -3
  190. package/api/theme/themeListAvailable.doc.mjs +5 -3
  191. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  192. package/api/theme/themeTargets.doc.mjs +1 -0
  193. package/api/theme/themeTemplate.doc.mjs +6 -2
  194. package/api/upgrade/_adapter.d.mts +32 -5
  195. package/api/upgrade/_adapter.mjs +124 -73
  196. package/api/upgrade/list/list.mjs +2 -1
  197. package/api/upgrade/list/list.test.mjs +73 -0
  198. package/api/upgrade/provider-agreement.test.mjs +152 -0
  199. package/api/upgrade/run/run.mjs +356 -59
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +8 -2
  202. package/api/upgrade/upgrade.type.d.mts +42 -4
  203. package/api/upgrade/upgrade.type.mjs +26 -10
  204. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  205. package/assets/codemods/integration-discovery.mjs +48 -4
  206. package/assets/codemods/integration-discovery.test.mjs +73 -0
  207. package/assets/codemods/integration-runner.mjs +56 -4
  208. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  209. package/assets/codemods/run-codemod.mjs +177 -34
  210. package/assets/codemods/runner.mjs +350 -102
  211. package/assets/codemods/term-log.mjs +32 -8
  212. package/assets/codemods/term-log.test.mjs +19 -1
  213. package/assets/codemods/transform-prop.mjs +109 -0
  214. package/assets/codemods/transform-prop.test.mjs +95 -0
  215. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  216. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  217. package/assets/codemods/transforms/next/index.mjs +19 -1
  218. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  219. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  220. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  221. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  222. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  223. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  224. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  225. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  226. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  227. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  228. package/assets/docs/getting-started.doc.mjs +2 -2
  229. package/assets/docs/internationalization.doc.mjs +7 -5
  230. package/assets/docs/layout.doc.dense.mjs +2 -2
  231. package/assets/docs/layout.doc.mjs +1 -1
  232. package/assets/docs/principles.doc.mjs +6 -6
  233. package/assets/docs/styling-libraries.doc.mjs +4 -4
  234. package/assets/docs/styling.doc.mjs +4 -4
  235. package/assets/docs/theme.doc.mjs +5 -5
  236. package/assets/docs/tokens.doc.mjs +1 -1
  237. package/assets/docs/tree/api.doc.mjs +30 -0
  238. package/assets/docs/tree/cli.doc.mjs +23 -0
  239. package/assets/docs/tree/commands.doc.mjs +25 -0
  240. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +95 -39
  241. package/assets/docs/tree/integrations.test.mjs +62 -0
  242. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  243. package/assets/docs/working-with-ai.doc.mjs +4 -4
  244. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  245. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  246. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  247. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  248. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  249. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  250. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  251. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  252. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  253. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  254. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  255. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  262. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  264. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  265. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  266. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  267. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  268. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  269. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  270. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  271. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  272. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  273. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  274. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  275. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  276. package/authoring/_shared/contract.ts +22 -0
  277. package/authoring/codemod/codemod.doc.mjs +7 -2
  278. package/authoring/codemod/parse.d.mts +8 -8
  279. package/authoring/codemod/parse.mjs +8 -6
  280. package/authoring/codemod/type.ts +12 -0
  281. package/authoring/config/config.doc.mjs +10 -2
  282. package/authoring/config/debug-composition.test.mjs +92 -0
  283. package/authoring/config/parse.d.mts +15 -13
  284. package/authoring/config/parse.mjs +27 -8
  285. package/authoring/config/parse.test.mjs +8 -0
  286. package/authoring/config/type.ts +29 -6
  287. package/authoring/debug/debug.doc.d.mts +11 -0
  288. package/authoring/debug/debug.doc.mjs +182 -0
  289. package/authoring/debug/parse.d.mts +7 -7
  290. package/authoring/debug/parse.mjs +3 -3
  291. package/authoring/discover/discover.doc.d.mts +13 -0
  292. package/authoring/discover/discover.doc.mjs +138 -0
  293. package/authoring/discover/parse.d.mts +24 -0
  294. package/authoring/discover/parse.mjs +128 -0
  295. package/authoring/discover/parse.test.mjs +124 -0
  296. package/authoring/discover/type.ts +87 -0
  297. package/authoring/doctypes/_schema.d.mts +240 -128
  298. package/authoring/doctypes/_schema.mjs +198 -16
  299. package/authoring/doctypes/base/graph-fields.doc.mjs +15 -6
  300. package/authoring/doctypes/base/type.ts +9 -4
  301. package/authoring/doctypes/command/command.doc.mjs +4 -3
  302. package/authoring/doctypes/command/parse.d.mts +2 -2
  303. package/authoring/doctypes/command/parse.mjs +1 -1
  304. package/authoring/doctypes/command/type.ts +4 -4
  305. package/authoring/doctypes/component/component.doc.mjs +5 -2
  306. package/authoring/doctypes/component/parse.d.mts +2 -2
  307. package/authoring/doctypes/component/parse.mjs +1 -1
  308. package/authoring/doctypes/component/type.ts +3 -3
  309. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  310. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  311. package/authoring/doctypes/enum/parse.d.mts +2 -2
  312. package/authoring/doctypes/enum/parse.mjs +1 -1
  313. package/authoring/doctypes/enum/type.ts +2 -2
  314. package/authoring/doctypes/function/function.doc.mjs +7 -2
  315. package/authoring/doctypes/function/parse.d.mts +2 -2
  316. package/authoring/doctypes/function/parse.mjs +1 -1
  317. package/authoring/doctypes/function/type.ts +4 -3
  318. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  319. package/authoring/doctypes/hook/parse.d.mts +2 -2
  320. package/authoring/doctypes/hook/parse.mjs +1 -1
  321. package/authoring/doctypes/hook/type.ts +3 -3
  322. package/authoring/doctypes/legacy.d.mts +6 -6
  323. package/authoring/doctypes/legacy.mjs +3 -3
  324. package/authoring/doctypes/load-contract.test.mjs +233 -0
  325. package/authoring/doctypes/namespace/namespace.doc.mjs +9 -9
  326. package/authoring/doctypes/namespace/parse.d.mts +2 -2
  327. package/authoring/doctypes/namespace/parse.mjs +1 -1
  328. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  329. package/authoring/doctypes/namespace/type.ts +6 -3
  330. package/authoring/doctypes/parse.d.mts +22 -20
  331. package/authoring/doctypes/parse.mjs +19 -14
  332. package/authoring/doctypes/reference/parse.d.mts +2 -2
  333. package/authoring/doctypes/reference/parse.mjs +1 -1
  334. package/authoring/doctypes/reference/reference.doc.mjs +39 -6
  335. package/authoring/doctypes/reference/type.ts +31 -14
  336. package/authoring/doctypes/schema/parse.d.mts +2 -2
  337. package/authoring/doctypes/schema/parse.mjs +1 -1
  338. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  339. package/authoring/doctypes/schema/type.ts +3 -4
  340. package/authoring/doctypes/template/parse.d.mts +94 -1
  341. package/authoring/doctypes/template/parse.mjs +37 -1
  342. package/authoring/doctypes/template/parse.test.mjs +18 -0
  343. package/authoring/doctypes/template/template.doc.mjs +13 -3
  344. package/authoring/doctypes/template/type.ts +12 -1
  345. package/authoring/doctypes/theme/parse.d.mts +35 -0
  346. package/authoring/doctypes/theme/parse.mjs +76 -0
  347. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  348. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  349. package/authoring/doctypes/theme/type.ts +42 -0
  350. package/authoring/doctypes/types.ts +12 -11
  351. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  352. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  353. package/authoring/gap-report/parse.d.mts +9 -9
  354. package/authoring/gap-report/parse.mjs +6 -6
  355. package/authoring/gap-report/type.ts +1 -1
  356. package/authoring/identity/identity.doc.mjs +3 -2
  357. package/authoring/identity/type.ts +10 -10
  358. package/authoring/index.d.mts +2 -0
  359. package/authoring/index.d.ts +32 -19
  360. package/authoring/index.mjs +3 -1
  361. package/authoring/integration/integration.doc.mjs +2 -2
  362. package/authoring/integration/parse.d.mts +2 -2
  363. package/authoring/integration/parse.mjs +1 -1
  364. package/authoring/integration/schema.d.mts +4 -4
  365. package/authoring/integration/schema.mjs +3 -3
  366. package/authoring/integration/type.ts +5 -11
  367. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  368. package/clients/cli/command-load-failure.test.mjs +83 -0
  369. package/clients/cli/commands/blog.doc.mjs +1 -1
  370. package/clients/cli/commands/blog.mjs +23 -8
  371. package/clients/cli/commands/blog.test.mjs +42 -1
  372. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  373. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  374. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  375. package/clients/cli/commands/build-theme.mjs +16 -50
  376. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  377. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  378. package/clients/cli/commands/build.doc.mjs +16 -8
  379. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  380. package/clients/cli/commands/build.mjs +137 -114
  381. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  382. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  383. package/clients/cli/commands/component/index.mjs +3 -8
  384. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  385. package/clients/cli/commands/component-package.test.mjs +46 -0
  386. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  387. package/clients/cli/commands/component.doc.mjs +1 -1
  388. package/clients/cli/commands/component.test.mjs +19 -0
  389. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  390. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  391. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  392. package/clients/cli/commands/discover.doc.mjs +55 -9
  393. package/clients/cli/commands/discover.mjs +393 -118
  394. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  395. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  396. package/clients/cli/commands/docs.doc.mjs +21 -9
  397. package/clients/cli/commands/docs.mjs +184 -70
  398. package/clients/cli/commands/docs.test.mjs +120 -16
  399. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  400. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  401. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  402. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  403. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  404. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  405. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  406. package/clients/cli/commands/doctor.doc.mjs +1 -1
  407. package/clients/cli/commands/doctor.mjs +59 -32
  408. package/clients/cli/commands/doctor.test.mjs +42 -0
  409. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  410. package/clients/cli/commands/gap-report.test.mjs +72 -0
  411. package/clients/cli/commands/hook/index.mjs +7 -17
  412. package/clients/cli/commands/hook.doc.mjs +1 -1
  413. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  414. package/clients/cli/commands/init.doc.mjs +20 -9
  415. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  416. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  417. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  418. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  419. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  420. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  421. package/clients/cli/commands/integration.doc.mjs +1 -1
  422. package/clients/cli/commands/integration.mjs +1 -0
  423. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  424. package/clients/cli/commands/json-contract.test.mjs +33 -0
  425. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  426. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  427. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  428. package/clients/cli/commands/layout.doc.mjs +3 -3
  429. package/clients/cli/commands/layout.mjs +21 -9
  430. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  431. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  432. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  433. package/clients/cli/commands/manifest.doc.mjs +1 -1
  434. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  435. package/clients/cli/commands/search.doc.mjs +7 -4
  436. package/clients/cli/commands/search.mjs +28 -9
  437. package/clients/cli/commands/search.test.mjs +75 -0
  438. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  439. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  440. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  441. package/clients/cli/commands/template.doc.mjs +52 -13
  442. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  443. package/clients/cli/commands/template.mjs +4 -91
  444. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  445. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  446. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  447. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  448. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  449. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  450. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  451. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  452. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  453. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  454. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  455. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  456. package/clients/cli/commands/theme.doc.mjs +1 -1
  457. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  458. package/clients/cli/commands/upgrade.doc.mjs +20 -8
  459. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  460. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  461. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  462. package/clients/cli/commands/upgrade.mjs +29 -7
  463. package/clients/cli/formatters/index.mjs +2 -0
  464. package/clients/cli/formatters/index.test.mjs +6 -0
  465. package/clients/cli/index.mjs +21 -30
  466. package/clients/cli/latest-version-env.test.mjs +50 -0
  467. package/clients/cli/lib/cli-error.test.mjs +7 -0
  468. package/clients/cli/lib/component-format.mjs +9 -9
  469. package/clients/cli/lib/component-format.test.mjs +1 -1
  470. package/clients/cli/lib/define-command.mjs +32 -6
  471. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  472. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  473. package/clients/cli/lib/hook-format.mjs +19 -10
  474. package/clients/cli/lib/json-shim.mjs +38 -2
  475. package/clients/cli/lib/json-shim.test.mjs +83 -0
  476. package/clients/cli/lib/manifest.d.ts +2 -0
  477. package/clients/cli/lib/manifest.mjs +32 -2
  478. package/clients/cli/lib/manifest.test.mjs +17 -0
  479. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  480. package/foundation/agent-docs/agent-docs.mjs +82 -12
  481. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  482. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  483. package/foundation/config/integration-debug.test.mjs +28 -3
  484. package/foundation/config/project-themes.test.mjs +11 -19
  485. package/foundation/config/project.d.mts +20 -11
  486. package/foundation/config/project.mjs +246 -89
  487. package/foundation/config/project.test.mjs +270 -21
  488. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  489. package/foundation/discovery/authoring-self-docs.mjs +38 -15
  490. package/foundation/discovery/authoring-self-docs.test.mjs +87 -12
  491. package/foundation/discovery/authoring-surface.d.mts +74 -0
  492. package/foundation/discovery/authoring-surface.mjs +525 -0
  493. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  494. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  495. package/foundation/discovery/cli-self-docs.mjs +490 -0
  496. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  497. package/foundation/discovery/component-discovery.d.mts +38 -0
  498. package/foundation/discovery/component-discovery.mjs +48 -0
  499. package/foundation/discovery/component-loader.d.mts +35 -38
  500. package/foundation/discovery/component-loader.mjs +53 -222
  501. package/foundation/discovery/docs-discovery.d.mts +114 -9
  502. package/foundation/discovery/docs-discovery.mjs +239 -30
  503. package/foundation/discovery/docs-discovery.test.mjs +150 -35
  504. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  505. package/foundation/discovery/docs-output-budget.mjs +1 -1
  506. package/foundation/discovery/docs-section-key.d.mts +28 -10
  507. package/foundation/discovery/docs-section-key.mjs +134 -33
  508. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  509. package/foundation/discovery/template-adapter.d.mts +113 -11
  510. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  511. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  512. package/foundation/discovery/template-adapter.mjs +772 -82
  513. package/foundation/discovery/template-adapter.test.mjs +57 -0
  514. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  515. package/foundation/discovery/template-conflict-release.mjs +40 -0
  516. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  517. package/foundation/discovery/theme-discovery.d.mts +67 -7
  518. package/foundation/discovery/theme-discovery.mjs +916 -186
  519. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  520. package/foundation/discovery/theming-targets.test.mjs +4 -0
  521. package/foundation/doc-compiler/bundle.d.mts +47 -0
  522. package/foundation/doc-compiler/bundle.mjs +278 -0
  523. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  524. package/foundation/doc-compiler/compile.d.mts +343 -0
  525. package/foundation/doc-compiler/compile.mjs +558 -0
  526. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  527. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  528. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  529. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  530. package/foundation/doc-compiler/import.d.mts +24 -0
  531. package/foundation/doc-compiler/import.mjs +59 -0
  532. package/foundation/doc-compiler/inputs.d.mts +102 -0
  533. package/foundation/doc-compiler/inputs.mjs +291 -0
  534. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  535. package/foundation/doc-compiler/ir.d.mts +22 -0
  536. package/foundation/doc-compiler/ir.mjs +471 -0
  537. package/foundation/doc-compiler/lenses.d.mts +36 -0
  538. package/foundation/doc-compiler/lenses.mjs +173 -0
  539. package/foundation/doc-compiler/links.d.mts +162 -0
  540. package/foundation/doc-compiler/links.mjs +294 -0
  541. package/foundation/doc-compiler/links.test.mjs +192 -0
  542. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  543. package/foundation/doc-compiler/overlays.d.mts +37 -0
  544. package/foundation/doc-compiler/overlays.mjs +206 -0
  545. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  546. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  547. package/foundation/doc-compiler/read.d.mts +127 -0
  548. package/foundation/doc-compiler/read.mjs +325 -0
  549. package/foundation/doc-compiler/read.test.mjs +313 -0
  550. package/foundation/doc-compiler/source.d.mts +33 -0
  551. package/foundation/doc-compiler/source.mjs +128 -0
  552. package/foundation/doc-compiler/tree.d.mts +288 -0
  553. package/foundation/doc-compiler/tree.mjs +876 -0
  554. package/foundation/doc-compiler/tree.test.mjs +598 -0
  555. package/foundation/fs/file-protection.d.mts +33 -0
  556. package/foundation/fs/file-protection.mjs +825 -0
  557. package/foundation/fs/file-protection.test.mjs +250 -0
  558. package/foundation/fs/module-loader.d.mts +1 -0
  559. package/foundation/fs/module-loader.mjs +50 -1
  560. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  561. package/foundation/fs/path-safety.d.mts +3 -2
  562. package/foundation/fs/path-safety.mjs +49 -19
  563. package/foundation/fs/path-safety.test.mjs +50 -0
  564. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  565. package/foundation/integrations/autolink.d.mts +58 -1
  566. package/foundation/integrations/autolink.mjs +143 -52
  567. package/foundation/integrations/autolink.test.mjs +1 -1
  568. package/foundation/integrations/cli-requirement.d.mts +45 -0
  569. package/foundation/integrations/cli-requirement.mjs +154 -0
  570. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  571. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  572. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  573. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  574. package/foundation/integrations/contribution-inventory.mjs +27 -24
  575. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  576. package/foundation/integrations/integration-warnings.d.mts +9 -2
  577. package/foundation/integrations/integration-warnings.mjs +51 -26
  578. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  579. package/foundation/integrations/integrations.d.mts +17 -1
  580. package/foundation/integrations/integrations.mjs +52 -98
  581. package/foundation/integrations/integrations.test.mjs +31 -0
  582. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  583. package/foundation/integrations/provider-resolution.d.mts +152 -0
  584. package/foundation/integrations/provider-resolution.mjs +576 -0
  585. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  586. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  587. package/foundation/integrations/theme-descriptor.mjs +44 -0
  588. package/foundation/integrations/validate-contributions.mjs +121 -29
  589. package/foundation/response/base.d.ts +8 -4
  590. package/foundation/response/error-codes.d.mts +3 -1
  591. package/foundation/response/error-codes.d.ts +2 -0
  592. package/foundation/response/error-codes.doc.mjs +13 -4
  593. package/foundation/response/error-codes.mjs +8 -2
  594. package/foundation/response/error-codes.test.mjs +137 -10
  595. package/foundation/response/json-contract.test.mjs +11 -0
  596. package/foundation/response/json.d.mts +4 -2
  597. package/foundation/response/json.mjs +8 -10
  598. package/foundation/response/response-types.doc.d.mts +5 -1
  599. package/foundation/response/response-types.doc.mjs +37 -22
  600. package/foundation/response/response-types.doc.test.mjs +158 -0
  601. package/foundation/response/response.doc.mjs +1 -1
  602. package/foundation/text/string-utils.d.mts +8 -0
  603. package/foundation/text/string-utils.mjs +40 -10
  604. package/foundation/xle/expand.d.mts +2 -0
  605. package/foundation/xle/expand.mjs +4 -3
  606. package/foundation/xle/expand.test.mjs +54 -0
  607. package/foundation/xle/xle.test.mjs +13 -0
  608. package/package.json +10 -11
  609. package/assets/templates/themes/manifest.json +0 -95
  610. package/clients/cli/lib/update-check.mjs +0 -83
  611. package/clients/cli/lib/update-check.test.mjs +0 -137
  612. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -0,0 +1,158 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * `theme add` writes a theme as one transaction: when a later file fails to
5
+ * publish, every file it already replaced gets its previous bytes back and
6
+ * every file it created is removed. Publishing is forced to fail by wrapping
7
+ * the two calls that publish a staged file: `linkSync` for a new file and
8
+ * `renameSync` for a replacement.
9
+ *
10
+ * Separate file because vi.mock is hoisted and affects the whole module.
11
+ */
12
+
13
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
14
+ import * as os from 'node:os';
15
+ import * as path from 'node:path';
16
+
17
+ const failures = vi.hoisted(() => ({
18
+ /** Fail the Nth staged-file publish (1-based); 0 disables. */
19
+ publish: 0,
20
+ /** Also fail every rollback restore. */
21
+ restore: false,
22
+ count: 0,
23
+ }));
24
+
25
+ vi.mock('node:fs', async importOriginal => {
26
+ const actual = /** @type {typeof import('node:fs')} */ (
27
+ await importOriginal()
28
+ );
29
+ /** @param {string} source */
30
+ const isStaged = source => path.basename(String(source)).includes('.tmp');
31
+ /** @param {string} source */
32
+ const isRestore = source =>
33
+ path.basename(String(source)).includes('.restore-');
34
+ /** @param {string} source @param {string} op */
35
+ const maybeFail = (source, op) => {
36
+ if (isStaged(source) && failures.publish > 0) {
37
+ failures.count++;
38
+ if (failures.count === failures.publish) {
39
+ throw Object.assign(new Error(`EIO: forced ${op} failure`), {
40
+ code: 'EIO',
41
+ });
42
+ }
43
+ }
44
+ if (isRestore(source) && failures.restore) {
45
+ throw Object.assign(new Error(`EIO: forced restore failure`), {
46
+ code: 'EIO',
47
+ });
48
+ }
49
+ };
50
+ return {
51
+ ...actual,
52
+ linkSync: vi.fn((source, destination) => {
53
+ maybeFail(source, 'link');
54
+ return actual.linkSync(source, destination);
55
+ }),
56
+ renameSync: vi.fn((source, destination) => {
57
+ maybeFail(source, 'rename');
58
+ return actual.renameSync(source, destination);
59
+ }),
60
+ };
61
+ });
62
+
63
+ const fs = await import('node:fs');
64
+ const {themeAdd} = await import('./add.mjs');
65
+ const {listThemes} = await import('../_adapter.mjs');
66
+
67
+ let tmpDir;
68
+
69
+ beforeEach(() => {
70
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-rollback-'));
71
+ failures.publish = 0;
72
+ failures.restore = false;
73
+ failures.count = 0;
74
+ });
75
+
76
+ afterEach(() => {
77
+ failures.publish = 0;
78
+ failures.restore = false;
79
+ fs.rmSync(tmpDir, {recursive: true, force: true});
80
+ });
81
+
82
+ function stoneFiles() {
83
+ const theme = listThemes().find(entry => entry.slug === 'stone');
84
+ if (!theme) throw new Error('missing bundled theme stone');
85
+ expect(theme.files.length).toBeGreaterThanOrEqual(2);
86
+ return theme.files.map(name =>
87
+ path.join(tmpDir, 'src', 'themes', 'stone', name),
88
+ );
89
+ }
90
+
91
+ /** Entries staging or rollback left behind in the theme directory. */
92
+ function strays() {
93
+ const dir = path.join(tmpDir, 'src', 'themes', 'stone');
94
+ if (!fs.existsSync(dir)) return [];
95
+ return fs
96
+ .readdirSync(dir, {recursive: true})
97
+ .map(String)
98
+ .filter(name => name.includes('.tmp') || name.includes('.restore-'));
99
+ }
100
+
101
+ describe('themeAdd rolls back a partial write', () => {
102
+ it('removes every created file when the second publish fails', async () => {
103
+ const files = stoneFiles();
104
+ failures.publish = 2;
105
+
106
+ await expect(themeAdd('stone', {cwd: tmpDir})).rejects.toMatchObject({
107
+ code: 'ERR_WRITE_FAILED',
108
+ });
109
+
110
+ for (const file of files) expect(fs.existsSync(file)).toBe(false);
111
+ expect(strays()).toEqual([]);
112
+ });
113
+
114
+ it('restores every replaced byte when the second publish fails', async () => {
115
+ const files = stoneFiles();
116
+ fs.mkdirSync(path.dirname(files[0]), {recursive: true});
117
+ const before = Buffer.from([0x00, 0xff, 0x0a, 0x62, 0x65, 0x66]);
118
+ fs.writeFileSync(files[0], before);
119
+ failures.publish = 2;
120
+
121
+ await expect(
122
+ themeAdd('stone', {cwd: tmpDir, overwrite: true}),
123
+ ).rejects.toMatchObject({code: 'ERR_WRITE_FAILED'});
124
+
125
+ expect(fs.readFileSync(files[0]).equals(before)).toBe(true);
126
+ for (const file of files.slice(1)) expect(fs.existsSync(file)).toBe(false);
127
+ expect(strays()).toEqual([]);
128
+ });
129
+
130
+ it('names a file it could not restore', async () => {
131
+ const files = stoneFiles();
132
+ fs.mkdirSync(path.dirname(files[0]), {recursive: true});
133
+ fs.writeFileSync(files[0], 'before\n');
134
+ failures.publish = 2;
135
+ failures.restore = true;
136
+
137
+ let error;
138
+ try {
139
+ await themeAdd('stone', {cwd: tmpDir, overwrite: true});
140
+ } catch (caught) {
141
+ error = caught;
142
+ }
143
+
144
+ expect(error?.code).toBe('ERR_WRITE_FAILED');
145
+ expect(error?.message).toContain('Could not restore');
146
+ expect(error?.message).toContain(files[0]);
147
+ });
148
+
149
+ it('writes every file when nothing fails', async () => {
150
+ const files = stoneFiles();
151
+
152
+ const result = await themeAdd('stone', {cwd: tmpDir});
153
+
154
+ expect(result.type).toBe('theme.add');
155
+ for (const file of files) expect(fs.existsSync(file)).toBe(true);
156
+ expect(strays()).toEqual([]);
157
+ });
158
+ });
@@ -0,0 +1,83 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
4
+ import * as fs from 'node:fs';
5
+ import * as os from 'node:os';
6
+ import * as path from 'node:path';
7
+ import {themeAdd} from './add.mjs';
8
+ import {listThemes} from '../_adapter.mjs';
9
+
10
+ let tmpDir;
11
+ let outsideDir;
12
+
13
+ beforeEach(() => {
14
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-staging-'));
15
+ outsideDir = fs.mkdtempSync(
16
+ path.join(os.tmpdir(), 'astryx-themeadd-outside-'),
17
+ );
18
+ });
19
+
20
+ afterEach(() => {
21
+ fs.rmSync(tmpDir, {recursive: true, force: true});
22
+ fs.rmSync(outsideDir, {recursive: true, force: true});
23
+ });
24
+
25
+ /**
26
+ * Where `theme add` writes the first file of `slug`.
27
+ * @param {string} slug
28
+ */
29
+ function firstDestination(slug) {
30
+ const theme = listThemes().find(entry => entry.slug === slug);
31
+ if (!theme) throw new Error(`missing bundled theme ${slug}`);
32
+ const dest = path.join(tmpDir, 'src', 'themes', slug, theme.files[0]);
33
+ fs.mkdirSync(path.dirname(dest), {recursive: true});
34
+ return dest;
35
+ }
36
+
37
+ // Staging names are unpredictable and created exclusively, so an entry planted
38
+ // at a predictable name beside the destination is never written through.
39
+ describe('themeAdd staging writes stay inside the project', () => {
40
+ it('never writes through a link planted at a predictable staging name', async () => {
41
+ const victim = path.join(outsideDir, 'victim.txt');
42
+ fs.writeFileSync(victim, 'outside\n');
43
+ const dest = firstDestination('stone');
44
+ fs.symlinkSync(victim, `${dest}.${process.pid}.tmp`);
45
+
46
+ await themeAdd('stone', {cwd: tmpDir});
47
+
48
+ expect(fs.readFileSync(victim, 'utf-8')).toBe('outside\n');
49
+ expect(fs.lstatSync(dest).isFile()).toBe(true);
50
+ });
51
+
52
+ it('never creates a file through a dangling link at a predictable staging name', async () => {
53
+ const victim = path.join(outsideDir, 'created.txt');
54
+ fs.symlinkSync(victim, `${firstDestination('stone')}.${process.pid}.tmp`);
55
+
56
+ await themeAdd('stone', {cwd: tmpDir});
57
+
58
+ expect(fs.existsSync(victim)).toBe(false);
59
+ });
60
+
61
+ it('refuses to replace a destination that links outside the project', async () => {
62
+ const victim = path.join(outsideDir, 'victim.txt');
63
+ fs.writeFileSync(victim, 'outside\n');
64
+ const dest = firstDestination('stone');
65
+ fs.symlinkSync(victim, dest);
66
+
67
+ await expect(
68
+ themeAdd('stone', {cwd: tmpDir, overwrite: true}),
69
+ ).rejects.toMatchObject({code: 'ERR_PATH_TRAVERSAL'});
70
+ expect(fs.readFileSync(victim, 'utf-8')).toBe('outside\n');
71
+ expect(fs.lstatSync(dest).isSymbolicLink()).toBe(true);
72
+ });
73
+
74
+ it('never creates a file through a dangling destination link', async () => {
75
+ const victim = path.join(outsideDir, 'created.txt');
76
+ fs.symlinkSync(victim, firstDestination('stone'));
77
+
78
+ await expect(themeAdd('stone', {cwd: tmpDir})).rejects.toMatchObject({
79
+ code: 'ERR_PATH_TRAVERSAL',
80
+ });
81
+ expect(fs.existsSync(victim)).toBe(false);
82
+ });
83
+ });
@@ -30,7 +30,20 @@ describe('themeAdd (api/theme/add)', () => {
30
30
  expect(result.data.slug).toBe('neutral');
31
31
  expect(result.data.package).toBe('@astryxdesign/cli');
32
32
  expect(result.data.outputDir).toBe(path.join('src', 'themes', 'neutral'));
33
- expect(result.data.files.length).toBeGreaterThan(0);
33
+ // Exactly what the bundle has always copied, in that order: the CLI's own
34
+ // descriptor stays behind.
35
+ expect(result.data.files).toEqual([
36
+ 'neutralTheme.ts',
37
+ 'icons.tsx',
38
+ 'neutralPalettes.ts',
39
+ 'neutralPalettes.generated.ts',
40
+ 'neutralPaletteRefs.generated.ts',
41
+ 'neutralPalettes.generated.receipt.json',
42
+ 'palette.config.json',
43
+ ]);
44
+ expect(
45
+ fs.readdirSync(path.join(tmpDir, 'src', 'themes', 'neutral')).sort(),
46
+ ).toEqual([...result.data.files].sort());
34
47
  for (const f of result.data.files) {
35
48
  expect(
36
49
  fs.existsSync(path.join(tmpDir, 'src', 'themes', 'neutral', f)),
@@ -251,23 +251,18 @@ describe('themeBuildFamily()', () => {
251
251
  const dir = makeDir();
252
252
  const files = writeFamily(dir);
253
253
  const outputDir = path.join(dir, 'themes');
254
- const blockedTmp = path.join(
255
- outputDir,
256
- `${FAMILY_KEY}.js.${process.pid}.tmp`,
257
- );
258
- fs.mkdirSync(blockedTmp);
254
+ // A directory where the JS output goes fails its staging after the CSS
255
+ // output was already staged.
256
+ fs.mkdirSync(path.join(outputDir, `${FAMILY_KEY}.js`));
259
257
 
260
258
  await expect(build(dir, files)).rejects.toThrow(
261
259
  /Failed to write theme outputs/,
262
260
  );
263
- for (const output of OUTPUTS) {
264
- expect(fs.existsSync(path.join(outputDir, output))).toBe(false);
265
- }
261
+ expect(fs.existsSync(path.join(outputDir, `${FAMILY_KEY}.css`))).toBe(false);
262
+ expect(fs.existsSync(path.join(outputDir, `${FAMILY_KEY}.d.ts`))).toBe(false);
266
263
  expect(
267
- fs.existsSync(
268
- path.join(outputDir, `${FAMILY_KEY}.css.${process.pid}.tmp`),
269
- ),
270
- ).toBe(false);
264
+ fs.readdirSync(outputDir).filter(name => name.includes('.tmp-')),
265
+ ).toEqual([]);
271
266
  });
272
267
 
273
268
  it('rejects invalid graphs and keys before touching an existing trio', async () => {
@@ -12,9 +12,10 @@
12
12
  * - A .d.ts (plus an optional .variants.d.ts for custom prop values)
13
13
  *
14
14
  * It performs the writes and returns a `theme.build` receipt — its `warnings`
15
- * carry override problems and any fonts the theme names but does not load
16
- * (font-warning.mjs) — or `null` when the theme produced no CSS (nothing to
17
- * build). Errors throw AstryxError (with
15
+ * carry override problems, every declaration core's generator dropped because
16
+ * its value could not stay one CSS declaration, and any fonts the theme names
17
+ * but does not load (font-warning.mjs) — or `null` when the theme produced no
18
+ * CSS (nothing to build). Errors throw AstryxError (with
18
19
  * a stable code). Human progress is emitted through the shared `logger`
19
20
  * (silent by default), so the CLI keeps its exact output while a programmatic
20
21
  * caller stays quiet.
@@ -52,6 +53,7 @@ import {
52
53
  } from '../../../foundation/fs/path-safety.mjs';
53
54
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
54
55
  import {AstryxError} from '../../error.mjs';
56
+ import {applyWrites} from '../../integration/add-helpers.mjs';
55
57
  import {logger} from '../../logger.mjs';
56
58
  import {loadComponentDoc} from '../../../foundation/discovery/component-loader.mjs';
57
59
  import {
@@ -223,26 +225,15 @@ function staleBuildOutputs(writes, cwd) {
223
225
  /** @param {Array<{dest: string, content: string}>} writes */
224
226
  function writeBuildOutputs(writes) {
225
227
  if (writes.length === 0) return;
226
- fs.mkdirSync(path.dirname(writes[0].dest), {recursive: true});
227
- /** @type {Array<{tmp: string, dest: string}>} */
228
- const staged = [];
229
228
  try {
230
- for (const write of writes) {
231
- const tmp = `${write.dest}.${process.pid}.tmp`;
232
- fs.writeFileSync(tmp, write.content);
233
- staged.push({tmp, dest: write.dest});
234
- }
235
- for (const stagedWrite of staged) {
236
- fs.renameSync(stagedWrite.tmp, stagedWrite.dest);
237
- }
229
+ applyWrites(
230
+ writes.map(write => ({
231
+ path: write.dest,
232
+ contents: write.content,
233
+ createOnly: false,
234
+ })),
235
+ );
238
236
  } catch (error) {
239
- for (const stagedWrite of staged) {
240
- try {
241
- fs.rmSync(stagedWrite.tmp, {force: true});
242
- } catch {
243
- // Best effort: the command still fails and never reports success.
244
- }
245
- }
246
237
  const message = `Failed to write theme outputs: ${/** @type {Error} */ (error).message}`;
247
238
  throw new AstryxError(message, undefined, ERROR_CODES.ERR_WRITE_FAILED);
248
239
  }
@@ -1870,7 +1861,7 @@ function validatePrivateVars(themeDef) {
1870
1861
  if (prop.startsWith('--_')) {
1871
1862
  errors.push(
1872
1863
  `Component "${component}" (${[key, ...path].join(' ')}) sets private var "${prop}". ` +
1873
- `Private vars (--_*) are internal — use standard CSS properties ` +
1864
+ `Private vars (--_*) are internal; use standard CSS properties ` +
1874
1865
  `(e.g. borderRadius, padding) instead. The pipeline expands them automatically.`,
1875
1866
  );
1876
1867
  }
@@ -1930,20 +1921,7 @@ function getThemeComponentLayers(themeDef) {
1930
1921
  */
1931
1922
  function validateCustomHeadingTypes(themeDef) {
1932
1923
  const layers = getThemeComponentLayers(themeDef);
1933
- const customTypes = new Set();
1934
- for (const {components} of layers) {
1935
- const headingRules = components.heading;
1936
- if (!headingRules || typeof headingRules !== 'object') continue;
1937
-
1938
- for (const key of Object.keys(headingRules)) {
1939
- for (const pair of key.split('+')) {
1940
- const colon = pair.indexOf(':');
1941
- if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
1942
- const value = pair.slice(colon + 1);
1943
- if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
1944
- }
1945
- }
1946
- }
1924
+ const customTypes = customHeadingTypes(themeDef);
1947
1925
 
1948
1926
  const errors = [];
1949
1927
  for (const type of customTypes) {
@@ -1970,10 +1948,65 @@ function validateCustomHeadingTypes(themeDef) {
1970
1948
  return errors;
1971
1949
  }
1972
1950
 
1951
+ /**
1952
+ * Custom Heading type names declared on the root or media-surface component
1953
+ * maps: the names the generated `HeadingTypeMap` augmentation enrolls.
1954
+ *
1955
+ * @param {{components?: Record<string, Record<string, unknown>>}} themeDef
1956
+ * @returns {Set<string>}
1957
+ */
1958
+ function customHeadingTypes(themeDef) {
1959
+ /** @type {Set<string>} */
1960
+ const customTypes = new Set();
1961
+ for (const {components} of getThemeComponentLayers(themeDef)) {
1962
+ const headingRules = components.heading;
1963
+ if (!headingRules || typeof headingRules !== 'object') continue;
1964
+
1965
+ for (const key of Object.keys(headingRules)) {
1966
+ for (const pair of key.split('+')) {
1967
+ const colon = pair.indexOf(':');
1968
+ if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
1969
+ const value = pair.slice(colon + 1);
1970
+ if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
1971
+ }
1972
+ }
1973
+ }
1974
+ return customTypes;
1975
+ }
1976
+
1977
+ /**
1978
+ * Custom Heading types with no standalone rule in the generated CSS. A type
1979
+ * whose every declaration the compiler dropped still passes the source check,
1980
+ * and its augmentation would then name a type that has no styles.
1981
+ *
1982
+ * @param {Set<string>} types
1983
+ * @param {string} css - Generated root component and media-surface rules.
1984
+ * @returns {string[]}
1985
+ */
1986
+ function headingTypesWithoutEmittedRule(types, css) {
1987
+ /** @type {string[]} */
1988
+ const missing = [];
1989
+ for (const type of types) {
1990
+ const selector = `.astryx-heading[data-type="${type}"]`;
1991
+ let emitted = false;
1992
+ for (
1993
+ let at = css.indexOf(selector);
1994
+ at !== -1 && !emitted;
1995
+ at = css.indexOf(selector, at + 1)
1996
+ ) {
1997
+ // A following attribute is a combined selector, not the standalone rule.
1998
+ emitted = css[at + selector.length] !== '[';
1999
+ }
2000
+ if (!emitted) missing.push(type);
2001
+ }
2002
+ return missing;
2003
+ }
2004
+
1973
2005
  /**
1974
2006
  * Return true when a style object contains a declaration that the theme
1975
- * generator can emit. Empty pseudo blocks do not count: they produce no CSS
1976
- * and would leave the generated type augmentation without a usable rule.
2007
+ * generator can emit. Empty pseudo blocks and blank values do not count: they
2008
+ * produce no usable CSS and would leave the generated type augmentation
2009
+ * without a usable rule.
1977
2010
  *
1978
2011
  * @param {unknown} styles
1979
2012
  * @returns {boolean}
@@ -1987,7 +2020,12 @@ function hasUsableStyleDeclaration(styles) {
1987
2020
  if (hasUsableStyleDeclaration(value)) return true;
1988
2021
  continue;
1989
2022
  }
1990
- if (value !== undefined && value !== null && typeof value !== 'object') {
2023
+ if (
2024
+ value !== undefined &&
2025
+ value !== null &&
2026
+ typeof value !== 'object' &&
2027
+ String(value).trim() !== ''
2028
+ ) {
1991
2029
  return true;
1992
2030
  }
1993
2031
  }
@@ -2162,14 +2200,14 @@ async function themeBuildInternal(
2162
2200
  const noticeMessages = [];
2163
2201
  for (const w of warnings) {
2164
2202
  warningMessages.push(w);
2165
- logger.warn(` ⚠ ${w}`);
2203
+ logger.warn(` [warn] ${w}`);
2166
2204
  }
2167
2205
 
2168
2206
  // Validate no private vars are set directly
2169
2207
  const privateVarErrors = validatePrivateVars(themeDef);
2170
2208
  for (const e of privateVarErrors) {
2171
2209
  warningMessages.push(e);
2172
- logger.error(` ✗ ${e}`);
2210
+ logger.error(` [error] ${e}`);
2173
2211
  }
2174
2212
  if (privateVarErrors.length > 0) {
2175
2213
  logger.error(
@@ -2195,7 +2233,7 @@ async function themeBuildInternal(
2195
2233
  // capability exports are checked against what the theme actually asks for.
2196
2234
  if (!_defineTheme || !_generateThemeRulesSplit) {
2197
2235
  throw new AstryxError(
2198
- 'Could not load @astryxdesign/core/theme — `astryx theme build` requires a ' +
2236
+ 'Could not load @astryxdesign/core/theme: `astryx theme build` requires a ' +
2199
2237
  'built, resolvable @astryxdesign/core so it emits the same CSS as the ' +
2200
2238
  'runtime <Theme>. Build @astryxdesign/core first (e.g. `pnpm -F @astryxdesign/core ' +
2201
2239
  'build`)' +
@@ -2303,7 +2341,15 @@ async function themeBuildInternal(
2303
2341
  const scopeSelector = themeScopeStart(themeDef.name);
2304
2342
  const scopeTo = THEME_SCOPE_TO;
2305
2343
 
2306
- const {component, prose} = _generateThemeRulesSplit(resolvedTheme);
2344
+ // Older cores ignore this optional collector. Current core writes the same
2345
+ // warning text here that runtime callers receive on the console.
2346
+ /** @type {string[]} */
2347
+ const droppedDeclarations = [];
2348
+
2349
+ const {component, prose} = _generateThemeRulesSplit(
2350
+ resolvedTheme,
2351
+ droppedDeclarations,
2352
+ );
2307
2353
  const cssParts = [];
2308
2354
  // Prose element defaults always ship — the `<Theme>` runtime
2309
2355
  // (generateThemeCSS) always emits them, so the build must too, or the
@@ -2322,7 +2368,7 @@ async function themeBuildInternal(
2322
2368
  let adaptationCss;
2323
2369
  try {
2324
2370
  adaptationCss = _generateAdaptationCSS
2325
- ? _generateAdaptationCSS(resolvedTheme)
2371
+ ? _generateAdaptationCSS(resolvedTheme, droppedDeclarations)
2326
2372
  : {component: '', prose: ''};
2327
2373
  } catch (error) {
2328
2374
  const message =
@@ -2368,13 +2414,37 @@ async function themeBuildInternal(
2368
2414
  // adaptations on the same resolved leaf.
2369
2415
  let onMediaCss = '';
2370
2416
  if (_generateOnMediaCSS) {
2371
- onMediaCss = _generateOnMediaCSS(resolvedTheme);
2417
+ onMediaCss = _generateOnMediaCSS(resolvedTheme, droppedDeclarations);
2372
2418
  if (onMediaCss) {
2373
2419
  cssParts.push(`@layer astryx-theme {\n${onMediaCss}\n}`);
2374
2420
  }
2375
2421
  }
2422
+ for (const message of droppedDeclarations) {
2423
+ const w = `Declaration ${message}. The generated CSS omits it; fix the value in the theme source.`;
2424
+ warningMessages.push(w);
2425
+ logger.warn(` [warn] ${w}`);
2426
+ }
2427
+ const unemittedHeadingTypes = headingTypesWithoutEmittedRule(
2428
+ customHeadingTypes(themeDef),
2429
+ `${component.join('\n')}\n${onMediaCss}`,
2430
+ );
2431
+ if (unemittedHeadingTypes.length > 0) {
2432
+ throw new AstryxError(
2433
+ unemittedHeadingTypes
2434
+ .map(
2435
+ type =>
2436
+ `Custom Heading type "${type}" has no declaration the theme ` +
2437
+ `compiler could emit: every declaration in its standalone ` +
2438
+ `"type:${type}" rule was dropped. Give that rule at least one ` +
2439
+ 'valid declaration.',
2440
+ )
2441
+ .join('\n'),
2442
+ undefined,
2443
+ ERROR_CODES.ERR_THEME_INVALID,
2444
+ );
2445
+ }
2376
2446
  if (cssParts.length === 0) {
2377
- logger.log('No overrides found — nothing to build.');
2447
+ logger.log('No overrides found; nothing to build.');
2378
2448
  return null;
2379
2449
  }
2380
2450
  // The data-token defaults are theme-independent and go in @layer
@@ -2425,7 +2495,18 @@ async function themeBuildInternal(
2425
2495
  // Guard: relative paths must not escape cwd via `../`. Absolute paths are
2426
2496
  // trusted (the user explicitly controls where output goes, like gcc -o).
2427
2497
  if (!path.isAbsolute(options.out)) {
2428
- assertWithin(options.out, cwd, {label: 'output path'});
2498
+ try {
2499
+ assertWithin(options.out, cwd, {label: 'output path'});
2500
+ } catch (err) {
2501
+ if (err instanceof PathSafetyError) {
2502
+ throw new AstryxError(
2503
+ err.message,
2504
+ undefined,
2505
+ ERROR_CODES.ERR_PATH_TRAVERSAL,
2506
+ );
2507
+ }
2508
+ throw err;
2509
+ }
2429
2510
  }
2430
2511
  } else {
2431
2512
  outPath = path.join(path.dirname(filePath), `${baseName}.css`);
@@ -2534,10 +2615,10 @@ async function themeBuildInternal(
2534
2615
  const stale = staleBuildOutputs(writes, cwd);
2535
2616
  const upToDate = stale.length === 0;
2536
2617
  if (upToDate) {
2537
- logger.log(`\n✓ Theme outputs are up to date with ${sourceRelative}.`);
2618
+ logger.log(`\n[ok] Theme outputs are up to date with ${sourceRelative}.`);
2538
2619
  } else {
2539
2620
  logger.error(
2540
- `\n✗ ${stale.length} theme output(s) are out of date with ${sourceRelative}:`,
2621
+ `\n[fail] ${stale.length} theme output(s) are out of date with ${sourceRelative}:`,
2541
2622
  );
2542
2623
  for (const s of stale) {
2543
2624
  logger.error(
@@ -2559,17 +2640,17 @@ async function themeBuildInternal(
2559
2640
 
2560
2641
  writeBuildOutputs(writes);
2561
2642
 
2562
- logger.log(`\n✓ ${path.relative(cwd, outPath)}`);
2643
+ logger.log(`\n[ok] ${path.relative(cwd, outPath)}`);
2563
2644
  logger.log(
2564
2645
  ` ${tokenCount} token overrides, ${componentCount} component overrides`,
2565
2646
  );
2566
2647
  logger.log(` ${size} KB`);
2567
- logger.log(`✓ ${path.relative(cwd, jsPath)}`);
2568
- logger.log(`✓ ${path.relative(cwd, dtsPath)}`);
2648
+ logger.log(`[ok] ${path.relative(cwd, jsPath)}`);
2649
+ logger.log(`[ok] ${path.relative(cwd, dtsPath)}`);
2569
2650
  if (variantDtsPath && variantDecl) {
2570
2651
  const augCount = (variantDecl.match(/': true;/g) || []).length;
2571
2652
  logger.log(
2572
- `✓ ${path.relative(cwd, variantDtsPath)} (${augCount} type augmentations)`,
2653
+ `[ok] ${path.relative(cwd, variantDtsPath)} (${augCount} type augmentations)`,
2573
2654
  );
2574
2655
  }
2575
2656
 
@@ -2579,7 +2660,7 @@ async function themeBuildInternal(
2579
2660
  const cssImport = importSpecifier(relOutDir, cssBase) + '.css';
2580
2661
  const exportName = `${toIdentifier(baseName)}Theme`;
2581
2662
  logger.log(`
2582
- Install in your app (paths are relative to a file in src/ — adjust if yours lives elsewhere):
2663
+ Install in your app (paths are relative to a file in src/; adjust if yours lives elsewhere):
2583
2664
 
2584
2665
  import { ${exportName} } from '${jsImport}';
2585
2666
  import '${cssImport}';
@@ -2611,7 +2692,7 @@ Or with a <link> tag:
2611
2692
  // Adaptation rules are resolved theme writes in their own right, so a family
2612
2693
  // named only inside one needs the same notice as one named at the root.
2613
2694
  for (const family of unloadedFonts) {
2614
- const msg = `Font "${family}" is named by this theme but not loaded — add a <link> or @font-face in your app (recipe: astryx docs typography)`;
2695
+ const msg = `Font "${family}" is named by this theme but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`;
2615
2696
  noticeMessages.push(msg);
2616
2697
  logger.log(` note: ${msg}`);
2617
2698
  }
@@ -2905,11 +2986,11 @@ export async function themeBuildFamily(
2905
2986
  const upToDate = stale.length === 0;
2906
2987
  if (upToDate) {
2907
2988
  logger.log(
2908
- `\n✓ Theme family outputs are up to date with ${sourceRelative}.`,
2989
+ `\n[ok] Theme family outputs are up to date with ${sourceRelative}.`,
2909
2990
  );
2910
2991
  } else {
2911
2992
  logger.error(
2912
- `\n✗ ${stale.length} theme family output(s) are out of date:`,
2993
+ `\n[fail] ${stale.length} theme family output(s) are out of date:`,
2913
2994
  );
2914
2995
  for (const entry of stale) {
2915
2996
  logger.error(
@@ -2929,7 +3010,7 @@ export async function themeBuildFamily(
2929
3010
  };
2930
3011
  }
2931
3012
  writeBuildOutputs(writes);
2932
- for (const output of Object.values(outputs)) logger.log(`✓ ${output}`);
3013
+ for (const output of Object.values(outputs)) logger.log(`[ok] ${output}`);
2933
3014
  const themeBindings = members.map(member => bindings.get(member.theme.name));
2934
3015
  const relOutDir = path.relative(cwd, outDir) || '.';
2935
3016
  const jsImport = importSpecifier(relOutDir, baseName);
@@ -2947,7 +3028,7 @@ Or load the same stylesheet natively:
2947
3028
  ];
2948
3029
  const notices = unloadedFonts.map(
2949
3030
  family =>
2950
- `Font "${family}" is named by this theme family but not loaded — add a <link> or @font-face in your app (recipe: astryx docs typography)`,
3031
+ `Font "${family}" is named by this theme family but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`,
2951
3032
  );
2952
3033
  for (const notice of notices) logger.log(` note: ${notice}`);
2953
3034
  return {
@@ -19,7 +19,7 @@
19
19
  * next one is covered without touching this file.
20
20
  *
21
21
  * Asserted on the receipt's `warnings` rather than on a rejection: a private
22
- * var is reported (logged `✗`, collected into the receipt) and the build then
22
+ * var is reported (logged `[error]`, collected into the receipt) and the build then
23
23
  * emits its CSS and resolves anyway. Asserting a throw would pass for the
24
24
  * wrong reason — it never throws, which is why a throwaway build read as a
25
25
  * pass on the first version of #5214.