@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
@@ -0,0 +1,369 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Colocated tests for the provider resolver: one outcome per real package
5
+ * directory, precedence, and the wrappers that keep their old results.
6
+ *
7
+ * Fixtures live under a repo-local temp dir, not /tmp, because Vite refuses to
8
+ * dynamically import a module from outside the project root.
9
+ */
10
+
11
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import {packageKey, resolveProviders} from './provider-resolution.mjs';
15
+ import {markProviderConflicts} from './integrations.mjs';
16
+ import {autolinkIntegrations} from './autolink.mjs';
17
+ import {Project, providerLedgerOf} from '../config/project.mjs';
18
+
19
+ let tmpDir;
20
+
21
+ beforeEach(() => {
22
+ tmpDir = fs.mkdtempSync(
23
+ path.join(process.cwd(), '.astryx-provider-resolution-'),
24
+ );
25
+ });
26
+
27
+ afterEach(() => {
28
+ fs.rmSync(tmpDir, {recursive: true, force: true});
29
+ });
30
+
31
+ /**
32
+ * A loaded-integration record in a real directory.
33
+ * @param {string} dir directory under the temp root
34
+ * @param {Record<string, unknown>} fields
35
+ * @returns {any}
36
+ */
37
+ function record(dir, fields) {
38
+ const packageDir = path.join(tmpDir, dir);
39
+ fs.mkdirSync(packageDir, {recursive: true});
40
+ return {
41
+ providerId: fields.name,
42
+ __spec: fields.name,
43
+ __packageDir: packageDir,
44
+ __manifestFile: path.join(packageDir, 'astryx.integration.mjs'),
45
+ ...fields,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * @param {string} source
51
+ * @param {any} integration
52
+ */
53
+ const candidate = (source, integration) => ({
54
+ source,
55
+ integration,
56
+ spec: integration.__spec,
57
+ });
58
+
59
+ /** @param {Map<string, any>} ledger */
60
+ function outcomes(ledger) {
61
+ return [...ledger.values()].map(entry => [entry.label, entry.outcome]);
62
+ }
63
+
64
+ describe('resolveProviders ledger', () => {
65
+ it('records one outcome for every candidate, keyed by real directory', () => {
66
+ const widgets = record('widgets', {
67
+ name: '@acme/widgets',
68
+ version: '1.0.0',
69
+ });
70
+ const renamed = record('renamed', {
71
+ name: '@acme/renamed',
72
+ version: '2.0.0',
73
+ providerId: '@acme/widgets',
74
+ });
75
+ const broken = record('broken', {
76
+ name: '@acme/broken',
77
+ version: '1.0.0',
78
+ __loadError: 'boom',
79
+ });
80
+ const {integrations, ledger} = resolveProviders([
81
+ candidate('configured', widgets),
82
+ candidate('configured', renamed),
83
+ candidate('configured', broken),
84
+ ]);
85
+
86
+ expect([...ledger.keys()]).toEqual(
87
+ [widgets, renamed, broken].map(entry => packageKey(entry.__packageDir)),
88
+ );
89
+ expect(outcomes(ledger)).toEqual([
90
+ ['@acme/widgets@1.0.0', 'contributes'],
91
+ ['@acme/renamed@2.0.0', 'set-aside'],
92
+ ['@acme/broken@1.0.0', 'load-failed'],
93
+ ]);
94
+ const setAside = ledger.get(packageKey(renamed.__packageDir));
95
+ expect(setAside).toMatchObject({
96
+ winner: packageKey(widgets.__packageDir),
97
+ reason: 'claimed-earlier',
98
+ integration: integrations[1],
99
+ });
100
+ expect(integrations[1].__providerConflict.claimedBy).toBe('@acme/widgets');
101
+ });
102
+
103
+ it('keeps two packages that share a name apart', () => {
104
+ const old = record('widgets-old', {
105
+ name: '@acme/widgets',
106
+ version: '1.0.0',
107
+ __spec: 'widgets-old',
108
+ });
109
+ const next = record('widgets-next', {
110
+ name: '@acme/widgets',
111
+ version: '2.0.0',
112
+ __spec: 'widgets-next',
113
+ });
114
+ const {ledger} = resolveProviders([
115
+ candidate('configured', old),
116
+ candidate('configured', next),
117
+ ]);
118
+
119
+ expect(outcomes(ledger)).toEqual([
120
+ ['@acme/widgets@1.0.0', 'contributes'],
121
+ ['@acme/widgets@2.0.0', 'set-aside'],
122
+ ]);
123
+ });
124
+
125
+ it('folds every spelling of one real directory into one entry', () => {
126
+ const ui = record('ui', {name: '@acme/ui', version: '0.1.22'});
127
+ const link = path.join(tmpDir, 'legacy-ui');
128
+ fs.symlinkSync(ui.__packageDir, link, 'dir');
129
+ const legacy = {...ui, __spec: '@acme/legacy-ui', __packageDir: link};
130
+ const {integrations, ledger} = resolveProviders([
131
+ candidate('configured', ui),
132
+ candidate('configured', legacy),
133
+ ]);
134
+
135
+ expect(integrations).toEqual([ui]);
136
+ expect([...ledger.keys()]).toEqual([packageKey(ui.__packageDir)]);
137
+ const [entry] = ledger.values();
138
+ expect(entry.outcome).toBe('contributes');
139
+ expect(entry.refs.map(ref => ref.spec)).toEqual([
140
+ '@acme/ui',
141
+ '@acme/legacy-ui',
142
+ ]);
143
+ });
144
+
145
+ it('records an autolinked copy of a loaded package as alias-of it', () => {
146
+ const ui = record('ui', {name: '@acme/ui', version: '0.1.22'});
147
+ const copy = record('legacy-ui', {
148
+ name: '@acme/ui',
149
+ version: '0.1.22',
150
+ __spec: '@acme/legacy-ui',
151
+ __autolinked: true,
152
+ });
153
+ const {integrations, ledger} = resolveProviders([
154
+ candidate('configured', ui),
155
+ candidate('autolinked', copy),
156
+ ]);
157
+
158
+ expect(integrations).toEqual([ui]);
159
+ expect(ledger.get(packageKey(copy.__packageDir))).toMatchObject({
160
+ outcome: 'alias-of',
161
+ aliasOf: packageKey(ui.__packageDir),
162
+ });
163
+ });
164
+
165
+ it('records an autolinked package that failed to load, and lists it nowhere', () => {
166
+ const packageDir = path.join(tmpDir, 'unreadable');
167
+ const {integrations, ledger} = resolveProviders([
168
+ {
169
+ source: 'autolinked',
170
+ spec: '@acme/unreadable',
171
+ packageDir,
172
+ error: 'bad json',
173
+ },
174
+ ]);
175
+
176
+ expect(integrations).toEqual([]);
177
+ expect(ledger.get(packageKey(packageDir))).toMatchObject({
178
+ outcome: 'load-failed',
179
+ error: 'bad json',
180
+ });
181
+ });
182
+
183
+ it('lets the package being authored replace its installed copy and claim first', () => {
184
+ const installed = record('installed', {
185
+ name: '@acme/widgets',
186
+ version: '1.0.0',
187
+ });
188
+ const rival = record('rival', {
189
+ name: '@acme/rival',
190
+ version: '1.0.0',
191
+ providerId: '@acme/widgets',
192
+ });
193
+ const local = record('local', {
194
+ name: '@acme/widgets',
195
+ version: '2.0.0-dev',
196
+ __local: true,
197
+ });
198
+ const {integrations, ledger} = resolveProviders([
199
+ candidate('configured', rival),
200
+ candidate('configured', installed),
201
+ candidate('local', local),
202
+ ]);
203
+
204
+ expect(integrations.map(entry => entry.name)).toEqual([
205
+ '@acme/rival',
206
+ '@acme/widgets',
207
+ ]);
208
+ expect(integrations[1]).toBe(local);
209
+ expect(ledger.get(packageKey(installed.__packageDir))).toMatchObject({
210
+ outcome: 'set-aside',
211
+ reason: 'replaced-by-authored',
212
+ winner: packageKey(local.__packageDir),
213
+ });
214
+ expect(ledger.get(packageKey(rival.__packageDir))).toMatchObject({
215
+ outcome: 'set-aside',
216
+ reason: 'authored-claims',
217
+ winner: packageKey(local.__packageDir),
218
+ });
219
+ });
220
+
221
+ it('keeps configured precedence ahead of autolinked', () => {
222
+ const configured = record('configured', {
223
+ name: '@acme/one',
224
+ version: '1.0.0',
225
+ });
226
+ const autolinked = record('autolinked', {
227
+ name: '@acme/two',
228
+ version: '1.0.0',
229
+ providerId: '@acme/one',
230
+ __autolinked: true,
231
+ });
232
+ const {ledger} = resolveProviders([
233
+ candidate('configured', configured),
234
+ candidate('autolinked', autolinked),
235
+ ]);
236
+
237
+ expect(outcomes(ledger)).toEqual([
238
+ ['@acme/one@1.0.0', 'contributes'],
239
+ ['@acme/two@1.0.0', 'set-aside'],
240
+ ]);
241
+ });
242
+
243
+ it('under the codemod policy, records the installed copy standing in', () => {
244
+ const local = record('local', {
245
+ name: '@acme/widgets',
246
+ version: '2.0.0-dev',
247
+ __local: true,
248
+ });
249
+ const installed = record('installed', {
250
+ name: '@acme/widgets',
251
+ version: '1.0.0',
252
+ });
253
+ const {integrations, ledger} = resolveProviders(
254
+ [candidate('configured', local), candidate('installed', installed)],
255
+ {codemods: true},
256
+ );
257
+
258
+ expect(integrations).toEqual([installed]);
259
+ expect(ledger.get(packageKey(local.__packageDir))).toMatchObject({
260
+ outcome: 'set-aside',
261
+ reason: 'installed-stands-in',
262
+ winner: packageKey(installed.__packageDir),
263
+ });
264
+ });
265
+ });
266
+
267
+ describe('markProviderConflicts', () => {
268
+ const a = {name: 'a', version: '1', providerId: 'x', __spec: 'a'};
269
+ const b = {name: 'b', version: '1', providerId: 'x', __spec: 'b'};
270
+ const alias = {name: 'a', version: '1', providerId: 'x', __spec: 'a-alias'};
271
+ const local = {
272
+ name: 'l',
273
+ version: '1',
274
+ providerId: 'x',
275
+ __spec: 'l',
276
+ __local: true,
277
+ };
278
+ /** @param {any[]} list */
279
+ const claimsOf = list =>
280
+ markProviderConflicts(list).map(entry => [
281
+ entry?.name ?? null,
282
+ entry?.__providerConflict?.claimedBy ?? null,
283
+ ]);
284
+
285
+ it('drops an alias of the winner and keeps a record listed twice', () => {
286
+ expect(claimsOf([a, b, alias, a, undefined])).toEqual([
287
+ ['a', null],
288
+ ['b', 'a'],
289
+ ['a', null],
290
+ [null, null],
291
+ ]);
292
+ });
293
+
294
+ it('lets the package being authored claim first', () => {
295
+ expect(claimsOf([a, undefined, b, alias, a, local])).toEqual([
296
+ ['a', 'l'],
297
+ [null, null],
298
+ ['b', 'l'],
299
+ ['a', 'l'],
300
+ ['a', 'l'],
301
+ ['l', null],
302
+ ]);
303
+ });
304
+ });
305
+
306
+ describe('Project.load keeps a ledger', () => {
307
+ /**
308
+ * @param {string} dir node_modules key
309
+ * @param {{name: string, version: string}} pkg
310
+ */
311
+ function install(dir, pkg) {
312
+ const pkgDir = path.join(tmpDir, 'node_modules', ...dir.split('/'));
313
+ fs.mkdirSync(pkgDir, {recursive: true});
314
+ fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify(pkg));
315
+ fs.writeFileSync(
316
+ path.join(pkgDir, 'astryx.integration.mjs'),
317
+ 'export default {};\n',
318
+ );
319
+ return pkgDir;
320
+ }
321
+
322
+ it('records the autolink alias it drops as alias-of the loaded copy', async () => {
323
+ fs.writeFileSync(
324
+ path.join(tmpDir, 'package.json'),
325
+ JSON.stringify({
326
+ name: 'consumer',
327
+ dependencies: {
328
+ '@acme/legacy-ui': 'npm:@acme/ui@0.1.22',
329
+ '@acme/ui': '0.1.22',
330
+ },
331
+ }),
332
+ );
333
+ const legacy = install('@acme/legacy-ui', {
334
+ name: '@acme/ui',
335
+ version: '0.1.22',
336
+ });
337
+ const ui = install('@acme/ui', {name: '@acme/ui', version: '0.1.22'});
338
+
339
+ const project = await Project.load(tmpDir, {fresh: true});
340
+ expect(project.loadedIntegrations.map(entry => entry.__spec)).toEqual([
341
+ '@acme/legacy-ui',
342
+ ]);
343
+ const ledger = providerLedgerOf(project);
344
+ expect(ledger.get(packageKey(ui))).toMatchObject({
345
+ outcome: 'alias-of',
346
+ aliasOf: packageKey(legacy),
347
+ });
348
+ expect(await autolinkIntegrations({projectDir: tmpDir})).toHaveLength(1);
349
+ });
350
+
351
+ it('records a dependency key that repeats a configured package', async () => {
352
+ fs.writeFileSync(
353
+ path.join(tmpDir, 'package.json'),
354
+ JSON.stringify({name: 'consumer', dependencies: {'@acme/ui': '0.1.22'}}),
355
+ );
356
+ fs.writeFileSync(
357
+ path.join(tmpDir, 'astryx.config.mjs'),
358
+ "export default {integrations: ['@acme/ui']};\n",
359
+ );
360
+ const ui = install('@acme/ui', {name: '@acme/ui', version: '0.1.22'});
361
+
362
+ const ledger = providerLedgerOf(await Project.load(tmpDir, {fresh: true}));
363
+ expect([...ledger.keys()]).toEqual([packageKey(ui)]);
364
+ expect(ledger.get(packageKey(ui))?.refs.map(ref => ref.source)).toEqual([
365
+ 'configured',
366
+ 'autolinked',
367
+ ]);
368
+ });
369
+ });
@@ -0,0 +1,8 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * @param {import('../../authoring/doctypes/theme/type.js').ThemeDoc} doc
6
+ * @returns {string}
7
+ */
8
+ export function themeDescriptorSource(doc: import("../../authoring/doctypes/theme/type.js").ThemeDoc): string;
@@ -0,0 +1,44 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The source text of a typed integration theme descriptor.
5
+ *
6
+ * @input A theme's descriptor fields.
7
+ * @output The `.doc.mjs` source that declares them under their ThemeDoc type.
8
+ * @position Shared by `integration add theme`, which scaffolds a descriptor,
9
+ * and the codemod that turns a 0.6 theme catalog into descriptors, so both
10
+ * write the same bytes.
11
+ */
12
+
13
+ /**
14
+ * A single-quoted JavaScript string literal for any text.
15
+ * @param {string} value
16
+ */
17
+ function quote(value) {
18
+ const escaped = value
19
+ .replace(/\\/gu, '\\\\')
20
+ .replace(/'/gu, "\\'")
21
+ .replace(/\n/gu, '\\n')
22
+ .replace(/\r/gu, '\\r')
23
+ .replace(/\u2028/gu, '\\u2028')
24
+ .replace(/\u2029/gu, '\\u2029');
25
+ return `'${escaped}'`;
26
+ }
27
+
28
+ /**
29
+ * @param {import('../../authoring/doctypes/theme/type.js').ThemeDoc} doc
30
+ * @returns {string}
31
+ */
32
+ export function themeDescriptorSource(doc) {
33
+ return [
34
+ "/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */",
35
+ 'export default {',
36
+ " type: 'theme',",
37
+ ` name: ${quote(doc.name)},`,
38
+ ` displayName: ${quote(doc.displayName)},`,
39
+ ` description: ${quote(doc.description)},`,
40
+ ` maintained: ${doc.maintained},`,
41
+ '};',
42
+ '',
43
+ ].join('\n');
44
+ }
@@ -25,6 +25,23 @@ import * as fs from 'node:fs';
25
25
  import * as path from 'node:path';
26
26
  import {isValidSemver} from '../env/semver.mjs';
27
27
  import {findSourceOnlyCandidates} from './contribution-inventory.mjs';
28
+ import {
29
+ CODEMOD_FILE_RE,
30
+ CODEMOD_SKIP_DIRS,
31
+ CODEMOD_TEST_RE,
32
+ createFixContext,
33
+ DOC_CANDIDATE_RE,
34
+ heldRootFix,
35
+ newComponentDocNote,
36
+ notACodemodFix,
37
+ notAComponentFix,
38
+ readContributionStamp,
39
+ sharedCodemodsRootFix,
40
+ stampOf,
41
+ themeImporting,
42
+ TEMPLATE_CANDIDATE_RE,
43
+ } from './contribution-fixes.mjs';
44
+ import {MANIFEST_BASENAMES} from './integrations.mjs';
28
45
  import {discoverIntegrationCodemods} from '../../assets/codemods/integration-discovery.mjs';
29
46
  import {discoverIntegrationTemplatesForOne} from '../discovery/template-adapter.mjs';
30
47
  import * as componentDiscovery from '../discovery/component-discovery.mjs';
@@ -119,32 +136,82 @@ function checkRoots(resolved, issues) {
119
136
  */
120
137
  async function checkCodemods(integration, issues) {
121
138
  if (!integration.codemods || !fs.existsSync(integration.codemods)) return;
139
+ const context = integration.__packageDir
140
+ ? createFixContext(integration.__packageDir, integration)
141
+ : null;
142
+ const shared = context && sharedCodemodsRootFix(context);
122
143
  for (const entry of fs.readdirSync(integration.codemods, {
123
144
  withFileTypes: true,
124
145
  })) {
125
146
  if (
126
147
  entry.isFile() &&
127
- /\.(?:ts|mjs|js)$/u.test(entry.name) &&
128
- !/\.(?:test|spec|fixture)\.(?:ts|mjs|js)$/u.test(entry.name)
148
+ CODEMOD_FILE_RE.test(entry.name) &&
149
+ !CODEMOD_TEST_RE.test(entry.name) &&
150
+ // The manifest sits beside package.json and is never a codemod.
151
+ !(
152
+ integration.codemods === integration.__packageDir &&
153
+ MANIFEST_BASENAMES.includes(entry.name)
154
+ )
129
155
  ) {
156
+ const shown = path
157
+ .relative(
158
+ integration.__packageDir ?? path.dirname(integration.codemods),
159
+ path.join(integration.codemods, entry.name),
160
+ )
161
+ .split(path.sep)
162
+ .join('/');
163
+ const root = path.dirname(shown);
164
+ const full = path.join(integration.codemods, entry.name);
165
+ const stamp =
166
+ context &&
167
+ (DOC_CANDIDATE_RE.test(entry.name) ||
168
+ TEMPLATE_CANDIDATE_RE.test(entry.name))
169
+ ? stampOf(context, full)
170
+ : null;
171
+ // A theme's entry module may sit beside its descriptor.
172
+ const themeDoc = ['.doc.mjs', '.doc.ts', '.doc.js']
173
+ .map(suffix => full.replace(/\.(?:ts|mjs|js)$/u, suffix))
174
+ .find(candidate => candidate !== full && fs.existsSync(candidate));
175
+ const themeStamp =
176
+ context && !stamp && themeDoc ? stampOf(context, themeDoc) : null;
177
+ let fix = `Fix: move it into the folder named for the version it migrates to, for example ${root}/1.2.0/${entry.name}.`;
178
+ if (shared) fix = sharedCodemodsRootFix(context, entry.name) ?? fix;
179
+ else if (context && stamp) fix = notACodemodFix(context, full, stamp);
180
+ else if (context && themeDoc && themeStamp?.type === 'theme') {
181
+ fix = notACodemodFix(context, themeDoc, themeStamp, entry.name);
182
+ } else if (context) {
183
+ const owner = themeImporting(context, full);
184
+ if (owner) {
185
+ fix = notACodemodFix(
186
+ context,
187
+ owner.doc,
188
+ owner.stamp,
189
+ owner.source,
190
+ entry.name,
191
+ );
192
+ }
193
+ }
130
194
  issues.push(
131
195
  issueWarning(
132
196
  'codemod_outside_version',
133
- `Codemod file "${path.join(integration.codemods, entry.name)}" is outside a version folder, so upgrade will never load it.`,
197
+ `Codemod file "${shown}" is outside a version folder, so upgrade will never load it. ${fix}`,
134
198
  ),
135
199
  );
136
200
  }
137
201
  if (
138
202
  entry.isDirectory() &&
139
- !['node_modules', '.git', '__tests__', '__fixtures__'].includes(
140
- entry.name,
141
- ) &&
203
+ !CODEMOD_SKIP_DIRS.has(entry.name) &&
142
204
  !isValidSemver(entry.name)
143
205
  ) {
206
+ const fix =
207
+ shared ??
208
+ (context &&
209
+ heldRootFix(context, path.join(integration.codemods, entry.name))) ??
210
+ 'Fix: rename it to the exact version its codemods migrate to.';
144
211
  issues.push(
145
212
  issueError(
146
213
  'invalid_codemod_version',
147
- `Codemod folder "${entry.name}" is not an exact semver version such as 1.2.0.`,
214
+ `Codemod folder "${entry.name}" is not an exact semver version such as 1.2.0. ${fix}`,
148
215
  ),
149
216
  );
150
217
  }
@@ -196,24 +263,51 @@ async function checkComponents(integration, issues) {
196
263
  if (typeof discover !== 'function') return; // feature not present yet
197
264
  try {
198
265
  const records = (await discover(integration)) ?? [];
266
+ /** @type {import('./contribution-fixes.mjs').FixContext | undefined} */
267
+ let context;
199
268
  for (const record of records) {
200
269
  if (record?.sourcePath == null) {
270
+ const docPath = record?.docPath;
271
+ let fix = `Fix: add ${record?.name}.tsx beside ${docPath ? path.basename(docPath) : `${record?.name}.doc.mjs`}.`;
272
+ const stamp = docPath ? readContributionStamp(docPath, false) : null;
273
+ if (stamp && stamp.type !== 'component' && integration.__packageDir) {
274
+ context ??= createFixContext(integration.__packageDir, integration);
275
+ fix = notAComponentFix(context, docPath, stamp);
276
+ }
201
277
  issues.push(
202
278
  issueError(
203
279
  'invalid_component',
204
- `Component "${record?.name}" is missing its same-stem source file ${record?.name}.tsx.`,
280
+ `Component "${record?.name}" is missing its same-stem source file ${record?.name}.tsx. ${fix}`,
205
281
  ),
206
282
  );
207
283
  }
208
284
  }
285
+ const overlap = integration.__packageDir
286
+ ? newComponentDocNote(
287
+ (context ??= createFixContext(integration.__packageDir, integration)),
288
+ )
289
+ : '';
209
290
  for (const name of findSourceOnlyCandidates(
210
291
  integration.components,
211
292
  records.map(record => record.name),
212
293
  )) {
294
+ // A hidden doc still pairs with its source; discovery just skips it.
295
+ if (
296
+ ['.doc.ts', '.doc.mjs', '.doc.js'].some(suffix =>
297
+ fs.existsSync(
298
+ path.join(
299
+ /** @type {string} */ (integration.components),
300
+ `${name}${suffix}`,
301
+ ),
302
+ ),
303
+ )
304
+ ) {
305
+ continue;
306
+ }
213
307
  issues.push(
214
308
  issueWarning(
215
309
  'source_without_component_doc',
216
- `Component source "${name}.tsx" has no same-stem metadata file ${name}.doc.mjs, so Astryx ignores it.`,
310
+ `Component source "${name}.tsx" has no same-stem metadata file ${name}.doc.mjs, so Astryx ignores it. Fix: add ${name}.doc.mjs beside it with type: 'component'; \`astryx docs authoring component-doc\` lists its fields.${overlap}`,
217
311
  ),
218
312
  );
219
313
  }
@@ -250,7 +344,7 @@ async function checkDocs(integration, issues) {
250
344
  }
251
345
 
252
346
  /**
253
- * Validate the integration's source-theme catalog. The same discovery function
347
+ * Validate the integration's source-theme descriptors. The same discovery function
254
348
  * powers Project and theme list/add, so validation cannot accept a shape that
255
349
  * consumers later fail to use.
256
350
  * @param {LoadedIntegration} integration
@@ -75,6 +75,8 @@ export function allErrorCodes(): string[];
75
75
  * | 'ERR_UNKNOWN_FEATURE'
76
76
  * | 'ERR_UNKNOWN_CODEMOD'
77
77
  * | 'ERR_CODEMOD_FAILED'
78
+ * | 'ERR_CODEMOD_PROTECTED'
79
+ * | 'ERR_CODEMOD_PROTECTION_SOURCE'
78
80
  * | 'ERR_NOT_FOUND'
79
81
  * | 'ERR_NO_DOC'
80
82
  * | 'ERR_NO_SHOWCASE'
@@ -107,4 +109,4 @@ export const ERROR_CODES: Readonly<Record<ErrorCode, ErrorCode>>;
107
109
  /**
108
110
  * Stable error-code string union. Append-only.
109
111
  */
110
- export type ErrorCode = ("ERR_UNKNOWN" | "ERR_UNKNOWN_COMMAND" | "ERR_UNKNOWN_SUBCOMMAND" | "ERR_INVALID_OPTION" | "ERR_INVALID_ARGUMENT" | "ERR_MISSING_ARGUMENT" | "ERR_INVALID_LANG" | "ERR_INVALID_DETAIL" | "ERR_NODE_VERSION" | "ERR_CORE_NOT_FOUND" | "ERR_CORE_INCOMPATIBLE" | "ERR_UNKNOWN_COMPONENT" | "ERR_UNKNOWN_HOOK" | "ERR_UNKNOWN_TOPIC" | "ERR_UNKNOWN_SECTION" | "ERR_UNKNOWN_CATEGORY" | "ERR_UNKNOWN_TEMPLATE" | "ERR_AMBIGUOUS_TEMPLATE" | "ERR_AMBIGUOUS_COMPONENT" | "ERR_AMBIGUOUS_THEME" | "ERR_UNKNOWN_THEME" | "ERR_INTEGRATION_ROOT_CONFLICT" | "ERR_INTEGRATION_EXPORT_CONFLICT" | "ERR_UNKNOWN_PACKAGE" | "ERR_UNKNOWN_AGENT" | "ERR_UNKNOWN_FEATURE" | "ERR_UNKNOWN_CODEMOD" | "ERR_CODEMOD_FAILED" | "ERR_NOT_FOUND" | "ERR_NO_DOC" | "ERR_NO_SHOWCASE" | "ERR_NO_SOURCE" | "ERR_INVALID_DOC" | "ERR_FILE_NOT_FOUND" | "ERR_FILE_EXISTS" | "ERR_PATH_TRAVERSAL" | "ERR_WRITE_FAILED" | "ERR_THEME_INVALID" | "ERR_THEME_LOAD" | "ERR_PALETTE_GENERATION" | "ERR_VERSION_DETECT" | "ERR_INVALID_VERSION" | "ERR_DEP_MISSING" | "ERR_GH_CLI" | "ERR_UNKNOWN_POST" | "ERR_FETCH_FAILED" | "ERR_LAYOUT_PARSE" | "ERR_LAYOUT_INVALID" | "ERR_UNCLASSIFIED_EXIT" | "ERR_SIGNAL_TERMINATED");
112
+ export type ErrorCode = ("ERR_UNKNOWN" | "ERR_UNKNOWN_COMMAND" | "ERR_UNKNOWN_SUBCOMMAND" | "ERR_INVALID_OPTION" | "ERR_INVALID_ARGUMENT" | "ERR_MISSING_ARGUMENT" | "ERR_INVALID_LANG" | "ERR_INVALID_DETAIL" | "ERR_NODE_VERSION" | "ERR_CORE_NOT_FOUND" | "ERR_CORE_INCOMPATIBLE" | "ERR_UNKNOWN_COMPONENT" | "ERR_UNKNOWN_HOOK" | "ERR_UNKNOWN_TOPIC" | "ERR_UNKNOWN_SECTION" | "ERR_UNKNOWN_CATEGORY" | "ERR_UNKNOWN_TEMPLATE" | "ERR_AMBIGUOUS_TEMPLATE" | "ERR_AMBIGUOUS_COMPONENT" | "ERR_AMBIGUOUS_THEME" | "ERR_UNKNOWN_THEME" | "ERR_INTEGRATION_ROOT_CONFLICT" | "ERR_INTEGRATION_EXPORT_CONFLICT" | "ERR_UNKNOWN_PACKAGE" | "ERR_UNKNOWN_AGENT" | "ERR_UNKNOWN_FEATURE" | "ERR_UNKNOWN_CODEMOD" | "ERR_CODEMOD_FAILED" | "ERR_CODEMOD_PROTECTED" | "ERR_CODEMOD_PROTECTION_SOURCE" | "ERR_NOT_FOUND" | "ERR_NO_DOC" | "ERR_NO_SHOWCASE" | "ERR_NO_SOURCE" | "ERR_INVALID_DOC" | "ERR_FILE_NOT_FOUND" | "ERR_FILE_EXISTS" | "ERR_PATH_TRAVERSAL" | "ERR_WRITE_FAILED" | "ERR_THEME_INVALID" | "ERR_THEME_LOAD" | "ERR_PALETTE_GENERATION" | "ERR_VERSION_DETECT" | "ERR_INVALID_VERSION" | "ERR_DEP_MISSING" | "ERR_GH_CLI" | "ERR_UNKNOWN_POST" | "ERR_FETCH_FAILED" | "ERR_LAYOUT_PARSE" | "ERR_LAYOUT_INVALID" | "ERR_UNCLASSIFIED_EXIT" | "ERR_SIGNAL_TERMINATED");
@@ -34,6 +34,8 @@ export type ErrorCode =
34
34
  | 'ERR_UNKNOWN_FEATURE'
35
35
  | 'ERR_UNKNOWN_CODEMOD'
36
36
  | 'ERR_CODEMOD_FAILED'
37
+ | 'ERR_CODEMOD_PROTECTED'
38
+ | 'ERR_CODEMOD_PROTECTION_SOURCE'
37
39
  | 'ERR_NOT_FOUND'
38
40
  | 'ERR_NO_DOC'
39
41
  | 'ERR_NO_SHOWCASE'
@@ -12,7 +12,7 @@ export const doc = {
12
12
  type: 'enum',
13
13
  name: 'error-codes',
14
14
  displayName: 'Error Codes',
15
- namespace: 'cli',
15
+ namespace: 'cli/api',
16
16
  description:
17
17
  'Stable, append-only machine-readable error codes on the --json error envelope (envelope.code). Codes never change or get removed; branch on these, not on prose.',
18
18
  members: [
@@ -154,6 +154,16 @@ export const doc = {
154
154
  value: 'ERR_CODEMOD_FAILED',
155
155
  description: 'One or more codemods failed during an upgrade run.',
156
156
  },
157
+ {
158
+ value: 'ERR_CODEMOD_PROTECTED',
159
+ description:
160
+ 'A required codemod change remains blocked by a protected consumer file.',
161
+ },
162
+ {
163
+ value: 'ERR_CODEMOD_PROTECTION_SOURCE',
164
+ description:
165
+ 'A working-tree protection declaration could not be read or parsed.',
166
+ },
157
167
  {
158
168
  value: 'ERR_NOT_FOUND',
159
169
  description:
@@ -203,7 +213,7 @@ export const doc = {
203
213
  {
204
214
  value: 'ERR_THEME_INVALID',
205
215
  description:
206
- 'A theme definition or contributed theme catalog is invalid.',
216
+ 'A theme definition or contributed theme descriptor is invalid.',
207
217
  },
208
218
  {
209
219
  value: 'ERR_THEME_LOAD',