@astryxdesign/cli 0.6.4 → 0.6.5-canary.031021b

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 (288) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +97 -90
  3. package/api/build/build.doc.mjs +6 -1
  4. package/api/build/build.test.mjs +22 -0
  5. package/api/build/kit/kit.mjs +44 -5
  6. package/api/component/_adapter.d.mts +25 -0
  7. package/api/component/_adapter.mjs +59 -5
  8. package/api/component/component.d.mts +6 -3
  9. package/api/component/component.doc.mjs +37 -17
  10. package/api/component/component.mjs +249 -9
  11. package/api/component/component.type.d.mts +25 -0
  12. package/api/component/component.type.mjs +44 -0
  13. package/api/discover/_adapter.d.mts +114 -6
  14. package/api/discover/_adapter.mjs +372 -17
  15. package/api/discover/_adapter.test.mjs +215 -0
  16. package/api/discover/_catalog-view.d.mts +115 -0
  17. package/api/discover/_catalog-view.mjs +203 -0
  18. package/api/discover/_catalog-view.test.mjs +128 -0
  19. package/api/discover/detail/detail.d.mts +18 -6
  20. package/api/discover/detail/detail.mjs +67 -13
  21. package/api/discover/detail/detail.test.mjs +85 -0
  22. package/api/discover/detail/item/item.d.mts +26 -0
  23. package/api/discover/detail/item/item.mjs +78 -0
  24. package/api/discover/detail/item/item.test.mjs +73 -0
  25. package/api/discover/discover.d.mts +3 -9
  26. package/api/discover/discover.doc.mjs +61 -18
  27. package/api/discover/discover.mjs +220 -36
  28. package/api/discover/discover.test.mjs +11 -2
  29. package/api/discover/discover.type.d.mts +147 -8
  30. package/api/discover/discover.type.mjs +102 -12
  31. package/api/discover/list/list.d.mts +20 -6
  32. package/api/discover/list/list.mjs +45 -12
  33. package/api/discover/list/list.test.mjs +46 -0
  34. package/api/discover/search/search.d.mts +18 -16
  35. package/api/discover/search/search.mjs +102 -56
  36. package/api/discover/search/search.test.mjs +144 -10
  37. package/api/docs/_adapter.d.mts +8 -3
  38. package/api/docs/_adapter.mjs +14 -6
  39. package/api/docs/docOverlays.test.mjs +27 -1
  40. package/api/docs/docs.doc.mjs +2 -2
  41. package/api/doctor/doctor.d.mts +8 -3
  42. package/api/doctor/doctor.doc.mjs +17 -8
  43. package/api/doctor/doctor.mjs +90 -9
  44. package/api/doctor/doctor.test.mjs +122 -10
  45. package/api/doctor/doctor.type.d.mts +1 -1
  46. package/api/doctor/doctor.type.mjs +1 -1
  47. package/api/gap-report/gap-report.doc.mjs +19 -10
  48. package/api/hook/hook.doc.mjs +6 -3
  49. package/api/index.d.mts +1 -0
  50. package/api/index.mjs +5 -3
  51. package/api/init/init.doc.mjs +17 -12
  52. package/api/integration/add-helpers.d.mts +5 -2
  53. package/api/integration/add-helpers.mjs +36 -9
  54. package/api/integration/add-theme.mjs +22 -1
  55. package/api/integration/add-theme.test.mjs +34 -0
  56. package/api/integration/authoring-checks.mjs +2 -2
  57. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  58. package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
  59. package/api/integration/pack-check.mjs +82 -9
  60. package/api/integration/pack-check.test.mjs +90 -0
  61. package/api/integration/pack-check.type.mjs +1 -1
  62. package/api/json/assertResponse.doc.mjs +1 -1
  63. package/api/json/isError.doc.mjs +1 -1
  64. package/api/search/search.d.mts +27 -1
  65. package/api/search/search.doc.mjs +2 -2
  66. package/api/search/search.mjs +228 -16
  67. package/api/swizzle/swizzle.doc.mjs +7 -5
  68. package/api/template/copy/copy.mjs +1 -1
  69. package/api/template/copy/copy.test.mjs +9 -0
  70. package/api/template/template.doc.mjs +2 -1
  71. package/api/theme/add/add.mjs +17 -25
  72. package/api/theme/add/add.rollback.test.mjs +158 -0
  73. package/api/theme/add/add.staging.test.mjs +40 -23
  74. package/api/theme/build/build.family.test.mjs +7 -12
  75. package/api/theme/build/build.mjs +8 -18
  76. package/api/theme/build/build.rollback.test.mjs +148 -0
  77. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  78. package/api/theme/listThemes.doc.mjs +1 -1
  79. package/api/theme/themeAdd.doc.mjs +9 -10
  80. package/api/theme/themeBuild.doc.mjs +13 -13
  81. package/api/theme/themeList.doc.mjs +1 -1
  82. package/api/theme/themeListAvailable.doc.mjs +2 -1
  83. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  84. package/api/theme/themeTargets.doc.mjs +3 -2
  85. package/api/theme/themeTemplate.doc.mjs +2 -1
  86. package/api/upgrade/run/files-changed.test.mjs +111 -0
  87. package/api/upgrade/run/run.mjs +5 -3
  88. package/api/upgrade/upgrade.doc.mjs +24 -22
  89. package/api/upgrade/upgrade.type.mjs +2 -2
  90. package/assets/codemods/__tests__/runner.test.mjs +3 -1
  91. package/assets/codemods/file-count.test.mjs +163 -0
  92. package/assets/codemods/integration-runner.mjs +3 -3
  93. package/assets/codemods/runner.mjs +5 -4
  94. package/assets/docs/README.md +4 -2
  95. package/assets/docs/browser-support.doc.mjs +11 -11
  96. package/assets/docs/color.doc.mjs +8 -2
  97. package/assets/docs/elevation.doc.mjs +6 -4
  98. package/assets/docs/getting-started.doc.mjs +5 -16
  99. package/assets/docs/icons.doc.mjs +2 -21
  100. package/assets/docs/illustrations.doc.mjs +7 -15
  101. package/assets/docs/internationalization.doc.mjs +7 -5
  102. package/assets/docs/layout.doc.dense.mjs +130 -82
  103. package/assets/docs/layout.doc.mjs +133 -77
  104. package/assets/docs/migration.doc.mjs +19 -21
  105. package/assets/docs/motion.doc.mjs +16 -3
  106. package/assets/docs/principles.doc.dense.mjs +5 -5
  107. package/assets/docs/principles.doc.mjs +8 -0
  108. package/assets/docs/principles.doc.zh.mjs +6 -6
  109. package/assets/docs/shape.doc.mjs +8 -3
  110. package/assets/docs/spacing.doc.mjs +7 -2
  111. package/assets/docs/styling-libraries.doc.mjs +6 -2
  112. package/assets/docs/styling.doc.mjs +19 -23
  113. package/assets/docs/theme.doc.dense.mjs +58 -18
  114. package/assets/docs/theme.doc.mjs +57 -47
  115. package/assets/docs/theme.doc.zh.mjs +9 -8
  116. package/assets/docs/tokens.doc.dense.mjs +2 -2
  117. package/assets/docs/tokens.doc.mjs +389 -8
  118. package/assets/docs/tokens.doc.zh.mjs +2 -2
  119. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  120. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  121. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  122. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  123. package/assets/docs/tree/block-template.doc.mjs +130 -0
  124. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  125. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  126. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  127. package/assets/docs/tree/checks.doc.mjs +119 -0
  128. package/assets/docs/tree/codemods.doc.mjs +147 -0
  129. package/assets/docs/tree/component-family.doc.mjs +113 -0
  130. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  131. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  132. package/assets/docs/tree/components.doc.mjs +23 -0
  133. package/assets/docs/tree/configuration.doc.mjs +23 -0
  134. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  135. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  136. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  137. package/assets/docs/tree/docs.doc.mjs +21 -0
  138. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  139. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  140. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  141. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  142. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  143. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  144. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  145. package/assets/docs/tree/help.doc.mjs +16 -0
  146. package/assets/docs/tree/integrations.doc.mjs +25 -451
  147. package/assets/docs/tree/links.doc.mjs +98 -0
  148. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  149. package/assets/docs/tree/page-template.doc.mjs +71 -0
  150. package/assets/docs/tree/publishing.doc.mjs +111 -0
  151. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  152. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  153. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  154. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  155. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  156. package/assets/docs/tree/ship.doc.mjs +16 -0
  157. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  158. package/assets/docs/tree/single-component.doc.mjs +165 -0
  159. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  160. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  161. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  162. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  163. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  164. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  165. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  166. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  167. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  168. package/assets/docs/tree/templates.doc.mjs +34 -0
  169. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  170. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  171. package/assets/docs/tree/themes.doc.mjs +39 -0
  172. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  173. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  174. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  175. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  176. package/assets/docs/tree/versioning.doc.mjs +161 -0
  177. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  178. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  179. package/assets/docs/typography.doc.mjs +24 -4
  180. package/assets/docs/working-with-ai.doc.mjs +30 -22
  181. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  182. package/authoring/config/config.doc.mjs +9 -1
  183. package/authoring/config/parse.d.mts +2 -0
  184. package/authoring/config/parse.mjs +19 -0
  185. package/authoring/config/parse.test.mjs +8 -0
  186. package/authoring/config/type.ts +11 -0
  187. package/authoring/discover/discover.doc.d.mts +13 -0
  188. package/authoring/discover/discover.doc.mjs +138 -0
  189. package/authoring/discover/parse.d.mts +24 -0
  190. package/authoring/discover/parse.mjs +128 -0
  191. package/authoring/discover/parse.test.mjs +124 -0
  192. package/authoring/discover/type.ts +87 -0
  193. package/authoring/doctypes/_schema.d.mts +3 -2
  194. package/authoring/doctypes/_schema.mjs +6 -0
  195. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  196. package/authoring/doctypes/base/type.ts +4 -2
  197. package/authoring/doctypes/component/component.doc.mjs +6 -0
  198. package/authoring/doctypes/component/type.ts +8 -0
  199. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  200. package/authoring/doctypes/reference/type.ts +5 -0
  201. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  202. package/authoring/doctypes/template/template.doc.mjs +1 -1
  203. package/authoring/doctypes/template/type.ts +2 -2
  204. package/authoring/index.d.mts +1 -0
  205. package/authoring/index.d.ts +10 -0
  206. package/authoring/index.mjs +1 -0
  207. package/authoring/integration/integration.doc.mjs +12 -10
  208. package/clients/cli/commands/component/index.mjs +152 -55
  209. package/clients/cli/commands/component-batch.test.mjs +341 -0
  210. package/clients/cli/commands/component-ownership.test.mjs +89 -0
  211. package/clients/cli/commands/component.doc.mjs +27 -9
  212. package/clients/cli/commands/discover.doc.mjs +53 -9
  213. package/clients/cli/commands/discover.mjs +393 -118
  214. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  215. package/clients/cli/commands/docs.doc.mjs +1 -1
  216. package/clients/cli/commands/docs.mjs +60 -17
  217. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  218. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  219. package/clients/cli/commands/doctor.doc.mjs +3 -1
  220. package/clients/cli/commands/doctor.mjs +49 -5
  221. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  222. package/clients/cli/commands/init.doc.mjs +9 -6
  223. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  224. package/clients/cli/commands/integration-authoring.test.mjs +61 -10
  225. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  226. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  227. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  228. package/clients/cli/commands/integration.doc.mjs +4 -4
  229. package/clients/cli/commands/integration.mjs +74 -43
  230. package/clients/cli/commands/manifest.doc.mjs +1 -1
  231. package/clients/cli/commands/search.doc.mjs +10 -3
  232. package/clients/cli/commands/search.mjs +21 -2
  233. package/clients/cli/commands/search.test.mjs +21 -4
  234. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  235. package/clients/cli/commands/template.doc.mjs +1 -1
  236. package/clients/cli/commands/text-json-parity.test.mjs +7 -1
  237. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  238. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  239. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  240. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  241. package/clients/cli/commands/theme.doc.mjs +2 -1
  242. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  243. package/clients/cli/index.mjs +28 -6
  244. package/clients/cli/lib/define-command.mjs +28 -4
  245. package/clients/cli/lib/define-command.test.mjs +54 -0
  246. package/clients/cli/lib/exit-codes.test.mjs +17 -1
  247. package/clients/cli/lib/json-shim.mjs +24 -14
  248. package/clients/cli/lib/manifest.mjs +18 -5
  249. package/clients/cli/lib/manifest.test.mjs +5 -2
  250. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  251. package/foundation/agent-docs/agent-docs.mjs +1 -1
  252. package/foundation/discovery/authoring-self-docs.mjs +1 -0
  253. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  254. package/foundation/discovery/cli-self-docs.mjs +16 -2
  255. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  256. package/foundation/discovery/docs-discovery.mjs +5 -1
  257. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  258. package/foundation/discovery/docs-section-key.d.mts +1 -1
  259. package/foundation/discovery/docs-section-key.mjs +1 -1
  260. package/foundation/doc-compiler/doc-loads.test.mjs +3 -2
  261. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  262. package/foundation/doc-compiler/tree.d.mts +4 -0
  263. package/foundation/doc-compiler/tree.mjs +6 -1
  264. package/foundation/integrations/cli-requirement.d.mts +26 -6
  265. package/foundation/integrations/cli-requirement.mjs +46 -11
  266. package/foundation/integrations/cli-requirement.test.mjs +7 -2
  267. package/foundation/integrations/contribution-inventory.mjs +1 -1
  268. package/foundation/integrations/integrations.d.mts +14 -1
  269. package/foundation/integrations/integrations.mjs +41 -1
  270. package/foundation/integrations/integrations.test.mjs +31 -0
  271. package/foundation/response/batch.type.d.mts +33 -0
  272. package/foundation/response/batch.type.mjs +34 -0
  273. package/foundation/response/error-codes.doc.mjs +6 -8
  274. package/foundation/response/error-codes.test.mjs +30 -5
  275. package/foundation/response/response-types.doc.d.mts +4 -3
  276. package/foundation/response/response-types.doc.mjs +40 -10
  277. package/foundation/response/response-types.doc.test.mjs +23 -0
  278. package/foundation/response/response.doc.mjs +11 -10
  279. package/package.json +9 -9
  280. package/api/docs/docs.test.mjs +0 -243
  281. package/api/docs/integration-tree.test.mjs +0 -555
  282. package/api/docs/integrationDocs.test.mjs +0 -314
  283. package/api/search/search.test.mjs +0 -512
  284. package/assets/docs/tree/integrations.test.mjs +0 -62
  285. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
  286. package/clients/cli/commands/docs.test.mjs +0 -294
  287. package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
  288. package/foundation/doc-compiler/tree.test.mjs +0 -598
