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

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 (276) hide show
  1. package/README.md +22 -1
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/build.doc.mjs +1 -0
  4. package/api/component/component.doc.mjs +1 -0
  5. package/api/discover/discover.doc.mjs +1 -0
  6. package/api/docs/_adapter.d.mts +3 -8
  7. package/api/docs/_adapter.mjs +13 -105
  8. package/api/docs/detail/section/section.mjs +9 -15
  9. package/api/docs/detail/section/section.test.mjs +15 -6
  10. package/api/docs/docs.doc.mjs +1 -0
  11. package/api/docs/list/list.mjs +3 -7
  12. package/api/doctor/doctor.d.mts +25 -3
  13. package/api/doctor/doctor.doc.mjs +1 -0
  14. package/api/doctor/doctor.mjs +186 -14
  15. package/api/doctor/doctor.test.mjs +332 -7
  16. package/api/gap-report/gap-report.doc.mjs +1 -0
  17. package/api/hook/_adapter.mjs +19 -5
  18. package/api/hook/hook.doc.mjs +1 -0
  19. package/api/hook/list/list.d.mts +1 -1
  20. package/api/hook/list/list.mjs +69 -17
  21. package/api/init/init.doc.mjs +1 -0
  22. package/api/integration/add-contribution.mjs +7 -7
  23. package/api/integration/add-contribution.test.mjs +37 -3
  24. package/api/integration/add-theme.mjs +34 -64
  25. package/api/integration/add-theme.test.mjs +105 -21
  26. package/api/integration/authoring-checks.test.mjs +8 -4
  27. package/api/integration/integration-block-exports.test.mjs +10 -6
  28. package/api/integration/integrationAdd.doc.mjs +1 -0
  29. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  30. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  31. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  32. package/api/integration/integrationAddDoc.doc.mjs +1 -0
  33. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  34. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  35. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  36. package/api/integration/integrationDocConflicts.doc.mjs +1 -0
  37. package/api/integration/integrationPackCheck.doc.mjs +1 -0
  38. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -0
  39. package/api/integration/pack-check.test.mjs +17 -47
  40. package/api/integration/summarizeIssues.doc.mjs +1 -0
  41. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  42. package/api/integration/validate-integration.mjs +50 -102
  43. package/api/integration/validate-integration.test.mjs +85 -23
  44. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  45. package/api/integration/validateIntegration.doc.mjs +1 -0
  46. package/api/json/assertResponse.doc.mjs +1 -0
  47. package/api/json/isError.doc.mjs +1 -0
  48. package/api/json/parseResponse.doc.mjs +1 -0
  49. package/api/layout/layoutCheck.doc.mjs +1 -0
  50. package/api/layout/layoutExpand.doc.mjs +1 -0
  51. package/api/layout/layoutGrammar.doc.mjs +1 -0
  52. package/api/search/search.doc.mjs +1 -0
  53. package/api/search/search.mjs +16 -6
  54. package/api/swizzle/swizzle.doc.mjs +1 -0
  55. package/api/template/template-suffix.test.mjs +41 -21
  56. package/api/template/template.doc.mjs +1 -0
  57. package/api/theme/_adapter.d.mts +2 -3
  58. package/api/theme/_adapter.mjs +4 -5
  59. package/api/theme/add/add.binary.test.mjs +10 -17
  60. package/api/theme/add/add.test.mjs +14 -1
  61. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  62. package/api/theme/integration-themes.test.mjs +39 -28
  63. package/api/theme/list/list.test.mjs +19 -20
  64. package/api/theme/listThemes.doc.mjs +6 -5
  65. package/api/theme/themeAdd.doc.mjs +4 -3
  66. package/api/theme/themeBuild.doc.mjs +1 -0
  67. package/api/theme/themeList.doc.mjs +6 -3
  68. package/api/theme/themeListAvailable.doc.mjs +5 -3
  69. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  70. package/api/theme/themeTargets.doc.mjs +1 -0
  71. package/api/theme/themeTemplate.doc.mjs +1 -0
  72. package/api/upgrade/_adapter.d.mts +32 -5
  73. package/api/upgrade/_adapter.mjs +97 -69
  74. package/api/upgrade/provider-agreement.test.mjs +152 -0
  75. package/api/upgrade/run/run.mjs +356 -59
  76. package/api/upgrade/upgrade.doc.mjs +6 -1
  77. package/api/upgrade/upgrade.type.d.mts +34 -0
  78. package/api/upgrade/upgrade.type.mjs +15 -0
  79. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  80. package/assets/codemods/integration-discovery.mjs +8 -2
  81. package/assets/codemods/integration-discovery.test.mjs +15 -0
  82. package/assets/codemods/integration-runner.mjs +56 -4
  83. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  84. package/assets/codemods/run-codemod.mjs +177 -34
  85. package/assets/codemods/runner.mjs +350 -102
  86. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  87. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  88. package/assets/codemods/transforms/next/index.mjs +19 -1
  89. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  90. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  91. package/assets/docs/cli-integrations.doc.mjs +30 -30
  92. package/assets/docs/cli.doc.mjs +15 -0
  93. package/assets/docs/theme.doc.mjs +1 -1
  94. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  95. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  96. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  97. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  98. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  99. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  100. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  101. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  102. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  103. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  104. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  105. package/authoring/codemod/codemod.doc.mjs +1 -1
  106. package/authoring/codemod/type.ts +12 -0
  107. package/authoring/config/config.doc.mjs +1 -1
  108. package/authoring/debug/debug.doc.d.mts +11 -0
  109. package/authoring/debug/debug.doc.mjs +182 -0
  110. package/authoring/doctypes/_schema.d.mts +71 -141
  111. package/authoring/doctypes/_schema.mjs +29 -2
  112. package/authoring/doctypes/base/graph-fields.doc.mjs +1 -1
  113. package/authoring/doctypes/command/command.doc.mjs +1 -1
  114. package/authoring/doctypes/command/type.ts +1 -1
  115. package/authoring/doctypes/component/type.ts +2 -2
  116. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  117. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  118. package/authoring/doctypes/enum/type.ts +1 -1
  119. package/authoring/doctypes/function/function.doc.mjs +1 -1
  120. package/authoring/doctypes/function/type.ts +1 -1
  121. package/authoring/doctypes/hook/type.ts +2 -2
  122. package/authoring/doctypes/load-contract.test.mjs +3 -2
  123. package/authoring/doctypes/namespace/namespace.doc.mjs +2 -2
  124. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  125. package/authoring/doctypes/namespace/type.ts +5 -2
  126. package/authoring/doctypes/parse.d.mts +4 -2
  127. package/authoring/doctypes/parse.mjs +10 -5
  128. package/authoring/doctypes/reference/reference.doc.mjs +6 -4
  129. package/authoring/doctypes/reference/type.ts +9 -10
  130. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  131. package/authoring/doctypes/schema/type.ts +1 -2
  132. package/authoring/doctypes/template/template.doc.mjs +3 -3
  133. package/authoring/doctypes/theme/parse.d.mts +35 -0
  134. package/authoring/doctypes/theme/parse.mjs +76 -0
  135. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  136. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  137. package/authoring/doctypes/theme/type.ts +42 -0
  138. package/authoring/doctypes/types.ts +2 -1
  139. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  140. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  141. package/authoring/index.d.mts +1 -0
  142. package/authoring/index.d.ts +7 -4
  143. package/authoring/index.mjs +2 -1
  144. package/authoring/integration/integration.doc.mjs +2 -2
  145. package/authoring/integration/type.ts +3 -9
  146. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  147. package/clients/cli/commands/blog.doc.mjs +1 -1
  148. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  149. package/clients/cli/commands/build-theme.mjs +10 -44
  150. package/clients/cli/commands/build.doc.mjs +1 -1
  151. package/clients/cli/commands/component.doc.mjs +1 -1
  152. package/clients/cli/commands/discover.doc.mjs +1 -1
  153. package/clients/cli/commands/docs.doc.mjs +1 -1
  154. package/clients/cli/commands/docs.mjs +15 -58
  155. package/clients/cli/commands/docs.test.mjs +11 -14
  156. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  157. package/clients/cli/commands/doctor-integration-docs.doc.mjs +1 -1
  158. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -1
  159. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  160. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  161. package/clients/cli/commands/doctor-integration.test.mjs +15 -7
  162. package/clients/cli/commands/doctor.doc.mjs +1 -1
  163. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  164. package/clients/cli/commands/hook.doc.mjs +1 -1
  165. package/clients/cli/commands/init.doc.mjs +1 -1
  166. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  167. package/clients/cli/commands/integration-add.doc.mjs +1 -1
  168. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  169. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  170. package/clients/cli/commands/integration.doc.mjs +1 -1
  171. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  172. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  173. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  174. package/clients/cli/commands/layout.doc.mjs +1 -1
  175. package/clients/cli/commands/manifest.doc.mjs +1 -1
  176. package/clients/cli/commands/search.doc.mjs +1 -1
  177. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  178. package/clients/cli/commands/template.doc.mjs +1 -1
  179. package/clients/cli/commands/text-json-parity.test.mjs +718 -0
  180. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  181. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  182. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  183. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  184. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  185. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  186. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  187. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  188. package/clients/cli/commands/theme.doc.mjs +1 -1
  189. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  190. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  191. package/clients/cli/commands/upgrade.mjs +29 -7
  192. package/clients/cli/formatters/index.mjs +2 -0
  193. package/clients/cli/formatters/index.test.mjs +6 -0
  194. package/clients/cli/lib/hook-format.mjs +14 -5
  195. package/foundation/config/project-themes.test.mjs +11 -19
  196. package/foundation/config/project.d.mts +8 -0
  197. package/foundation/config/project.mjs +50 -21
  198. package/foundation/config/project.test.mjs +3 -16
  199. package/foundation/discovery/authoring-self-docs.d.mts +6 -0
  200. package/foundation/discovery/authoring-self-docs.mjs +17 -7
  201. package/foundation/discovery/authoring-self-docs.test.mjs +3 -2
  202. package/foundation/discovery/authoring-surface.d.mts +74 -0
  203. package/foundation/discovery/authoring-surface.mjs +525 -0
  204. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  205. package/foundation/discovery/cli-self-docs.d.mts +113 -0
  206. package/foundation/discovery/cli-self-docs.mjs +514 -0
  207. package/foundation/discovery/cli-self-docs.test.mjs +437 -0
  208. package/foundation/discovery/component-loader.d.mts +35 -38
  209. package/foundation/discovery/component-loader.mjs +53 -222
  210. package/foundation/discovery/docs-discovery.d.mts +3 -2
  211. package/foundation/discovery/docs-discovery.mjs +14 -17
  212. package/foundation/discovery/docs-discovery.test.mjs +15 -15
  213. package/foundation/discovery/docs-section-key.d.mts +19 -8
  214. package/foundation/discovery/docs-section-key.mjs +119 -32
  215. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  216. package/foundation/discovery/template-adapter.d.mts +7 -0
  217. package/foundation/discovery/template-adapter.mjs +27 -40
  218. package/foundation/discovery/template-adapter.test.mjs +15 -0
  219. package/foundation/discovery/theme-discovery.d.mts +67 -7
  220. package/foundation/discovery/theme-discovery.mjs +916 -186
  221. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  222. package/foundation/doc-compiler/bundle.d.mts +47 -0
  223. package/foundation/doc-compiler/bundle.mjs +218 -0
  224. package/foundation/doc-compiler/bundle.test.mjs +255 -0
  225. package/foundation/doc-compiler/compile.d.mts +200 -0
  226. package/foundation/doc-compiler/compile.mjs +259 -9
  227. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  228. package/foundation/doc-compiler/diagnostics.mjs +277 -0
  229. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  230. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  231. package/foundation/doc-compiler/import.d.mts +24 -0
  232. package/foundation/doc-compiler/import.mjs +59 -0
  233. package/foundation/doc-compiler/inputs.d.mts +102 -0
  234. package/foundation/doc-compiler/inputs.mjs +286 -0
  235. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  236. package/foundation/doc-compiler/ir.d.mts +13 -0
  237. package/foundation/doc-compiler/ir.mjs +189 -12
  238. package/foundation/doc-compiler/lower-doc.test.mjs +492 -0
  239. package/foundation/doc-compiler/overlays.d.mts +37 -0
  240. package/foundation/doc-compiler/overlays.mjs +206 -0
  241. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  242. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  243. package/foundation/doc-compiler/read.d.mts +126 -0
  244. package/foundation/doc-compiler/read.mjs +320 -0
  245. package/foundation/doc-compiler/read.test.mjs +313 -0
  246. package/foundation/doc-compiler/source.d.mts +33 -0
  247. package/foundation/doc-compiler/source.mjs +128 -0
  248. package/foundation/fs/file-protection.d.mts +33 -0
  249. package/foundation/fs/file-protection.mjs +825 -0
  250. package/foundation/fs/file-protection.test.mjs +250 -0
  251. package/foundation/integrations/autolink.d.mts +58 -1
  252. package/foundation/integrations/autolink.mjs +143 -57
  253. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  254. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  255. package/foundation/integrations/contribution-inventory.d.mts +1 -1
  256. package/foundation/integrations/contribution-inventory.mjs +17 -20
  257. package/foundation/integrations/contribution-inventory.test.mjs +67 -27
  258. package/foundation/integrations/integrations.d.mts +3 -0
  259. package/foundation/integrations/integrations.mjs +11 -97
  260. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  261. package/foundation/integrations/provider-resolution.d.mts +152 -0
  262. package/foundation/integrations/provider-resolution.mjs +576 -0
  263. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  264. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  265. package/foundation/integrations/theme-descriptor.mjs +44 -0
  266. package/foundation/integrations/validate-contributions.mjs +104 -10
  267. package/foundation/response/error-codes.d.mts +3 -1
  268. package/foundation/response/error-codes.d.ts +2 -0
  269. package/foundation/response/error-codes.doc.mjs +12 -2
  270. package/foundation/response/error-codes.mjs +7 -1
  271. package/foundation/response/error-codes.test.mjs +55 -11
  272. package/foundation/response/response-types.doc.mjs +1 -1
  273. package/foundation/response/response.doc.mjs +1 -1
  274. package/foundation/text/string-utils.mjs +22 -10
  275. package/package.json +10 -9
  276. package/assets/templates/themes/manifest.json +0 -95
