@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ebaebc4

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 (188) hide show
  1. package/README.md +2 -1
  2. package/api/build/build.type.d.mts +2 -2
  3. package/api/build/build.type.mjs +2 -2
  4. package/api/component/component.type.d.mts +6 -6
  5. package/api/component/component.type.mjs +19 -19
  6. package/api/discover/discover.type.d.mts +4 -4
  7. package/api/discover/discover.type.mjs +10 -10
  8. package/api/docs/_adapter.d.mts +37 -24
  9. package/api/docs/_adapter.mjs +169 -83
  10. package/api/docs/compiled-topics.test.mjs +78 -0
  11. package/api/docs/detail/detail.mjs +14 -63
  12. package/api/docs/detail/section/section.d.mts +1 -1
  13. package/api/docs/detail/section/section.mjs +44 -20
  14. package/api/docs/detail/section/section.test.mjs +41 -0
  15. package/api/docs/docs.d.mts +7 -2
  16. package/api/docs/docs.doc.mjs +27 -10
  17. package/api/docs/docs.mjs +16 -9
  18. package/api/docs/docs.test.mjs +6 -0
  19. package/api/docs/docs.type.d.mts +40 -3
  20. package/api/docs/docs.type.mjs +36 -8
  21. package/api/docs/index/index.d.mts +18 -0
  22. package/api/docs/index/index.mjs +32 -0
  23. package/api/docs/index/index.test.mjs +62 -0
  24. package/api/docs/integrationDocs.test.mjs +106 -0
  25. package/api/doctor/doctor.d.mts +48 -0
  26. package/api/doctor/doctor.mjs +232 -0
  27. package/api/doctor/doctor.test.mjs +196 -0
  28. package/api/hook/hook.type.d.mts +3 -3
  29. package/api/hook/hook.type.mjs +11 -11
  30. package/api/hook/list/list.d.mts +1 -1
  31. package/api/integration/add-contribution.mjs +5 -3
  32. package/api/integration/add-contribution.test.mjs +4 -4
  33. package/api/integration/integration-authoring.type.d.mts +1 -1
  34. package/api/integration/pack-check.mjs +49 -7
  35. package/api/integration/pack-check.test.mjs +249 -0
  36. package/api/search/search.d.mts +1 -1
  37. package/api/search/search.mjs +5 -5
  38. package/api/search/search.type.d.mts +2 -2
  39. package/api/search/search.type.mjs +1 -1
  40. package/api/swizzle/swizzle.type.d.mts +2 -2
  41. package/api/swizzle/swizzle.type.mjs +2 -2
  42. package/api/template/template.d.mts +1 -1
  43. package/api/template/template.type.d.mts +6 -6
  44. package/api/template/template.type.mjs +12 -12
  45. package/api/theme/build/build.mjs +20 -6
  46. package/api/theme/build/build.test.mjs +127 -0
  47. package/api/theme/palette/generate/generate.mjs +1 -1
  48. package/api/theme/palette/generate/generator.d.mts +10 -13
  49. package/api/theme/palette/generate/generator.mjs +7 -3
  50. package/api/theme/theme.type.d.mts +170 -11
  51. package/api/theme/theme.type.mjs +94 -27
  52. package/api/upgrade/_adapter.mjs +71 -5
  53. package/api/upgrade/project-context.test.mjs +272 -0
  54. package/api/upgrade/upgrade.doc.mjs +4 -3
  55. package/api/upgrade/upgrade.type.d.mts +5 -5
  56. package/api/upgrade/upgrade.type.mjs +11 -11
  57. package/assets/codemods/integration-discovery.mjs +40 -2
  58. package/assets/codemods/integration-discovery.test.mjs +58 -0
  59. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  60. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  61. package/assets/docs/README.md +9 -0
  62. package/assets/docs/authoring.doc.mjs +14 -0
  63. package/assets/docs/cli-integrations.doc.mjs +86 -15
  64. package/assets/docs/styling-libraries.doc.mjs +1 -1
  65. package/assets/docs/working-with-ai.doc.mjs +1 -1
  66. package/authoring/_shared/contract.ts +22 -0
  67. package/authoring/codemod/codemod.doc.mjs +6 -1
  68. package/authoring/codemod/parse.d.mts +8 -8
  69. package/authoring/codemod/parse.mjs +8 -6
  70. package/authoring/config/parse.d.mts +13 -13
  71. package/authoring/config/parse.mjs +8 -8
  72. package/authoring/config/type.ts +3 -3
  73. package/authoring/debug/parse.d.mts +5 -5
  74. package/authoring/debug/parse.mjs +3 -3
  75. package/authoring/doctypes/_schema.d.mts +788 -23
  76. package/authoring/doctypes/_schema.mjs +492 -39
  77. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  78. package/authoring/doctypes/base/graph-fields.doc.mjs +62 -0
  79. package/authoring/doctypes/base/type.ts +40 -0
  80. package/authoring/doctypes/command/command.doc.mjs +3 -2
  81. package/authoring/doctypes/command/parse.d.mts +2 -2
  82. package/authoring/doctypes/command/parse.mjs +1 -1
  83. package/authoring/doctypes/command/type.ts +3 -2
  84. package/authoring/doctypes/component/component.doc.mjs +6 -3
  85. package/authoring/doctypes/component/parse.d.mts +2 -2
  86. package/authoring/doctypes/component/parse.mjs +1 -1
  87. package/authoring/doctypes/component/type.ts +4 -3
  88. package/authoring/doctypes/enum/parse.d.mts +2 -2
  89. package/authoring/doctypes/enum/parse.mjs +1 -1
  90. package/authoring/doctypes/enum/type.ts +3 -1
  91. package/authoring/doctypes/function/function.doc.mjs +4 -0
  92. package/authoring/doctypes/function/parse.d.mts +2 -2
  93. package/authoring/doctypes/function/parse.mjs +1 -1
  94. package/authoring/doctypes/function/type.ts +6 -2
  95. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  96. package/authoring/doctypes/hook/parse.d.mts +2 -2
  97. package/authoring/doctypes/hook/parse.mjs +1 -1
  98. package/authoring/doctypes/hook/type.ts +3 -2
  99. package/authoring/doctypes/legacy.d.mts +8 -6
  100. package/authoring/doctypes/legacy.mjs +5 -4
  101. package/authoring/doctypes/load-contract.test.mjs +207 -0
  102. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  103. package/authoring/doctypes/namespace/namespace.doc.mjs +132 -0
  104. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  105. package/authoring/doctypes/namespace/parse.mjs +25 -0
  106. package/authoring/doctypes/namespace/parse.test.mjs +165 -0
  107. package/authoring/doctypes/namespace/type.ts +71 -0
  108. package/authoring/doctypes/parse.d.mts +20 -18
  109. package/authoring/doctypes/parse.mjs +16 -10
  110. package/authoring/doctypes/parse.test.mjs +77 -3
  111. package/authoring/doctypes/reference/parse.d.mts +2 -2
  112. package/authoring/doctypes/reference/parse.mjs +8 -5
  113. package/authoring/doctypes/reference/reference.doc.mjs +17 -4
  114. package/authoring/doctypes/reference/type.ts +51 -5
  115. package/authoring/doctypes/schema/parse.d.mts +2 -2
  116. package/authoring/doctypes/schema/parse.mjs +1 -1
  117. package/authoring/doctypes/schema/type.ts +3 -2
  118. package/authoring/doctypes/template/parse.d.mts +92 -1
  119. package/authoring/doctypes/template/parse.mjs +36 -2
  120. package/authoring/doctypes/template/parse.test.mjs +8 -2
  121. package/authoring/doctypes/template/template.doc.mjs +4 -0
  122. package/authoring/doctypes/template/type.ts +5 -2
  123. package/authoring/doctypes/types.ts +10 -9
  124. package/authoring/gap-report/parse.d.mts +10 -10
  125. package/authoring/gap-report/parse.mjs +6 -6
  126. package/authoring/gap-report/type.ts +1 -1
  127. package/authoring/identity/identity.doc.d.mts +9 -0
  128. package/authoring/identity/identity.doc.mjs +61 -0
  129. package/authoring/identity/type.ts +132 -0
  130. package/authoring/index.d.mts +1 -0
  131. package/authoring/index.d.ts +49 -17
  132. package/authoring/index.mjs +1 -0
  133. package/authoring/integration/integration.doc.mjs +13 -6
  134. package/authoring/integration/parse.d.mts +2 -2
  135. package/authoring/integration/parse.mjs +1 -1
  136. package/authoring/integration/parse.test.mjs +10 -1
  137. package/authoring/integration/schema.d.mts +6 -4
  138. package/authoring/integration/schema.mjs +9 -3
  139. package/authoring/integration/type.ts +23 -6
  140. package/authoring/shadcn/receipt.d.mts +6 -6
  141. package/clients/cli/commands/docs.doc.mjs +13 -3
  142. package/clients/cli/commands/docs.mjs +121 -21
  143. package/clients/cli/commands/docs.test.mjs +88 -0
  144. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  145. package/clients/cli/commands/theme-palette-generate.doc.mjs +8 -4
  146. package/clients/cli/commands/upgrade.doc.mjs +2 -2
  147. package/clients/cli/formatters/index.mjs +162 -1
  148. package/clients/cli/formatters/index.test.mjs +91 -0
  149. package/clients/cli/lib/manifest.mjs +7 -2
  150. package/foundation/config/project.mjs +21 -6
  151. package/foundation/discovery/authoring-self-docs.d.mts +69 -0
  152. package/foundation/discovery/authoring-self-docs.mjs +214 -0
  153. package/foundation/discovery/authoring-self-docs.test.mjs +154 -0
  154. package/foundation/discovery/component-discovery.d.mts +1 -1
  155. package/foundation/discovery/component-discovery.mjs +2 -1
  156. package/foundation/discovery/docs-discovery.d.mts +11 -4
  157. package/foundation/discovery/docs-discovery.mjs +208 -88
  158. package/foundation/discovery/docs-discovery.test.mjs +279 -13
  159. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  160. package/foundation/discovery/docs-output-budget.mjs +50 -0
  161. package/foundation/discovery/docs-section-key.d.mts +98 -0
  162. package/foundation/discovery/docs-section-key.mjs +221 -0
  163. package/foundation/discovery/docs-section-key.test.mjs +224 -0
  164. package/foundation/discovery/template-adapter.mjs +2 -1
  165. package/foundation/discovery/theming-targets.test.mjs +4 -0
  166. package/foundation/doc-compiler/compile.d.mts +162 -0
  167. package/foundation/doc-compiler/compile.mjs +262 -0
  168. package/foundation/doc-compiler/doc-compiler.test.mjs +687 -0
  169. package/foundation/doc-compiler/ir.d.mts +9 -0
  170. package/foundation/doc-compiler/ir.mjs +287 -0
  171. package/foundation/doc-compiler/lenses.d.mts +33 -0
  172. package/foundation/doc-compiler/lenses.mjs +127 -0
  173. package/foundation/identity/provider-identity.d.mts +90 -0
  174. package/foundation/identity/provider-identity.mjs +320 -0
  175. package/foundation/identity/provider-identity.test.mjs +254 -0
  176. package/foundation/identity/providers.d.mts +7 -0
  177. package/foundation/identity/providers.mjs +16 -0
  178. package/foundation/integrations/autolink.mjs +12 -5
  179. package/foundation/integrations/integration-warnings.mjs +6 -0
  180. package/foundation/integrations/integrations.d.mts +46 -2
  181. package/foundation/integrations/integrations.mjs +167 -8
  182. package/foundation/integrations/integrations.test.mjs +384 -1
  183. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  184. package/foundation/integrations/validate-contributions.d.mts +2 -0
  185. package/foundation/integrations/validate-contributions.mjs +10 -0
  186. package/foundation/response/json-contract.test.mjs +46 -17
  187. package/foundation/response/response-types.doc.mjs +6 -1
  188. package/package.json +9 -11
