@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98
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 +38 -15
- package/api/blog/blog.doc.mjs +1 -0
- package/api/build/_adapter.d.mts +50 -0
- package/api/build/_adapter.mjs +60 -0
- package/api/build/build.doc.mjs +14 -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.doc.mjs +1 -0
- package/api/component/component.mjs +60 -12
- package/api/component/list/list.mjs +3 -2
- package/api/discover/discover.doc.mjs +1 -0
- package/api/docs/_adapter.d.mts +252 -34
- package/api/docs/_adapter.mjs +923 -132
- 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 +39 -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 +28 -12
- 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 +54 -4
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +331 -16
- package/api/doctor/doctor.test.mjs +420 -7
- package/api/gap-report/gap-report.doc.mjs +1 -0
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/hook/list/list.mjs +69 -17
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +2 -1
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +114 -12
- package/api/integration/add-contribution.test.mjs +174 -3
- package/api/integration/add-theme.mjs +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +179 -7
- 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/integration-block-exports.test.mjs +10 -6
- package/api/integration/integrationAdd.doc.mjs +7 -0
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +1 -0
- package/api/integration/integrationAddDoc.doc.mjs +8 -1
- package/api/integration/integrationAddTemplate.doc.mjs +1 -0
- package/api/integration/integrationAddTheme.doc.mjs +6 -5
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
- package/api/integration/integrationDocConflicts.doc.mjs +2 -1
- package/api/integration/integrationPackCheck.doc.mjs +2 -1
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
- package/api/integration/pack-check.mjs +34 -0
- package/api/integration/pack-check.test.mjs +138 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +50 -102
- package/api/integration/validate-integration.test.mjs +88 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +2 -1
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +1 -0
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -0
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search.d.mts +59 -1
- package/api/search/search.doc.mjs +5 -3
- package/api/search/search.mjs +458 -77
- 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/swizzle/swizzle.doc.mjs +1 -0
- 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-suffix.test.mjs +41 -21
- package/api/template/template.doc.mjs +28 -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/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +10 -17
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/generateTonalPalette.doc.mjs +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/template/template.test.mjs +5 -0
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +1 -0
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +97 -69
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +356 -59
- package/api/upgrade/upgrade.doc.mjs +6 -1
- package/api/upgrade/upgrade.type.d.mts +34 -0
- package/api/upgrade/upgrade.type.mjs +15 -0
- package/assets/codemods/__tests__/runner.test.mjs +330 -8
- package/assets/codemods/integration-discovery.mjs +8 -2
- package/assets/codemods/integration-discovery.test.mjs +15 -0
- package/assets/codemods/integration-runner.mjs +56 -4
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +350 -102
- package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/next/index.mjs +19 -1
- package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
- 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 +5 -5
- 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} +66 -38
- 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/DateInput/DateInputDateRange.tsx +1 -1
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
- 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/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
- package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
- package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
- package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
- package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
- package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
- package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
- package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
- package/authoring/codemod/codemod.doc.mjs +1 -1
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -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 +8 -6
- 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/component/type.ts +2 -2
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- 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/hook/type.ts +2 -2
- package/authoring/doctypes/load-contract.test.mjs +28 -2
- 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.d.mts +4 -2
- package/authoring/doctypes/parse.mjs +10 -5
- package/authoring/doctypes/reference/reference.doc.mjs +35 -6
- package/authoring/doctypes/reference/type.ts +30 -13
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -2
- 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 +9 -3
- package/authoring/doctypes/template/type.ts +8 -0
- package/authoring/doctypes/theme/parse.d.mts +35 -0
- package/authoring/doctypes/theme/parse.mjs +76 -0
- package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
- package/authoring/doctypes/theme/theme.doc.mjs +79 -0
- package/authoring/doctypes/theme/type.ts +42 -0
- package/authoring/doctypes/types.ts +2 -1
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/identity/identity.doc.mjs +2 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +7 -4
- package/authoring/index.mjs +2 -1
- package/authoring/integration/integration.doc.mjs +2 -2
- package/authoring/integration/type.ts +4 -10
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.mjs +11 -45
- package/clients/cli/commands/build.doc.mjs +12 -7
- 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/component.doc.mjs +1 -1
- package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
- package/clients/cli/commands/discover.doc.mjs +1 -1
- package/clients/cli/commands/discover.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +21 -9
- package/clients/cli/commands/docs.mjs +184 -70
- package/clients/cli/commands/docs.test.mjs +120 -16
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.test.mjs +90 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +53 -16
- package/clients/cli/commands/gap-report.doc.mjs +1 -1
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/init.doc.mjs +1 -1
- package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
- package/clients/cli/commands/integration-add.doc.mjs +11 -1
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/layout-check.doc.mjs +1 -1
- package/clients/cli/commands/layout-expand.doc.mjs +1 -1
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +1 -1
- package/clients/cli/commands/search.mjs +11 -2
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +24 -7
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/text-json-parity.test.mjs +719 -0
- package/clients/cli/commands/theme-add.doc.mjs +2 -2
- package/clients/cli/commands/theme-build.doc.mjs +1 -1
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.doc.mjs +1 -1
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +3 -2
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +2 -0
- package/clients/cli/formatters/index.test.mjs +6 -0
- package/clients/cli/index.mjs +8 -0
- package/clients/cli/lib/hook-format.mjs +14 -5
- 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-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +123 -80
- package/foundation/config/project.test.mjs +144 -21
- package/foundation/discovery/authoring-self-docs.d.mts +18 -0
- package/foundation/discovery/authoring-self-docs.mjs +37 -15
- package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
- package/foundation/discovery/authoring-surface.d.mts +74 -0
- package/foundation/discovery/authoring-surface.mjs +525 -0
- package/foundation/discovery/authoring-surface.test.mjs +392 -0
- package/foundation/discovery/cli-self-docs.d.mts +119 -0
- package/foundation/discovery/cli-self-docs.mjs +490 -0
- package/foundation/discovery/cli-self-docs.test.mjs +375 -0
- package/foundation/discovery/component-discovery.d.mts +38 -0
- package/foundation/discovery/component-discovery.mjs +48 -0
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +112 -11
- package/foundation/discovery/docs-discovery.mjs +231 -36
- package/foundation/discovery/docs-discovery.test.mjs +111 -32
- 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 +99 -6
- package/foundation/discovery/template-adapter.mjs +480 -57
- package/foundation/discovery/template-adapter.test.mjs +57 -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/discovery/theme-discovery.d.mts +67 -7
- package/foundation/discovery/theme-discovery.mjs +916 -186
- package/foundation/discovery/theme-discovery.test.mjs +613 -219
- package/foundation/doc-compiler/bundle.d.mts +47 -0
- package/foundation/doc-compiler/bundle.mjs +278 -0
- package/foundation/doc-compiler/bundle.test.mjs +266 -0
- package/foundation/doc-compiler/compile.d.mts +220 -39
- package/foundation/doc-compiler/compile.mjs +311 -15
- package/foundation/doc-compiler/diagnostics.d.mts +126 -0
- package/foundation/doc-compiler/diagnostics.mjs +305 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +291 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +13 -0
- package/foundation/doc-compiler/ir.mjs +196 -12
- 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 +495 -0
- package/foundation/doc-compiler/overlays.d.mts +37 -0
- package/foundation/doc-compiler/overlays.mjs +206 -0
- package/foundation/doc-compiler/parse-readable.d.mts +9 -0
- package/foundation/doc-compiler/parse-readable.mjs +29 -0
- package/foundation/doc-compiler/read.d.mts +127 -0
- package/foundation/doc-compiler/read.mjs +325 -0
- package/foundation/doc-compiler/read.test.mjs +313 -0
- package/foundation/doc-compiler/source.d.mts +33 -0
- package/foundation/doc-compiler/source.mjs +128 -0
- 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/fs/file-protection.d.mts +33 -0
- package/foundation/fs/file-protection.mjs +825 -0
- package/foundation/fs/file-protection.test.mjs +250 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -57
- 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-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +3 -2
- package/foundation/integrations/contribution-inventory.mjs +27 -24
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- 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/integrations.d.mts +3 -0
- package/foundation/integrations/integrations.mjs +11 -97
- package/foundation/integrations/provider-ledger.test.mjs +275 -0
- package/foundation/integrations/provider-resolution.d.mts +152 -0
- package/foundation/integrations/provider-resolution.mjs +576 -0
- package/foundation/integrations/provider-resolution.test.mjs +369 -0
- package/foundation/integrations/theme-descriptor.d.mts +8 -0
- package/foundation/integrations/theme-descriptor.mjs +44 -0
- package/foundation/integrations/validate-contributions.mjs +121 -29
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.d.ts +2 -0
- package/foundation/response/error-codes.doc.mjs +12 -2
- package/foundation/response/error-codes.mjs +7 -1
- package/foundation/response/error-codes.test.mjs +55 -11
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +20 -11
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +40 -10
- package/foundation/xle/expand.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -9
- package/assets/templates/themes/manifest.json +0 -95
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
import * as fs from 'node:fs';
|
|
25
25
|
import * as path from 'node:path';
|
|
26
26
|
import {createRequire} from 'node:module';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {parseTemplate} from '../../authoring/doctypes/template/parse.mjs';
|
|
27
|
+
import {readDocView} from '../doc-compiler/read.mjs';
|
|
28
|
+
import {importDocModule} from '../doc-compiler/import.mjs';
|
|
30
29
|
import {CLI_ROOT, discoverExternalPackages} from '../fs/paths.mjs';
|
|
31
30
|
import {CORE_PROVIDER_ID} from '../identity/providers.mjs';
|
|
32
31
|
import {Project} from '../config/project.mjs';
|
|
@@ -46,6 +45,53 @@ export function pkgOf(t) {
|
|
|
46
45
|
return t.package ?? CORE_PACKAGE;
|
|
47
46
|
}
|
|
48
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
|
+
|
|
49
95
|
/**
|
|
50
96
|
* A discovered template (page or block), normalized across core, external, and
|
|
51
97
|
* integration sources. Not every source populates every field, so
|
|
@@ -68,6 +114,11 @@ export function pkgOf(t) {
|
|
|
68
114
|
* @property {string} filePath
|
|
69
115
|
* @property {string} docPath
|
|
70
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
|
|
71
122
|
*/
|
|
72
123
|
|
|
73
124
|
/**
|
|
@@ -76,6 +127,15 @@ export function pkgOf(t) {
|
|
|
76
127
|
* @property {string} package
|
|
77
128
|
* @property {string} [template]
|
|
78
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
|
|
79
139
|
*/
|
|
80
140
|
|
|
81
141
|
/**
|
|
@@ -84,31 +144,28 @@ export function pkgOf(t) {
|
|
|
84
144
|
*/
|
|
85
145
|
|
|
86
146
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* name.
|
|
147
|
+
* Released compatibility suffixes, in precedence order. Stable 0.6.0
|
|
148
|
+
* documented `.template.*`, so discovery keeps reading those files while all
|
|
149
|
+
* new authoring uses `.doc.mjs`.
|
|
91
150
|
*/
|
|
92
151
|
const TEMPLATE_SUFFIXES = ['.template.ts', '.template.mjs', '.template.js'];
|
|
93
152
|
|
|
94
153
|
/**
|
|
95
|
-
*
|
|
96
|
-
* `.doc
|
|
97
|
-
* had their own name; it is still accepted during the transition window.
|
|
154
|
+
* Descriptor suffixes for templates, in precedence order. New authoring always
|
|
155
|
+
* emits `.doc.mjs`; the TypeScript and JavaScript variants remain readable.
|
|
98
156
|
*/
|
|
99
157
|
const DOC_SUFFIXES = ['.doc.ts', '.doc.mjs', '.doc.js'];
|
|
100
158
|
|
|
101
159
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* like a legacy `Foo.doc.mjs`.
|
|
160
|
+
* Every template-spec suffix, in the released precedence a core page directory
|
|
161
|
+
* uses to pick one file. Integration discovery never picks: every match is a
|
|
162
|
+
* template, so two specs for one stem list twice and read as ambiguous.
|
|
106
163
|
*/
|
|
107
164
|
const ALL_TEMPLATE_SUFFIXES = [...TEMPLATE_SUFFIXES, ...DOC_SUFFIXES];
|
|
108
165
|
|
|
109
166
|
/**
|
|
110
167
|
* The template-spec suffix present on `file`, or null if none matches.
|
|
111
|
-
* Recognizes both the canonical `.
|
|
168
|
+
* Recognizes both the canonical `.doc.*` and released `.template.*` families.
|
|
112
169
|
* @param {string} file
|
|
113
170
|
* @returns {string | null}
|
|
114
171
|
*/
|
|
@@ -122,16 +179,6 @@ function matchedTemplateSuffix(file) {
|
|
|
122
179
|
*/
|
|
123
180
|
const TEMPLATE_SUFFIX_RE = /\.(template|doc)\.(ts|mjs|js)$/;
|
|
124
181
|
|
|
125
|
-
/** @type {ReturnType<typeof createJiti> | undefined} */
|
|
126
|
-
let jitiInstance;
|
|
127
|
-
/** Lazily-created jiti for loading `.ts` template specs (JSX-capable). */
|
|
128
|
-
function getJiti() {
|
|
129
|
-
if (!jitiInstance) {
|
|
130
|
-
jitiInstance = createJiti(import.meta.url, {jsx: true});
|
|
131
|
-
}
|
|
132
|
-
return jitiInstance;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
182
|
/**
|
|
136
183
|
* Load an integration template doc module and validate it against the template
|
|
137
184
|
* envelope at the load boundary. Default export only — `.ts` via jiti,
|
|
@@ -144,7 +191,13 @@ function getJiti() {
|
|
|
144
191
|
* @param {string} [label]
|
|
145
192
|
*/
|
|
146
193
|
async function loadIntegrationDoc(file, label) {
|
|
147
|
-
return
|
|
194
|
+
return readDocView(file, {
|
|
195
|
+
root: 'templates',
|
|
196
|
+
exports: ['default'],
|
|
197
|
+
label: label ?? file,
|
|
198
|
+
strict: true,
|
|
199
|
+
value: 'parsed',
|
|
200
|
+
});
|
|
148
201
|
}
|
|
149
202
|
|
|
150
203
|
const TEMPLATES_DIR = path.join(CLI_ROOT, 'assets', 'templates');
|
|
@@ -492,12 +545,11 @@ function unsafeFixtureReference(source, at, reason) {
|
|
|
492
545
|
/**
|
|
493
546
|
* Load a template-spec module and return its metadata object. Supports both
|
|
494
547
|
* families of suffix:
|
|
495
|
-
* -
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
* Prefers the default export, falling back to the named `doc` export
|
|
499
|
-
*
|
|
500
|
-
* `Foo.doc.mjs` (`doc` export). `.ts` is loaded via jiti; `.mjs`/`.js` via a
|
|
548
|
+
* - Canonical `.doc.*` specs may export the stamped object (`type: 'page' |
|
|
549
|
+
* 'block'`) as the default export or use the historical named `doc` export.
|
|
550
|
+
* - Released `.template.*` compatibility specs use the same object shape.
|
|
551
|
+
* Prefers the default export, falling back to the named `doc` export. `.ts` is
|
|
552
|
+
* loaded via jiti; `.mjs`/`.js` via a
|
|
501
553
|
* native dynamic import. Returns null if the file does not exist.
|
|
502
554
|
*
|
|
503
555
|
* @param {string} docPath absolute path to the spec file
|
|
@@ -505,10 +557,7 @@ function unsafeFixtureReference(source, at, reason) {
|
|
|
505
557
|
*/
|
|
506
558
|
async function loadDocModule(docPath) {
|
|
507
559
|
if (!fs.existsSync(docPath)) return null;
|
|
508
|
-
|
|
509
|
-
? await getJiti().import(docPath)
|
|
510
|
-
: await import(`file://${docPath}`);
|
|
511
|
-
return docModule.default ?? docModule.doc;
|
|
560
|
+
return readDocView(docPath, {root: 'templates', loader: 'template'});
|
|
512
561
|
}
|
|
513
562
|
|
|
514
563
|
/**
|
|
@@ -553,11 +602,11 @@ function findDocFiles(dir, pattern) {
|
|
|
553
602
|
|
|
554
603
|
/**
|
|
555
604
|
* Resolve the template-spec file for a core page directory: the first existing
|
|
556
|
-
*
|
|
605
|
+
* metadata file in {@link ALL_TEMPLATE_SUFFIXES} precedence, or null.
|
|
557
606
|
* @param {string} dirPath
|
|
558
607
|
* @returns {string | null}
|
|
559
608
|
*/
|
|
560
|
-
function findPageDocFile(dirPath) {
|
|
609
|
+
export function findPageDocFile(dirPath) {
|
|
561
610
|
for (const suffix of ALL_TEMPLATE_SUFFIXES) {
|
|
562
611
|
const candidate = path.join(dirPath, `template${suffix}`);
|
|
563
612
|
if (fs.existsSync(candidate)) return candidate;
|
|
@@ -696,44 +745,297 @@ export async function discoverCoreTemplates() {
|
|
|
696
745
|
}
|
|
697
746
|
|
|
698
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.
|
|
699
950
|
* @param {string} [cwd]
|
|
700
|
-
* @returns {Promise<DiscoveredTemplate[]>}
|
|
951
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
701
952
|
*/
|
|
702
|
-
|
|
953
|
+
async function discoverAllSources(cwd = process.cwd()) {
|
|
703
954
|
const [core, external, integration] = await Promise.all([
|
|
704
955
|
discoverCoreTemplates(),
|
|
705
956
|
discoverExternalBlocks(cwd),
|
|
706
957
|
discoverIntegrationTemplates(cwd),
|
|
707
958
|
]);
|
|
708
|
-
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) =>
|
|
709
973
|
a.name.localeCompare(b.name),
|
|
710
974
|
);
|
|
711
975
|
}
|
|
712
976
|
|
|
713
977
|
/**
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
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.
|
|
717
1023
|
*
|
|
718
1024
|
* @param {string} [cwd]
|
|
719
|
-
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError
|
|
1025
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: Array<TemplateDiscoveryError | TemplateReplacementError>}>}
|
|
720
1026
|
*/
|
|
721
1027
|
export async function discoverAllWithErrors(cwd = process.cwd()) {
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const templates = [...pages, ...blocks, ...integration.templates].sort(
|
|
728
|
-
(a, b) => a.name.localeCompare(b.name),
|
|
729
|
-
);
|
|
730
|
-
return {templates, errors: integration.errors};
|
|
1028
|
+
const resolved = await resolveAllSources(cwd);
|
|
1029
|
+
return {
|
|
1030
|
+
templates: effectiveTemplateDiscovery(resolved.templates),
|
|
1031
|
+
errors: resolved.errors,
|
|
1032
|
+
};
|
|
731
1033
|
}
|
|
732
1034
|
|
|
733
1035
|
/**
|
|
734
1036
|
* Recursively collect integration template-spec files under `root`.
|
|
735
1037
|
* Returns absolute paths to files ending in one of ALL_TEMPLATE_SUFFIXES
|
|
736
|
-
* (canonical `.
|
|
1038
|
+
* (canonical `.doc.*` or released `.template.*` compatibility files).
|
|
737
1039
|
*
|
|
738
1040
|
* @param {string} root
|
|
739
1041
|
* @returns {string[]}
|
|
@@ -763,7 +1065,7 @@ function findIntegrationDocFiles(root) {
|
|
|
763
1065
|
* Discover templates contributed by configured integrations.
|
|
764
1066
|
*
|
|
765
1067
|
* For each integration with a resolved `templates` root, every
|
|
766
|
-
* `<id>.
|
|
1068
|
+
* canonical `<id>.doc.{mjs,ts,js}` (or released `<id>.template.{ts,mjs,js}`) file is a
|
|
767
1069
|
* template whose id is its path relative to the templates root with the
|
|
768
1070
|
* matched suffix stripped (kebab-case, may be nested). The doc's `type`
|
|
769
1071
|
* (page|block) decides scaffolding — there is no `/pages` vs `/blocks`
|
|
@@ -820,13 +1122,51 @@ async function discoverIntegrationTemplates(cwd = process.cwd()) {
|
|
|
820
1122
|
return {templates, errors};
|
|
821
1123
|
}
|
|
822
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
|
+
|
|
823
1163
|
/**
|
|
824
1164
|
* Discover the templates contributed by a SINGLE integration. Same per-template
|
|
825
1165
|
* rules as {@link discoverIntegrationTemplates} (same-stem source required,
|
|
826
1166
|
* page|block type required); broken templates are recorded in `errors` rather
|
|
827
1167
|
* than thrown. Exposed for `doctor integration validate` and template authoring checks.
|
|
828
1168
|
*
|
|
829
|
-
* @param {{name?: string, __spec?: string, templates?: string}} integration
|
|
1169
|
+
* @param {{name?: string, __spec?: string, __autolinked?: boolean, templates?: string}} integration
|
|
830
1170
|
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
831
1171
|
*/
|
|
832
1172
|
export async function discoverIntegrationTemplatesForOne(integration) {
|
|
@@ -855,6 +1195,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
855
1195
|
template: id,
|
|
856
1196
|
message: `Template "${id}" is missing its same-stem source file ${path.basename(sourcePath)}.`,
|
|
857
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
|
+
}
|
|
858
1212
|
continue;
|
|
859
1213
|
}
|
|
860
1214
|
|
|
@@ -867,6 +1221,20 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
867
1221
|
template: id,
|
|
868
1222
|
message: `Template "${id}" failed to load: ${/** @type {any} */ (err).message}`,
|
|
869
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
|
+
}
|
|
870
1238
|
continue;
|
|
871
1239
|
}
|
|
872
1240
|
|
|
@@ -880,6 +1248,18 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
880
1248
|
template: id,
|
|
881
1249
|
message: `Template "${id}" is missing a "type" of "page" or "block". Stamp the default export with type: 'page' or type: 'block'.`,
|
|
882
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
|
+
}
|
|
883
1263
|
continue;
|
|
884
1264
|
}
|
|
885
1265
|
|
|
@@ -907,12 +1287,55 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
907
1287
|
filePath: sourcePath,
|
|
908
1288
|
docPath,
|
|
909
1289
|
package: pkgLabel,
|
|
1290
|
+
autolinked: integration.__autolinked,
|
|
1291
|
+
replaces: doc?.replaces,
|
|
910
1292
|
});
|
|
911
1293
|
}
|
|
912
1294
|
|
|
1295
|
+
reportSamePackageReplacements(
|
|
1296
|
+
pkgLabel,
|
|
1297
|
+
integration.__autolinked,
|
|
1298
|
+
templates,
|
|
1299
|
+
errors,
|
|
1300
|
+
);
|
|
913
1301
|
return {templates, errors};
|
|
914
1302
|
}
|
|
915
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
|
+
|
|
916
1339
|
/**
|
|
917
1340
|
* @param {string} componentName
|
|
918
1341
|
* @param {string} [cwd]
|