@@ -63,7 +63,9 @@ describe('authoring self-docs', () => {
63
63
  describe('what the authoring docs say about the docs tree', () => {
64
64
  it('marks exactly the fields the docs tree does not read yet', () => {
65
65
  const notReadYet = graphFieldsDoc.fields
66
- .filter(field => /Not read yet/.test(field.description))
66
+ .filter(field =>
67
+ /^Reserved: .*Nothing reads it today/.test(field.description),
68
+ )
67
69
  .map(field => field.name);
68
70
  // The tree reads `placement` for every guide (spec:AST-046); the other
69
71
  // graph fields are still refused by every topic reader.
@@ -79,7 +81,9 @@ describe('what the authoring docs say about the docs tree', () => {
79
81
  expect(placement.description).toMatch(
80
82
  /Read for every guide, the CLI's and each integration's/,
81
83
  );
82
- expect(graphFieldsDoc.description).toMatch(/not built yet/);
84
+ expect(graphFieldsDoc.description).toMatch(
85
+ /Nothing reads `aliases` or `audience` today/,
86
+ );
83
87
  });
84
88
 
85
89
  it('keeps graph blocks behind the separate GraphContentBlock type, and a section takes a reference block', () => {
@@ -315,12 +315,20 @@ function functionSection(fn, index) {
315
315
  }
316
316
  const params = fn.params ?? [];
317
317
  if (params.length > 0) {
318
+ // A Default column only when some parameter declares a default, so a
319
+ // function with none keeps a three-column table.
320
+ const defaults = params.some(
321
+ (/** @type {any} */ p) => typeof p.default === 'string' && p.default !== '',
322
+ );
318
323
  content.push({
319
324
  type: 'table',
320
- headers: ['Parameter', 'Type', 'Description'],
325
+ headers: defaults
326
+ ? ['Parameter', 'Type', 'Default', 'Description']
327
+ : ['Parameter', 'Type', 'Description'],
321
328
  rows: params.map((/** @type {any} */ p) => [
322
- `\`${p.name}\``,
329
+ `\`${p.name}\`${p.required ? ' (required)' : ''}`,
323
330
  `\`${p.type ?? ''}\``,
331
+ ...(defaults ? [p.default ?? ''] : []),
324
332
  p.description ?? '',
325
333
  ]),
326
334
  });
@@ -405,6 +413,12 @@ export function cliDocSection(doc, index) {
405
413
  if (doc.type === 'command') return commandSection(doc, index);
406
414
  if (doc.type === 'function') return functionSection(doc, index);
407
415
  if (doc.type === 'enum') return enumSection(doc);
416
+ if (doc.type === 'namespace')
417
+ return {
418
+ id: routeSegment(doc.name),
419
+ title: doc.title,
420
+ content: doc.blocks ?? [],
421
+ };
408
422
  return {...selfDocSection(doc), id: routeSegment(doc.name)};
409
423
  }
410
424
 
@@ -315,6 +315,26 @@ describe('cliDocSection', () => {
315
315
  ]);
316
316
  });
317
317
 
318
+ it('marks required parameters and adds a Default column when a parameter has a default', () => {
319
+ const beta = fn('beta', {
320
+ params: [
321
+ {name: 'slug', type: 'string', description: 'The slug.', required: true},
322
+ {name: 'options.limit', type: 'number', description: 'How many.', default: '20'},
323
+ ],
324
+ });
325
+ const table = cliDocSection(beta, indexOf([beta])).content.find(
326
+ (/** @type {any} */ block) => block.type === 'table',
327
+ );
328
+ expect(table).toEqual({
329
+ type: 'table',
330
+ headers: ['Parameter', 'Type', 'Default', 'Description'],
331
+ rows: [
332
+ ['`slug` (required)', '`string`', '', 'The slug.'],
333
+ ['`options.limit`', '`number`', '20', 'How many.'],
334
+ ],
335
+ });
336
+ });
337
+
318
338
  it('renders an API function, an enum, and a schema', () => {
319
339
  const alpha = fn('alpha', {
320
340
  description: 'Longer.',
@@ -626,7 +626,11 @@ function findMergeTarget(sections, section) {
626
626
  const sameTitle = sections.findIndex(
627
627
  candidate => sourceTitle(candidate) === title,
628
628
  );
629
- return sameTitle;
629
+ if (sameTitle !== -1) return sameTitle;
630
+ // A base section retitled later keeps its old key as its `id`, so an
631
+ // extension that still names it by the old title finds it by that id. A
632
+ // title variant of a section with no id stays a separate section.
633
+ return sections.findIndex(candidate => candidate.id === key);
630
634
  }
631
635
 
632
636
  /**
@@ -509,6 +509,27 @@ describe('mergeTopic', () => {
509
509
  expect(base.sections[0].content[0].text).toBe('npm i');
510
510
  });
511
511
 
512
+ it('finds a retitled base section by the key its old title derives', () => {
513
+ // A base section retitled later keeps its old key as its `id`; an
514
+ // extension that still names it by the old title replaces it.
515
+ const merged = mergeTopic(
516
+ {
517
+ ...base,
518
+ sections: [
519
+ {id: 'quick-start', title: 'Wrap your app in a theme', content: []},
520
+ {id: 'tokens', title: 'Tokens', content: []},
521
+ ],
522
+ },
523
+ {sections: [{title: 'Quick Start', content: []}]},
524
+ );
525
+ expect(merged.sections.map(section => [section.id, section.title])).toEqual(
526
+ [
527
+ ['quick-start', 'Quick Start'],
528
+ ['tokens', 'Tokens'],
529
+ ],
530
+ );
531
+ });
532
+
512
533
  it('replaces a section by stable ID even when its title changes', () => {
513
534
  const merged = mergeTopic(
514
535
  {
@@ -32,7 +32,7 @@ export function sectionTitleKey(title: unknown): string;
32
32
  export function sectionKey(section: any): string;
33
33
  /**
34
34
  * A name as a docs-tree route segment: lowercase words joined by hyphens, so
35
- * `integrationPackCheck` and `integration pack` both read naturally.
35
+ * `integrationPackCheck` and `integration verify` both read naturally.
36
36
  * @param {string} name
37
37
  * @returns {string}
38
38
  */
@@ -95,7 +95,7 @@ function legacyTitleMatchIndex(sections, query) {
95
95
 
96
96
  /**
97
97
  * A name as a docs-tree route segment: lowercase words joined by hyphens, so
98
- * `integrationPackCheck` and `integration pack` both read naturally.
98
+ * `integrationPackCheck` and `integration verify` both read naturally.
99
99
  * @param {string} name
100
100
  * @returns {string}
101
101
  */
@@ -53,8 +53,8 @@ const RUNNERS = {
53
53
  sites: ['import(node:child_process)', 'worker_threads.Worker'],
54
54
  },
55
55
  'api/integration/pack-check.mjs': {
56
- runs: '`npm pack`, `tar`, and a Node script that resolves the packed specifiers, to check a package as published',
57
- sites: ['child_process.spawnSync ×3'],
56
+ runs: '`npm pack`, `tar`, and a Node script that resolves the packed specifiers, to check a package as published, and the package\'s doc modules, to see which need a newer CLI',
57
+ sites: ['child_process.spawnSync ×3', 'loadTopicModule'],
58
58
  },
59
59
  'api/swizzle/_github.mjs': {
60
60
  runs: '`gh auth status`, to check the GitHub CLI is signed in',
@@ -188,6 +188,7 @@ const RUNNERS = {
188
188
  'import ../../../api/integration/integrationPackCheck.doc.mjs',
189
189
  'import ./integration-add.doc.mjs',
190
190
  'import ./integration-pack.doc.mjs',
191
+ 'import ./integration-verify.doc.mjs',
191
192
  'import ./integration.doc.mjs',
192
193
  ],
193
194
  },
@@ -90,7 +90,6 @@ describe('collectDocInputs over what this repo ships', () => {
90
90
  ).toEqual([
91
91
  'Chat',
92
92
  'ChatComposerTokenElement',
93
- 'Code',
94
93
  'ContextMenuItem',
95
94
  'Heading',
96
95
  'Indicator',
@@ -240,6 +240,10 @@ export type TreeNode = {
240
240
  name: string;
241
241
  title: string;
242
242
  summary: string;
243
+ /**
244
+ * an authored namespace's search keywords
245
+ */
246
+ keywords?: string[] | undefined;
243
247
  /**
244
248
  * the parent's route; null at the top
245
249
  */
@@ -58,7 +58,7 @@ export const KIND_GROUPS = Object.freeze({
58
58
  schema: {
59
59
  segment: 'schemas',
60
60
  title: 'Schemas',
61
- summary: 'Every shape the CLI reads or returns, field by field.',
61
+ summary: 'Data shapes, field by field.',
62
62
  },
63
63
  enum: {
64
64
  segment: 'enums',
@@ -140,6 +140,7 @@ export const UNORGANIZED = 'unorganized';
140
140
  * @property {string} name
141
141
  * @property {string} title
142
142
  * @property {string} summary
143
+ * @property {string[]} [keywords] an authored namespace's search keywords
143
144
  * @property {string | null} parent the parent's route; null at the top
144
145
  * @property {string | null} slot the parent slot this node sits in
145
146
  * @property {number | null} order
@@ -420,10 +421,14 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
420
421
  name: input.doc.name,
421
422
  title: input.doc.title,
422
423
  summary: input.doc.summary,
424
+ ...(Array.isArray(input.doc.keywords)
425
+ ? {keywords: input.doc.keywords}
426
+ : {}),
423
427
  parent: parentRoute,
424
428
  slot: home?.slot ?? null,
425
429
  order: home?.order ?? null,
426
430
  generated: false,
431
+ ref: {selfDoc: input.doc},
427
432
  slots: Object.entries(input.doc.slots).map(([name, slot]) => ({
428
433
  name,
429
434
  title: slot.title,
@@ -20,13 +20,31 @@ export function lowestAdmitted(range: unknown): string | null;
20
20
  */
21
21
  export function docsTreeCliProblem(pkg: any): string | null;
22
22
  /**
23
- * Why a package with a template that sets `replaces` would lose its templates
24
- * and doc topics on an older CLI (spec:AST-035), or null when its declared CLI
23
+ * Why a package with a template that sets `replaces` would lose templates and
24
+ * doc topics on an older CLI (spec:AST-035), or null when its declared CLI
25
25
  * range admits only CLIs that read the field.
26
26
  * @param {any} pkg package.json
27
27
  * @returns {string | null}
28
28
  */
29
29
  export function replacesCliProblem(pkg: any): string | null;
30
+ /**
31
+ * Why a package with a doc section that sets `id` would lose its doc topics on
32
+ * an older CLI, or null when its declared CLI range admits only CLIs that read
33
+ * the field. Published 0.6.3 rejects a section `id` and hides every doc topic
34
+ * the package ships.
35
+ * @param {any} pkg package.json
36
+ * @returns {string | null}
37
+ */
38
+ export function sectionIdsCliProblem(pkg: any): string | null;
39
+ /**
40
+ * Why a package that ships a theme would lose its themes and doc topics on an
41
+ * older CLI, or null when its declared CLI range admits only CLIs that read
42
+ * typed theme descriptors. Published 0.6.3 rejects a themes root with no
43
+ * `manifest.json` catalog, which `integration add theme` no longer writes.
44
+ * @param {any} pkg package.json
45
+ * @returns {string | null}
46
+ */
47
+ export function themesCliProblem(pkg: any): string | null;
30
48
  /**
31
49
  * The package.json with a CLI peer that reads namespace docs: optional, unless
32
50
  * the package already says otherwise.
@@ -37,9 +55,11 @@ export function withDocsTreeCli(pkg: any): any;
37
55
  export const CLI_PACKAGE: "@astryxdesign/cli";
38
56
  /**
39
57
  * The first CLI release that reads an integration's docs tree (namespace docs
40
- * and placed guides) and its templates' `replaces`. A release before it can
41
- * hide every doc topic a package with a namespace doc or a placed guide
42
- * ships, with no warning, and it rejects `replaces` and withholds the
43
- * package's templates and doc topics.
58
+ * and placed guides), its templates' `replaces`, and its typed theme
59
+ * descriptors. A release before it can hide every doc topic a package with a
60
+ * namespace doc or a placed guide ships, with no warning; it rejects
61
+ * `replaces`, drops that template, and hides the package's doc topics; and it
62
+ * rejects a themes root without the `manifest.json` catalog the CLI no longer
63
+ * writes, withholding the package's themes and doc topics.
44
64
  */
45
65
  export const DOCS_TREE_CLI: "0.7.0";
@@ -6,8 +6,9 @@
6
6
  * @input A package.json object.
7
7
  * @output Whether its `peerDependencies` range for `@astryxdesign/cli` admits
8
8
  * only CLIs that read namespace docs, and the package.json that declares it.
9
- * @position foundation/integrations; read by `integration add doc --parent`,
10
- * which declares the peer, and `integration pack --check`, which requires it.
9
+ * @position foundation/integrations; read by `integration add doc --parent`
10
+ * and `integration add theme`, which declare the peer, and
11
+ * `integration verify`, which requires it.
11
12
  */
12
13
 
13
14
  import {semverCompare} from '../env/semver.mjs';
@@ -16,10 +17,12 @@ export const CLI_PACKAGE = '@astryxdesign/cli';
16
17
 
17
18
  /**
18
19
  * The first CLI release that reads an integration's docs tree (namespace docs
19
- * and placed guides) and its templates' `replaces`. A release before it can
20
- * hide every doc topic a package with a namespace doc or a placed guide
21
- * ships, with no warning, and it rejects `replaces` and withholds the
22
- * package's templates and doc topics.
20
+ * and placed guides), its templates' `replaces`, and its typed theme
21
+ * descriptors. A release before it can hide every doc topic a package with a
22
+ * namespace doc or a placed guide ships, with no warning; it rejects
23
+ * `replaces`, drops that template, and hides the package's doc topics; and it
24
+ * rejects a themes root without the `manifest.json` catalog the CLI no longer
25
+ * writes, withholding the package's themes and doc topics.
23
26
  */
24
27
  export const DOCS_TREE_CLI = '0.7.0';
25
28
 
@@ -92,11 +95,11 @@ function cliRangeProblem(pkg, feature, loss) {
92
95
  const range = pkg?.peerDependencies?.[CLI_PACKAGE];
93
96
  const fix = `"${CLI_PACKAGE}": ">=${DOCS_TREE_CLI}" in peerDependencies (optional in peerDependenciesMeta, if the CLI is not required)`;
94
97
  if (typeof range !== 'string') {
95
- return `The package ${feature} but declares no ${CLI_PACKAGE} peer. A CLI older than ${DOCS_TREE_CLI} ${loss}. Declare ${fix}.`;
98
+ return `The package ${feature} but declares no ${CLI_PACKAGE} peer. A stable CLI before ${DOCS_TREE_CLI} ${loss}. Declare ${fix}.`;
96
99
  }
97
100
  const lowest = lowestAdmitted(range);
98
101
  if (lowest == null || semverCompare(lowest, DOCS_TREE_CLI) < 0) {
99
- return `The package ${feature}, but its ${CLI_PACKAGE} peer range "${range}" admits a CLI older than ${DOCS_TREE_CLI}, which ${loss}. Declare ${fix}.`;
102
+ return `The package ${feature}, but its ${CLI_PACKAGE} peer range "${range}" admits a stable CLI before ${DOCS_TREE_CLI}, which ${loss}. Declare ${fix}.`;
100
103
  }
101
104
  return null;
102
105
  }
@@ -119,8 +122,8 @@ export function docsTreeCliProblem(pkg) {
119
122
  }
120
123
 
121
124
  /**
122
- * Why a package with a template that sets `replaces` would lose its templates
123
- * and doc topics on an older CLI (spec:AST-035), or null when its declared CLI
125
+ * Why a package with a template that sets `replaces` would lose templates and
126
+ * doc topics on an older CLI (spec:AST-035), or null when its declared CLI
124
127
  * range admits only CLIs that read the field.
125
128
  * @param {any} pkg package.json
126
129
  * @returns {string | null}
@@ -129,7 +132,39 @@ export function replacesCliProblem(pkg) {
129
132
  return cliRangeProblem(
130
133
  pkg,
131
134
  'has a template that sets `replaces`',
132
- "rejects the field, and withholds the package's templates and doc topics",
135
+ "rejects the field, drops that template, and hides the package's doc topics",
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Why a package with a doc section that sets `id` would lose its doc topics on
141
+ * an older CLI, or null when its declared CLI range admits only CLIs that read
142
+ * the field. Published 0.6.3 rejects a section `id` and hides every doc topic
143
+ * the package ships.
144
+ * @param {any} pkg package.json
145
+ * @returns {string | null}
146
+ */
147
+ export function sectionIdsCliProblem(pkg) {
148
+ return cliRangeProblem(
149
+ pkg,
150
+ 'has a doc section that sets `id`',
151
+ 'rejects the field, and can hide every doc topic the package ships',
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Why a package that ships a theme would lose its themes and doc topics on an
157
+ * older CLI, or null when its declared CLI range admits only CLIs that read
158
+ * typed theme descriptors. Published 0.6.3 rejects a themes root with no
159
+ * `manifest.json` catalog, which `integration add theme` no longer writes.
160
+ * @param {any} pkg package.json
161
+ * @returns {string | null}
162
+ */
163
+ export function themesCliProblem(pkg) {
164
+ return cliRangeProblem(
165
+ pkg,
166
+ 'ships a theme',
167
+ "cannot read typed theme descriptors, and can drop the package's themes and hide its doc topics",
133
168
  );
134
169
  }
135
170
 
@@ -32,6 +32,11 @@ describe('docsTreeCliProblem', () => {
32
32
  expect(docsTreeCliProblem({name: '@acme/kit'})).toContain(
33
33
  'declares no @astryxdesign/cli peer',
34
34
  );
35
+ // Stable releases: a canary built from main before its release reports
36
+ // the older version and reads every feature.
37
+ expect(docsTreeCliProblem({name: '@acme/kit'})).toContain(
38
+ 'A stable CLI before 0.7.0 does not read the docs tree',
39
+ );
35
40
  });
36
41
 
37
42
  it.each(['^0.6.0', '^0.6.0 || >=0.7.0', '*', '>=0.6.3'])(
@@ -39,7 +44,7 @@ describe('docsTreeCliProblem', () => {
39
44
  range => {
40
45
  expect(
41
46
  docsTreeCliProblem({peerDependencies: {'@astryxdesign/cli': range}}),
42
- ).toContain('admits a CLI older than');
47
+ ).toContain('admits a stable CLI before');
43
48
  },
44
49
  );
45
50
 
@@ -76,7 +81,7 @@ describe('replacesCliProblem', () => {
76
81
  );
77
82
  expect(
78
83
  replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '^0.6.0'}}),
79
- ).toContain('admits a CLI older than');
84
+ ).toContain('admits a stable CLI before');
80
85
  expect(
81
86
  replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '>=0.7.0'}}),
82
87
  ).toBeNull();
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file Contribution inventory for integration pack-check.
4
+ * @file Contribution inventory for `astryx integration verify`.
5
5
  *
6
6
  * Enumerates the files and contribution identities an integration package
7
7
  * declares, for cross-referencing against the npm tarball. The file inventory
@@ -45,7 +45,7 @@ export function findManifestPaths(dir: string): string[];
45
45
  * @param {string} file absolute manifest path
46
46
  * @param {string} [label] used in error messages
47
47
  * @param {{fresh?: boolean}} [options]
48
- * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, agentDocsError?: string}>}
48
+ * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string}>}
49
49
  */
50
50
  export function loadManifest(file: string, label?: string, { fresh }?: {
51
51
  fresh?: boolean;
@@ -55,6 +55,8 @@ export function loadManifest(file: string, label?: string, { fresh }?: {
55
55
  debug?: import("../../authoring/debug/type").DebugEventHandler;
56
56
  gapReport?: import("../../authoring/gap-report/type").GapReportHandler;
57
57
  gapReportError?: string;
58
+ discover?: import("../../authoring/discover/type").DiscoverSource;
59
+ discoverError?: string;
58
60
  agentDocsError?: string;
59
61
  }>;
60
62
  /**
@@ -154,6 +156,9 @@ export function loadIntegrations(specs?: string[], { cwd, fresh, resolveProvider
154
156
  * validated package-owned handler from the `gapReport` NAMED export.
155
157
  * @property {string} [__gapReportError] isolated named-handler validation error.
156
158
  * Named exports are not manifest keys — see {@link loadManifest}.
159
+ * @property {import('../../authoring/discover/type').DiscoverSource} [__discover]
160
+ * validated catalog source from the `discover` NAMED export.
161
+ * @property {string} [__discoverError] isolated discover-source validation error.
157
162
  */
158
163
  /** Conventional manifest basenames, in load-precedence order. */
159
164
  export const MANIFEST_BASENAMES: string[];
@@ -245,4 +250,12 @@ export type LoadedIntegration = {
245
250
  * Named exports are not manifest keys — see {@link loadManifest}.
246
251
  */
247
252
  __gapReportError?: string | undefined;
253
+ /**
254
+ * validated catalog source from the `discover` NAMED export.
255
+ */
256
+ __discover?: import("../../authoring/discover/type").DiscoverSource | undefined;
257
+ /**
258
+ * isolated discover-source validation error.
259
+ */
260
+ __discoverError?: string | undefined;
248
261
  };
@@ -29,6 +29,7 @@ import {
29
29
  } from '../../authoring/integration/schema.mjs';
30
30
  import {importUserModule, findPresentFiles} from '../fs/module-loader.mjs';
31
31
  import {parseGapReportHandler} from '../../authoring/gap-report/parse.mjs';
32
+ import {parseDiscoverSource} from '../../authoring/discover/parse.mjs';
32
33
  import {resolveProviders} from './provider-resolution.mjs';
33
34
 
34
35
  /**
@@ -80,6 +81,9 @@ import {resolveProviders} from './provider-resolution.mjs';
80
81
  * validated package-owned handler from the `gapReport` NAMED export.
81
82
  * @property {string} [__gapReportError] isolated named-handler validation error.
82
83
  * Named exports are not manifest keys — see {@link loadManifest}.
84
+ * @property {import('../../authoring/discover/type').DiscoverSource} [__discover]
85
+ * validated catalog source from the `discover` NAMED export.
86
+ * @property {string} [__discoverError] isolated discover-source validation error.
83
87
  */
84
88
 
85
89
  /** Conventional manifest basenames, in load-precedence order. */
@@ -167,6 +171,23 @@ function parseGapReportHandlerExport(value, label) {
167
171
  }
168
172
  }
169
173
 
174
+ /**
175
+ * Parse the optional named discover source the same way: a malformed one is
176
+ * isolated from every other contribution and reported by `astryx discover`.
177
+ *
178
+ * @param {unknown} value
179
+ * @param {string} label
180
+ * @returns {{source?: import('../../authoring/discover/type').DiscoverSource, error?: string}}
181
+ */
182
+ function parseDiscoverSourceExport(value, label) {
183
+ if (value === undefined) return {};
184
+ try {
185
+ return {source: parseDiscoverSource(value, `${label} named export "discover"`)};
186
+ } catch (error) {
187
+ return {error: error instanceof Error ? error.message : String(error)};
188
+ }
189
+ }
190
+
170
191
  /**
171
192
  * Load and validate a manifest module's default export, while isolating the
172
193
  * optional `agentDocs` contribution from the manifest's other fields.
@@ -183,7 +204,7 @@ function parseGapReportHandlerExport(value, label) {
183
204
  * @param {string} file absolute manifest path
184
205
  * @param {string} [label] used in error messages
185
206
  * @param {{fresh?: boolean}} [options]
186
- * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, agentDocsError?: string}>}
207
+ * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string}>}
187
208
  */
188
209
  export async function loadManifest(
189
210
  file,
@@ -194,6 +215,7 @@ export async function loadManifest(
194
215
  const raw = mod?.default;
195
216
  const baseManifest = parseIntegrationBase(raw, label);
196
217
  const gapReport = parseGapReportHandlerExport(mod?.gapReport, label);
218
+ const discover = parseDiscoverSourceExport(mod?.discover, label);
197
219
  const hasAgentDocs =
198
220
  raw != null &&
199
221
  typeof raw === 'object' &&
@@ -228,6 +250,8 @@ export async function loadManifest(
228
250
  : undefined,
229
251
  gapReport: gapReport.handler,
230
252
  gapReportError: gapReport.error,
253
+ discover: discover.source,
254
+ discoverError: discover.error,
231
255
  agentDocsError,
232
256
  };
233
257
  }
@@ -345,6 +369,10 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
345
369
  let gapReportHandler;
346
370
  /** @type {string | undefined} */
347
371
  let gapReportError;
372
+ /** @type {import('../../authoring/discover/type').DiscoverSource | undefined} */
373
+ let discoverSource;
374
+ /** @type {string | undefined} */
375
+ let discoverError;
348
376
  /** @type {string | undefined} */
349
377
  let agentDocsError;
350
378
  try {
@@ -354,6 +382,8 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
354
382
  debug: debugHandler,
355
383
  gapReport: gapReportHandler,
356
384
  gapReportError,
385
+ discover: discoverSource,
386
+ discoverError,
357
387
  agentDocsError,
358
388
  } = await loadManifest(manifestFile, `Integration ${spec}`, {fresh}));
359
389
  } catch (err) {
@@ -397,6 +427,8 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
397
427
  __debug: debugHandler,
398
428
  __gapReport: gapReportHandler,
399
429
  __gapReportError: gapReportError,
430
+ __discover: discoverSource,
431
+ __discoverError: discoverError,
400
432
  __spec: spec,
401
433
  __packageDir: packageDir,
402
434
  __packageExports: pkg.exports ?? null,
@@ -448,6 +480,10 @@ export async function loadIntegrations(
448
480
  let gapReportHandler;
449
481
  /** @type {string | undefined} */
450
482
  let gapReportError;
483
+ /** @type {import('../../authoring/discover/type').DiscoverSource | undefined} */
484
+ let discoverSource;
485
+ /** @type {string | undefined} */
486
+ let discoverError;
451
487
  /** @type {string | undefined} */
452
488
  let agentDocsError;
453
489
  try {
@@ -457,6 +493,8 @@ export async function loadIntegrations(
457
493
  debug: debugHandler,
458
494
  gapReport: gapReportHandler,
459
495
  gapReportError,
496
+ discover: discoverSource,
497
+ discoverError,
460
498
  agentDocsError,
461
499
  } = await loadManifest(manifestFile, `Integration ${spec}`, {fresh}));
462
500
  } catch (err) {
@@ -507,6 +545,8 @@ export async function loadIntegrations(
507
545
  __debug: debugHandler,
508
546
  __gapReport: gapReportHandler,
509
547
  __gapReportError: gapReportError,
548
+ __discover: discoverSource,
549
+ __discoverError: discoverError,
510
550
  __spec: spec,
511
551
  __packageDir: packageDir,
512
552
  __packageExports: pkg.exports ?? null,
@@ -785,3 +785,34 @@ describe('the `gapReport` named export', () => {
785
785
  expect(loaded.__unknownKeys).toEqual(['gapReport']);
786
786
  });
787
787
  });
788
+
789
+ describe('the `discover` named export', () => {
790
+ it('is carried out of the manifest module as __discover', async () => {
791
+ writeManifestPackage(tmpDir, {
792
+ body:
793
+ `export async function discover() { return {schemaVersion: 1}; }\n` +
794
+ `export default {issuesUrl: 'https://example.com/i'};\n`,
795
+ });
796
+
797
+ const [loaded] = await loadIntegrations(['@acme/widgets'], {cwd: tmpDir});
798
+
799
+ expect(typeof loaded.__discover).toBe('function');
800
+ expect(loaded.__discoverError).toBeUndefined();
801
+ expect(loaded.__unknownKeys).toEqual([]);
802
+ expect(loaded.issuesUrl).toBe('https://example.com/i');
803
+ });
804
+
805
+ it('keeps the manifest and records why when the export is not a function', async () => {
806
+ writeManifestPackage(tmpDir, {
807
+ body:
808
+ `export const discover = {url: 'https://example.com/catalog.json'};\n` +
809
+ `export default {issuesUrl: 'https://example.com/i'};\n`,
810
+ });
811
+
812
+ const [loaded] = await loadIntegrations(['@acme/widgets'], {cwd: tmpDir});
813
+
814
+ expect(loaded.__discover).toBeUndefined();
815
+ expect(loaded.__discoverError).toContain('must be a function');
816
+ expect(loaded.issuesUrl).toBe('https://example.com/i');
817
+ });
818
+ });
@@ -0,0 +1,33 @@
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
+ * One complete row for an accepted selector.
6
+ */
7
+ export type BatchRow<TResult, TCandidate> = ({
8
+ selector: string;
9
+ status: "found";
10
+ result: TResult;
11
+ } | {
12
+ selector: string;
13
+ status: "ambiguous";
14
+ code: string;
15
+ error: string;
16
+ candidates: TCandidate[];
17
+ } | {
18
+ selector: string;
19
+ status: "not_found" | "error";
20
+ code: string;
21
+ error: string;
22
+ suggestions?: import("./base").Suggestion[];
23
+ });
24
+ /**
25
+ * One ordered batch receipt inside the shared `{type, data}` success envelope.
26
+ */
27
+ export type BatchResponse<TType extends string, TResult, TCandidate> = {
28
+ type: TType;
29
+ data: {
30
+ count: number;
31
+ results: BatchRow<TResult, TCandidate>[];
32
+ };
33
+ };