@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
|
@@ -25,6 +25,7 @@ import * as fs from 'node:fs';
|
|
|
25
25
|
import * as path from 'node:path';
|
|
26
26
|
import {createRequire} from 'node:module';
|
|
27
27
|
import {readDocView} from '../doc-compiler/read.mjs';
|
|
28
|
+
import {importDocModule} from '../doc-compiler/import.mjs';
|
|
28
29
|
import {CLI_ROOT, discoverExternalPackages} from '../fs/paths.mjs';
|
|
29
30
|
import {CORE_PROVIDER_ID} from '../identity/providers.mjs';
|
|
30
31
|
import {Project} from '../config/project.mjs';
|
|
@@ -44,6 +45,53 @@ export function pkgOf(t) {
|
|
|
44
45
|
return t.package ?? CORE_PACKAGE;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Every id that should resolve to a discovered template. Replacements keep
|
|
50
|
+
* their own integration id and also own the Core id they replace.
|
|
51
|
+
* @param {{dirName: string, replaces?: string}} template
|
|
52
|
+
* @returns {string[]}
|
|
53
|
+
*/
|
|
54
|
+
export function templateLookupIds(template) {
|
|
55
|
+
if (template.replaces && template.replaces !== template.dirName) {
|
|
56
|
+
return [template.dirName, template.replaces];
|
|
57
|
+
}
|
|
58
|
+
return [template.dirName];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove entries shadowed in the default discovery view while retaining them in
|
|
63
|
+
* package-scoped views. An active replacement owns its target id. A rejected
|
|
64
|
+
* same-id declaration falls back to Core when a Core template of the same kind
|
|
65
|
+
* owns that id; otherwise the integration template remains available by kind.
|
|
66
|
+
* @param {DiscoveredTemplate[]} templates
|
|
67
|
+
* @returns {DiscoveredTemplate[]}
|
|
68
|
+
*/
|
|
69
|
+
export function effectiveTemplateDiscovery(templates) {
|
|
70
|
+
const activeTargets = new Set(
|
|
71
|
+
templates
|
|
72
|
+
.filter(template => template.replaces != null)
|
|
73
|
+
.map(template => `${template.type}:${template.replaces}`),
|
|
74
|
+
);
|
|
75
|
+
const coreIds = new Set(
|
|
76
|
+
templates
|
|
77
|
+
.filter(template => pkgOf(template) === CORE_PACKAGE)
|
|
78
|
+
.map(template => `${template.type}:${template.dirName}`),
|
|
79
|
+
);
|
|
80
|
+
return templates.filter(template => {
|
|
81
|
+
if (
|
|
82
|
+
template.replacementRejected &&
|
|
83
|
+
template.replacementTarget === template.dirName &&
|
|
84
|
+
coreIds.has(`${template.type}:${template.dirName}`)
|
|
85
|
+
) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
!activeTargets.has(`${template.type}:${template.dirName}`) ||
|
|
90
|
+
template.replaces != null
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
47
95
|
/**
|
|
48
96
|
* A discovered template (page or block), normalized across core, external, and
|
|
49
97
|
* integration sources. Not every source populates every field, so
|
|
@@ -66,6 +114,11 @@ export function pkgOf(t) {
|
|
|
66
114
|
* @property {string} filePath
|
|
67
115
|
* @property {string} docPath
|
|
68
116
|
* @property {string} [package]
|
|
117
|
+
* @property {boolean} [autolinked] whether the owning integration was discovered
|
|
118
|
+
* from package.json rather than named in astryx.config
|
|
119
|
+
* @property {string} [replaces] active Core template id this integration template replaces
|
|
120
|
+
* @property {boolean} [replacementRejected] whether an invalid declaration was disabled
|
|
121
|
+
* @property {string} [replacementTarget] disabled declaration target
|
|
69
122
|
*/
|
|
70
123
|
|
|
71
124
|
/**
|
|
@@ -74,6 +127,15 @@ export function pkgOf(t) {
|
|
|
74
127
|
* @property {string} package
|
|
75
128
|
* @property {string} [template]
|
|
76
129
|
* @property {string} message
|
|
130
|
+
* @property {string} [code]
|
|
131
|
+
* @property {'warning' | 'error'} [severity]
|
|
132
|
+
* @property {string} [replacementTarget] Core target whose replacement set is invalid
|
|
133
|
+
* @property {boolean} [autolinked] whether the declaration came from an autolinked integration
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A semantic error in an integration template replacement declaration.
|
|
138
|
+
* @typedef {TemplateDiscoveryError & {code: 'missing_template_replacement_target' | 'ambiguous_template_replacement' | 'invalid_template_replacement', severity: 'warning' | 'error'}} TemplateReplacementError
|
|
77
139
|
*/
|
|
78
140
|
|
|
79
141
|
/**
|
|
@@ -683,38 +745,291 @@ export async function discoverCoreTemplates() {
|
|
|
683
745
|
}
|
|
684
746
|
|
|
685
747
|
/**
|
|
748
|
+
* Apply valid integration replacements to a raw template set.
|
|
749
|
+
*
|
|
750
|
+
* One declaration owns its Core target. When different configured packages
|
|
751
|
+
* replace the same target, the later package wins and discovery returns a
|
|
752
|
+
* warning, matching integration-doc replacement order. Multiple declarations
|
|
753
|
+
* inside one package are invalid and fail closed. Missing targets and kind
|
|
754
|
+
* mismatches also fail closed: Core stays selected and every integration
|
|
755
|
+
* template remains addressable by its own id.
|
|
756
|
+
*
|
|
757
|
+
* @param {DiscoveredTemplate[]} templates
|
|
758
|
+
* @param {TemplateDiscoveryError[]} [declarationErrors]
|
|
759
|
+
* @returns {{templates: DiscoveredTemplate[], errors: TemplateReplacementError[]}}
|
|
760
|
+
*/
|
|
761
|
+
export function applyTemplateReplacements(templates, declarationErrors = []) {
|
|
762
|
+
/** @type {Map<string, DiscoveredTemplate[]>} */
|
|
763
|
+
const coreById = new Map();
|
|
764
|
+
/** @type {Map<string, DiscoveredTemplate[]>} */
|
|
765
|
+
const replacementsByTarget = new Map();
|
|
766
|
+
|
|
767
|
+
for (const template of templates) {
|
|
768
|
+
if (pkgOf(template) === CORE_PACKAGE) {
|
|
769
|
+
const matches = coreById.get(template.dirName) ?? [];
|
|
770
|
+
matches.push(template);
|
|
771
|
+
coreById.set(template.dirName, matches);
|
|
772
|
+
}
|
|
773
|
+
if (template.replaces != null) {
|
|
774
|
+
const replacements = replacementsByTarget.get(template.replaces) ?? [];
|
|
775
|
+
replacements.push(template);
|
|
776
|
+
replacementsByTarget.set(template.replaces, replacements);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const activeReplacements = new Set();
|
|
781
|
+
const replacedCore = new Set();
|
|
782
|
+
/** @type {TemplateReplacementError[]} */
|
|
783
|
+
const errors = declarationErrors.map(error => ({
|
|
784
|
+
...error,
|
|
785
|
+
code: /** @type {TemplateReplacementError['code']} */ (
|
|
786
|
+
error.code ?? 'invalid_template_replacement'
|
|
787
|
+
),
|
|
788
|
+
severity: error.severity ?? 'error',
|
|
789
|
+
}));
|
|
790
|
+
/** @param {TemplateReplacementError} error */
|
|
791
|
+
const pushError = error => {
|
|
792
|
+
if (
|
|
793
|
+
errors.some(
|
|
794
|
+
existing =>
|
|
795
|
+
existing.code === error.code &&
|
|
796
|
+
existing.package === error.package &&
|
|
797
|
+
existing.template === error.template &&
|
|
798
|
+
existing.message === error.message,
|
|
799
|
+
)
|
|
800
|
+
) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
errors.push(error);
|
|
804
|
+
};
|
|
805
|
+
for (const [target, replacements] of replacementsByTarget) {
|
|
806
|
+
const targetErrors = errors.filter(
|
|
807
|
+
error => error.replacementTarget === target,
|
|
808
|
+
);
|
|
809
|
+
const hasExplicitIntent =
|
|
810
|
+
replacements.some(replacement => !replacement.autolinked) ||
|
|
811
|
+
targetErrors.some(error => !error.autolinked);
|
|
812
|
+
const contenders = hasExplicitIntent
|
|
813
|
+
? replacements.filter(replacement => !replacement.autolinked)
|
|
814
|
+
: replacements;
|
|
815
|
+
let targetInvalid = targetErrors.some(
|
|
816
|
+
error =>
|
|
817
|
+
error.severity === 'error' && (!hasExplicitIntent || !error.autolinked),
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
/** @type {Map<string, DiscoveredTemplate[]>} */
|
|
821
|
+
const byPackage = new Map();
|
|
822
|
+
for (const replacement of replacements) {
|
|
823
|
+
const pkg = pkgOf(replacement);
|
|
824
|
+
const fromPackage = byPackage.get(pkg) ?? [];
|
|
825
|
+
fromPackage.push(replacement);
|
|
826
|
+
byPackage.set(pkg, fromPackage);
|
|
827
|
+
}
|
|
828
|
+
for (const [pkg, declarations] of byPackage) {
|
|
829
|
+
if (declarations.length < 2) continue;
|
|
830
|
+
if (!hasExplicitIntent || !declarations[0].autolinked) {
|
|
831
|
+
targetInvalid = true;
|
|
832
|
+
}
|
|
833
|
+
const message =
|
|
834
|
+
`${pkg} declares ${declarations.length} templates as replacements for Core ` +
|
|
835
|
+
`template "${target}" (${declarations.map(template => template.dirName).join(', ')}). ` +
|
|
836
|
+
'One package must declare at most one replacement for a Core target.';
|
|
837
|
+
for (const declaration of declarations) {
|
|
838
|
+
pushError({
|
|
839
|
+
code: 'ambiguous_template_replacement',
|
|
840
|
+
severity: 'error',
|
|
841
|
+
package: pkg,
|
|
842
|
+
template: declaration.dirName,
|
|
843
|
+
replacementTarget: target,
|
|
844
|
+
autolinked: declaration.autolinked,
|
|
845
|
+
message,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const coreMatches = coreById.get(target) ?? [];
|
|
851
|
+
/** @type {Map<DiscoveredTemplate, DiscoveredTemplate>} */
|
|
852
|
+
const coreMatchByReplacement = new Map();
|
|
853
|
+
for (const replacement of replacements) {
|
|
854
|
+
const invalidAffectsTarget =
|
|
855
|
+
!hasExplicitIntent || !replacement.autolinked;
|
|
856
|
+
if (coreMatches.length === 0) {
|
|
857
|
+
if (invalidAffectsTarget) targetInvalid = true;
|
|
858
|
+
pushError({
|
|
859
|
+
code: 'missing_template_replacement_target',
|
|
860
|
+
severity: 'error',
|
|
861
|
+
package: pkgOf(replacement),
|
|
862
|
+
template: replacement.dirName,
|
|
863
|
+
replacementTarget: target,
|
|
864
|
+
autolinked: replacement.autolinked,
|
|
865
|
+
message: `Template "${replacement.dirName}" replaces "${target}", which is not a Core template id.`,
|
|
866
|
+
});
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
const sameType = coreMatches.filter(
|
|
870
|
+
core => core.type === replacement.type,
|
|
871
|
+
);
|
|
872
|
+
if (sameType.length !== 1) {
|
|
873
|
+
if (invalidAffectsTarget) targetInvalid = true;
|
|
874
|
+
const kinds = coreMatches.map(core => core.type).join(', ');
|
|
875
|
+
pushError({
|
|
876
|
+
code: 'invalid_template_replacement',
|
|
877
|
+
severity: 'error',
|
|
878
|
+
package: pkgOf(replacement),
|
|
879
|
+
template: replacement.dirName,
|
|
880
|
+
replacementTarget: target,
|
|
881
|
+
autolinked: replacement.autolinked,
|
|
882
|
+
message:
|
|
883
|
+
`Template "${replacement.dirName}" is a ${replacement.type} template, but Core ` +
|
|
884
|
+
`template "${target}" is ${kinds || 'not available'}. A replacement must have the same type.`,
|
|
885
|
+
});
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
coreMatchByReplacement.set(replacement, sameType[0]);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const validContenders = contenders.filter(replacement =>
|
|
892
|
+
coreMatchByReplacement.has(replacement),
|
|
893
|
+
);
|
|
894
|
+
if (targetInvalid || validContenders.length === 0) continue;
|
|
895
|
+
|
|
896
|
+
const replacement = validContenders[validContenders.length - 1];
|
|
897
|
+
const validReplacements = replacements.filter(candidate =>
|
|
898
|
+
coreMatchByReplacement.has(candidate),
|
|
899
|
+
);
|
|
900
|
+
if (validReplacements.length > 1) {
|
|
901
|
+
const autolinkedLost =
|
|
902
|
+
hasExplicitIntent &&
|
|
903
|
+
validReplacements.some(candidate => candidate.autolinked);
|
|
904
|
+
const allAutolinked = validReplacements.every(
|
|
905
|
+
candidate => candidate.autolinked,
|
|
906
|
+
);
|
|
907
|
+
pushError({
|
|
908
|
+
code: 'ambiguous_template_replacement',
|
|
909
|
+
severity: 'warning',
|
|
910
|
+
package: pkgOf(replacement),
|
|
911
|
+
template: replacement.dirName,
|
|
912
|
+
replacementTarget: target,
|
|
913
|
+
autolinked: replacement.autolinked,
|
|
914
|
+
message: autolinkedLost
|
|
915
|
+
? `Core template "${target}" is replaced by ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is explicitly configured, so it wins over autolinked integrations.`
|
|
916
|
+
: allAutolinked
|
|
917
|
+
? `Core template "${target}" is replaced by autolinked dependencies ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is listed later in package.json dependencies, so it wins. Add the intended package to astryx.config integrations to make precedence explicit.`
|
|
918
|
+
: `Core template "${target}" is replaced by ${validReplacements.map(candidate => pkgOf(candidate)).join(', ')}. ${pkgOf(replacement)} is configured later, so it wins.`,
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
activeReplacements.add(replacement);
|
|
923
|
+
const coreMatch = coreMatchByReplacement.get(replacement);
|
|
924
|
+
if (coreMatch) replacedCore.add(coreMatch);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const effective = templates.flatMap(template => {
|
|
928
|
+
if (replacedCore.has(template)) return [];
|
|
929
|
+
if (template.replaces != null && !activeReplacements.has(template)) {
|
|
930
|
+
const fallback = {
|
|
931
|
+
...template,
|
|
932
|
+
replacementRejected: true,
|
|
933
|
+
replacementTarget: template.replaces,
|
|
934
|
+
};
|
|
935
|
+
delete fallback.replaces;
|
|
936
|
+
return [fallback];
|
|
937
|
+
}
|
|
938
|
+
return [template];
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
return {
|
|
942
|
+
templates: effective.sort((a, b) => a.name.localeCompare(b.name)),
|
|
943
|
+
errors,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Discover the raw Core, external, and integration template set before
|
|
949
|
+
* replacement declarations are applied.
|
|
686
950
|
* @param {string} [cwd]
|
|
687
|
-
* @returns {Promise<DiscoveredTemplate[]>}
|
|
951
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
688
952
|
*/
|
|
689
|
-
|
|
953
|
+
async function discoverAllSources(cwd = process.cwd()) {
|
|
690
954
|
const [core, external, integration] = await Promise.all([
|
|
691
955
|
discoverCoreTemplates(),
|
|
692
956
|
discoverExternalBlocks(cwd),
|
|
693
957
|
discoverIntegrationTemplates(cwd),
|
|
694
958
|
]);
|
|
695
|
-
return
|
|
959
|
+
return {
|
|
960
|
+
templates: [...core, ...external, ...integration.templates],
|
|
961
|
+
errors: integration.errors,
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Discover every template without hiding replaced Core originals. Internal
|
|
967
|
+
* package-qualified selection uses this view.
|
|
968
|
+
* @param {string} [cwd]
|
|
969
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
970
|
+
*/
|
|
971
|
+
export async function discoverAllUnresolved(cwd = process.cwd()) {
|
|
972
|
+
return (await discoverAllSources(cwd)).templates.sort((a, b) =>
|
|
696
973
|
a.name.localeCompare(b.name),
|
|
697
974
|
);
|
|
698
975
|
}
|
|
699
976
|
|
|
700
977
|
/**
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
978
|
+
* Resolve replacement declarations while retaining package-addressable entries
|
|
979
|
+
* that are shadowed or rejected in the default discovery view.
|
|
980
|
+
* @param {string} [cwd]
|
|
981
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: Array<TemplateDiscoveryError | TemplateReplacementError>}>}
|
|
982
|
+
*/
|
|
983
|
+
async function resolveAllSources(cwd = process.cwd()) {
|
|
984
|
+
const discovered = await discoverAllSources(cwd);
|
|
985
|
+
const replacementErrors = discovered.errors.filter(
|
|
986
|
+
error => error.replacementTarget != null,
|
|
987
|
+
);
|
|
988
|
+
const resolved = applyTemplateReplacements(
|
|
989
|
+
discovered.templates,
|
|
990
|
+
replacementErrors,
|
|
991
|
+
);
|
|
992
|
+
return {
|
|
993
|
+
templates: resolved.templates,
|
|
994
|
+
errors: [
|
|
995
|
+
...discovered.errors.filter(error => error.replacementTarget == null),
|
|
996
|
+
...resolved.errors,
|
|
997
|
+
],
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Discover the resolved catalog before default-view shadowing. Internal
|
|
1003
|
+
* package-qualified selection uses this view.
|
|
1004
|
+
* @param {string} [cwd]
|
|
1005
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
1006
|
+
*/
|
|
1007
|
+
export async function discoverAllResolved(cwd = process.cwd()) {
|
|
1008
|
+
return (await resolveAllSources(cwd)).templates;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* @param {string} [cwd]
|
|
1013
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
1014
|
+
*/
|
|
1015
|
+
export async function discoverAll(cwd = process.cwd()) {
|
|
1016
|
+
return effectiveTemplateDiscovery(await discoverAllResolved(cwd));
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Like {@link discoverAll} but also returns integration-template discovery and
|
|
1021
|
+
* replacement-declaration errors. Use this when the caller wants to warn about
|
|
1022
|
+
* malformed integration templates or inactive replacement declarations.
|
|
704
1023
|
*
|
|
705
1024
|
* @param {string} [cwd]
|
|
706
|
-
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError
|
|
1025
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: Array<TemplateDiscoveryError | TemplateReplacementError>}>}
|
|
707
1026
|
*/
|
|
708
1027
|
export async function discoverAllWithErrors(cwd = process.cwd()) {
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const templates = [...pages, ...blocks, ...integration.templates].sort(
|
|
715
|
-
(a, b) => a.name.localeCompare(b.name),
|
|
716
|
-
);
|
|
717
|
-
return {templates, errors: integration.errors};
|
|
1028
|
+
const resolved = await resolveAllSources(cwd);
|
|
1029
|
+
return {
|
|
1030
|
+
templates: effectiveTemplateDiscovery(resolved.templates),
|
|
1031
|
+
errors: resolved.errors,
|
|
1032
|
+
};
|
|
718
1033
|
}
|
|
719
1034
|
|
|
720
1035
|
/**
|
|
@@ -807,13 +1122,51 @@ async function discoverIntegrationTemplates(cwd = process.cwd()) {
|
|
|
807
1122
|
return {templates, errors};
|
|
808
1123
|
}
|
|
809
1124
|
|
|
1125
|
+
/**
|
|
1126
|
+
* Report replacement declarations that cannot hold inside one package: two of
|
|
1127
|
+
* its templates replacing the same Core target. Cross-package precedence and
|
|
1128
|
+
* target checks happen in {@link applyTemplateReplacements}.
|
|
1129
|
+
* @param {string} pkg
|
|
1130
|
+
* @param {boolean | undefined} autolinked
|
|
1131
|
+
* @param {DiscoveredTemplate[]} templates
|
|
1132
|
+
* @param {TemplateDiscoveryError[]} errors
|
|
1133
|
+
*/
|
|
1134
|
+
function reportSamePackageReplacements(pkg, autolinked, templates, errors) {
|
|
1135
|
+
/** @type {Map<string, string[]>} */
|
|
1136
|
+
const idsByTarget = new Map();
|
|
1137
|
+
for (const template of templates) {
|
|
1138
|
+
if (template.replaces == null) continue;
|
|
1139
|
+
const ids = idsByTarget.get(template.replaces) ?? [];
|
|
1140
|
+
ids.push(template.dirName);
|
|
1141
|
+
idsByTarget.set(template.replaces, ids);
|
|
1142
|
+
}
|
|
1143
|
+
for (const [target, ids] of idsByTarget) {
|
|
1144
|
+
if (ids.length < 2) continue;
|
|
1145
|
+
const message =
|
|
1146
|
+
`${pkg} declares ${ids.length} templates as replacements for Core template ` +
|
|
1147
|
+
`"${target}" (${ids.join(', ')}). One package must declare at most one ` +
|
|
1148
|
+
'replacement for a Core target.';
|
|
1149
|
+
for (const template of ids) {
|
|
1150
|
+
errors.push({
|
|
1151
|
+
code: 'ambiguous_template_replacement',
|
|
1152
|
+
severity: 'error',
|
|
1153
|
+
package: pkg,
|
|
1154
|
+
autolinked,
|
|
1155
|
+
template,
|
|
1156
|
+
replacementTarget: target,
|
|
1157
|
+
message,
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
810
1163
|
/**
|
|
811
1164
|
* Discover the templates contributed by a SINGLE integration. Same per-template
|
|
812
1165
|
* rules as {@link discoverIntegrationTemplates} (same-stem source required,
|
|
813
1166
|
* page|block type required); broken templates are recorded in `errors` rather
|
|
814
1167
|
* than thrown. Exposed for `doctor integration validate` and template authoring checks.
|
|
815
1168
|
*
|
|
816
|
-
* @param {{name?: string, __spec?: string, templates?: string}} integration
|
|
1169
|
+
* @param {{name?: string, __spec?: string, __autolinked?: boolean, templates?: string}} integration
|
|
817
1170
|
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
818
1171
|
*/
|
|
819
1172
|
export async function discoverIntegrationTemplatesForOne(integration) {
|
|
@@ -842,6 +1195,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
842
1195
|
template: id,
|
|
843
1196
|
message: `Template "${id}" is missing its same-stem source file ${path.basename(sourcePath)}.`,
|
|
844
1197
|
});
|
|
1198
|
+
// A replacement this unusable template declares still counts, so its
|
|
1199
|
+
// target fails closed instead of going to a sibling.
|
|
1200
|
+
const declared = await declaredReplacement(docPath, id);
|
|
1201
|
+
if (declared != null) {
|
|
1202
|
+
errors.push({
|
|
1203
|
+
code: 'invalid_template_replacement',
|
|
1204
|
+
severity: 'error',
|
|
1205
|
+
package: pkgLabel,
|
|
1206
|
+
autolinked: integration.__autolinked,
|
|
1207
|
+
template: id,
|
|
1208
|
+
replacementTarget: declared,
|
|
1209
|
+
message: `Template "${id}" replaces "${declared}", but it cannot be used: it is missing its same-stem source file ${path.basename(sourcePath)}.`,
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
845
1212
|
continue;
|
|
846
1213
|
}
|
|
847
1214
|
|
|
@@ -854,6 +1221,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
854
1221
|
template: id,
|
|
855
1222
|
message: `Template "${id}" failed to load: ${/** @type {any} */ (err).message}`,
|
|
856
1223
|
});
|
|
1224
|
+
// A replacement this unusable template declares still counts, so its
|
|
1225
|
+
// target fails closed instead of going to a sibling (spec:AST-035 FR4).
|
|
1226
|
+
const declared = await declaredReplacement(docPath, id);
|
|
1227
|
+
if (declared != null) {
|
|
1228
|
+
errors.push({
|
|
1229
|
+
code: 'invalid_template_replacement',
|
|
1230
|
+
severity: 'error',
|
|
1231
|
+
package: pkgLabel,
|
|
1232
|
+
autolinked: integration.__autolinked,
|
|
1233
|
+
template: id,
|
|
1234
|
+
replacementTarget: declared,
|
|
1235
|
+
message: `Template "${id}" replaces "${declared}", but it cannot be used: its metadata does not load.`,
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
857
1238
|
continue;
|
|
858
1239
|
}
|
|
859
1240
|
|
|
@@ -867,6 +1248,18 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
867
1248
|
template: id,
|
|
868
1249
|
message: `Template "${id}" is missing a "type" of "page" or "block". Stamp the default export with type: 'page' or type: 'block'.`,
|
|
869
1250
|
});
|
|
1251
|
+
const declared = doc?.replaces ?? (await declaredReplacement(docPath, id));
|
|
1252
|
+
if (declared != null) {
|
|
1253
|
+
errors.push({
|
|
1254
|
+
code: 'invalid_template_replacement',
|
|
1255
|
+
severity: 'error',
|
|
1256
|
+
package: pkgLabel,
|
|
1257
|
+
autolinked: integration.__autolinked,
|
|
1258
|
+
template: id,
|
|
1259
|
+
replacementTarget: declared,
|
|
1260
|
+
message: `Template "${id}" replaces "${declared}", but it cannot be used: it has no "type".`,
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
870
1263
|
continue;
|
|
871
1264
|
}
|
|
872
1265
|
|
|
@@ -894,12 +1287,55 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
894
1287
|
filePath: sourcePath,
|
|
895
1288
|
docPath,
|
|
896
1289
|
package: pkgLabel,
|
|
1290
|
+
autolinked: integration.__autolinked,
|
|
1291
|
+
replaces: doc?.replaces,
|
|
897
1292
|
});
|
|
898
1293
|
}
|
|
899
1294
|
|
|
1295
|
+
reportSamePackageReplacements(
|
|
1296
|
+
pkgLabel,
|
|
1297
|
+
integration.__autolinked,
|
|
1298
|
+
templates,
|
|
1299
|
+
errors,
|
|
1300
|
+
);
|
|
900
1301
|
return {templates, errors};
|
|
901
1302
|
}
|
|
902
1303
|
|
|
1304
|
+
/**
|
|
1305
|
+
* The replacement a template doc declares, even when the doc does not validate
|
|
1306
|
+
* or load: an unusable template's declaration still counts, so its target
|
|
1307
|
+
* fails closed (spec:AST-035 FR4). Null when it declares none.
|
|
1308
|
+
* @param {string} docPath
|
|
1309
|
+
* @param {string} id
|
|
1310
|
+
* @returns {Promise<string | null>}
|
|
1311
|
+
*/
|
|
1312
|
+
async function declaredReplacement(docPath, id) {
|
|
1313
|
+
try {
|
|
1314
|
+
const doc = await loadIntegrationDoc(docPath, `Template "${id}"`);
|
|
1315
|
+
return doc?.replaces ?? null;
|
|
1316
|
+
} catch {
|
|
1317
|
+
// The doc does not validate; read what it declares without validating.
|
|
1318
|
+
}
|
|
1319
|
+
try {
|
|
1320
|
+
// The guarded importer: anything the doc writes to stdout as it loads
|
|
1321
|
+
// goes to stderr, so `--json` output stays clean.
|
|
1322
|
+
const raw = (await importDocModule(docPath))?.default;
|
|
1323
|
+
if (raw != null && typeof raw === 'object') {
|
|
1324
|
+
return typeof raw.replaces === 'string' ? raw.replaces : null;
|
|
1325
|
+
}
|
|
1326
|
+
} catch {
|
|
1327
|
+
// The module does not load at all; its text may still name a target.
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
const match = /\breplaces\s*:\s*['"]([^'"\n]+)['"]/u.exec(
|
|
1331
|
+
fs.readFileSync(docPath, 'utf8'),
|
|
1332
|
+
);
|
|
1333
|
+
return match ? match[1] : null;
|
|
1334
|
+
} catch {
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
903
1339
|
/**
|
|
904
1340
|
* @param {string} componentName
|
|
905
1341
|
* @param {string} [cwd]
|
|
@@ -17,6 +17,7 @@ import * as fs from 'node:fs';
|
|
|
17
17
|
import * as os from 'node:os';
|
|
18
18
|
import * as path from 'node:path';
|
|
19
19
|
import {
|
|
20
|
+
effectiveTemplateDiscovery,
|
|
20
21
|
extractComponents,
|
|
21
22
|
findPageDocFile,
|
|
22
23
|
stripTemplateAssetRefs,
|
|
@@ -179,3 +180,44 @@ describe('findPageDocFile', () => {
|
|
|
179
180
|
);
|
|
180
181
|
});
|
|
181
182
|
});
|
|
183
|
+
|
|
184
|
+
describe('effectiveTemplateDiscovery', () => {
|
|
185
|
+
it('shadows only the matching template kind', () => {
|
|
186
|
+
const pageReplacement = {
|
|
187
|
+
type: 'page',
|
|
188
|
+
dirName: 'acme-dashboard',
|
|
189
|
+
name: 'Acme dashboard',
|
|
190
|
+
description: '',
|
|
191
|
+
filePath: '/acme-dashboard.tsx',
|
|
192
|
+
docPath: '/acme-dashboard.doc.mjs',
|
|
193
|
+
package: '@acme/widgets',
|
|
194
|
+
replaces: 'dashboard',
|
|
195
|
+
};
|
|
196
|
+
const corePage = {
|
|
197
|
+
type: 'page',
|
|
198
|
+
dirName: 'dashboard',
|
|
199
|
+
name: 'Dashboard page',
|
|
200
|
+
description: '',
|
|
201
|
+
filePath: '/dashboard-page.tsx',
|
|
202
|
+
docPath: '/dashboard-page.doc.mjs',
|
|
203
|
+
};
|
|
204
|
+
const coreBlock = {
|
|
205
|
+
type: 'block',
|
|
206
|
+
dirName: 'dashboard',
|
|
207
|
+
name: 'Dashboard block',
|
|
208
|
+
description: '',
|
|
209
|
+
filePath: '/dashboard-block.tsx',
|
|
210
|
+
docPath: '/dashboard-block.doc.mjs',
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const effective = effectiveTemplateDiscovery([
|
|
214
|
+
corePage,
|
|
215
|
+
coreBlock,
|
|
216
|
+
pageReplacement,
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
expect(effective).toContain(pageReplacement);
|
|
220
|
+
expect(effective).toContain(coreBlock);
|
|
221
|
+
expect(effective).not.toContain(corePage);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
* Whether this release exposes replacement relationships in the public conflict API.
|
|
6
|
+
* Both the supported version and a deliberate projection update are required;
|
|
7
|
+
* a routine package-version bump cannot expand the public shape by itself.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} [version]
|
|
10
|
+
* @param {'warning-only' | 'expanded'} [projection]
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
export function expandedTemplateConflictSchemaActive(version?: string, projection?: "warning-only" | "expanded"): boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Release boundary for the expanded integration-template conflict schema.
|
|
5
|
+
*
|
|
6
|
+
* @input The CLI package version and an explicit public-schema projection.
|
|
7
|
+
* @output Whether replacement-specific conflict fields may reach the stable API.
|
|
8
|
+
* @position Private compatibility gate for integration authoring diagnostics.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import {semverCompare} from '../env/semver.mjs';
|
|
14
|
+
import {CLI_ROOT} from '../fs/paths.mjs';
|
|
15
|
+
|
|
16
|
+
const EXPANDED_TEMPLATE_CONFLICT_SCHEMA_CLI = '0.7.0';
|
|
17
|
+
/** @type {'warning-only' | 'expanded'} */
|
|
18
|
+
const TEMPLATE_CONFLICT_SCHEMA_PROJECTION = 'warning-only';
|
|
19
|
+
const CLI_VERSION = JSON.parse(
|
|
20
|
+
fs.readFileSync(path.join(CLI_ROOT, 'package.json'), 'utf8'),
|
|
21
|
+
).version;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether this release exposes replacement relationships in the public conflict API.
|
|
25
|
+
* Both the supported version and a deliberate projection update are required;
|
|
26
|
+
* a routine package-version bump cannot expand the public shape by itself.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} [version]
|
|
29
|
+
* @param {'warning-only' | 'expanded'} [projection]
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
export function expandedTemplateConflictSchemaActive(
|
|
33
|
+
version = CLI_VERSION,
|
|
34
|
+
projection = TEMPLATE_CONFLICT_SCHEMA_PROJECTION,
|
|
35
|
+
) {
|
|
36
|
+
return (
|
|
37
|
+
projection === 'expanded' &&
|
|
38
|
+
semverCompare(version, EXPANDED_TEMPLATE_CONFLICT_SCHEMA_CLI) >= 0
|
|
39
|
+
);
|
|
40
|
+
}
|