@@ -32,7 +32,7 @@ afterEach(() => {
32
32
  });
33
33
 
34
34
  describe('integrationAddTheme', () => {
35
- it('writes a valid source theme, catalog, root, package files entry, and receipt', async () => {
35
+ it('writes a typed same-stem descriptor and source with no central catalog', async () => {
36
36
  setup();
37
37
  const result = await integrationAddTheme('ocean', {cwd: tmpDir});
38
38
 
@@ -45,7 +45,7 @@ describe('integrationAddTheme', () => {
45
45
  manifest: 'astryx.integration.mjs',
46
46
  files: [
47
47
  'themes/ocean/oceanTheme.ts',
48
- 'themes/manifest.json',
48
+ 'themes/ocean/oceanTheme.doc.mjs',
49
49
  'package.json',
50
50
  'astryx.integration.mjs',
51
51
  ],
@@ -56,21 +56,17 @@ describe('integrationAddTheme', () => {
56
56
  expect(
57
57
  fs.readFileSync(path.join(tmpDir, 'themes/ocean/oceanTheme.ts'), 'utf-8'),
58
58
  ).toContain('export const oceanTheme = defineTheme');
59
- expect(
60
- JSON.parse(
61
- fs.readFileSync(path.join(tmpDir, 'themes/manifest.json'), 'utf-8'),
62
- ),
63
- ).toMatchObject({
64
- version: 1,
65
- themes: [
66
- {
67
- slug: 'ocean',
68
- entry: 'oceanTheme.ts',
69
- exportName: 'oceanTheme',
70
- files: ['oceanTheme.ts'],
71
- },
72
- ],
73
- });
59
+
60
+ const descriptor = fs.readFileSync(
61
+ path.join(tmpDir, 'themes/ocean/oceanTheme.doc.mjs'),
62
+ 'utf-8',
63
+ );
64
+ expect(descriptor).toContain("@astryxdesign/cli/authoring').ThemeDoc");
65
+ expect(descriptor).toContain("type: 'theme'");
66
+ expect(descriptor).toContain("name: 'ocean'");
67
+ expect(fs.existsSync(path.join(tmpDir, 'themes/manifest.json'))).toBe(
68
+ false,
69
+ );
74
70
  expect(
75
71
  fs.readFileSync(path.join(tmpDir, 'astryx.integration.mjs'), 'utf-8'),
76
72
  ).toContain("themes: './themes'");
@@ -96,6 +92,7 @@ describe('integrationAddTheme', () => {
96
92
  expect(result.data.dryRun).toBe(true);
97
93
  expect(result.data.root).toEqual({path: './themes', created: true});
98
94
  expect(result.data.files).toContain('themes/ocean/oceanTheme.ts');
95
+ expect(result.data.files).toContain('themes/ocean/oceanTheme.doc.mjs');
99
96
  expect(fs.existsSync(path.join(tmpDir, 'themes'))).toBe(false);
100
97
  expect(
101
98
  fs.readFileSync(path.join(tmpDir, 'astryx.integration.mjs'), 'utf-8'),
@@ -125,22 +122,69 @@ describe('integrationAddTheme', () => {
125
122
  expect(
126
123
  fs.existsSync(path.join(tmpDir, 'src/themes/ocean/oceanTheme.ts')),
127
124
  ).toBe(true);
125
+ expect(
126
+ fs.existsSync(path.join(tmpDir, 'src/themes/ocean/oceanTheme.doc.mjs')),
127
+ ).toBe(true);
128
128
  });
129
129
 
130
- it('refuses an invalid existing catalog without changing its bytes', async () => {
130
+ it('keeps each theme to its own folder: a second theme touches no shared file', async () => {
131
+ setup({manifest: "export default {themes: './themes'};\n"});
132
+ await integrationAddTheme('ocean', {cwd: tmpDir});
133
+ const shared = ['package.json', 'astryx.integration.mjs'].map(file =>
134
+ fs.readFileSync(path.join(tmpDir, file), 'utf-8'),
135
+ );
136
+
137
+ const result = await integrationAddTheme('reef', {cwd: tmpDir});
138
+
139
+ expect(result.data.files).toEqual([
140
+ 'themes/reef/reefTheme.ts',
141
+ 'themes/reef/reefTheme.doc.mjs',
142
+ ]);
143
+ expect(fs.readdirSync(path.join(tmpDir, 'themes')).sort()).toEqual([
144
+ 'ocean',
145
+ 'reef',
146
+ ]);
147
+ expect(
148
+ ['package.json', 'astryx.integration.mjs'].map(file =>
149
+ fs.readFileSync(path.join(tmpDir, file), 'utf-8'),
150
+ ),
151
+ ).toEqual(shared);
152
+ });
153
+
154
+ it('refuses an obsolete central catalog without changing its bytes', async () => {
131
155
  setup();
132
156
  fs.mkdirSync(path.join(tmpDir, 'themes'));
133
157
  const catalog = path.join(tmpDir, 'themes', 'manifest.json');
134
- fs.writeFileSync(catalog, '{not-json\n');
158
+ fs.writeFileSync(catalog, '{"version":1}\n');
135
159
 
136
160
  await expect(
137
161
  integrationAddTheme('ocean', {cwd: tmpDir}),
138
162
  ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
139
- expect(fs.readFileSync(catalog, 'utf-8')).toBe('{not-json\n');
163
+ expect(fs.readFileSync(catalog, 'utf-8')).toBe('{"version":1}\n');
140
164
  expect(fs.existsSync(path.join(tmpDir, 'themes', 'ocean'))).toBe(false);
141
165
  });
142
166
 
143
- it('refuses duplicate themes and existing source files', async () => {
167
+ it('refuses an invalid existing descriptor without changing it', async () => {
168
+ setup();
169
+ const existing = path.join(tmpDir, 'themes', 'forest');
170
+ fs.mkdirSync(existing, {recursive: true});
171
+ const descriptor = path.join(existing, 'forestTheme.doc.mjs');
172
+ fs.writeFileSync(descriptor, 'export default {type: "theme"};\n');
173
+ fs.writeFileSync(
174
+ path.join(existing, 'forestTheme.ts'),
175
+ 'export const forestTheme = {};\n',
176
+ );
177
+
178
+ await expect(
179
+ integrationAddTheme('ocean', {cwd: tmpDir}),
180
+ ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
181
+ expect(fs.readFileSync(descriptor, 'utf-8')).toBe(
182
+ 'export default {type: "theme"};\n',
183
+ );
184
+ expect(fs.existsSync(path.join(tmpDir, 'themes', 'ocean'))).toBe(false);
185
+ });
186
+
187
+ it('refuses a duplicate valid theme directory', async () => {
144
188
  setup();
145
189
  await integrationAddTheme('ocean', {cwd: tmpDir});
146
190
  await expect(
@@ -148,6 +192,46 @@ describe('integrationAddTheme', () => {
148
192
  ).rejects.toMatchObject({code: 'ERR_FILE_EXISTS'});
149
193
  });
150
194
 
195
+ it('refuses an existing invalid theme directory', async () => {
196
+ setup();
197
+ fs.mkdirSync(path.join(tmpDir, 'themes', 'ocean'), {recursive: true});
198
+ fs.writeFileSync(
199
+ path.join(tmpDir, 'themes', 'ocean', 'oceanTheme.ts'),
200
+ 'export const oceanTheme = {};\n',
201
+ );
202
+ await expect(
203
+ integrationAddTheme('ocean', {cwd: tmpDir}),
204
+ ).rejects.toMatchObject({code: 'ERR_THEME_INVALID'});
205
+ });
206
+
207
+ it.each([
208
+ ['an empty folder', []],
209
+ ['a folder holding .gitkeep', ['.gitkeep']],
210
+ ['a folder holding NOTES.md', ['NOTES.md']],
211
+ ])('fills %s', async (_, files) => {
212
+ setup();
213
+ const dir = path.join(tmpDir, 'themes', 'ocean');
214
+ fs.mkdirSync(dir, {recursive: true});
215
+ for (const file of files) fs.writeFileSync(path.join(dir, file), 'x\n');
216
+ const result = await integrationAddTheme('ocean', {cwd: tmpDir});
217
+ expect(result.data.written).toBe(true);
218
+ expect(fs.readdirSync(dir).sort()).toEqual(
219
+ [...files, 'oceanTheme.doc.mjs', 'oceanTheme.ts'].sort(),
220
+ );
221
+ });
222
+
223
+ it('refuses to overwrite a file it would write', async () => {
224
+ setup();
225
+ await integrationAddTheme('ocean', {cwd: tmpDir});
226
+ await expect(
227
+ integrationAddTheme('ocean', {cwd: tmpDir}),
228
+ ).rejects.toMatchObject({
229
+ code: 'ERR_FILE_EXISTS',
230
+ message:
231
+ 'Refusing to overwrite existing file themes/ocean/oceanTheme.ts.',
232
+ });
233
+ });
234
+
151
235
  it.each(['../ocean', 'Ocean', 'ocean theme', '.ocean'])(
152
236
  'refuses unsafe or noncanonical name %s',
153
237
  async name => {
@@ -33,7 +33,7 @@ function writeIntegration({id, name, type = 'block', root = tmpDir}) {
33
33
  const stem = path.join(root, 'templates', id);
34
34
  fs.mkdirSync(path.dirname(stem), {recursive: true});
35
35
  fs.writeFileSync(
36
- `${stem}.template.mjs`,
36
+ `${stem}.doc.mjs`,
37
37
  `export default {type: '${type}', name: ${JSON.stringify(name)}, description: 'fixture'};\n`,
38
38
  );
39
39
  fs.writeFileSync(
@@ -176,7 +176,7 @@ describe('integrationTemplateConflicts', () => {
176
176
  );
177
177
  fs.mkdirSync(path.join(tmpDir, 'templates'), {recursive: true});
178
178
  fs.writeFileSync(
179
- path.join(tmpDir, 'templates', 'broken.template.mjs'),
179
+ path.join(tmpDir, 'templates', 'broken.doc.mjs'),
180
180
  `throw new Error('broken fixture');\n`,
181
181
  );
182
182
  fs.writeFileSync(
@@ -221,7 +221,9 @@ describe('integrationComponentConflicts', () => {
221
221
  expect(core).toBeDefined();
222
222
  writeComponentIntegration(core.name);
223
223
 
224
- const result = await integrationComponentConflicts(undefined, {cwd: tmpDir});
224
+ const result = await integrationComponentConflicts(undefined, {
225
+ cwd: tmpDir,
226
+ });
225
227
 
226
228
  expect(result.type).toBe('integration.component-conflicts');
227
229
  expect(result.data.issues).toEqual([]);
@@ -240,7 +242,9 @@ describe('integrationComponentConflicts', () => {
240
242
  it('does not flag an integration-only component name', async () => {
241
243
  writeComponentIntegration('AcmeOnlyWidget');
242
244
 
243
- const result = await integrationComponentConflicts(undefined, {cwd: tmpDir});
245
+ const result = await integrationComponentConflicts(undefined, {
246
+ cwd: tmpDir,
247
+ });
244
248
 
245
249
  expect(result.data.conflicts).toEqual([]);
246
250
  expect(result.data.issues).toEqual([]);
@@ -51,7 +51,7 @@ const ESBUILD_BIN = resolveBin('esbuild/bin/esbuild');
51
51
  let tmpDir;
52
52
 
53
53
  /**
54
- * Build an @acme/widgets fixture: a block template (`.template.ts` doc +
54
+ * Build an @acme/widgets fixture: a block template (released `.template.ts` compatibility descriptor +
55
55
  * same-stem `.tsx` source) under `templates/`, plus an astryx.integration
56
56
  * manifest, plus a caller-supplied `exports` map.
57
57
  * @param {Record<string, string> | undefined} exportsMap
@@ -71,7 +71,7 @@ function makeWidgets(exportsMap) {
71
71
  path.join(pkgDir, 'astryx.integration.mjs'),
72
72
  `export default { templates: './templates' };\n`,
73
73
  );
74
- // The template-spec doc (canonical `.template.*` family).
74
+ // The released `.template.*` compatibility form remains resolvable.
75
75
  fs.writeFileSync(
76
76
  path.join(blockDir, 'GaugeShowcase.template.ts'),
77
77
  `export default { name: 'Gauge showcase', description: 'A gauge.' };\n`,
@@ -127,10 +127,14 @@ function writeTsconfig() {
127
127
  /** @returns {{ok: boolean, out: string}} */
128
128
  function runTsc() {
129
129
  try {
130
- execFileSync(process.execPath, [TSC_BIN, '--noEmit', '-p', 'tsconfig.json'], {
131
- cwd: tmpDir,
132
- stdio: 'pipe',
133
- });
130
+ execFileSync(
131
+ process.execPath,
132
+ [TSC_BIN, '--noEmit', '-p', 'tsconfig.json'],
133
+ {
134
+ cwd: tmpDir,
135
+ stdio: 'pipe',
136
+ },
137
+ );
134
138
  return {ok: true, out: ''};
135
139
  } catch (e) {
136
140
  return {ok: false, out: `${e.stdout ?? ''}${e.stderr ?? ''}`};
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAdd',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAdd()',
9
10
  summary: 'Add one working contribution to an integration package.',
10
11
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddAgentDoc',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddAgentDoc()',
9
10
  summary: 'Append one validated agent-guidance line.',
10
11
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddCodemod',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddCodemod()',
9
10
  summary: 'Add a safe identity codemod at an exact target version.',
10
11
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddComponent',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddComponent()',
9
10
  summary: 'Add a complete component contribution.',
10
11
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddDoc',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddDoc()',
9
10
  summary: 'Add a complete reference-doc contribution.',
10
11
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddTemplate',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddTemplate()',
9
10
  summary: 'Add a complete page or block template contribution.',
10
11
  description:
@@ -5,10 +5,11 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationAddTheme',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationAddTheme()',
9
10
  summary: 'Add a source theme to an integration package.',
10
11
  description:
11
- 'Creates an integration manifest when the package has none, writes a minimal named-export defineTheme source file, adds its entry to the root theme catalog, declares the optional themes root on first use, and includes the manifest and root in package.json files when the package already uses an allowlist. Every path is confined to the package and existing theme files are never overwritten.',
12
+ 'Creates an integration manifest when the package has none, writes a minimal named-export defineTheme source file plus its strongly typed same-stem ThemeDoc, declares the optional themes root on first use, and includes the manifest and root in package.json files when the package already uses an allowlist. Every path is confined to the package and existing theme directories are never overwritten.',
12
13
  importPath: '@astryxdesign/cli/api',
13
14
  signature:
14
15
  'integrationAddTheme(name: string, options?: IntegrationAddThemeOptions): Promise<IntegrationAddResponse>',
@@ -17,7 +18,7 @@ export const doc = {
17
18
  {
18
19
  name: 'name',
19
20
  type: 'string',
20
- description: 'Lowercase kebab-case theme name and catalog slug.',
21
+ description: 'Lowercase kebab-case theme name and directory slug.',
21
22
  required: true,
22
23
  },
23
24
  {
@@ -51,7 +52,7 @@ export const doc = {
51
52
  },
52
53
  {
53
54
  code: 'ERR_FILE_EXISTS',
54
- when: 'the theme slug or source file already exists',
55
+ when: 'the theme slug directory already exists',
55
56
  },
56
57
  {
57
58
  code: 'ERR_PATH_TRAVERSAL',
@@ -63,7 +64,7 @@ export const doc = {
63
64
  },
64
65
  {
65
66
  code: 'ERR_THEME_INVALID',
66
- when: 'the package, integration manifest, or theme catalog is invalid',
67
+ when: 'the package, integration manifest, or theme descriptor is invalid',
67
68
  },
68
69
  {
69
70
  code: 'ERR_WRITE_FAILED',
@@ -80,6 +81,6 @@ export const doc = {
80
81
  code: "await integrationAddTheme('ocean', {dryRun: true});",
81
82
  },
82
83
  ],
83
- command: 'integration add',
84
+ command: 'integration add theme',
84
85
  related: ['themeList', 'themeAdd', 'validateIntegration'],
85
86
  };
@@ -7,6 +7,7 @@ export const doc = {
7
7
  type: 'function',
8
8
  kind: 'api',
9
9
  name: 'integrationComponentConflicts',
10
+ namespace: 'cli/api',
10
11
  displayName: 'integrationComponentConflicts()',
11
12
  summary: 'Find integration component names that also exist in Core.',
12
13
  description:
@@ -7,6 +7,7 @@ export const doc = {
7
7
  type: 'function',
8
8
  kind: 'api',
9
9
  name: 'integrationDocConflicts',
10
+ namespace: 'cli/api',
10
11
  displayName: 'integrationDocConflicts()',
11
12
  summary: 'Classify integration doc overlaps with Core topics.',
12
13
  description:
@@ -5,6 +5,7 @@ export const doc = {
5
5
  type: 'function',
6
6
  kind: 'api',
7
7
  name: 'integrationPackCheck',
8
+ namespace: 'cli/api',
8
9
  displayName: 'integrationPackCheck()',
9
10
  summary: 'Prove an integration package survives npm packing.',
10
11
  description:
@@ -9,6 +9,7 @@ export const doc = {
9
9
  type: 'function',
10
10
  kind: 'api',
11
11
  name: 'integrationTemplateConflicts',
12
+ namespace: 'cli/api',
12
13
  displayName: 'integrationTemplateConflicts()',
13
14
  summary: 'Find integration template ids that also exist in Core.',
14
15
  description:
@@ -53,25 +53,10 @@ function writePackage({
53
53
  "import {defineTheme} from '@astryxdesign/core/theme';\n\nexport const oceanTheme = defineTheme({name: 'ocean'});\n",
54
54
  );
55
55
  fs.writeFileSync(
56
- path.join(root, 'manifest.json'),
57
- JSON.stringify(
58
- {
59
- version: 1,
60
- themes: [
61
- {
62
- slug: 'ocean',
63
- displayName: 'Ocean',
64
- description: 'Ocean theme.',
65
- maintained: true,
66
- entry: 'oceanTheme.ts',
67
- exportName: 'oceanTheme',
68
- files: ['oceanTheme.ts'],
69
- },
70
- ],
71
- },
72
- null,
73
- 2,
74
- ) + '\n',
56
+ path.join(root, 'ocean', 'oceanTheme.doc.mjs'),
57
+ `/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
58
+ export default {type: 'theme', name: 'ocean', displayName: 'Ocean', description: 'Ocean theme.', maintained: true};
59
+ `,
75
60
  );
76
61
  }
77
62
  if (components) {
@@ -109,7 +94,7 @@ describe('integrationPackCheck', () => {
109
94
  );
110
95
  });
111
96
 
112
- it('fails when a theme entry omits its catalog export', async () => {
97
+ it('fails when a theme entry omits its inferred runtime export', async () => {
113
98
  writePackage({files: ['astryx.integration.mjs', 'themes']});
114
99
  fs.writeFileSync(
115
100
  path.join(tmpDir, 'themes', 'ocean', 'oceanTheme.ts'),
@@ -129,19 +114,12 @@ describe('integrationPackCheck', () => {
129
114
  );
130
115
  });
131
116
 
132
- it('fails when a theme imports a file omitted from its catalog', async () => {
117
+ it('fails when a theme imports a missing local file', async () => {
133
118
  writePackage({files: ['astryx.integration.mjs', 'themes']});
134
- fs.mkdirSync(path.join(tmpDir, 'themes', 'ocean', 'tokens'), {
135
- recursive: true,
136
- });
137
119
  fs.writeFileSync(
138
120
  path.join(tmpDir, 'themes', 'ocean', 'oceanTheme.ts'),
139
121
  "import {oceanPalette} from './tokens/ocean.palette';\nexport const oceanTheme = {oceanPalette};\n",
140
122
  );
141
- fs.writeFileSync(
142
- path.join(tmpDir, 'themes', 'ocean', 'tokens', 'ocean.palette.ts'),
143
- 'export const oceanPalette = {};\n',
144
- );
145
123
 
146
124
  const result = await integrationPackCheck({cwd: tmpDir});
147
125
 
@@ -150,7 +128,7 @@ describe('integrationPackCheck', () => {
150
128
  expect.objectContaining({
151
129
  code: 'invalid_theme',
152
130
  message: expect.stringContaining(
153
- 'must resolve to a listed file inside the theme directory',
131
+ 'must resolve to a file inside the theme directory',
154
132
  ),
155
133
  }),
156
134
  );
@@ -231,7 +209,7 @@ describe('integrationPackCheck', () => {
231
209
  );
232
210
  fs.mkdirSync(path.join(tmpDir, 'templates'));
233
211
  fs.writeFileSync(
234
- path.join(tmpDir, 'templates', 'account-page.template.mjs'),
212
+ path.join(tmpDir, 'templates', 'account-page.doc.mjs'),
235
213
  "export default {type: 'page', name: 'Account page', description: 'Account page.'};\n",
236
214
  );
237
215
  fs.writeFileSync(
@@ -314,10 +292,10 @@ describe('integrationPackCheck', () => {
314
292
  const script = [
315
293
  "const fs=require('fs')",
316
294
  "fs.renameSync('themes/ocean','themes/storm')",
317
- "const p='themes/manifest.json'",
318
- 'const x=JSON.parse(fs.readFileSync(p))',
319
- "x.themes[0].slug='storm'",
320
- 'fs.writeFileSync(p,JSON.stringify(x))',
295
+ "const p='themes/storm/oceanTheme.doc.mjs'",
296
+ "let x=fs.readFileSync(p,'utf8')",
297
+ "x=x.replace(/name: 'ocean'/, 'name: '+String.fromCharCode(39)+'storm'+String.fromCharCode(39))",
298
+ 'fs.writeFileSync(p,x)',
321
299
  ].join(';');
322
300
  writePackage({
323
301
  files: ['astryx.integration.mjs', 'themes'],
@@ -513,23 +491,15 @@ describe('integrationPackCheck', () => {
513
491
  });
514
492
 
515
493
  describe('pack-check mutation tests', () => {
516
- it('detects when a theme file is deleted after initial add', async () => {
494
+ it('detects when a theme source is deleted after initial authoring', async () => {
517
495
  writePackage({files: ['astryx.integration.mjs', 'themes']});
518
-
519
- // Write a second theme file then delete it — catalog still references it
520
- const catalog = JSON.parse(
521
- fs.readFileSync(path.join(tmpDir, 'themes', 'manifest.json'), 'utf-8'),
522
- );
523
- catalog.themes[0].files.push('tokens.ts');
524
- fs.writeFileSync(
525
- path.join(tmpDir, 'themes', 'manifest.json'),
526
- JSON.stringify(catalog, null, 2) + '\n',
527
- );
528
- // tokens.ts doesn't exist on disk → will be in inventory but not on disk
529
- // (Phase 1 catches this; the file won't be in the pack list either)
496
+ fs.rmSync(path.join(tmpDir, 'themes', 'ocean', 'oceanTheme.ts'));
530
497
 
531
498
  const result = await integrationPackCheck({cwd: tmpDir});
532
499
  expect(result.data.packable).toBe(false);
500
+ expect(result.data.issues).toContainEqual(
501
+ expect.objectContaining({code: 'invalid_theme'}),
502
+ );
533
503
  });
534
504
 
535
505
  it('detects when files[] is narrowed after add removes themes', async () => {
@@ -12,6 +12,7 @@ export const doc = {
12
12
  type: 'function',
13
13
  kind: 'api',
14
14
  name: 'summarizeIssues',
15
+ namespace: 'cli/api',
15
16
  displayName: 'summarizeIssues()',
16
17
  summary:
17
18
  'Tally integration issues by severity into error and warning counts.',