@astryxdesign/cli 0.7.2-canary.e2909cf → 0.7.2-canary.e4bdd70

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 (194) hide show
  1. package/README.md +15 -14
  2. package/api/build/help/help.mjs +1 -1
  3. package/api/component/_adapter.d.mts +41 -1
  4. package/api/component/_adapter.mjs +93 -7
  5. package/api/component/component.mjs +22 -5
  6. package/api/component/integration-export-name.test.mjs +644 -0
  7. package/api/docs/_adapter.d.mts +11 -0
  8. package/api/docs/_adapter.mjs +244 -1
  9. package/api/docs/integrationDocs.test.mjs +124 -0
  10. package/api/doctor/theme-checks.mjs +1 -1
  11. package/api/gap-report/gap-report.mjs +67 -4
  12. package/api/index.d.mts +2 -0
  13. package/api/index.mjs +2 -0
  14. package/api/init/run/run.mjs +2 -2
  15. package/api/integration/_adapter.d.mts +12 -0
  16. package/api/integration/_adapter.mjs +54 -0
  17. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  18. package/api/integration/add-contribution.mjs +8 -4
  19. package/api/integration/add-contribution.test.mjs +4 -2
  20. package/api/integration/authoring-checks.mjs +11 -2
  21. package/api/integration/authoring-checks.test.mjs +39 -0
  22. package/api/integration/authoring-checks.type.d.mts +9 -0
  23. package/api/integration/authoring-checks.type.mjs +14 -1
  24. package/api/integration/check/check.d.mts +96 -0
  25. package/api/integration/check/check.mjs +357 -0
  26. package/api/integration/check/check.test.mjs +489 -0
  27. package/api/integration/integration-check.type.d.mts +118 -0
  28. package/api/integration/integration-check.type.mjs +84 -0
  29. package/api/integration/integrationCheck.doc.d.mts +10 -0
  30. package/api/integration/integrationCheck.doc.mjs +73 -0
  31. package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
  32. package/api/integration/pack-check.mjs +51 -97
  33. package/api/integration/pack-check.test.mjs +359 -0
  34. package/api/integration/validate-integration.mjs +8 -0
  35. package/api/integration/validate-integration.test.mjs +11 -0
  36. package/api/integration/validate-integration.type.d.mts +8 -0
  37. package/api/integration/validate-integration.type.mjs +12 -1
  38. package/api/integration/validateIntegration.doc.mjs +17 -3
  39. package/api/search/search.d.mts +22 -6
  40. package/api/search/search.domain-priority.test.mjs +93 -18
  41. package/api/search/search.integration-check.test.mjs +24 -0
  42. package/api/search/search.mjs +133 -22
  43. package/api/swizzle/copy/copy.mjs +28 -14
  44. package/api/theme/build/build.family.test.mjs +54 -6
  45. package/api/theme/build/build.mjs +114 -17
  46. package/api/theme/themeBuild.doc.mjs +4 -4
  47. package/assets/docs/README.md +10 -0
  48. package/assets/docs/author-a-theme.doc.mjs +1 -1
  49. package/assets/docs/getting-started.doc.mjs +3 -3
  50. package/assets/docs/styling-overview.doc.mjs +1 -1
  51. package/assets/docs/theme.doc.dense.mjs +1 -1
  52. package/assets/docs/theme.doc.mjs +1 -1
  53. package/assets/docs/theme.doc.zh.mjs +1 -1
  54. package/assets/docs/tree/add-a-component.doc.mjs +11 -1
  55. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  56. package/assets/docs/tree/add-a-topic.doc.mjs +10 -7
  57. package/assets/docs/tree/agent-guidance.doc.mjs +2 -2
  58. package/assets/docs/tree/block-template.doc.mjs +8 -20
  59. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  60. package/assets/docs/tree/check-the-props.doc.mjs +196 -0
  61. package/assets/docs/tree/check-your-docs.doc.mjs +51 -5
  62. package/assets/docs/tree/checks.doc.mjs +24 -16
  63. package/assets/docs/tree/codemods.doc.mjs +4 -4
  64. package/assets/docs/tree/component-family.doc.mjs +8 -6
  65. package/assets/docs/tree/component-imports.doc.mjs +10 -2
  66. package/assets/docs/tree/components.doc.mjs +1 -1
  67. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +7 -18
  68. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  69. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  70. package/assets/docs/tree/docs.doc.mjs +3 -3
  71. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  72. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  73. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  74. package/assets/docs/tree/extend-or-replace.doc.mjs +6 -6
  75. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  76. package/assets/docs/tree/generate-a-palette.doc.mjs +5 -3
  77. package/assets/docs/tree/grade-template-with-agent.doc.mjs +1 -1
  78. package/assets/docs/tree/integrations.doc.mjs +8 -0
  79. package/assets/docs/tree/links.doc.mjs +6 -5
  80. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  81. package/assets/docs/tree/page-template.doc.mjs +4 -8
  82. package/assets/docs/tree/publishing.doc.mjs +1 -1
  83. package/assets/docs/tree/quick-start.doc.mjs +37 -21
  84. package/assets/docs/tree/replace-a-core-component.doc.mjs +4 -6
  85. package/assets/docs/tree/replace-a-core-template.doc.mjs +6 -8
  86. package/assets/docs/tree/sections-and-placement.doc.mjs +4 -6
  87. package/assets/docs/tree/see-it-in-an-app.doc.mjs +4 -2
  88. package/assets/docs/tree/short-and-findable.doc.mjs +6 -5
  89. package/assets/docs/tree/single-component.doc.mjs +15 -6
  90. package/assets/docs/tree/start-a-template.doc.mjs +3 -3
  91. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  92. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  93. package/assets/docs/tree/template-doc-overview.doc.mjs +4 -15
  94. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  95. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  96. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  97. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  98. package/assets/docs/tree/templates.doc.mjs +1 -1
  99. package/assets/docs/tree/test-in-an-app.doc.mjs +3 -3
  100. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  101. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  102. package/assets/docs/tree/troubleshooting.doc.mjs +13 -5
  103. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  104. package/assets/docs/tree/upgrading.doc.mjs +3 -3
  105. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +3 -1
  106. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  107. package/assets/docs/tree/versioning.doc.mjs +7 -6
  108. package/assets/docs/tree/write-the-template-file.doc.mjs +4 -3
  109. package/assets/docs/use-a-theme.doc.mjs +2 -2
  110. package/assets/theme.template.ts +1 -1
  111. package/authoring/codemod/codemod.doc.mjs +7 -3
  112. package/authoring/config/config.doc.mjs +9 -6
  113. package/authoring/config/type.ts +4 -2
  114. package/authoring/discover/discover.doc.mjs +1 -1
  115. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  116. package/authoring/doctypes/command/command.doc.mjs +14 -12
  117. package/authoring/doctypes/component/component.doc.mjs +29 -26
  118. package/authoring/doctypes/component/type.ts +13 -8
  119. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  120. package/authoring/doctypes/function/function.doc.mjs +9 -8
  121. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  122. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  123. package/authoring/doctypes/reference/reference.doc.mjs +11 -10
  124. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  125. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  126. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  127. package/authoring/doctypes/template/parse.d.mts +6 -2
  128. package/authoring/doctypes/template/parse.mjs +3 -1
  129. package/authoring/doctypes/template/template.doc.mjs +3 -3
  130. package/authoring/doctypes/template/type.ts +22 -0
  131. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  132. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  133. package/authoring/identity/identity.doc.mjs +3 -3
  134. package/authoring/index.d.ts +1 -0
  135. package/authoring/integration/integration.doc.mjs +12 -4
  136. package/authoring/integration/parse.test.mjs +10 -0
  137. package/authoring/integration/schema.d.mts +2 -0
  138. package/authoring/integration/schema.mjs +1 -0
  139. package/authoring/integration/type.ts +4 -0
  140. package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
  141. package/clients/cli/commands/build-theme.mjs +1 -1
  142. package/clients/cli/commands/build-theme.path-safety.test.mjs +155 -21
  143. package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
  144. package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
  145. package/clients/cli/commands/doctor-integration.test.mjs +837 -0
  146. package/clients/cli/commands/doctor.mjs +190 -0
  147. package/clients/cli/commands/integration-check.doc.mjs +85 -0
  148. package/clients/cli/commands/integration-check.test.mjs +267 -0
  149. package/clients/cli/commands/integration.doc.mjs +6 -2
  150. package/clients/cli/commands/integration.mjs +107 -1
  151. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  152. package/clients/cli/commands/theme-build.doc.mjs +2 -2
  153. package/clients/cli/index.mjs +1 -0
  154. package/foundation/config/project.mjs +82 -0
  155. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  156. package/foundation/discovery/component-discovery.d.mts +178 -10
  157. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  158. package/foundation/discovery/component-discovery.mjs +659 -62
  159. package/foundation/discovery/component-replacement.mjs +18 -0
  160. package/foundation/discovery/component-replacement.test.mjs +40 -0
  161. package/foundation/discovery/doc-types.d.mts +157 -0
  162. package/foundation/discovery/doc-types.mjs +1224 -0
  163. package/foundation/discovery/doc-types.test.mjs +652 -0
  164. package/foundation/discovery/docs-discovery.d.mts +81 -0
  165. package/foundation/discovery/docs-discovery.mjs +57 -0
  166. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  167. package/foundation/discovery/integration-doc-files.mjs +231 -0
  168. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  169. package/foundation/discovery/module-exports.d.mts +27 -0
  170. package/foundation/discovery/module-exports.mjs +356 -31
  171. package/foundation/discovery/module-exports.test.mjs +172 -0
  172. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  173. package/foundation/discovery/package-tsconfig.mjs +228 -0
  174. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  175. package/foundation/discovery/project-typescript.d.mts +23 -0
  176. package/foundation/discovery/project-typescript.mjs +61 -0
  177. package/foundation/discovery/prop-drift.d.mts +278 -0
  178. package/foundation/discovery/prop-drift.mjs +2263 -0
  179. package/foundation/discovery/prop-drift.test.mjs +2219 -0
  180. package/foundation/discovery/type-match.d.mts +53 -0
  181. package/foundation/discovery/type-match.mjs +660 -0
  182. package/foundation/discovery/type-match.test.mjs +169 -0
  183. package/foundation/discovery/type-text.d.mts +68 -0
  184. package/foundation/discovery/type-text.mjs +658 -0
  185. package/foundation/discovery/type-text.test.mjs +196 -0
  186. package/foundation/discovery/unresolved-references.d.mts +62 -0
  187. package/foundation/discovery/unresolved-references.mjs +790 -0
  188. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  189. package/foundation/doc-compiler/doc-loads.test.mjs +6 -0
  190. package/foundation/doc-compiler/links.d.mts +9 -5
  191. package/foundation/doc-compiler/links.mjs +4 -2
  192. package/foundation/integrations/contribution-inventory.mjs +6 -1
  193. package/foundation/response/response-types.doc.mjs +7 -2
  194. package/package.json +9 -9