@@ -0,0 +1,207 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Every authored doc kind has a compile-time lock between its load check
5
+ * and its published type: the `_*DriftLock` typedefs in `_schema.mjs` and
6
+ * `template/parse.mjs`, which the strict typecheck evaluates. A lock names each
7
+ * place the loader is deliberately looser than the type. These tests pin that
8
+ * looser behavior and the published unknown-field policy, so tightening a load
9
+ * check is a reviewed change rather than a silent one.
10
+ */
11
+
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import {fileURLToPath, pathToFileURL} from 'node:url';
15
+ import {describe, expect, it} from 'vitest';
16
+ import {parseDoc} from './parse.mjs';
17
+ import {AuthoredDocKindSchema} from './_schema.mjs';
18
+ import {doc as graphFieldsDoc} from './base/graph-fields.doc.mjs';
19
+ import {problemsInTopic} from '../../foundation/discovery/docs-discovery.mjs';
20
+
21
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
22
+ const KINDS = AuthoredDocKindSchema.options;
23
+
24
+ /** The lock that pins each kind's load check to its published type. */
25
+ const LOCKS = {
26
+ component: '_ComponentDocDriftLock',
27
+ function: '_FunctionDocDriftLock',
28
+ generic: '_ReferenceDocDriftLock',
29
+ page: '_PageTemplateDocDriftLock',
30
+ block: '_BlockTemplateDocDriftLock',
31
+ schema: '_SchemaDocDriftLock',
32
+ command: '_CommandDocDriftLock',
33
+ enum: '_EnumDocDriftLock',
34
+ namespace: '_NamespaceDocDriftLock',
35
+ };
36
+
37
+ /** One valid example doc per kind, from the typed examples. */
38
+ const EXAMPLES = Object.fromEntries(
39
+ await Promise.all(
40
+ KINDS.map(async kind => {
41
+ const file = {generic: 'reference', function: 'function'}[kind] ?? kind;
42
+ const url = pathToFileURL(
43
+ path.join(HERE, '../../test/authoring-types', `${file}.doc.mjs`),
44
+ );
45
+ return [kind, (await import(url.href)).docs];
46
+ }),
47
+ ),
48
+ );
49
+
50
+ describe('load check vs published type', () => {
51
+ it('locks every doc kind', () => {
52
+ const source = ['_schema.mjs', 'template/parse.mjs']
53
+ .map(file => fs.readFileSync(path.join(HERE, file), 'utf8'))
54
+ .join('\n');
55
+ expect(Object.keys(LOCKS).sort()).toEqual([...KINDS].sort());
56
+ const locks = [
57
+ ...Object.values(LOCKS),
58
+ '_AuthoredDocKindDriftLock',
59
+ '_HookDocIsFunctionDocLock',
60
+ ];
61
+ for (const lock of locks) {
62
+ expect(source, `${lock} is missing`).toMatch(
63
+ new RegExp(`\\}\\s*${lock}\\b`),
64
+ );
65
+ }
66
+ });
67
+
68
+ it('dispatches every doc kind and refuses any other', () => {
69
+ for (const kind of KINDS) {
70
+ expect(() => parseDoc(EXAMPLES[kind], `${kind}.doc.mjs`)).not.toThrow();
71
+ }
72
+ expect(() => parseDoc({type: 'widget', name: 'x'}, 'x.doc.mjs')).toThrow(
73
+ /unsupported type "widget"/,
74
+ );
75
+ });
76
+ });
77
+
78
+ describe('where loading is deliberately looser than the type', () => {
79
+ it('a stamped component needs no displayName; usage, theming and examples are unchecked', () => {
80
+ expect(() =>
81
+ parseDoc(
82
+ {
83
+ type: 'component',
84
+ name: 'Badge',
85
+ category: 'badges',
86
+ props: [],
87
+ usage: 'free text',
88
+ theming: 5,
89
+ examples: [{title: 'Basic'}],
90
+ },
91
+ 'Badge.doc.mjs',
92
+ ),
93
+ ).not.toThrow();
94
+ });
95
+
96
+ it('each entry of a stamped group doc needs a name, and nothing more', () => {
97
+ const group = (/** @type {unknown[]} */ components) => ({
98
+ type: 'component',
99
+ name: 'Tabs',
100
+ components,
101
+ });
102
+ expect(() =>
103
+ parseDoc(
104
+ group([{name: 'Tab'}, {name: 'TabPanel', description: 'x'}]),
105
+ 'Tabs.doc.mjs',
106
+ ),
107
+ ).not.toThrow();
108
+ for (const bad of [null, 5, 'Tab', {}, {name: ''}, {displayName: 'Tab'}]) {
109
+ expect(() => parseDoc(group([bad]), 'Tabs.doc.mjs')).toThrow(
110
+ /components\.0/,
111
+ );
112
+ }
113
+ });
114
+
115
+ it('a stamped function needs no displayName; usage is unchecked', () => {
116
+ expect(() =>
117
+ parseDoc(
118
+ {type: 'function', name: 'useX', params: [], returns: [], usage: 5},
119
+ 'useX.doc.mjs',
120
+ ),
121
+ ).not.toThrow();
122
+ });
123
+
124
+ it('a stamped generic doc loads without title, description or sections, but is no topic', () => {
125
+ const doc = parseDoc({type: 'generic', name: 'notes'}, 'notes.doc.mjs');
126
+ expect(doc.title).toBe('notes');
127
+ expect(problemsInTopic(doc)).toEqual([
128
+ 'description: expected a non-empty string',
129
+ 'sections: expected at least one section',
130
+ ]);
131
+ });
132
+
133
+ it('a template needs no displayName or aspectRatio and may use its own category', () => {
134
+ expect(() =>
135
+ parseDoc(
136
+ {type: 'block', name: 'widget-demo', category: 'components/Widget'},
137
+ 'widget-demo.doc.mjs',
138
+ ),
139
+ ).not.toThrow();
140
+ expect(() =>
141
+ parseDoc(
142
+ {type: 'page', name: 'widget-page', category: 'Widgets'},
143
+ 'widget-page.doc.mjs',
144
+ ),
145
+ ).not.toThrow();
146
+ });
147
+
148
+ it('schema, command and enum docs load only as their type allows', () => {
149
+ const {fields, ...schemaDoc} = EXAMPLES.schema;
150
+ const {summary, ...commandDoc} = EXAMPLES.command;
151
+ const {members, ...enumDoc} = EXAMPLES.enum;
152
+ expect(() => parseDoc(schemaDoc, 's.doc.mjs')).toThrow(/fields/);
153
+ expect(() => parseDoc(commandDoc, 'c.doc.mjs')).toThrow(/summary/);
154
+ expect(() => parseDoc(enumDoc, 'e.doc.mjs')).toThrow(/members/);
155
+ });
156
+ });
157
+
158
+ describe('unknown fields', () => {
159
+ const keeps = KINDS.filter(kind => {
160
+ try {
161
+ parseDoc({...EXAMPLES[kind], notAField: true}, `${kind}.doc.mjs`);
162
+ return true;
163
+ } catch {
164
+ return false;
165
+ }
166
+ });
167
+
168
+ it('match the published policy', () => {
169
+ const policy = graphFieldsDoc.notes
170
+ .map(note => ('text' in note ? note.text : ''))
171
+ .find(text => text.startsWith('Unknown fields'));
172
+ expect(policy, 'graph-fields.doc.mjs states the policy').toBeDefined();
173
+ const [kept, refused] = /** @type {string} */ (policy).split(
174
+ ' docs accept',
175
+ );
176
+ const named = (/** @type {string} */ text) =>
177
+ [...text.matchAll(/`([a-z]+)`/g)].map(match => match[1]).sort();
178
+ expect(named(kept)).toEqual([...keeps].sort());
179
+ expect(named(refused).filter(kind => KINDS.includes(kind))).toEqual(
180
+ KINDS.filter(kind => !keeps.includes(kind)).sort(),
181
+ );
182
+ });
183
+
184
+ it('are refused inside sections and content blocks', () => {
185
+ const [section] = EXAMPLES.generic.sections;
186
+ expect(() =>
187
+ parseDoc(
188
+ {...EXAMPLES.generic, sections: [{...section, notAField: true}]},
189
+ 'r.doc.mjs',
190
+ ),
191
+ ).toThrow();
192
+ expect(() =>
193
+ parseDoc(
194
+ {
195
+ ...EXAMPLES.generic,
196
+ sections: [
197
+ {
198
+ ...section,
199
+ content: [{type: 'prose', text: 'x', notAField: true}],
200
+ },
201
+ ],
202
+ },
203
+ 'r.doc.mjs',
204
+ ),
205
+ ).toThrow();
206
+ });
207
+ });
@@ -0,0 +1,9 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in authoring/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * @file SchemaDoc for NamespaceDoc, the authored hierarchy and layout owner.
6
+ * @position packages/cli/authoring/doctypes/namespace — doc-type documentation
7
+ */
8
+ /** @type {import('@astryxdesign/cli/authoring').SchemaDoc} */
9
+ export const doc: import("@astryxdesign/cli/authoring").SchemaDoc;
@@ -0,0 +1,132 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file SchemaDoc for NamespaceDoc, the authored hierarchy and layout owner.
5
+ * @position packages/cli/authoring/doctypes/namespace — doc-type documentation
6
+ */
7
+
8
+ /** @type {import('@astryxdesign/cli/authoring').SchemaDoc} */
9
+ export const doc = {
10
+ type: 'schema',
11
+ name: 'namespace-doc',
12
+ displayName: 'NamespaceDoc',
13
+ namespace: 'authoring',
14
+ description:
15
+ "Declares named navigation slots and renderer-neutral layout blocks for already-discovered docs. It never scans folders or copies child documents. Not loaded yet: only the docs graph reads namespace docs, and it is not built, so keep them out of an integration's docs directory for now.",
16
+ appliesTo: '<namespace>.doc.mjs',
17
+ fields: [
18
+ {
19
+ name: 'type',
20
+ type: "'namespace'",
21
+ description: 'Doc-kind discriminant.',
22
+ required: true,
23
+ },
24
+ {
25
+ name: 'name',
26
+ type: 'string',
27
+ description:
28
+ 'Stable provider-local identity. Moving the namespace does not change this value.',
29
+ required: true,
30
+ },
31
+ {
32
+ name: 'title',
33
+ type: 'string',
34
+ description: 'Human-readable page title.',
35
+ required: true,
36
+ },
37
+ {
38
+ name: 'summary',
39
+ type: 'string',
40
+ description: 'One-line summary used in listings and search results.',
41
+ required: true,
42
+ },
43
+ {
44
+ name: 'placement',
45
+ type: 'DocPlacement',
46
+ description:
47
+ 'Optional canonical parent request: {parent, slot?, order?}. Invalid explicit placement will fail compilation instead of falling back.',
48
+ },
49
+ {
50
+ name: 'aliases',
51
+ type: 'string[]',
52
+ description:
53
+ 'Prior names or routes the docs graph will keep resolving to this doc.',
54
+ },
55
+ {
56
+ name: 'audience',
57
+ type: "'public' | 'internal'",
58
+ description: "Bundle audience. Defaults to 'public'.",
59
+ default: "'public'",
60
+ },
61
+ {
62
+ name: 'keywords',
63
+ type: 'string[]',
64
+ description: 'Search terms not already present in the title or summary.',
65
+ },
66
+ {
67
+ name: 'slots',
68
+ type: 'Record<string, NamespaceSlot>',
69
+ description:
70
+ 'Named placement and collection targets. Each slot declares a title and accepted doc kinds; configured providers require an explicit extension slot.',
71
+ required: true,
72
+ },
73
+ {
74
+ name: 'adopts',
75
+ type: 'NamespaceAdoptionRule[]',
76
+ description:
77
+ 'Provider-local rules that adopt otherwise-unplaced docs from a logical discovery group. They never scan a folder.',
78
+ },
79
+ {
80
+ name: 'blocks',
81
+ type: 'ReferenceContentBlock[]',
82
+ description:
83
+ 'Ordered layout content. V1 adds only workflow, collection, and reference to the existing prose, heading, code, table, list, and token-ref blocks.',
84
+ },
85
+ ],
86
+ examples: [
87
+ {
88
+ label: 'A CLI namespace with one adopted source group',
89
+ code: `/** @type {import('@astryxdesign/cli/authoring').NamespaceDoc} */
90
+ export const docs = {
91
+ type: 'namespace',
92
+ name: 'cli',
93
+ title: 'Astryx CLI',
94
+ summary: 'Commands, APIs, and integration authoring.',
95
+ slots: {
96
+ guides: {title: 'Guides', accepts: {kinds: ['namespace', 'generic']}},
97
+ reference: {
98
+ title: 'Reference',
99
+ accepts: {kinds: ['namespace', 'command']},
100
+ },
101
+ },
102
+ adopts: [{
103
+ source: {group: 'cli-commands', kinds: ['command']},
104
+ into: 'reference',
105
+ }],
106
+ blocks: [
107
+ {type: 'collection', source: {slot: 'guides'}, presentation: 'cards'},
108
+ {type: 'collection', source: {slot: 'reference'}, presentation: 'compact'},
109
+ ],
110
+ };`,
111
+ },
112
+ ],
113
+ notes: [
114
+ {
115
+ type: 'prose',
116
+ text: "Namespace docs are not loaded yet. Only the docs graph reads them, and it is not built. A namespace doc in an integration's docs directory fails to load as a topic, and with it every topic that package contributes, until the file is removed.",
117
+ },
118
+ {
119
+ type: 'prose',
120
+ text: 'Child docs request one canonical home with placement. Collections store and render stable references to those docs; they never create a second identity or parent.',
121
+ },
122
+ {
123
+ type: 'list',
124
+ style: 'dont',
125
+ items: [
126
+ 'Use a directory as implicit navigation.',
127
+ 'Put JSX, HTML, ANSI, callbacks, or custom renderer code in a doc.',
128
+ 'Use choice, callout, or checklist blocks before the full block-extension contract exists.',
129
+ ],
130
+ },
131
+ ],
132
+ };
@@ -0,0 +1,12 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in authoring/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /** @typedef {import('../types.js').NamespaceDoc} NamespaceDoc */
5
+ /**
6
+ * Validate an unknown value as a NamespaceDoc, or throw a readable error.
7
+ * @param {unknown} input
8
+ * @param {string} [label]
9
+ * @returns {NamespaceDoc}
10
+ */
11
+ export function parseNamespace(input: unknown, label?: string): NamespaceDoc;
12
+ export type NamespaceDoc = import("../types.js").NamespaceDoc;
@@ -0,0 +1,25 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Namespace doc parser. Zod is sealed in `../_schema.mjs`; consumers call
5
+ * `parseNamespace` or use `parseDoc`.
6
+ */
7
+
8
+ import {NamespaceDocKindSchema} from '../_schema.mjs';
9
+ import {formatZodError} from '../../_shared/errors.mjs';
10
+
11
+ /** @typedef {import('../types.js').NamespaceDoc} NamespaceDoc */
12
+
13
+ /**
14
+ * Validate an unknown value as a NamespaceDoc, or throw a readable error.
15
+ * @param {unknown} input
16
+ * @param {string} [label]
17
+ * @returns {NamespaceDoc}
18
+ */
19
+ export function parseNamespace(input, label = 'namespace doc') {
20
+ const result = NamespaceDocKindSchema.safeParse(input);
21
+ if (!result.success) {
22
+ throw new Error(formatZodError(label, result.error));
23
+ }
24
+ return result.data;
25
+ }
@@ -0,0 +1,165 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+ import {parseDoc, parseNamespace, parseReference} from '../../index.mjs';
5
+
6
+ const namespaceDoc = {
7
+ type: 'namespace',
8
+ name: 'integrations',
9
+ title: 'Author integrations',
10
+ summary: 'Publish reusable Astryx packages.',
11
+ aliases: ['cli-integrations'],
12
+ placement: {parent: 'namespace:cli', slot: 'guides', order: 10},
13
+ slots: {
14
+ contributions: {
15
+ title: 'What do you want to publish?',
16
+ accepts: {kinds: ['namespace']},
17
+ },
18
+ providerGuides: {
19
+ title: 'Provider guidance',
20
+ accepts: {providers: 'configured', kinds: ['generic', 'schema']},
21
+ },
22
+ },
23
+ adopts: [
24
+ {
25
+ source: {group: 'cli-api', kinds: ['function', 'schema', 'enum']},
26
+ into: 'contributions',
27
+ groupBy: 'kind',
28
+ },
29
+ ],
30
+ blocks: [
31
+ {
32
+ type: 'workflow',
33
+ title: 'Package lifecycle',
34
+ steps: [
35
+ {title: 'Add', references: ['command:integration-add']},
36
+ {
37
+ title: 'Validate',
38
+ references: ['command:doctor-integration-validate'],
39
+ },
40
+ ],
41
+ },
42
+ {
43
+ type: 'collection',
44
+ source: {slot: 'contributions'},
45
+ presentation: 'cards',
46
+ },
47
+ {
48
+ type: 'reference',
49
+ target: 'schema:integration',
50
+ projection: {fields: ['components', 'docs']},
51
+ },
52
+ ],
53
+ };
54
+
55
+ describe('NamespaceDoc', () => {
56
+ it('accepts the reviewed plain-object shape through both parsers', () => {
57
+ expect(parseNamespace(namespaceDoc)).toEqual(namespaceDoc);
58
+ expect(parseDoc(namespaceDoc)).toEqual(namespaceDoc);
59
+ });
60
+
61
+ it('reports stable nested field paths', () => {
62
+ expect(() =>
63
+ parseNamespace({
64
+ ...namespaceDoc,
65
+ blocks: [{type: 'workflow', steps: [{title: ''}]}],
66
+ }),
67
+ ).toThrow(/blocks\.0\.steps\.0\.title/u);
68
+ });
69
+
70
+ it.each(['choice', 'callout', 'checklist'])(
71
+ 'rejects unsupported %s blocks',
72
+ type => {
73
+ expect(() => parseNamespace({...namespaceDoc, blocks: [{type}]})).toThrow(
74
+ /blocks\.0\.type/u,
75
+ );
76
+ },
77
+ );
78
+
79
+ it('rejects misspelled fields instead of silently dropping them', () => {
80
+ expect(() =>
81
+ parseNamespace({...namespaceDoc, summmary: 'misspelled'}),
82
+ ).toThrow(/summmary/u);
83
+ });
84
+
85
+ it('requires at least one declared slot', () => {
86
+ expect(() => parseNamespace({...namespaceDoc, slots: {}})).toThrow(
87
+ /at least one slot is required/u,
88
+ );
89
+ });
90
+
91
+ it('rejects a collection that names an undeclared slot', () => {
92
+ expect(() =>
93
+ parseNamespace({
94
+ ...namespaceDoc,
95
+ blocks: [{type: 'collection', source: {slot: 'missing'}}],
96
+ }),
97
+ ).toThrow(/blocks\.0\.source\.slot.*declared slot/u);
98
+ });
99
+
100
+ it('rejects an adoption rule that names an undeclared slot', () => {
101
+ expect(() =>
102
+ parseNamespace({
103
+ ...namespaceDoc,
104
+ adopts: [{source: {group: 'cli-api'}, into: 'missing'}],
105
+ }),
106
+ ).toThrow(/adopts\.0\.into.*declared slot/u);
107
+ });
108
+
109
+ it('rejects an adoption rule whose generated kind is not accepted', () => {
110
+ expect(() =>
111
+ parseNamespace({
112
+ ...namespaceDoc,
113
+ adopts: [
114
+ {
115
+ source: {group: 'cli-api', kinds: ['command']},
116
+ into: 'providerGuides',
117
+ groupBy: 'kind',
118
+ },
119
+ ],
120
+ }),
121
+ ).toThrow(/does not accept adopted kind "namespace"/u);
122
+
123
+ expect(() =>
124
+ parseNamespace({
125
+ ...namespaceDoc,
126
+ adopts: [
127
+ {
128
+ source: {group: 'cli-api', kinds: ['command']},
129
+ into: 'providerGuides',
130
+ },
131
+ ],
132
+ }),
133
+ ).toThrow(/does not accept adopted kind "command"/u);
134
+ });
135
+ });
136
+
137
+ describe('semantic blocks in existing docs', () => {
138
+ it('accepts workflow, collection, and reference in a ReferenceDoc section', () => {
139
+ const parsed = parseReference({
140
+ type: 'generic',
141
+ name: 'publishing',
142
+ title: 'Publishing',
143
+ description: 'Publish an integration.',
144
+ sections: [
145
+ {
146
+ id: 'start',
147
+ title: 'Start',
148
+ content: namespaceDoc.blocks,
149
+ },
150
+ ],
151
+ });
152
+ expect(parsed.sections[0].id).toBe('start');
153
+ expect(parsed.sections[0].content.map(block => block.type)).toEqual([
154
+ 'workflow',
155
+ 'collection',
156
+ 'reference',
157
+ ]);
158
+ });
159
+
160
+ it('rejects an unknown stamped doc kind instead of treating it as legacy', () => {
161
+ expect(() => parseDoc({type: 'choice', name: 'x', props: []})).toThrow(
162
+ /unsupported type "choice"/u,
163
+ );
164
+ });
165
+ });
@@ -0,0 +1,71 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Namespace doc types. A namespace owns navigation slots and a
5
+ * renderer-neutral layout over already-discovered docs. It never scans files
6
+ * or copies child documents.
7
+ */
8
+
9
+ import type {AuthoredDocGraphFields, AuthoredDocKind} from '../base/type.js';
10
+ import type {ReferenceContentBlock} from '../reference/type.js';
11
+
12
+ /** Which providers may contribute appearances to a namespace slot. */
13
+ export type NamespaceProviderScope = 'same' | 'configured';
14
+
15
+ /** Constraints declared by the namespace that owns a slot. */
16
+ export interface NamespaceSlotAcceptance {
17
+ /** Authored doc kinds accepted by this slot. */
18
+ kinds: AuthoredDocKind[];
19
+ /** Omit for the namespace provider; `configured` admits provider appearances. */
20
+ providers?: NamespaceProviderScope;
21
+ }
22
+
23
+ /** One named placement and collection target owned by a NamespaceDoc. */
24
+ export interface NamespaceSlot {
25
+ /** Human-readable heading for children in this slot. */
26
+ title: string;
27
+ /** Which docs may be placed or shown in this slot. */
28
+ accepts: NamespaceSlotAcceptance;
29
+ }
30
+
31
+ /** One logical source group that a namespace may adopt. */
32
+ export interface NamespaceAdoptionSource {
33
+ /** Provider-local discovery group, such as `cli-commands`. */
34
+ group: string;
35
+ /** Optional subset of authored kinds from the group. */
36
+ kinds?: AuthoredDocKind[];
37
+ }
38
+
39
+ /**
40
+ * Assigns otherwise-unplaced docs from one provider-local source group to a
41
+ * child namespace. Discovery defines groups; this rule never scans a folder.
42
+ */
43
+ export interface NamespaceAdoptionRule {
44
+ source: NamespaceAdoptionSource;
45
+ /** Slot owned by this namespace that becomes the canonical destination. */
46
+ into: string;
47
+ /** Generate one child namespace per authored kind. */
48
+ groupBy?: 'kind';
49
+ }
50
+
51
+ /**
52
+ * An authored documentation namespace. Its ordered blocks control layout while
53
+ * slots and adoption rules describe where already-discovered docs may appear.
54
+ */
55
+ export interface NamespaceDoc extends AuthoredDocGraphFields {
56
+ type: 'namespace';
57
+ /** Stable provider-local name. Navigation changes do not change this value. */
58
+ name: string;
59
+ /** Human-readable page title. */
60
+ title: string;
61
+ /** One-line summary shown in listings and search results. */
62
+ summary: string;
63
+ /** Search terms that are not already present in the title or summary. */
64
+ keywords?: string[];
65
+ /** Named child-placement and collection targets. */
66
+ slots: Record<string, NamespaceSlot>;
67
+ /** Optional source-adoption rules for otherwise-unplaced docs. */
68
+ adopts?: NamespaceAdoptionRule[];
69
+ /** Ordered renderer-neutral content and collection blocks. */
70
+ blocks?: ReferenceContentBlock[];
71
+ }
@@ -1,14 +1,15 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in authoring/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
- /** @typedef {import('./types').ComponentDoc} ComponentDoc */
5
- /** @typedef {import('./types').HookDoc} HookDoc */
6
- /** @typedef {import('./types').FunctionDoc} FunctionDoc */
7
- /** @typedef {import('./types').ReferenceDoc} ReferenceDoc */
8
- /** @typedef {import('./types').TemplateDoc} TemplateDoc */
9
- /** @typedef {import('./types').SchemaDoc} SchemaDoc */
10
- /** @typedef {import('./types').CommandDoc} CommandDoc */
11
- /** @typedef {import('./types').EnumDoc} EnumDoc */
4
+ /** @typedef {import('./types.js').ComponentDoc} ComponentDoc */
5
+ /** @typedef {import('./types.js').HookDoc} HookDoc */
6
+ /** @typedef {import('./types.js').FunctionDoc} FunctionDoc */
7
+ /** @typedef {import('./types.js').ReferenceDoc} ReferenceDoc */
8
+ /** @typedef {import('./types.js').TemplateDoc} TemplateDoc */
9
+ /** @typedef {import('./types.js').SchemaDoc} SchemaDoc */
10
+ /** @typedef {import('./types.js').CommandDoc} CommandDoc */
11
+ /** @typedef {import('./types.js').EnumDoc} EnumDoc */
12
+ /** @typedef {import('./types.js').NamespaceDoc} NamespaceDoc */
12
13
  /**
13
14
  * Validate an unknown loaded doc value into its typed shape, or throw.
14
15
  * Dispatches on the stamped `type`; unstamped docs fall back to
@@ -17,14 +18,15 @@
17
18
  *
18
19
  * @param {unknown} input
19
20
  * @param {string} [label]
20
- * @returns {ComponentDoc | HookDoc | FunctionDoc | ReferenceDoc | TemplateDoc | SchemaDoc | CommandDoc | EnumDoc}
21
+ * @returns {ComponentDoc | HookDoc | FunctionDoc | ReferenceDoc | TemplateDoc | SchemaDoc | CommandDoc | EnumDoc | NamespaceDoc}
21
22
  */
