@astryxdesign/cli 0.6.3-canary.7ada42e → 0.6.3-canary.7ec7be0
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.
- package/README.md +21 -19
- package/api/build/_adapter.d.mts +50 -0
- package/api/build/_adapter.mjs +60 -0
- package/api/build/build.doc.mjs +13 -7
- package/api/build/build.test.mjs +184 -6
- package/api/build/build.type.d.mts +57 -2
- package/api/build/build.type.mjs +30 -7
- package/api/build/help/help.d.mts +4 -0
- package/api/build/help/help.mjs +24 -11
- package/api/build/kit/kit.d.mts +4 -1
- package/api/build/kit/kit.mjs +165 -49
- package/api/build/kit/rank.d.mts +44 -0
- package/api/build/kit/rank.mjs +432 -0
- package/api/build/kit/rank.test.mjs +196 -0
- package/api/component/_adapter.d.mts +6 -12
- package/api/component/_adapter.mjs +20 -10
- package/api/component/component.mjs +60 -12
- package/api/component/list/list.mjs +3 -2
- package/api/docs/_adapter.d.mts +251 -28
- package/api/docs/_adapter.mjs +918 -35
- package/api/docs/detail/detail.mjs +11 -3
- package/api/docs/detail/section/section.mjs +24 -13
- package/api/docs/detail/section/section.test.mjs +15 -6
- package/api/docs/docs.d.mts +5 -3
- package/api/docs/docs.doc.mjs +38 -17
- package/api/docs/docs.mjs +44 -8
- package/api/docs/docs.test.mjs +158 -4
- package/api/docs/docs.type.d.mts +181 -2
- package/api/docs/docs.type.mjs +117 -3
- package/api/docs/index/index.mjs +11 -3
- package/api/docs/index/index.test.mjs +1 -1
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +14 -14
- package/api/docs/list/list.mjs +25 -5
- package/api/docs/node/node.d.mts +43 -0
- package/api/docs/node/node.mjs +192 -0
- package/api/docs/reference-blocks.test.mjs +406 -0
- package/api/doctor/doctor.d.mts +30 -2
- package/api/doctor/doctor.mjs +151 -8
- package/api/doctor/doctor.test.mjs +95 -7
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +1 -1
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +107 -5
- package/api/integration/add-contribution.test.mjs +137 -0
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +171 -3
- package/api/integration/authoring-checks.type.mjs +6 -1
- package/api/integration/integration-authoring.type.d.mts +2 -0
- package/api/integration/integration-authoring.type.mjs +2 -0
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddDoc.doc.mjs +7 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationPackCheck.doc.mjs +1 -1
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +3 -0
- package/api/integration/pack-check.mjs +34 -0
- package/api/integration/pack-check.test.mjs +121 -0
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration.test.mjs +3 -3
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/index.ts +2 -0
- package/api/layout/_adapter.mjs +20 -5
- package/api/search/search.d.mts +59 -1
- package/api/search/search.doc.mjs +4 -3
- package/api/search/search.mjs +442 -71
- package/api/search/search.test.mjs +91 -2
- package/api/search/search.type.d.mts +13 -1
- package/api/search/search.type.mjs +4 -1
- package/api/template/list/list.mjs +1 -0
- package/api/template/table-floating-bulk-actions.test.mjs +66 -0
- package/api/template/template-integration.test.mjs +1072 -3
- package/api/template/template.doc.mjs +27 -7
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +6 -8
- package/api/template/template.type.mjs +3 -2
- package/api/theme/template/template.test.mjs +5 -0
- package/api/theme/themeTemplate.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/layout.doc.dense.mjs +2 -2
- package/assets/docs/layout.doc.mjs +1 -1
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +3 -3
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +4 -4
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +37 -9
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +3 -3
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
- package/authoring/debug/parse.d.mts +3 -3
- package/authoring/doctypes/_schema.d.mts +119 -117
- package/authoring/doctypes/_schema.mjs +54 -3
- package/authoring/doctypes/base/graph-fields.doc.mjs +7 -5
- package/authoring/doctypes/base/type.ts +6 -5
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +2 -2
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -1
- package/authoring/doctypes/function/function.doc.mjs +3 -2
- package/authoring/doctypes/function/type.ts +3 -2
- package/authoring/doctypes/load-contract.test.mjs +26 -1
- package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
- package/authoring/doctypes/namespace/parse.test.mjs +23 -25
- package/authoring/doctypes/namespace/type.ts +5 -2
- package/authoring/doctypes/parse.mjs +3 -2
- package/authoring/doctypes/reference/reference.doc.mjs +34 -5
- package/authoring/doctypes/reference/type.ts +28 -11
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -1
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +4 -0
- package/authoring/doctypes/template/parse.test.mjs +18 -0
- package/authoring/doctypes/template/template.doc.mjs +6 -0
- package/authoring/doctypes/template/type.ts +8 -0
- package/authoring/identity/identity.doc.mjs +2 -2
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/type.ts +1 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.mjs +1 -1
- package/clients/cli/commands/build.doc.mjs +11 -6
- package/clients/cli/commands/build.mjs +105 -67
- package/clients/cli/commands/build.text-fields.test.mjs +41 -0
- package/clients/cli/commands/component/index.mjs +1 -1
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
- package/clients/cli/commands/discover.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +20 -8
- package/clients/cli/commands/docs.mjs +183 -69
- package/clients/cli/commands/docs.test.mjs +120 -16
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -2
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +14 -7
- package/clients/cli/commands/doctor-integration.test.mjs +75 -1
- package/clients/cli/commands/doctor.mjs +53 -16
- package/clients/cli/commands/integration-add.doc.mjs +10 -0
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/search.mjs +11 -2
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/template.doc.mjs +23 -6
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/index.mjs +8 -0
- package/clients/cli/lib/manifest.mjs +9 -2
- package/foundation/agent-docs/agent-docs.d.mts +3 -2
- package/foundation/agent-docs/agent-docs.mjs +13 -9
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- package/foundation/config/project.d.mts +12 -11
- package/foundation/config/project.mjs +73 -59
- package/foundation/config/project.test.mjs +141 -5
- package/foundation/discovery/authoring-self-docs.d.mts +12 -0
- package/foundation/discovery/authoring-self-docs.mjs +20 -8
- package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
- package/foundation/discovery/cli-self-docs.d.mts +40 -34
- package/foundation/discovery/cli-self-docs.mjs +91 -115
- package/foundation/discovery/cli-self-docs.test.mjs +104 -166
- package/foundation/discovery/component-discovery.d.mts +38 -0
- package/foundation/discovery/component-discovery.mjs +48 -0
- package/foundation/discovery/docs-discovery.d.mts +109 -9
- package/foundation/discovery/docs-discovery.mjs +223 -24
- package/foundation/discovery/docs-discovery.test.mjs +103 -27
- package/foundation/discovery/docs-output-budget.d.mts +2 -2
- package/foundation/discovery/docs-output-budget.mjs +1 -1
- package/foundation/discovery/docs-section-key.d.mts +28 -10
- package/foundation/discovery/docs-section-key.mjs +134 -33
- package/foundation/discovery/docs-section-key.test.mjs +41 -19
- package/foundation/discovery/template-adapter.d.mts +92 -6
- package/foundation/discovery/template-adapter.mjs +453 -17
- package/foundation/discovery/template-adapter.test.mjs +42 -0
- package/foundation/discovery/template-conflict-release.d.mts +13 -0
- package/foundation/discovery/template-conflict-release.mjs +40 -0
- package/foundation/discovery/template-conflict-release.test.mjs +40 -0
- package/foundation/doc-compiler/bundle.mjs +61 -1
- package/foundation/doc-compiler/bundle.test.mjs +21 -10
- package/foundation/doc-compiler/compile.d.mts +113 -132
- package/foundation/doc-compiler/compile.mjs +57 -10
- package/foundation/doc-compiler/diagnostics.mjs +28 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +2 -2
- package/foundation/doc-compiler/inputs.d.mts +2 -2
- package/foundation/doc-compiler/inputs.mjs +6 -1
- package/foundation/doc-compiler/ir.mjs +7 -0
- package/foundation/doc-compiler/lenses.d.mts +8 -5
- package/foundation/doc-compiler/lenses.mjs +50 -4
- package/foundation/doc-compiler/links.d.mts +162 -0
- package/foundation/doc-compiler/links.mjs +294 -0
- package/foundation/doc-compiler/links.test.mjs +192 -0
- package/foundation/doc-compiler/lower-doc.test.mjs +5 -2
- package/foundation/doc-compiler/read.d.mts +3 -2
- package/foundation/doc-compiler/read.mjs +6 -1
- package/foundation/doc-compiler/tree.d.mts +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +84 -0
- package/foundation/integrations/contribution-inventory.d.mts +2 -1
- package/foundation/integrations/contribution-inventory.mjs +10 -4
- package/foundation/integrations/contribution-inventory.test.mjs +19 -0
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +51 -26
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/validate-contributions.mjs +25 -27
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +19 -10
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +18 -0
- package/foundation/xle/expand.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +9 -9
- package/assets/docs/cli.doc.mjs +0 -15
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The CLI an integration's namespace docs need (spec:AST-046 FR11).
|
|
5
|
+
*
|
|
6
|
+
* @input A package.json object.
|
|
7
|
+
* @output Whether its `peerDependencies` range for `@astryxdesign/cli` admits
|
|
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.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {semverCompare} from '../env/semver.mjs';
|
|
14
|
+
|
|
15
|
+
export const CLI_PACKAGE = '@astryxdesign/cli';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 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.
|
|
23
|
+
*/
|
|
24
|
+
export const DOCS_TREE_CLI = '0.7.0';
|
|
25
|
+
|
|
26
|
+
const VERSION_RE = /^v?(\d+|[xX*])(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A version or partial version as MAJOR.MINOR.PATCH, a wildcard part as 0.
|
|
30
|
+
* @param {string} text
|
|
31
|
+
* @returns {string | null}
|
|
32
|
+
*/
|
|
33
|
+
function lowerVersion(text) {
|
|
34
|
+
const m = VERSION_RE.exec(text.trim());
|
|
35
|
+
if (!m) return null;
|
|
36
|
+
return [m[1], m[2], m[3]]
|
|
37
|
+
.map(part => (part == null || /^[xX*]$/.test(part) ? '0' : part))
|
|
38
|
+
.join('.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The lowest version one space-joined comparator set admits, or null when it
|
|
43
|
+
* has no lower bound this can read.
|
|
44
|
+
* @param {string} set
|
|
45
|
+
* @returns {string | null}
|
|
46
|
+
*/
|
|
47
|
+
function lowestOfSet(set) {
|
|
48
|
+
if (set === '' || set === '*' || /^[xX]$/.test(set)) return null;
|
|
49
|
+
const hyphen = /^(\S+)\s+-\s+\S+$/.exec(set);
|
|
50
|
+
if (hyphen) return lowerVersion(hyphen[1]);
|
|
51
|
+
/** @type {string | null} */
|
|
52
|
+
let lowest = null;
|
|
53
|
+
for (const comparator of set.split(/\s+/)) {
|
|
54
|
+
const m = /^(>=|<=|>|<|=|\^|~)?(.+)$/.exec(comparator);
|
|
55
|
+
if (!m) return null;
|
|
56
|
+
if (m[1] === '<' || m[1] === '<=') continue;
|
|
57
|
+
const version = lowerVersion(m[2]);
|
|
58
|
+
if (version == null) return null;
|
|
59
|
+
if (lowest == null || semverCompare(version, lowest) > 0) lowest = version;
|
|
60
|
+
}
|
|
61
|
+
return lowest;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The lowest version a semver range admits: the least of its `||`
|
|
66
|
+
* alternatives. Null when an alternative has no lower bound, or the range is
|
|
67
|
+
* not one this can read, so the caller treats it as admitting every version.
|
|
68
|
+
* @param {unknown} range
|
|
69
|
+
* @returns {string | null}
|
|
70
|
+
*/
|
|
71
|
+
export function lowestAdmitted(range) {
|
|
72
|
+
if (typeof range !== 'string') return null;
|
|
73
|
+
/** @type {string | null} */
|
|
74
|
+
let lowest = null;
|
|
75
|
+
for (const alternative of range.split('||')) {
|
|
76
|
+
const low = lowestOfSet(alternative.trim());
|
|
77
|
+
if (low == null) return null;
|
|
78
|
+
if (lowest == null || semverCompare(low, lowest) < 0) lowest = low;
|
|
79
|
+
}
|
|
80
|
+
return lowest;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Why a package that uses a feature an older CLI cannot read would lose it,
|
|
85
|
+
* or null when its declared CLI range admits only CLIs that read it.
|
|
86
|
+
* @param {any} pkg package.json
|
|
87
|
+
* @param {string} feature what the package does, e.g. "ships a namespace doc"
|
|
88
|
+
* @param {string} loss what an older CLI does with it
|
|
89
|
+
* @returns {string | null}
|
|
90
|
+
*/
|
|
91
|
+
function cliRangeProblem(pkg, feature, loss) {
|
|
92
|
+
const range = pkg?.peerDependencies?.[CLI_PACKAGE];
|
|
93
|
+
const fix = `"${CLI_PACKAGE}": ">=${DOCS_TREE_CLI}" in peerDependencies (optional in peerDependenciesMeta, if the CLI is not required)`;
|
|
94
|
+
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}.`;
|
|
96
|
+
}
|
|
97
|
+
const lowest = lowestAdmitted(range);
|
|
98
|
+
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}.`;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Why a package that ships a namespace doc or a placed guide would lose its
|
|
106
|
+
* docs on an older CLI, or null when its declared CLI range admits only CLIs
|
|
107
|
+
* that read the docs tree. Published 0.6.3 hides every topic of a package
|
|
108
|
+
* with a namespace doc; builds of main before the docs tree also do so for a
|
|
109
|
+
* placed guide.
|
|
110
|
+
* @param {any} pkg package.json
|
|
111
|
+
* @returns {string | null}
|
|
112
|
+
*/
|
|
113
|
+
export function docsTreeCliProblem(pkg) {
|
|
114
|
+
return cliRangeProblem(
|
|
115
|
+
pkg,
|
|
116
|
+
'ships a namespace doc or a placed guide',
|
|
117
|
+
'does not read the docs tree, and can hide every doc topic the package ships',
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
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
|
|
124
|
+
* range admits only CLIs that read the field.
|
|
125
|
+
* @param {any} pkg package.json
|
|
126
|
+
* @returns {string | null}
|
|
127
|
+
*/
|
|
128
|
+
export function replacesCliProblem(pkg) {
|
|
129
|
+
return cliRangeProblem(
|
|
130
|
+
pkg,
|
|
131
|
+
'has a template that sets `replaces`',
|
|
132
|
+
"rejects the field, and withholds the package's templates and doc topics",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The package.json with a CLI peer that reads namespace docs: optional, unless
|
|
138
|
+
* the package already says otherwise.
|
|
139
|
+
* @param {any} pkg package.json
|
|
140
|
+
* @returns {any}
|
|
141
|
+
*/
|
|
142
|
+
export function withDocsTreeCli(pkg) {
|
|
143
|
+
const meta = pkg.peerDependenciesMeta ?? {};
|
|
144
|
+
return {
|
|
145
|
+
...pkg,
|
|
146
|
+
peerDependencies: {
|
|
147
|
+
...(pkg.peerDependencies ?? {}),
|
|
148
|
+
[CLI_PACKAGE]: `>=${DOCS_TREE_CLI}`,
|
|
149
|
+
},
|
|
150
|
+
peerDependenciesMeta: meta[CLI_PACKAGE]
|
|
151
|
+
? meta
|
|
152
|
+
: {...meta, [CLI_PACKAGE]: {optional: true}},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import {
|
|
5
|
+
DOCS_TREE_CLI,
|
|
6
|
+
lowestAdmitted,
|
|
7
|
+
docsTreeCliProblem,
|
|
8
|
+
replacesCliProblem,
|
|
9
|
+
withDocsTreeCli,
|
|
10
|
+
} from './cli-requirement.mjs';
|
|
11
|
+
|
|
12
|
+
describe('lowestAdmitted', () => {
|
|
13
|
+
it.each([
|
|
14
|
+
['>=0.7.0', '0.7.0'],
|
|
15
|
+
['^0.6.0', '0.6.0'],
|
|
16
|
+
['~0.7.1', '0.7.1'],
|
|
17
|
+
['0.7.x', '0.7.0'],
|
|
18
|
+
['>=0.7.0 <0.8.0', '0.7.0'],
|
|
19
|
+
['^0.6.0 || >=0.7.0', '0.6.0'],
|
|
20
|
+
['0.8.0 - 0.9.0', '0.8.0'],
|
|
21
|
+
['*', null],
|
|
22
|
+
['<1.0.0', null],
|
|
23
|
+
['latest', null],
|
|
24
|
+
['workspace:*', null],
|
|
25
|
+
])('%s admits %s at the lowest', (range, lowest) => {
|
|
26
|
+
expect(lowestAdmitted(range)).toBe(lowest);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('docsTreeCliProblem', () => {
|
|
31
|
+
it('asks for a CLI peer when there is none', () => {
|
|
32
|
+
expect(docsTreeCliProblem({name: '@acme/kit'})).toContain(
|
|
33
|
+
'declares no @astryxdesign/cli peer',
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it.each(['^0.6.0', '^0.6.0 || >=0.7.0', '*', '>=0.6.3'])(
|
|
38
|
+
'refuses %s, which admits a CLI that cannot read a namespace doc',
|
|
39
|
+
range => {
|
|
40
|
+
expect(
|
|
41
|
+
docsTreeCliProblem({peerDependencies: {'@astryxdesign/cli': range}}),
|
|
42
|
+
).toContain('admits a CLI older than');
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
it.each(['>=0.7.0', '^0.7.2', '>=0.7.0 <2'])('accepts %s', range => {
|
|
47
|
+
expect(
|
|
48
|
+
docsTreeCliProblem({peerDependencies: {'@astryxdesign/cli': range}}),
|
|
49
|
+
).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('declares the peer as optional, and keeps what the package says of it', () => {
|
|
53
|
+
const declared = withDocsTreeCli({name: '@acme/kit'});
|
|
54
|
+
expect(declared.peerDependencies).toEqual({
|
|
55
|
+
'@astryxdesign/cli': `>=${DOCS_TREE_CLI}`,
|
|
56
|
+
});
|
|
57
|
+
expect(declared.peerDependenciesMeta).toEqual({
|
|
58
|
+
'@astryxdesign/cli': {optional: true},
|
|
59
|
+
});
|
|
60
|
+
expect(docsTreeCliProblem(declared)).toBeNull();
|
|
61
|
+
const required = withDocsTreeCli({
|
|
62
|
+
peerDependencies: {react: '^19.0.0', '@astryxdesign/cli': '^0.6.0'},
|
|
63
|
+
peerDependenciesMeta: {'@astryxdesign/cli': {optional: false}},
|
|
64
|
+
});
|
|
65
|
+
expect(required.peerDependencies.react).toBe('^19.0.0');
|
|
66
|
+
expect(required.peerDependenciesMeta['@astryxdesign/cli']).toEqual({
|
|
67
|
+
optional: false,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('replacesCliProblem', () => {
|
|
73
|
+
it('asks a package that sets replaces for a CLI that reads the field', () => {
|
|
74
|
+
expect(replacesCliProblem({name: '@acme/kit'})).toContain(
|
|
75
|
+
'sets `replaces`',
|
|
76
|
+
);
|
|
77
|
+
expect(
|
|
78
|
+
replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '^0.6.0'}}),
|
|
79
|
+
).toContain('admits a CLI older than');
|
|
80
|
+
expect(
|
|
81
|
+
replacesCliProblem({peerDependencies: {'@astryxdesign/cli': '>=0.7.0'}}),
|
|
82
|
+
).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -26,7 +26,7 @@ export function computeRequiredFiles(loaded: import("./integrations.mjs").Loaded
|
|
|
26
26
|
* @typedef {object} ContributionIdentities
|
|
27
27
|
* @property {{slug: string, exportName: string}[]} themes
|
|
28
28
|
* @property {string[]} components
|
|
29
|
-
* @property {{id: string, type: string, name: string}[]} templates
|
|
29
|
+
* @property {{id: string, type: string, name: string, replaces?: string}[]} templates
|
|
30
30
|
* @property {{version: string, id: string}[]} codemods
|
|
31
31
|
* @property {string[]} docs
|
|
32
32
|
* @property {string[]} agentDocsAppend
|
|
@@ -127,6 +127,7 @@ export type ContributionIdentities = {
|
|
|
127
127
|
id: string;
|
|
128
128
|
type: string;
|
|
129
129
|
name: string;
|
|
130
|
+
replaces?: string;
|
|
130
131
|
}[];
|
|
131
132
|
codemods: {
|
|
132
133
|
version: string;
|
|
@@ -189,7 +189,7 @@ export function computeRequiredFiles(loaded) {
|
|
|
189
189
|
* @typedef {object} ContributionIdentities
|
|
190
190
|
* @property {{slug: string, exportName: string}[]} themes
|
|
191
191
|
* @property {string[]} components
|
|
192
|
-
* @property {{id: string, type: string, name: string}[]} templates
|
|
192
|
+
* @property {{id: string, type: string, name: string, replaces?: string}[]} templates
|
|
193
193
|
* @property {{version: string, id: string}[]} codemods
|
|
194
194
|
* @property {string[]} docs
|
|
195
195
|
* @property {string[]} agentDocsAppend
|
|
@@ -278,7 +278,12 @@ export async function collectIdentities(loaded) {
|
|
|
278
278
|
errors.push({kind: 'templates', message: templateError.message});
|
|
279
279
|
}
|
|
280
280
|
identities.templates = templates
|
|
281
|
-
.map(t => ({
|
|
281
|
+
.map(t => ({
|
|
282
|
+
id: t.dirName,
|
|
283
|
+
type: t.type,
|
|
284
|
+
name: t.name,
|
|
285
|
+
...(t.replaces == null ? {} : {replaces: t.replaces}),
|
|
286
|
+
}))
|
|
282
287
|
.sort((a, b) => a.id.localeCompare(b.id));
|
|
283
288
|
} catch (err) {
|
|
284
289
|
errors.push({
|
|
@@ -309,12 +314,13 @@ export async function collectIdentities(loaded) {
|
|
|
309
314
|
|
|
310
315
|
if (loaded.docs && fs.existsSync(loaded.docs)) {
|
|
311
316
|
try {
|
|
312
|
-
const {records, errors: docErrors} =
|
|
317
|
+
const {records, guides, errors: docErrors} =
|
|
313
318
|
await discoverIntegrationDocs(loaded);
|
|
314
319
|
for (const docError of docErrors) {
|
|
315
320
|
errors.push({kind: 'docs', message: docError.message});
|
|
316
321
|
}
|
|
317
|
-
|
|
322
|
+
// A guide placed in the docs tree is a doc contribution too.
|
|
323
|
+
identities.docs = [...records, ...guides].map(r => r.name).sort();
|
|
318
324
|
} catch (err) {
|
|
319
325
|
errors.push({kind: 'docs', message: /** @type {Error} */ (err).message});
|
|
320
326
|
}
|
|
@@ -304,6 +304,25 @@ describe('compareIdentities', () => {
|
|
|
304
304
|
expect(issues[0].message).toContain('dash');
|
|
305
305
|
});
|
|
306
306
|
|
|
307
|
+
it('errors when a packed template replaces a different target', () => {
|
|
308
|
+
const template = {id: 'acme-shell', type: 'page', name: 'Acme shell'};
|
|
309
|
+
const local = {
|
|
310
|
+
...base(),
|
|
311
|
+
templates: [{...template, replaces: 'shell-side-nav'}],
|
|
312
|
+
};
|
|
313
|
+
const packed = {
|
|
314
|
+
...base(),
|
|
315
|
+
templates: [{...template, replaces: 'shell-top-nav'}],
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
expect(compareIdentities(local, packed)).toEqual([
|
|
319
|
+
expect.objectContaining({
|
|
320
|
+
code: 'identity_mismatch',
|
|
321
|
+
message: expect.stringContaining('acme-shell'),
|
|
322
|
+
}),
|
|
323
|
+
]);
|
|
324
|
+
});
|
|
325
|
+
|
|
307
326
|
it('errors when a codemod is missing from packed', () => {
|
|
308
327
|
const local = {
|
|
309
328
|
...base(),
|
|
@@ -8,10 +8,17 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Warning: <pkg> has N integration issue(s). Run: astryx doctor integration validate <pkg>
|
|
10
10
|
*
|
|
11
|
-
* @param {Array<import('./integrations.mjs').LoadedIntegration>
|
|
11
|
+
* @param {Array<import('./integrations.mjs').LoadedIntegration> | {issues: () => Promise<Array<{package: string, code: string, severity: string, message: string}>>}} source loaded integrations or their Project owner
|
|
12
12
|
* @param {{json?: boolean}} [options]
|
|
13
13
|
* @returns {Promise<void>}
|
|
14
14
|
*/
|
|
15
|
-
export function warnOnIntegrationIssues(
|
|
15
|
+
export function warnOnIntegrationIssues(source: Array<import("./integrations.mjs").LoadedIntegration> | {
|
|
16
|
+
issues: () => Promise<Array<{
|
|
17
|
+
package: string;
|
|
18
|
+
code: string;
|
|
19
|
+
severity: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}>>;
|
|
22
|
+
}, { json }?: {
|
|
16
23
|
json?: boolean;
|
|
17
24
|
}): Promise<void>;
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* contributions or spamming per-contribution diagnostics.
|
|
11
11
|
*
|
|
12
12
|
* Design constraints (all enforced here):
|
|
13
|
-
* - Reuses
|
|
14
|
-
*
|
|
13
|
+
* - Reuses Project.issues() when a Project is available, so cross-package
|
|
14
|
+
* replacement warnings are visible; accepts loaded integrations for legacy
|
|
15
|
+
* internal callers and tests.
|
|
15
16
|
* - Writes to STDERR only, so it never corrupts a --json stdout envelope.
|
|
16
17
|
* - Suppressed entirely in --json mode.
|
|
17
18
|
* - Best-effort: never throws, never changes the exit code. Broken
|
|
@@ -28,38 +29,62 @@ import {validateLoadedIntegration} from './validate-contributions.mjs';
|
|
|
28
29
|
*
|
|
29
30
|
* Warning: <pkg> has N integration issue(s). Run: astryx doctor integration validate <pkg>
|
|
30
31
|
*
|
|
31
|
-
* @param {Array<import('./integrations.mjs').LoadedIntegration>
|
|
32
|
+
* @param {Array<import('./integrations.mjs').LoadedIntegration> | {issues: () => Promise<Array<{package: string, code: string, severity: string, message: string}>>}} source loaded integrations or their Project owner
|
|
32
33
|
* @param {{json?: boolean}} [options]
|
|
33
34
|
* @returns {Promise<void>}
|
|
34
35
|
*/
|
|
35
|
-
export async function warnOnIntegrationIssues(
|
|
36
|
+
export async function warnOnIntegrationIssues(source, {json = false} = {}) {
|
|
36
37
|
try {
|
|
37
|
-
// Cheap guard: only do work when integrations are actually configured.
|
|
38
38
|
if (json) return;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
|
|
40
|
+
/** @type {Map<string, number>} */
|
|
41
|
+
const issueCounts = new Map();
|
|
42
|
+
const fromProject =
|
|
43
|
+
source != null &&
|
|
44
|
+
!Array.isArray(source) &&
|
|
45
|
+
typeof source.issues === 'function';
|
|
46
|
+
|
|
47
|
+
if (fromProject) {
|
|
48
|
+
const issues = await source.issues();
|
|
49
|
+
for (const issue of issues) {
|
|
50
|
+
// A provider-conflict issue carries the specific message that
|
|
51
|
+
// `doctor integration validate` would not explain. Print it directly.
|
|
52
|
+
if (issue.code === 'duplicate_provider') {
|
|
53
|
+
console.error(`Warning: ${issue.message}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
issueCounts.set(
|
|
57
|
+
issue.package,
|
|
58
|
+
(issueCounts.get(issue.package) ?? 0) + 1,
|
|
59
|
+
);
|
|
49
60
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
61
|
+
} else if (Array.isArray(source)) {
|
|
62
|
+
for (const integration of source) {
|
|
63
|
+
if (!integration || typeof integration !== 'object') continue;
|
|
64
|
+
// A package set aside for a provider-ID conflict is fine in isolation,
|
|
65
|
+
// so `doctor integration validate` would not explain it. Say it here.
|
|
66
|
+
if (integration.__providerConflict) {
|
|
67
|
+
console.error(`Warning: ${integration.__providerConflict.message}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
let issues;
|
|
71
|
+
try {
|
|
72
|
+
issues = await validateLoadedIntegration(integration);
|
|
73
|
+
} catch {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(issues) || issues.length === 0) continue;
|
|
77
|
+
const pkg = integration.name ?? integration.__spec ?? '(integration)';
|
|
78
|
+
issueCounts.set(pkg, issues.length);
|
|
56
79
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const [pkg, count] of issueCounts) {
|
|
83
|
+
const command = fromProject
|
|
84
|
+
? 'astryx doctor'
|
|
85
|
+
: `astryx doctor integration validate ${pkg}`;
|
|
60
86
|
console.error(
|
|
61
|
-
`Warning: ${pkg} has ${
|
|
62
|
-
`Run: astryx doctor integration validate ${pkg}`,
|
|
87
|
+
`Warning: ${pkg} has ${count} integration issue(s). Run: ${command}`,
|
|
63
88
|
);
|
|
64
89
|
}
|
|
65
90
|
} catch {
|
|
@@ -37,7 +37,12 @@ afterEach(() => {
|
|
|
37
37
|
* Build a loaded-integration-shaped object. Roots are absolute (mirrors
|
|
38
38
|
* loadIntegrations' resolveRoot).
|
|
39
39
|
*/
|
|
40
|
-
function loaded({
|
|
40
|
+
function loaded({
|
|
41
|
+
name = '@acme/widgets',
|
|
42
|
+
components,
|
|
43
|
+
templates,
|
|
44
|
+
codemods,
|
|
45
|
+
} = {}) {
|
|
41
46
|
return {
|
|
42
47
|
name,
|
|
43
48
|
version: '1.0.0',
|
|
@@ -62,6 +67,25 @@ describe('warnOnIntegrationIssues', () => {
|
|
|
62
67
|
);
|
|
63
68
|
});
|
|
64
69
|
|
|
70
|
+
it('surfaces cross-package issues collected by Project', async () => {
|
|
71
|
+
const project = {
|
|
72
|
+
issues: async () => [
|
|
73
|
+
{
|
|
74
|
+
package: '@acme/later',
|
|
75
|
+
code: 'ambiguous_template_replacement',
|
|
76
|
+
severity: 'warning',
|
|
77
|
+
message: 'Later configured replacement wins.',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
await warnOnIntegrationIssues(project, {json: false});
|
|
83
|
+
|
|
84
|
+
expect(errLines).toEqual([
|
|
85
|
+
'Warning: @acme/later has 1 integration issue(s). Run: astryx doctor',
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
65
89
|
it('emits nothing in --json mode (keeps stdout/JSON clean)', async () => {
|
|
66
90
|
const integration = loaded({components: 'does-not-exist'});
|
|
67
91
|
await warnOnIntegrationIssues([integration], {json: true});
|
|
@@ -116,4 +140,53 @@ describe('warnOnIntegrationIssues', () => {
|
|
|
116
140
|
).resolves.toBeUndefined();
|
|
117
141
|
expect(errLines).toHaveLength(0);
|
|
118
142
|
});
|
|
143
|
+
|
|
144
|
+
it('prints the specific provider-conflict message via the array path', async () => {
|
|
145
|
+
const conflict = {
|
|
146
|
+
name: '@acme/charts',
|
|
147
|
+
version: '2.0.0',
|
|
148
|
+
__spec: '@acme/charts',
|
|
149
|
+
__packageDir: tmpDir,
|
|
150
|
+
__providerConflict: {
|
|
151
|
+
providerId: '@acme/lab',
|
|
152
|
+
claimedBy: '@acme/lab',
|
|
153
|
+
message:
|
|
154
|
+
'@acme/charts@2.0.0 and @acme/lab@1.0.0 both claim provider ID ' +
|
|
155
|
+
'"@acme/lab". @acme/lab@1.0.0 loads first and is used; ' +
|
|
156
|
+
'@acme/charts@2.0.0 contributes nothing until one package changes ' +
|
|
157
|
+
'its providerId.',
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
await warnOnIntegrationIssues([conflict], {json: false});
|
|
161
|
+
|
|
162
|
+
expect(errLines).toHaveLength(1);
|
|
163
|
+
expect(errLines[0]).toBe(`Warning: ${conflict.__providerConflict.message}`);
|
|
164
|
+
// Must NOT print the generic validate pointer for a provider conflict.
|
|
165
|
+
expect(errLines[0]).not.toContain('integration issue(s)');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('prints the specific provider-conflict message via the Project path', async () => {
|
|
169
|
+
const conflictMessage =
|
|
170
|
+
'@acme/charts@2.0.0 and @acme/lab@1.0.0 both claim provider ID ' +
|
|
171
|
+
'"@acme/lab". @acme/lab@1.0.0 loads first and is used; ' +
|
|
172
|
+
'@acme/charts@2.0.0 contributes nothing until one package changes ' +
|
|
173
|
+
'its providerId.';
|
|
174
|
+
const project = {
|
|
175
|
+
issues: async () => [
|
|
176
|
+
{
|
|
177
|
+
package: '@acme/charts@2.0.0',
|
|
178
|
+
code: 'duplicate_provider',
|
|
179
|
+
severity: 'warning',
|
|
180
|
+
message: conflictMessage,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
await warnOnIntegrationIssues(project, {json: false});
|
|
186
|
+
|
|
187
|
+
expect(errLines).toHaveLength(1);
|
|
188
|
+
expect(errLines[0]).toBe(`Warning: ${conflictMessage}`);
|
|
189
|
+
// Must NOT print the generic validate pointer for a provider conflict.
|
|
190
|
+
expect(errLines[0]).not.toContain('integration issue(s)');
|
|
191
|
+
});
|
|
119
192
|
});
|
|
@@ -236,7 +236,11 @@ async function checkTemplates(integration, issues) {
|
|
|
236
236
|
try {
|
|
237
237
|
const {errors} = await discoverIntegrationTemplatesForOne(integration);
|
|
238
238
|
for (const e of errors) {
|
|
239
|
-
issues.push(
|
|
239
|
+
issues.push({
|
|
240
|
+
code: e.code ?? 'invalid_template',
|
|
241
|
+
severity: e.severity ?? 'error',
|
|
242
|
+
message: e.message,
|
|
243
|
+
});
|
|
240
244
|
}
|
|
241
245
|
} catch (err) {
|
|
242
246
|
issues.push(
|
|
@@ -246,41 +250,35 @@ async function checkTemplates(integration, issues) {
|
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
/**
|
|
249
|
-
* Validate the integration's components
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* than hard-failing.
|
|
253
|
-
*
|
|
254
|
-
* `discoverIntegrationComponents` returns ownership records and does not throw
|
|
255
|
-
* on a missing same-stem source — it records `sourcePath: null`. We surface
|
|
256
|
-
* each such record as an `invalid_component` error.
|
|
253
|
+
* Validate the integration's components through the same load-and-parse boundary
|
|
254
|
+
* that component detail uses. Missing source and invalid metadata are reported
|
|
255
|
+
* per component so valid siblings remain available.
|
|
257
256
|
* @param {LoadedIntegration} integration loaded-integration-shaped object
|
|
258
257
|
* @param {Issue[]} issues
|
|
259
258
|
*/
|
|
260
259
|
async function checkComponents(integration, issues) {
|
|
261
260
|
if (!integration.components || !fs.existsSync(integration.components)) return;
|
|
262
|
-
const discover = componentDiscovery.
|
|
261
|
+
const discover = componentDiscovery.discoverValidIntegrationComponents;
|
|
263
262
|
if (typeof discover !== 'function') return; // feature not present yet
|
|
264
263
|
try {
|
|
265
|
-
const
|
|
264
|
+
const {discovered, errors} = await discover(integration);
|
|
266
265
|
/** @type {import('./contribution-fixes.mjs').FixContext | undefined} */
|
|
267
266
|
let context;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
),
|
|
282
|
-
);
|
|
267
|
+
const byName = new Map(discovered.map(record => [record.name, record]));
|
|
268
|
+
for (const error of errors) {
|
|
269
|
+
const record = byName.get(error.name);
|
|
270
|
+
if (record == null || record.sourcePath != null) {
|
|
271
|
+
issues.push(issueError('invalid_component', error.message));
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const docPath = record.docPath;
|
|
275
|
+
let fix = `Fix: add ${record.name}.tsx beside ${docPath ? path.basename(docPath) : `${record.name}.doc.mjs`}.`;
|
|
276
|
+
const stamp = docPath ? readContributionStamp(docPath, false) : null;
|
|
277
|
+
if (stamp && stamp.type !== 'component' && integration.__packageDir) {
|
|
278
|
+
context ??= createFixContext(integration.__packageDir, integration);
|
|
279
|
+
fix = notAComponentFix(context, docPath, stamp);
|
|
283
280
|
}
|
|
281
|
+
issues.push(issueError('invalid_component', `${error.message} ${fix}`));
|
|
284
282
|
}
|
|
285
283
|
const overlap = integration.__packageDir
|
|
286
284
|
? newComponentDocNote(
|
|
@@ -289,7 +287,7 @@ async function checkComponents(integration, issues) {
|
|
|
289
287
|
: '';
|
|
290
288
|
for (const name of findSourceOnlyCandidates(
|
|
291
289
|
integration.components,
|
|
292
|
-
|
|
290
|
+
discovered.map(record => record.name),
|
|
293
291
|
)) {
|
|
294
292
|
// A hidden doc still pairs with its source; discovery just skips it.
|
|
295
293
|
if (
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
* @file EnumDoc for the `type` discriminant carried on every --json success
|
|
6
6
|
* envelope. The vocabulary mirrors the RESPONSE_TYPES map (each command's
|
|
7
7
|
* `jsonOut(...)` call sites) in `clients/cli/lib/manifest.mjs`; a consumer
|
|
8
|
-
* switches on `type` to narrow the `data` payload.
|
|
8
|
+
* switches on `type` to narrow the `data` payload. Descriptions follow the
|
|
9
|
+
* currently published response projection, not a future package-version boundary.
|
|
10
|
+
*
|
|
11
|
+
* @input Public response discriminants and their currently released payloads.
|
|
12
|
+
* @output Generated consumer reference for every typed JSON success response.
|
|
9
13
|
* @position packages/cli/foundation/response — enum documentation
|
|
10
14
|
*/
|
|
11
15
|
/** @type {import('@astryxdesign/cli/authoring').EnumDoc} */
|