@@ -98,6 +98,72 @@ describe('integrationAddComponent name collisions', () => {
98
98
  expect(snapshot()).toEqual(before);
99
99
  });
100
100
 
101
+ it('adds a name a family doc lists but the module it imports does not export', async () => {
102
+ fs.writeFileSync(
103
+ path.join(tmpDir, 'package.json'),
104
+ `${JSON.stringify({name: '@acme/widgets', version: '1.0.0', files: ['dist'], exports: {'./tabs': './components/tabs/index.ts'}}, null, 2)}\n`,
105
+ );
106
+ const tabs = path.join(tmpDir, 'components', 'tabs');
107
+ fs.mkdirSync(tabs, {recursive: true});
108
+ fs.writeFileSync(
109
+ path.join(tabs, 'AcmeTabs.doc.mjs'),
110
+ "export default {type: 'component', name: 'AcmeTabs', import: '@acme/widgets/tabs', description: 'x', components: [{name: 'AcmeTabExtra', description: 'x'}]};\n",
111
+ );
112
+ fs.writeFileSync(
113
+ path.join(tabs, 'AcmeTabs.tsx'),
114
+ 'export function AcmeTabs() { return null; }\nexport function AcmeTabExtra() { return null; }\n',
115
+ );
116
+ fs.writeFileSync(
117
+ path.join(tabs, 'index.ts'),
118
+ "export {AcmeTabs} from './AcmeTabs';\n",
119
+ );
120
+
121
+ const result = await integrationAdd('component', 'AcmeTabExtra', {
122
+ cwd: tmpDir,
123
+ dryRun: true,
124
+ });
125
+ expect(result.data.files).toContain('components/AcmeTabExtra.doc.mjs');
126
+ });
127
+
128
+ it('refuses a member a family exports through built output', async () => {
129
+ fs.writeFileSync(
130
+ path.join(tmpDir, 'package.json'),
131
+ `${JSON.stringify({name: '@acme/widgets', version: '1.0.0', files: ['dist'], exports: {'./tabs': './dist/tabs/index.js'}}, null, 2)}\n`,
132
+ );
133
+ const tabs = path.join(tmpDir, 'components', 'tabs');
134
+ fs.mkdirSync(tabs, {recursive: true});
135
+ fs.writeFileSync(
136
+ path.join(tabs, 'AcmeTabs.doc.mjs'),
137
+ "export default {type: 'component', name: 'AcmeTabs', import: '@acme/widgets/tabs', description: 'x', components: [{name: 'AcmeTab', description: 'x'}]};\n",
138
+ );
139
+ fs.writeFileSync(
140
+ path.join(tabs, 'AcmeTabs.tsx'),
141
+ 'export function AcmeTabs() { return null; }\n',
142
+ );
143
+ fs.writeFileSync(
144
+ path.join(tabs, 'AcmeTab.tsx'),
145
+ 'export function AcmeTab() { return null; }\n',
146
+ );
147
+ const dist = path.join(tmpDir, 'dist', 'tabs');
148
+ fs.mkdirSync(dist, {recursive: true});
149
+ fs.writeFileSync(
150
+ path.join(dist, 'index.js'),
151
+ "export {AcmeTabs} from './tabs.js';\nexport {AcmeTab} from './tab.js';\n",
152
+ );
153
+ fs.writeFileSync(
154
+ path.join(dist, 'tabs.js'),
155
+ 'export function AcmeTabs() { return null; }\n',
156
+ );
157
+ fs.writeFileSync(
158
+ path.join(dist, 'tab.js'),
159
+ 'export function AcmeTab() { return null; }\n',
160
+ );
161
+
162
+ await expect(
163
+ integrationAdd('component', 'AcmeTab', {cwd: tmpDir, dryRun: true}),
164
+ ).rejects.toThrow('Refusing to add component "AcmeTab"');
165
+ });
166
+
101
167
  it('still adds a component whose name is free', async () => {
102
168
  writeAuthoredComponent('components/AcmeWidget/AcmeWidget.doc.mjs');
103
169
 
@@ -264,9 +264,13 @@ async function addComponent(name, options) {
264
264
  ERROR_CODES.ERR_FILE_EXISTS,
265
265
  );
266
266
  }
267
- // Discovery keys components by doc stem anywhere under the root, so a second
268
- // doc with this name would shadow one of the two.
269
- const existingDoc = findIntegrationComponentDoc({components: root}, name);
267
+ // A second doc under a name discovery already records, by a doc's file name
268
+ // anywhere under the root or by a name a doc documents, would shadow one of
269
+ // the two.
270
+ const existingDoc = findIntegrationComponentDoc(
271
+ {name: owner, components: root, __packageDir: packageDir},
272
+ name,
273
+ );
270
274
  if (existingDoc != null) {
271
275
  throw new AstryxError(
272
276
  `Refusing to add component "${name}": ${projectPath(path.relative(packageDir, existingDoc))} already defines it.`,
@@ -691,7 +695,7 @@ async function addTemplate(name, options) {
691
695
  const sourcePath = projectPath(path.relative(packageDir, sourceFile));
692
696
  const extensionlessPath = sourcePath.replace(/\.tsx?$/u, '');
693
697
  const blockFields = templateType === 'block' ? '\n aspectRatio: 1,' : '';
694
- const specContents = `/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */\nexport default {\n type: '${templateType}',\n name: '${name}',\n displayName: '${kebabToTitle(name)}',\n description: '${kebabToTitle(name)} template.',${blockFields}\n};\n`;
698
+ const specContents = `/** @type {import('@astryxdesign/cli/authoring').IntegrationTemplateDoc} */\nexport default {\n type: '${templateType}',\n name: '${name}',\n displayName: '${kebabToTitle(name)}',\n description: '${kebabToTitle(name)} template.',${blockFields}\n};\n`;
695
699
  const sourceContents = `export default function ${pascalName}() {\n return <div>${kebabToTitle(name)}</div>;\n}\n`;
696
700
 
697
701
  /** @type {import('./add-helpers.mjs').WritePlan[]} */
@@ -536,7 +536,9 @@ describe('integrationAdd template', () => {
536
536
  path.join(tmpDir, 'templates/my-widget.doc.mjs'),
537
537
  'utf-8',
538
538
  );
539
- expect(spec).toContain("@astryxdesign/cli/authoring').TemplateDoc");
539
+ expect(spec).toContain(
540
+ "@astryxdesign/cli/authoring').IntegrationTemplateDoc",
541
+ );
540
542
  expect(spec).toContain("type: 'page'");
541
543
  expect(spec).toContain("name: 'my-widget'");
542
544
 
@@ -830,7 +832,7 @@ describe('typed descriptor conformance', () => {
830
832
  for (const [relativePath, type] of [
831
833
  ['components/MyWidget.doc.mjs', 'ComponentDoc'],
832
834
  ['docs/my-guide.doc.mjs', 'ReferenceDoc'],
833
- ['templates/my-page.doc.mjs', 'TemplateDoc'],
835
+ ['templates/my-page.doc.mjs', 'IntegrationTemplateDoc'],
834
836
  ['themes/ocean/oceanTheme.doc.mjs', 'ThemeDoc'],
835
837
  ]) {
836
838
  const source = fs.readFileSync(path.join(tmpDir, relativePath), 'utf-8');
@@ -3,6 +3,10 @@
3
3
  /**
4
4
  * @file Integration authoring diagnostics against the built-in Core catalog.
5
5
  *
6
+ * The components check also compares each component doc's props with the
7
+ * component's TypeScript props (foundation/discovery/prop-drift.mjs); those
8
+ * findings are warnings under `propDrift`.
9
+ *
6
10
  * Templates may intentionally replace Core identities; undeclared same-id
7
11
  * overlaps stay fail-closed and require package selection. The CLI package
8
12
  * version gates replacement-specific fields so 0.6.x keeps its released shape.
@@ -25,6 +29,7 @@ import {
25
29
  discoverOwnedComponents,
26
30
  } from '../../foundation/discovery/component-discovery.mjs';
27
31
  import {loadComponentReplacements} from '../component/_adapter.mjs';
32
+ import {integrationPropDrift} from './_adapter.mjs';
28
33
  import {
29
34
  discoverBuiltinTopics,
30
35
  discoverIntegrationDocs,
@@ -242,6 +247,10 @@ export async function integrationComponentConflicts(pkg, options = {}) {
242
247
  };
243
248
  }
244
249
 
250
+ // Each documented prop against the component's TypeScript props, read with
251
+ // the project's own compiler; without one the report says it was skipped.
252
+ const propDrift = await integrationPropDrift(resolved.integration, cwd);
253
+
245
254
  const coreDir = findCoreDir(cwd);
246
255
  if (!coreDir) {
247
256
  issues.push({
@@ -252,7 +261,7 @@ export async function integrationComponentConflicts(pkg, options = {}) {
252
261
  });
253
262
  return {
254
263
  type: 'integration.component-conflicts',
255
- data: {validated, name, version, conflicts: [], issues},
264
+ data: {validated, name, version, conflicts: [], issues, propDrift},
256
265
  };
257
266
  }
258
267
 
@@ -296,7 +305,7 @@ export async function integrationComponentConflicts(pkg, options = {}) {
296
305
 
297
306
  return {
298
307
  type: 'integration.component-conflicts',
299
- data: {validated, name, version, conflicts, issues},
308
+ data: {validated, name, version, conflicts, issues, propDrift},
300
309
  };
301
310
  }
302
311
 
@@ -417,6 +417,45 @@ describe('integrationComponentConflicts', () => {
417
417
  expect(result.data.conflicts).toEqual([]);
418
418
  expect(result.data.issues).toEqual([]);
419
419
  });
420
+
421
+ it("compares each component doc's props with the component's TypeScript props, as warnings", async () => {
422
+ writeComponentIntegration('AcmeBadge');
423
+ fs.writeFileSync(
424
+ path.join(tmpDir, 'components', 'AcmeBadge.doc.mjs'),
425
+ `export default {type: 'component', name: 'AcmeBadge', displayName: 'AcmeBadge', description: 'Fixture component.', props: [{name: 'label', type: 'string', description: 'Text.', required: true}, {name: 'size', type: "'sm' | 'md'", description: 'Size.'}]};\n`,
426
+ );
427
+ fs.writeFileSync(
428
+ path.join(tmpDir, 'components', 'AcmeBadge.tsx'),
429
+ 'export function AcmeBadge(props: {label: string; count?: number}) { return null; }\n',
430
+ );
431
+
432
+ const result = await integrationComponentConflicts(undefined, {
433
+ cwd: tmpDir,
434
+ });
435
+
436
+ expect(result.data.issues).toEqual([]);
437
+ expect(result.data.propDrift).toMatchObject({
438
+ status: 'checked',
439
+ components: 1,
440
+ unchecked: [],
441
+ });
442
+ expect(
443
+ result.data.propDrift?.findings.map(f => [f.prop, f.kind, f.severity]),
444
+ ).toEqual([
445
+ ['count', 'not_in_doc', 'warning'],
446
+ ['size', 'not_in_code', 'warning'],
447
+ ]);
448
+ });
449
+
450
+ it('leaves propDrift out when the package has no components root', async () => {
451
+ writeDocIntegration({name: 'acme-setup'});
452
+
453
+ const result = await integrationComponentConflicts(undefined, {
454
+ cwd: tmpDir,
455
+ });
456
+
457
+ expect(result.data).not.toHaveProperty('propDrift');
458
+ });
420
459
  });
421
460
 
422
461
  describe('integrationDocConflicts', () => {
@@ -47,6 +47,14 @@ export type IntegrationTemplateConflictResponse = {
47
47
  issues: import("../../foundation/integrations/issue").AstryxIntegrationIssue[];
48
48
  };
49
49
  };
50
+ /**
51
+ * Documented props compared with the component's TypeScript props. Present
52
+ * when the manifest loads and declares a components root. `status: 'skipped'`
53
+ * means nothing was compared (no TypeScript in the project) and `reason` says
54
+ * why. Every finding is a warning.
55
+ */
56
+ export type PropDriftReport = import("../../foundation/discovery/prop-drift.mjs").PropDriftReport;
57
+ export type PropDriftFinding = import("../../foundation/discovery/prop-drift.mjs").PropDriftFinding;
50
58
  export type IntegrationComponentConflictResponse = {
51
59
  type: "integration.component-conflicts";
52
60
  data: {
@@ -55,6 +63,7 @@ export type IntegrationComponentConflictResponse = {
55
63
  version: string | null;
56
64
  conflicts: IntegrationComponentConflict[];
57
65
  issues: import("../../foundation/integrations/issue").AstryxIntegrationIssue[];
66
+ propDrift?: PropDriftReport;
58
67
  };
59
68
  };
60
69
  export type IntegrationDocConflictResponse = {
@@ -58,10 +58,23 @@
58
58
  * @property {{validated: boolean, name: string | null, version: string | null, conflicts: IntegrationTemplateConflict[], issues: import('../../foundation/integrations/issue').AstryxIntegrationIssue[]}} data
59
59
  */
60
60
 
61
+ /**
62
+ * Documented props compared with the component's TypeScript props. Present
63
+ * when the manifest loads and declares a components root. `status: 'skipped'`
64
+ * means nothing was compared (no TypeScript in the project) and `reason` says
65
+ * why. Every finding is a warning.
66
+ *
67
+ * @typedef {import('../../foundation/discovery/prop-drift.mjs').PropDriftReport} PropDriftReport
68
+ */
69
+
70
+ /**
71
+ * @typedef {import('../../foundation/discovery/prop-drift.mjs').PropDriftFinding} PropDriftFinding
72
+ */
73
+
61
74
  /**
62
75
  * @typedef {object} IntegrationComponentConflictResponse
63
76
  * @property {'integration.component-conflicts'} type
64
- * @property {{validated: boolean, name: string | null, version: string | null, conflicts: IntegrationComponentConflict[], issues: import('../../foundation/integrations/issue').AstryxIntegrationIssue[]}} data
77
+ * @property {{validated: boolean, name: string | null, version: string | null, conflicts: IntegrationComponentConflict[], issues: import('../../foundation/integrations/issue').AstryxIntegrationIssue[], propDrift?: PropDriftReport}} data
65
78
  */
66
79
 
67
80
  /**
@@ -0,0 +1,96 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * The code a skipped comparison carries under `--strict`: its key in the
6
+ * check's result, in snake case, then `_skipped` (`propDrift` is
7
+ * `prop_drift_skipped`).
8
+ * @param {string} key
9
+ */
10
+ export function skippedCode(key: string): string;
11
+ /**
12
+ * Whether a value is a report a check carries beside its issues: a
13
+ * comparison with a `status` of `checked` or `skipped` and its `findings`.
14
+ * @param {unknown} value
15
+ * @returns {value is IntegrationCheckReport}
16
+ */
17
+ export function isCheckReport(value: unknown): value is IntegrationCheckReport;
18
+ /**
19
+ * Every warning in one check's result, and every piece of work it skipped,
20
+ * as issues: what `--strict` reports as errors. The counts come from the
21
+ * same list, so the default run and the strict run read the same data.
22
+ * @param {IntegrationCheckName} check
23
+ * @param {IntegrationCheckResponse} response
24
+ * @param {{noPackage?: boolean}} [context] `noPackage`: validate found no
25
+ * package.json, which only it reports; then no other check names a
26
+ * missing manifest as skipped work
27
+ * @returns {{warnings: Array<Omit<IntegrationCheckFailure, 'check' | 'severity'>>, skipped: Array<Omit<IntegrationCheckFailure, 'check' | 'severity'>>}}
28
+ */
29
+ export function strictIssues(check: IntegrationCheckName, response: IntegrationCheckResponse, context?: {
30
+ noPackage?: boolean;
31
+ }): {
32
+ warnings: Array<Omit<IntegrationCheckFailure, "check" | "severity">>;
33
+ skipped: Array<Omit<IntegrationCheckFailure, "check" | "severity">>;
34
+ };
35
+ /**
36
+ * Count one check's result the way its own subcommand exits. Errors are what
37
+ * make it exit 1: error-severity issues, and for `docs` error findings.
38
+ * Warnings are warning issues, conflicts and findings, and the findings of
39
+ * any report the check carries. Skipped names work that did not run: no
40
+ * integration manifest, or a report that was skipped.
41
+ * @param {IntegrationCheckResponse} response
42
+ * @param {{noPackage?: boolean}} [context] as {@link strictIssues} takes it
43
+ * @returns {Omit<IntegrationCheckResult, 'check' | 'response'>}
44
+ */
45
+ export function summarizeCheck(response: IntegrationCheckResponse, context?: {
46
+ noPackage?: boolean;
47
+ }): Omit<IntegrationCheckResult, "check" | "response">;
48
+ /**
49
+ * Run every `doctor integration` check against the integration package in
50
+ * `cwd`, as its own build does, and return the `integration.check` envelope.
51
+ *
52
+ * @param {IntegrationCheckOptions} [options]
53
+ * @returns {Promise<IntegrationCheckCommandResponse>}
54
+ */
55
+ export function integrationCheck(options?: IntegrationCheckOptions): Promise<IntegrationCheckCommandResponse>;
56
+ /**
57
+ * Count the four checks' results into one summary. Each check's own row
58
+ * counts as its subcommand exits; the totals and `failures` count validate's
59
+ * issues once, though the other checks repeat them, and every other finding
60
+ * on its own, so two findings with one message stay two.
61
+ * @param {IntegrationCheckResponse[]} responses validate first
62
+ * @param {boolean} strict
63
+ */
64
+ export function combineChecks(responses: IntegrationCheckResponse[], strict: boolean): {
65
+ checks: import("../integration-check.type.mjs").IntegrationCheckResult[];
66
+ errors: number;
67
+ warnings: number;
68
+ skipped: string[];
69
+ unchecked: number;
70
+ failures: import("../integration-check.type.mjs").IntegrationCheckFailure[];
71
+ failed: boolean;
72
+ };
73
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckName} IntegrationCheckName */
74
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckReport} IntegrationCheckReport */
75
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckResponse} IntegrationCheckResponse */
76
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckResult} IntegrationCheckResult */
77
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckOptions} IntegrationCheckOptions */
78
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckCommandResponse} IntegrationCheckCommandResponse */
79
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckFailure} IntegrationCheckFailure */
80
+ /** The checks `integration check` runs, in the order it runs them. */
81
+ export const INTEGRATION_CHECKS: readonly ["validate", "components", "templates", "docs"];
82
+ /** The skipped-work line for a package with no integration manifest. */
83
+ export const NO_MANIFEST_SKIP: "No astryx.integration.* found next to package.json, so nothing was checked.";
84
+ export namespace CONFLICT_CODES {
85
+ let components: "component_name_conflict";
86
+ let templates: "template_id_conflict";
87
+ }
88
+ /** The code a check with no integration manifest carries under `--strict`. */
89
+ export const NO_MANIFEST_CODE: "missing_manifest";
90
+ export type IntegrationCheckName = import("../integration-check.type.mjs").IntegrationCheckName;
91
+ export type IntegrationCheckReport = import("../integration-check.type.mjs").IntegrationCheckReport;
92
+ export type IntegrationCheckResponse = import("../integration-check.type.mjs").IntegrationCheckResponse;
93
+ export type IntegrationCheckResult = import("../integration-check.type.mjs").IntegrationCheckResult;
94
+ export type IntegrationCheckOptions = import("../integration-check.type.mjs").IntegrationCheckOptions;
95
+ export type IntegrationCheckCommandResponse = import("../integration-check.type.mjs").IntegrationCheckCommandResponse;
96
+ export type IntegrationCheckFailure = import("../integration-check.type.mjs").IntegrationCheckFailure;