22
- export function parseDoc(input: unknown, label?: string): ComponentDoc | HookDoc | FunctionDoc | ReferenceDoc | TemplateDoc | SchemaDoc | CommandDoc | EnumDoc;
23
- export type ComponentDoc = import("./types").ComponentDoc;
24
- export type HookDoc = import("./types").HookDoc;
25
- export type FunctionDoc = import("./types").FunctionDoc;
26
- export type ReferenceDoc = import("./types").ReferenceDoc;
27
- export type TemplateDoc = import("./types").TemplateDoc;
28
- export type SchemaDoc = import("./types").SchemaDoc;
29
- export type CommandDoc = import("./types").CommandDoc;
30
- export type EnumDoc = import("./types").EnumDoc;
23
+ export function parseDoc(input: unknown, label?: string): ComponentDoc | HookDoc | FunctionDoc | ReferenceDoc | TemplateDoc | SchemaDoc | CommandDoc | EnumDoc | NamespaceDoc;
24
+ export type ComponentDoc = import("./types.js").ComponentDoc;
25
+ export type HookDoc = import("./types.js").HookDoc;
26
+ export type FunctionDoc = import("./types.js").FunctionDoc;
27
+ export type ReferenceDoc = import("./types.js").ReferenceDoc;
28
+ export type TemplateDoc = import("./types.js").TemplateDoc;
29
+ export type SchemaDoc = import("./types.js").SchemaDoc;
30
+ export type CommandDoc = import("./types.js").CommandDoc;
31
+ export type EnumDoc = import("./types.js").EnumDoc;
32
+ export type NamespaceDoc = import("./types.js").NamespaceDoc;