@astryxdesign/cli 0.6.3 → 0.6.4-canary.0e1fbdb
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/CHANGELOG.md +194 -0
- package/README.md +152 -107
- 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 +22 -10
- package/api/build/build.test.mjs +219 -8
- package/api/build/build.type.d.mts +91 -2
- package/api/build/build.type.mjs +52 -8
- package/api/build/help/help.d.mts +12 -5
- package/api/build/help/help.mjs +69 -6
- package/api/build/kit/kit.d.mts +4 -1
- package/api/build/kit/kit.mjs +208 -53
- 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 +31 -12
- package/api/component/_adapter.mjs +79 -15
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +49 -19
- package/api/component/component.mjs +339 -22
- package/api/component/component.test.mjs +38 -0
- package/api/component/component.type.d.mts +47 -11
- package/api/component/component.type.mjs +76 -24
- package/api/component/detail/blocks/blocks.d.mts +2 -1
- package/api/component/detail/blocks/blocks.mjs +4 -3
- package/api/component/list/list.d.mts +0 -5
- package/api/component/list/list.mjs +40 -11
- package/api/discover/_adapter.d.mts +114 -6
- package/api/discover/_adapter.mjs +372 -17
- package/api/discover/_adapter.test.mjs +215 -0
- package/api/discover/_catalog-view.d.mts +115 -0
- package/api/discover/_catalog-view.mjs +203 -0
- package/api/discover/_catalog-view.test.mjs +128 -0
- package/api/discover/detail/detail.d.mts +18 -6
- package/api/discover/detail/detail.mjs +67 -13
- package/api/discover/detail/detail.test.mjs +85 -0
- package/api/discover/detail/item/item.d.mts +26 -0
- package/api/discover/detail/item/item.mjs +78 -0
- package/api/discover/detail/item/item.test.mjs +73 -0
- package/api/discover/discover.d.mts +3 -9
- package/api/discover/discover.doc.mjs +62 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +150 -11
- package/api/discover/discover.type.mjs +107 -17
- package/api/discover/list/list.d.mts +20 -6
- package/api/discover/list/list.mjs +45 -12
- package/api/discover/list/list.test.mjs +46 -0
- package/api/discover/search/search.d.mts +18 -16
- package/api/discover/search/search.mjs +102 -56
- package/api/discover/search/search.test.mjs +144 -10
- package/api/docs/_adapter.d.mts +277 -41
- package/api/docs/_adapter.mjs +993 -108
- package/api/docs/compiled-topics.test.mjs +78 -0
- package/api/docs/detail/detail.mjs +22 -63
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +54 -19
- package/api/docs/detail/section/section.test.mjs +50 -0
- package/api/docs/docOverlays.test.mjs +27 -1
- package/api/docs/docs.d.mts +10 -3
- package/api/docs/docs.doc.mjs +55 -16
- package/api/docs/docs.mjs +53 -10
- package/api/docs/docs.type.d.mts +221 -5
- package/api/docs/docs.type.mjs +153 -11
- package/api/docs/index/index.d.mts +18 -0
- package/api/docs/index/index.mjs +40 -0
- package/api/docs/index/index.test.mjs +62 -0
- 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 +104 -1
- package/api/doctor/doctor.doc.mjs +18 -8
- package/api/doctor/doctor.mjs +635 -7
- package/api/doctor/doctor.test.mjs +732 -11
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/gap-report/gap-report.doc.mjs +27 -14
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +7 -3
- package/api/hook/hook.type.d.mts +3 -3
- package/api/hook/hook.type.mjs +11 -11
- package/api/hook/list/list.d.mts +2 -2
- package/api/hook/list/list.mjs +69 -17
- package/api/index.d.mts +2 -1
- package/api/index.mjs +6 -3
- package/api/init/init.doc.mjs +22 -12
- package/api/init/init.test.mjs +41 -1
- package/api/init/remove/remove.mjs +1 -1
- package/api/init/run/run.mjs +20 -10
- package/api/integration/add-contribution.component-names.test.mjs +120 -0
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +130 -15
- package/api/integration/add-contribution.test.mjs +258 -7
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +56 -65
- package/api/integration/add-theme.test.mjs +139 -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 +3 -1
- 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 +14 -4
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +2 -1
- 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 +5 -4
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
- package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
- package/api/integration/pack-check.mjs +160 -11
- package/api/integration/pack-check.test.mjs +477 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +15 -2
- 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 +52 -102
- package/api/integration/validate-integration.test.mjs +179 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +3 -2
- package/api/json/assertResponse.doc.mjs +2 -1
- package/api/json/envelope-types.test.mjs +76 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +2 -1
- package/api/json/parseResponse.doc.mjs +3 -2
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/expand/expand.mjs +7 -5
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.mjs +2 -1
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +2 -1
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search-return-type.test.mjs +54 -0
- package/api/search/search.d.mts +89 -12
- package/api/search/search.doc.mjs +8 -2
- package/api/search/search.mjs +697 -97
- package/api/search/search.type.d.mts +15 -3
- package/api/search/search.type.mjs +5 -2
- package/api/swizzle/copy/copy.mjs +28 -11
- package/api/swizzle/swizzle.doc.mjs +8 -5
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/copy/copy.mjs +18 -24
- package/api/template/copy/copy.test.mjs +26 -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.d.mts +1 -1
- package/api/template/template.doc.mjs +32 -9
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +12 -14
- package/api/template/template.type.mjs +15 -14
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +84 -0
- package/api/theme/add/add.mjs +31 -22
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +83 -0
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +140 -59
- package/api/theme/build/build.public-component-vars.test.mjs +1 -1
- package/api/theme/build/build.receipt-doc.test.mjs +111 -0
- package/api/theme/build/build.rollback.test.mjs +148 -0
- package/api/theme/build/build.test.mjs +127 -0
- package/api/theme/build/font-warning.mjs +3 -3
- package/api/theme/build/font-warning.test.mjs +5 -2
- package/api/theme/generateTonalPalette.doc.mjs +2 -2
- 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/palette/generate/generate.mjs +8 -3
- package/api/theme/palette/generate/generate.test.mjs +96 -0
- package/api/theme/palette/generate/generator.d.mts +10 -13
- package/api/theme/palette/generate/generator.mjs +15 -4
- package/api/theme/palette/generate/generator.test.mjs +10 -0
- package/api/theme/template/template.mjs +11 -2
- package/api/theme/template/template.test.mjs +20 -0
- package/api/theme/theme.type.d.mts +170 -11
- package/api/theme/theme.type.mjs +94 -27
- package/api/theme/themeAdd.doc.mjs +12 -12
- package/api/theme/themeBuild.doc.mjs +21 -17
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +6 -3
- package/api/theme/themePaletteGenerate.doc.mjs +16 -8
- package/api/theme/themeTargets.doc.mjs +4 -2
- package/api/theme/themeTemplate.doc.mjs +8 -3
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +139 -22
- package/api/upgrade/list/list.mjs +2 -1
- package/api/upgrade/list/list.test.mjs +73 -0
- package/api/upgrade/project-context.test.mjs +272 -0
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/files-changed.test.mjs +111 -0
- package/api/upgrade/run/run.mjs +358 -59
- package/api/upgrade/status/status.mjs +2 -2
- package/api/upgrade/upgrade.doc.mjs +32 -23
- package/api/upgrade/upgrade.type.d.mts +43 -5
- package/api/upgrade/upgrade.type.mjs +29 -13
- package/assets/codemods/__tests__/registry.test.mjs +1 -0
- package/assets/codemods/__tests__/runner.test.mjs +332 -8
- package/assets/codemods/file-count.test.mjs +163 -0
- package/assets/codemods/integration-discovery.mjs +48 -4
- package/assets/codemods/integration-discovery.test.mjs +73 -0
- package/assets/codemods/integration-runner.mjs +59 -7
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/registry.mjs +1 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +353 -104
- package/assets/codemods/term-log.mjs +32 -8
- package/assets/codemods/term-log.test.mjs +19 -1
- package/assets/codemods/transform-prop.mjs +109 -0
- package/assets/codemods/transform-prop.test.mjs +95 -0
- package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
- package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
- package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
- package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
- package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
- package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
- package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
- package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/docs/README.md +12 -1
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +8 -2
- package/assets/docs/elevation.doc.mjs +6 -4
- package/assets/docs/getting-started.doc.mjs +6 -17
- package/assets/docs/icons.doc.mjs +2 -21
- package/assets/docs/illustrations.doc.mjs +7 -15
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +132 -84
- package/assets/docs/layout.doc.mjs +134 -78
- package/assets/docs/migration.doc.mjs +19 -21
- package/assets/docs/motion.doc.mjs +16 -3
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +14 -6
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +8 -3
- package/assets/docs/spacing.doc.mjs +7 -2
- package/assets/docs/styling-libraries.doc.mjs +10 -6
- package/assets/docs/styling.doc.mjs +22 -26
- package/assets/docs/theme.doc.dense.mjs +58 -18
- package/assets/docs/theme.doc.mjs +60 -50
- package/assets/docs/theme.doc.zh.mjs +9 -8
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +390 -9
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/add-a-component.doc.mjs +75 -0
- package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
- package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
- package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/block-template.doc.mjs +130 -0
- package/assets/docs/tree/build-the-template.doc.mjs +28 -0
- package/assets/docs/tree/building-blocks.doc.mjs +46 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
- package/assets/docs/tree/checks.doc.mjs +119 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/codemods.doc.mjs +147 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/tree/component-family.doc.mjs +113 -0
- package/assets/docs/tree/component-imports.doc.mjs +69 -0
- package/assets/docs/tree/component-lookups.doc.mjs +149 -0
- package/assets/docs/tree/components.doc.mjs +23 -0
- package/assets/docs/tree/configuration.doc.mjs +23 -0
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
- package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
- package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
- package/assets/docs/tree/docs.doc.mjs +21 -0
- package/assets/docs/tree/document-the-template.doc.mjs +28 -0
- package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
- package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
- package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
- package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
- package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
- package/assets/docs/tree/help.doc.mjs +16 -0
- package/assets/docs/tree/integrations.doc.mjs +40 -0
- package/assets/docs/tree/links.doc.mjs +98 -0
- package/assets/docs/tree/package-and-test.doc.mjs +32 -0
- package/assets/docs/tree/page-template.doc.mjs +71 -0
- package/assets/docs/tree/publishing.doc.mjs +111 -0
- package/assets/docs/tree/quick-start.doc.mjs +272 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
- package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
- package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
- package/assets/docs/tree/ship.doc.mjs +16 -0
- package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
- package/assets/docs/tree/single-component.doc.mjs +165 -0
- package/assets/docs/tree/start-a-template.doc.mjs +143 -0
- package/assets/docs/tree/subcomponent.doc.mjs +115 -0
- package/assets/docs/tree/template-assets.doc.mjs +64 -0
- package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
- package/assets/docs/tree/template-fonts.doc.mjs +102 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
- package/assets/docs/tree/template-icons.doc.mjs +97 -0
- package/assets/docs/tree/template-images-media.doc.mjs +127 -0
- package/assets/docs/tree/template-styles.doc.mjs +93 -0
- package/assets/docs/tree/templates.doc.mjs +34 -0
- package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
- package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
- package/assets/docs/tree/themes.doc.mjs +39 -0
- package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
- package/assets/docs/tree/upgrading.doc.mjs +103 -0
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
- package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
- package/assets/docs/tree/versioning.doc.mjs +161 -0
- package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
- package/assets/docs/typography.doc.mjs +24 -4
- package/assets/docs/working-with-ai.doc.mjs +34 -26
- 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/InternationalizationProvider/InternationalizationProvider01ShippedLocale.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/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -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/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
- package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
- package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
- package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
- package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
- package/assets/templates/pages/table-tree/page.tsx +1704 -0
- package/assets/templates/pages/table-tree/template.doc.mjs +12 -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/_shared/contract.ts +22 -0
- package/authoring/codemod/codemod.doc.mjs +7 -2
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +8 -6
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +11 -3
- package/authoring/config/debug-composition.test.mjs +92 -0
- package/authoring/config/parse.d.mts +15 -13
- package/authoring/config/parse.mjs +27 -8
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +29 -6
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/debug/parse.d.mts +8 -8
- package/authoring/debug/parse.mjs +3 -3
- package/authoring/discover/discover.doc.d.mts +13 -0
- package/authoring/discover/discover.doc.mjs +138 -0
- package/authoring/discover/parse.d.mts +24 -0
- package/authoring/discover/parse.mjs +128 -0
- package/authoring/discover/parse.test.mjs +124 -0
- package/authoring/discover/type.ts +87 -0
- package/authoring/doctypes/_schema.d.mts +792 -24
- package/authoring/doctypes/_schema.mjs +549 -39
- package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
- package/authoring/doctypes/base/type.ts +43 -0
- package/authoring/doctypes/command/command.doc.mjs +4 -3
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +5 -4
- package/authoring/doctypes/component/component.doc.mjs +12 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +14 -5
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +4 -2
- package/authoring/doctypes/function/function.doc.mjs +7 -2
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +9 -4
- package/authoring/doctypes/hook/hook.doc.mjs +4 -0
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +5 -4
- package/authoring/doctypes/legacy.d.mts +8 -6
- package/authoring/doctypes/legacy.mjs +5 -4
- package/authoring/doctypes/load-contract.test.mjs +233 -0
- package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
- package/authoring/doctypes/namespace/parse.d.mts +12 -0
- package/authoring/doctypes/namespace/parse.mjs +25 -0
- package/authoring/doctypes/namespace/parse.test.mjs +163 -0
- package/authoring/doctypes/namespace/type.ts +74 -0
- package/authoring/doctypes/parse.d.mts +22 -18
- package/authoring/doctypes/parse.mjs +22 -11
- package/authoring/doctypes/parse.test.mjs +77 -3
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +8 -5
- package/authoring/doctypes/reference/reference.doc.mjs +55 -6
- package/authoring/doctypes/reference/type.ts +75 -7
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/schema/type.ts +4 -4
- package/authoring/doctypes/template/parse.d.mts +94 -1
- package/authoring/doctypes/template/parse.mjs +40 -2
- package/authoring/doctypes/template/parse.test.mjs +26 -2
- package/authoring/doctypes/template/template.doc.mjs +13 -3
- package/authoring/doctypes/template/type.ts +13 -2
- 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 +12 -10
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/gap-report/parse.d.mts +10 -10
- package/authoring/gap-report/parse.mjs +6 -6
- package/authoring/gap-report/type.ts +1 -1
- package/authoring/identity/identity.doc.d.mts +9 -0
- package/authoring/identity/identity.doc.mjs +61 -0
- package/authoring/identity/type.ts +132 -0
- package/authoring/index.d.mts +3 -0
- package/authoring/index.d.ts +62 -17
- package/authoring/index.mjs +4 -1
- package/authoring/integration/integration.doc.mjs +22 -13
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +10 -1
- package/authoring/integration/schema.d.mts +6 -4
- package/authoring/integration/schema.mjs +9 -3
- package/authoring/integration/type.ts +19 -8
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/command-load-failure.test.mjs +83 -0
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/blog.mjs +23 -8
- package/clients/cli/commands/blog.test.mjs +42 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
- package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
- package/clients/cli/commands/build-theme.mjs +16 -50
- package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
- package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
- package/clients/cli/commands/build.doc.mjs +16 -8
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
- package/clients/cli/commands/build.mjs +137 -114
- package/clients/cli/commands/build.playbook.test.mjs +75 -0
- package/clients/cli/commands/build.text-fields.test.mjs +81 -0
- package/clients/cli/commands/component/index.mjs +153 -61
- package/clients/cli/commands/component-batch.test.mjs +341 -0
- package/clients/cli/commands/component-ownership.test.mjs +92 -3
- package/clients/cli/commands/component-package.test.mjs +46 -0
- package/clients/cli/commands/component-resolution.test.mjs +21 -0
- package/clients/cli/commands/component.doc.mjs +28 -10
- package/clients/cli/commands/component.test.mjs +19 -0
- package/clients/cli/commands/detail-levels.test.mjs +2 -2
- package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
- package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
- package/clients/cli/commands/discover.doc.mjs +55 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
- package/clients/cli/commands/docs.doc.mjs +28 -6
- package/clients/cli/commands/docs.mjs +295 -38
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +6 -5
- 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.package-json.test.mjs +53 -0
- package/clients/cli/commands/doctor-integration.test.mjs +143 -8
- package/clients/cli/commands/doctor.doc.mjs +4 -2
- package/clients/cli/commands/doctor.mjs +108 -37
- package/clients/cli/commands/doctor.test.mjs +42 -0
- package/clients/cli/commands/gap-report.doc.mjs +27 -15
- package/clients/cli/commands/gap-report.test.mjs +72 -0
- package/clients/cli/commands/hook/index.mjs +7 -17
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
- package/clients/cli/commands/init.doc.mjs +24 -10
- package/clients/cli/commands/init.flag-help.test.mjs +153 -0
- package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
- package/clients/cli/commands/integration-add.doc.mjs +39 -13
- package/clients/cli/commands/integration-authoring.test.mjs +74 -19
- package/clients/cli/commands/integration-pack.doc.mjs +6 -10
- package/clients/cli/commands/integration-real-world.test.mjs +4 -10
- package/clients/cli/commands/integration-verify.doc.mjs +22 -0
- package/clients/cli/commands/integration.doc.mjs +5 -5
- package/clients/cli/commands/integration.mjs +75 -43
- package/clients/cli/commands/interactive-guard.test.mjs +101 -24
- package/clients/cli/commands/json-contract.test.mjs +33 -0
- package/clients/cli/commands/layout-check.doc.mjs +15 -4
- package/clients/cli/commands/layout-expand.doc.mjs +22 -5
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +3 -3
- package/clients/cli/commands/layout.mjs +21 -9
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/manifest.doc.mjs +2 -2
- package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
- package/clients/cli/commands/search.doc.mjs +16 -6
- package/clients/cli/commands/search.mjs +49 -11
- package/clients/cli/commands/search.test.mjs +92 -0
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +4 -3
- package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
- package/clients/cli/commands/template.doc.mjs +53 -14
- package/clients/cli/commands/template.flag-help.test.mjs +117 -0
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/template.path-help.test.mjs +40 -0
- package/clients/cli/commands/text-json-parity.test.mjs +725 -0
- package/clients/cli/commands/theme-add.doc.mjs +5 -4
- package/clients/cli/commands/theme-build.doc.mjs +8 -7
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +12 -7
- package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
- package/clients/cli/commands/theme-palette.doc.mjs +2 -3
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +3 -3
- package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
- package/clients/cli/commands/theme-template.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +3 -2
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
- package/clients/cli/commands/upgrade.doc.mjs +83 -12
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
- package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +164 -1
- package/clients/cli/formatters/index.test.mjs +97 -0
- package/clients/cli/index.mjs +47 -34
- package/clients/cli/latest-version-env.test.mjs +50 -0
- package/clients/cli/lib/cli-error.test.mjs +7 -0
- package/clients/cli/lib/component-format.mjs +9 -9
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +56 -6
- package/clients/cli/lib/define-command.test.mjs +54 -0
- package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
- package/clients/cli/lib/exit-codes.test.mjs +113 -0
- package/clients/cli/lib/hook-format.mjs +19 -10
- package/clients/cli/lib/json-shim.mjs +62 -16
- package/clients/cli/lib/json-shim.test.mjs +83 -0
- package/clients/cli/lib/manifest.d.ts +2 -0
- package/clients/cli/lib/manifest.mjs +53 -6
- package/clients/cli/lib/manifest.test.mjs +22 -2
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.d.mts +7 -2
- package/foundation/agent-docs/agent-docs.mjs +83 -13
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
- package/foundation/config/integration-debug.test.mjs +28 -3
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +263 -91
- package/foundation/config/project.test.mjs +270 -21
- package/foundation/discovery/authoring-self-docs.d.mts +87 -0
- package/foundation/discovery/authoring-self-docs.mjs +237 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +174 -0
- 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 +504 -0
- package/foundation/discovery/cli-self-docs.test.mjs +395 -0
- package/foundation/discovery/component-discovery.d.mts +39 -1
- package/foundation/discovery/component-discovery.mjs +50 -1
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +119 -11
- package/foundation/discovery/docs-discovery.mjs +427 -108
- package/foundation/discovery/docs-discovery.test.mjs +386 -20
- package/foundation/discovery/docs-output-budget.d.mts +28 -0
- package/foundation/discovery/docs-output-budget.mjs +50 -0
- package/foundation/discovery/docs-section-key.d.mts +116 -0
- package/foundation/discovery/docs-section-key.mjs +322 -0
- package/foundation/discovery/docs-section-key.test.mjs +246 -0
- package/foundation/discovery/template-adapter.d.mts +113 -11
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
- package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
- package/foundation/discovery/template-adapter.mjs +774 -83
- 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/discovery/theming-targets.test.mjs +4 -0
- 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 +343 -0
- package/foundation/doc-compiler/compile.mjs +558 -0
- 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 +714 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +1643 -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 +298 -0
- package/foundation/doc-compiler/ir.d.mts +22 -0
- package/foundation/doc-compiler/ir.mjs +471 -0
- package/foundation/doc-compiler/lenses.d.mts +36 -0
- package/foundation/doc-compiler/lenses.mjs +173 -0
- 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 +292 -0
- package/foundation/doc-compiler/tree.mjs +881 -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/fs/module-loader.d.mts +1 -0
- package/foundation/fs/module-loader.mjs +50 -1
- package/foundation/fs/module-loader.stdout.test.mjs +332 -0
- package/foundation/fs/path-safety.d.mts +3 -2
- package/foundation/fs/path-safety.mjs +49 -19
- package/foundation/fs/path-safety.test.mjs +50 -0
- package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
- package/foundation/identity/provider-identity.d.mts +90 -0
- package/foundation/identity/provider-identity.mjs +320 -0
- package/foundation/identity/provider-identity.test.mjs +254 -0
- package/foundation/identity/providers.d.mts +7 -0
- package/foundation/identity/providers.mjs +16 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -45
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +65 -0
- package/foundation/integrations/cli-requirement.mjs +189 -0
- package/foundation/integrations/cli-requirement.test.mjs +89 -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 +28 -25
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +52 -21
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/integrations.d.mts +63 -3
- package/foundation/integrations/integrations.mjs +122 -9
- package/foundation/integrations/integrations.test.mjs +415 -1
- package/foundation/integrations/provider-conflicts.test.mjs +125 -0
- 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.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +131 -29
- package/foundation/response/base.d.ts +8 -4
- package/foundation/response/batch.type.d.mts +33 -0
- package/foundation/response/batch.type.mjs +34 -0
- 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 +19 -12
- package/foundation/response/error-codes.mjs +8 -2
- package/foundation/response/error-codes.test.mjs +166 -14
- package/foundation/response/json-contract.test.mjs +57 -17
- package/foundation/response/json.d.mts +4 -2
- package/foundation/response/json.mjs +8 -10
- package/foundation/response/response-types.doc.d.mts +7 -2
- package/foundation/response/response-types.doc.mjs +69 -25
- package/foundation/response/response-types.doc.test.mjs +181 -0
- package/foundation/response/response.doc.mjs +12 -11
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +40 -10
- package/foundation/xle/expand.d.mts +2 -0
- package/foundation/xle/expand.mjs +4 -3
- package/foundation/xle/expand.test.mjs +54 -0
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -11
- package/api/docs/docs.test.mjs +0 -83
- package/api/docs/integrationDocs.test.mjs +0 -208
- package/api/search/search.test.mjs +0 -389
- package/assets/docs/cli-integrations.doc.mjs +0 -367
- package/assets/templates/themes/manifest.json +0 -95
- package/clients/cli/commands/docs.test.mjs +0 -102
- package/clients/cli/lib/update-check.mjs +0 -83
- package/clients/cli/lib/update-check.test.mjs +0 -137
- package/clients/cli/update-hint-commands.test.mjs +0 -54
- package/foundation/agent-docs/agent-docs.test.mjs +0 -1141
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Text/JSON parity — every command's human output is a projection of its
|
|
5
|
+
* JSON envelope.
|
|
6
|
+
*
|
|
7
|
+
* Architecture: cli-surface INV4, INV5. Spec: AST-042 FR1.
|
|
8
|
+
*
|
|
9
|
+
* The test:
|
|
10
|
+
* 1. Enumerates every JSON-capable command from the live manifest and fails
|
|
11
|
+
* when a command has no parity case. A new command must add one.
|
|
12
|
+
* 2. Runs each case in text mode and --json mode via the in-process harness.
|
|
13
|
+
* 3. Checks:
|
|
14
|
+
* a. Every `key: value` record line in the text names a key that exists
|
|
15
|
+
* in the JSON data (criterion 1 — text shows no fact JSON lacks).
|
|
16
|
+
* b. Every JSON leaf field appears in the text, unless the allowlist
|
|
17
|
+
* forgives it with a reason (criterion 2).
|
|
18
|
+
*
|
|
19
|
+
* Allowlist entries are stale-checked: an entry that forgives a field that
|
|
20
|
+
* does not appear anywhere in the JSON output FAILS the test, so the
|
|
21
|
+
* allowlist shrinks over time.
|
|
22
|
+
*
|
|
23
|
+
* DO NOT add allowlist entries without a reason. Headings and invocation-
|
|
24
|
+
* prefixed hints are the only categories that are structurally OK to forgive;
|
|
25
|
+
* everything else is a divergence that should be fixed.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import {describe, it, expect, beforeAll, afterAll} from 'vitest';
|
|
29
|
+
import * as fs from 'node:fs';
|
|
30
|
+
import * as path from 'node:path';
|
|
31
|
+
import * as os from 'node:os';
|
|
32
|
+
import {fileURLToPath} from 'node:url';
|
|
33
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
34
|
+
|
|
35
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const REPO_ROOT = path.resolve(__dirname, '../../../../..');
|
|
37
|
+
|
|
38
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a text value for comparison: collapse whitespace, strip the
|
|
42
|
+
* package-manager invocation prefix, and undo ASCII normalization.
|
|
43
|
+
* @param {string} s
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function normalize(s) {
|
|
47
|
+
return s
|
|
48
|
+
.replace(/\s+/g, ' ')
|
|
49
|
+
.replace(/(npx|pnpm exec|yarn dlx|bunx)\s+astryx/g, 'astryx')
|
|
50
|
+
.replace(/[\u2014\u2013]/g, '-')
|
|
51
|
+
.replace(/[\u2018\u2019]/g, "'")
|
|
52
|
+
.replace(/[\u201C\u201D]/g, '"')
|
|
53
|
+
.replace(/\u2026/g, '...')
|
|
54
|
+
.trim();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract `key: value` record lines from text output. The formatter always
|
|
59
|
+
* produces `key: value` (padded) or `key: value`.
|
|
60
|
+
*
|
|
61
|
+
* Lines whose key is a known hint prefix (e.g. "Usage:") are excluded —
|
|
62
|
+
* those are invocation hints allowed by INV4, not data records.
|
|
63
|
+
* @param {string} text
|
|
64
|
+
* @returns {Map<string, string[]>}
|
|
65
|
+
*/
|
|
66
|
+
function extractRecordKeys(text) {
|
|
67
|
+
const HINT_PREFIXES = new Set(['Usage', 'Summary', 'Read', 'Example', 'Tip']);
|
|
68
|
+
/** @type {Map<string, string[]>} */
|
|
69
|
+
const map = new Map();
|
|
70
|
+
for (const line of text.split('\n')) {
|
|
71
|
+
// Match "word-key: value" but not URLs, headings, or code fences
|
|
72
|
+
const m = line.match(/^(\w[\w-]*):\s{1,}\S/);
|
|
73
|
+
if (!m) continue;
|
|
74
|
+
const key = m[1];
|
|
75
|
+
if (HINT_PREFIXES.has(key)) continue;
|
|
76
|
+
const value = line.slice(line.indexOf(':', key.length) + 1).trim();
|
|
77
|
+
if (!map.has(key)) map.set(key, []);
|
|
78
|
+
map.get(key).push(normalize(value));
|
|
79
|
+
}
|
|
80
|
+
return map;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extract bulleted list items from text output (`- item`).
|
|
85
|
+
* @param {string} text
|
|
86
|
+
* @returns {string[]}
|
|
87
|
+
*/
|
|
88
|
+
function extractListItems(text) {
|
|
89
|
+
return text
|
|
90
|
+
.split('\n')
|
|
91
|
+
.filter(line => /^- \S/.test(line))
|
|
92
|
+
.map(line => normalize(line.slice(2)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract standalone text blocks — lines that are not record lines, list
|
|
97
|
+
* items, section headings, hint lines, or blank. These correspond to
|
|
98
|
+
* `text(...)` formatter calls and must carry facts from the JSON.
|
|
99
|
+
* @param {string} text
|
|
100
|
+
* @returns {string[]}
|
|
101
|
+
*/
|
|
102
|
+
function extractTextBlocks(text) {
|
|
103
|
+
const HINT_PREFIXES = /^(Usage|Summary|Read|Example|Tip|Import from|Related blocks|Each key|Props take|One component|No results|Try a|Browse instead|No failures|All checks|Some checks|Pass --\w)[:.]?/i;
|
|
104
|
+
const lines = text.split('\n');
|
|
105
|
+
/** @type {string[]} */
|
|
106
|
+
const blocks = [];
|
|
107
|
+
for (let i = 0; i < lines.length; i++) {
|
|
108
|
+
const trimmed = lines[i].trim();
|
|
109
|
+
if (!trimmed) continue;
|
|
110
|
+
// Skip record lines
|
|
111
|
+
if (/^\w[\w-]*:\s{1,}\S/.test(trimmed)) continue;
|
|
112
|
+
// Skip list items
|
|
113
|
+
if (/^- \S/.test(trimmed)) continue;
|
|
114
|
+
// Skip hints
|
|
115
|
+
if (HINT_PREFIXES.test(trimmed)) continue;
|
|
116
|
+
// Skip lines that look like code, commands, or status tokens
|
|
117
|
+
if (trimmed.startsWith('`') || trimmed.startsWith('$')) continue;
|
|
118
|
+
if (trimmed.startsWith('[ok]') || trimmed.startsWith('[skip]') ||
|
|
119
|
+
trimmed.startsWith('[fail]') || trimmed.startsWith('[warn]') ||
|
|
120
|
+
trimmed.startsWith('!') || trimmed.startsWith('(')) continue;
|
|
121
|
+
// Skip continuation lines (indented, part of a wrapped record or list)
|
|
122
|
+
if (/^\s{2,}\S/.test(lines[i])) continue;
|
|
123
|
+
// Skip section headings: a line preceded by blank (or start) that belongs
|
|
124
|
+
// to a block followed by blank. Section blocks can be multi-line (heading
|
|
125
|
+
// + subtitle), so check whether THIS line's block ends before a blank.
|
|
126
|
+
// Only skip short, heading-like text (no sentence punctuation) to avoid
|
|
127
|
+
// suppressing real text() facts.
|
|
128
|
+
const prevBlank = i === 0 || !lines[i - 1]?.trim();
|
|
129
|
+
const nextBlank = i === lines.length - 1 || !lines[i + 1]?.trim();
|
|
130
|
+
const looksLikeHeading = trimmed.length <= 80 && !/[.!?]$/.test(trimmed);
|
|
131
|
+
// Single-line heading: blank before AND after, and heading-like
|
|
132
|
+
if (prevBlank && nextBlank && looksLikeHeading) continue;
|
|
133
|
+
// Multi-line heading block: blank before this line, heading-like
|
|
134
|
+
if (prevBlank && !nextBlank && looksLikeHeading) {
|
|
135
|
+
let j = i + 1;
|
|
136
|
+
while (j < lines.length && lines[j]?.trim()) j++;
|
|
137
|
+
if (j - i <= 3) continue;
|
|
138
|
+
}
|
|
139
|
+
// Subtitle line: preceded by a heading-like line preceded by blank
|
|
140
|
+
if (!prevBlank && nextBlank && i >= 1) {
|
|
141
|
+
const prevLine = lines[i - 1]?.trim() ?? '';
|
|
142
|
+
const prevPrevBlank = i < 2 || !lines[i - 2]?.trim();
|
|
143
|
+
if (prevPrevBlank && prevLine.length <= 80 && !/[.!?]$/.test(prevLine)) continue;
|
|
144
|
+
}
|
|
145
|
+
blocks.push(normalize(trimmed));
|
|
146
|
+
}
|
|
147
|
+
return blocks;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Collect every leaf string value from JSON data so we can verify text content
|
|
152
|
+
* appears somewhere in the JSON.
|
|
153
|
+
* @param {unknown} obj
|
|
154
|
+
* @returns {Set<string>}
|
|
155
|
+
*/
|
|
156
|
+
function jsonLeafValues(obj) {
|
|
157
|
+
/** @type {Set<string>} */
|
|
158
|
+
const values = new Set();
|
|
159
|
+
if (obj == null) return values;
|
|
160
|
+
if (typeof obj === 'string') { values.add(normalize(obj)); return values; }
|
|
161
|
+
if (typeof obj === 'number' || typeof obj === 'boolean') { values.add(String(obj)); return values; }
|
|
162
|
+
if (Array.isArray(obj)) { for (const item of obj) { for (const v of jsonLeafValues(item)) values.add(v); } return values; }
|
|
163
|
+
if (typeof obj === 'object') { for (const value of Object.values(obj)) { for (const v of jsonLeafValues(value)) values.add(v); } }
|
|
164
|
+
return values;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Collect every PRIMITIVE leaf key from a JSON value. Returns keys whose
|
|
169
|
+
* values are strings, numbers, booleans, or arrays of primitives. Does NOT
|
|
170
|
+
* return intermediate container keys (objects, arrays of objects).
|
|
171
|
+
* @param {unknown} obj
|
|
172
|
+
* @returns {Set<string>}
|
|
173
|
+
*/
|
|
174
|
+
function jsonLeafKeys(obj) {
|
|
175
|
+
/** @type {Set<string>} */
|
|
176
|
+
const keys = new Set();
|
|
177
|
+
if (obj == null || typeof obj !== 'object') return keys;
|
|
178
|
+
if (Array.isArray(obj)) {
|
|
179
|
+
for (const item of obj) {
|
|
180
|
+
if (item != null && typeof item === 'object' && !Array.isArray(item)) {
|
|
181
|
+
for (const k of jsonLeafKeys(item)) keys.add(k);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return keys;
|
|
185
|
+
}
|
|
186
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
187
|
+
if (value == null || typeof value !== 'object') {
|
|
188
|
+
keys.add(key);
|
|
189
|
+
} else if (Array.isArray(value)) {
|
|
190
|
+
if (value.length > 0 && typeof value[0] !== 'object') {
|
|
191
|
+
keys.add(key);
|
|
192
|
+
} else {
|
|
193
|
+
for (const item of value) {
|
|
194
|
+
if (item != null && typeof item === 'object' && !Array.isArray(item)) {
|
|
195
|
+
for (const k of jsonLeafKeys(item)) keys.add(k);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
for (const k of jsonLeafKeys(value)) keys.add(k);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return keys;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Map each leaf key to the set of values it takes across the JSON data.
|
|
208
|
+
* Used for the JSON→text value-presence check on inline-layout commands.
|
|
209
|
+
* @param {unknown} obj
|
|
210
|
+
* @returns {Map<string, Set<string>>}
|
|
211
|
+
*/
|
|
212
|
+
function jsonKeyValues(obj) {
|
|
213
|
+
/** @type {Map<string, Set<string>>} */
|
|
214
|
+
const map = new Map();
|
|
215
|
+
function walk(/** @type {unknown} */ node) {
|
|
216
|
+
if (node == null || typeof node !== 'object') return;
|
|
217
|
+
if (Array.isArray(node)) { for (const item of node) walk(item); return; }
|
|
218
|
+
for (const [key, value] of Object.entries(node)) {
|
|
219
|
+
if (value == null) continue;
|
|
220
|
+
if (typeof value !== 'object') {
|
|
221
|
+
if (!map.has(key)) map.set(key, new Set());
|
|
222
|
+
map.get(key).add(normalize(String(value)));
|
|
223
|
+
} else if (Array.isArray(value)) {
|
|
224
|
+
if (value.length > 0 && typeof value[0] !== 'object') {
|
|
225
|
+
if (!map.has(key)) map.set(key, new Set());
|
|
226
|
+
for (const v of value) map.get(key).add(normalize(String(v)));
|
|
227
|
+
} else {
|
|
228
|
+
for (const item of value) walk(item);
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
walk(value);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
walk(obj);
|
|
236
|
+
return map;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Check whether a JSON object (at any depth) contains a given key name.
|
|
241
|
+
* Used for the stale-allowlist check: the field must exist SOMEWHERE in the
|
|
242
|
+
* full JSON output even if our leaf extractor skips it.
|
|
243
|
+
* @param {unknown} obj
|
|
244
|
+
* @param {string} target
|
|
245
|
+
* @returns {boolean}
|
|
246
|
+
*/
|
|
247
|
+
function jsonContainsKey(obj, target) {
|
|
248
|
+
if (obj == null || typeof obj !== 'object') return false;
|
|
249
|
+
if (Array.isArray(obj)) return obj.some(item => jsonContainsKey(item, target));
|
|
250
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
251
|
+
if (key === target) return true;
|
|
252
|
+
if (jsonContainsKey(value, target)) return true;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ─── Test cases ─────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @typedef {object} ParityCase
|
|
261
|
+
* @property {string} name - The manifest command name.
|
|
262
|
+
* @property {string[]} args - CLI args after `astryx`.
|
|
263
|
+
* @property {string} [cwd] - Working directory; defaults to REPO_ROOT.
|
|
264
|
+
* @property {(dir: string) => void} [setup] - Prepare a tmpDir before the run.
|
|
265
|
+
* @property {string[]} [jsonFieldAllowlist] - JSON keys forgiven from text.
|
|
266
|
+
* @property {boolean} [skipFieldChecks] - Skip both record-key and field checks.
|
|
267
|
+
* @property {boolean} [errorExpected] - The command is expected to error.
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
/** @type {ParityCase[]} */
|
|
271
|
+
const CASES = [
|
|
272
|
+
{
|
|
273
|
+
name: 'blog',
|
|
274
|
+
args: ['blog'],
|
|
275
|
+
// Blog fetches the network feed. In CI the feed is unreachable, so
|
|
276
|
+
// the command errors. The envelope shape is still checked.
|
|
277
|
+
errorExpected: true,
|
|
278
|
+
skipFieldChecks: true,
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: 'build',
|
|
282
|
+
args: ['build'],
|
|
283
|
+
// PR #6566 fixes the playbook divergence.
|
|
284
|
+
skipFieldChecks: true,
|
|
285
|
+
jsonFieldAllowlist: [
|
|
286
|
+
'playbook — PR #6566 adds full playbook fields to the JSON envelope',
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'component',
|
|
291
|
+
args: ['component', '--list'],
|
|
292
|
+
// #6507 touches this handler; divergences deferred.
|
|
293
|
+
skipFieldChecks: true,
|
|
294
|
+
jsonFieldAllowlist: [
|
|
295
|
+
'detail — internal detail-level tag, not a user-facing datum',
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'discover',
|
|
300
|
+
args: ['discover'],
|
|
301
|
+
cwd: '__TMP__',
|
|
302
|
+
// Empty result (no config). The meta.configured field is projected as
|
|
303
|
+
// prose in the text.
|
|
304
|
+
skipFieldChecks: true,
|
|
305
|
+
jsonFieldAllowlist: [
|
|
306
|
+
'configured — expressed as prose "No integrations configured"',
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: 'docs',
|
|
311
|
+
args: ['docs'],
|
|
312
|
+
jsonFieldAllowlist: [
|
|
313
|
+
'topic — inline layout lead column, not a key: value record',
|
|
314
|
+
'description — inline layout trailing column',
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'doctor',
|
|
319
|
+
args: ['doctor'],
|
|
320
|
+
jsonFieldAllowlist: [
|
|
321
|
+
'fix — only present on checks that have a remediation',
|
|
322
|
+
'pass — shown in prose "Summary: N passed, ..."',
|
|
323
|
+
'warn — shown in prose "Summary: ..., N warnings"',
|
|
324
|
+
'fail — shown in prose "Summary: ..., N failures"',
|
|
325
|
+
'info — shown in prose "Summary: ..., N info" when nonzero',
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'doctor integration validate',
|
|
330
|
+
args: ['doctor', 'integration', 'validate'],
|
|
331
|
+
skipFieldChecks: true,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: 'doctor integration components',
|
|
335
|
+
args: ['doctor', 'integration', 'components'],
|
|
336
|
+
skipFieldChecks: true,
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'doctor integration docs',
|
|
340
|
+
args: ['doctor', 'integration', 'docs'],
|
|
341
|
+
skipFieldChecks: true,
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'doctor integration templates',
|
|
345
|
+
args: ['doctor', 'integration', 'templates'],
|
|
346
|
+
skipFieldChecks: true,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: 'gap-report',
|
|
350
|
+
args: ['gap-report', '--list-categories'],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: 'hook',
|
|
354
|
+
args: ['hook', '--list', '--detail', 'compact'],
|
|
355
|
+
jsonFieldAllowlist: [
|
|
356
|
+
'detail — internal detail-level tag, not a user-facing datum',
|
|
357
|
+
'import — shown inside the code() formatted block, not as a record',
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: 'init',
|
|
362
|
+
args: ['init'],
|
|
363
|
+
cwd: '__TMP__',
|
|
364
|
+
// Init emits text via the API logger, not the formatter kit.
|
|
365
|
+
skipFieldChecks: true,
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: 'integration add',
|
|
369
|
+
args: ['integration', 'add', 'component', 'TestComp', '--dry-run'],
|
|
370
|
+
// Needs a valid integration project to work; errors without one.
|
|
371
|
+
errorExpected: true,
|
|
372
|
+
skipFieldChecks: true,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: 'integration verify',
|
|
376
|
+
args: ['integration', 'verify'],
|
|
377
|
+
// Needs a packable project; pack itself may exit differently.
|
|
378
|
+
skipFieldChecks: true,
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
// The deprecated alias of `integration verify`.
|
|
382
|
+
name: 'integration pack',
|
|
383
|
+
args: ['integration', 'pack', '--check'],
|
|
384
|
+
skipFieldChecks: true,
|
|
385
|
+
},
|
|
386
|
+
// ── Layout command: remove these cases when the layout command is deleted ──
|
|
387
|
+
{
|
|
388
|
+
name: 'layout check',
|
|
389
|
+
args: ['layout', 'check', 'Button'],
|
|
390
|
+
skipFieldChecks: true,
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
name: 'layout expand',
|
|
394
|
+
args: ['layout', 'expand', 'Button'],
|
|
395
|
+
skipFieldChecks: true,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: 'layout grammar',
|
|
399
|
+
args: ['layout', 'grammar'],
|
|
400
|
+
skipFieldChecks: true,
|
|
401
|
+
},
|
|
402
|
+
// ── End layout cases ──
|
|
403
|
+
{
|
|
404
|
+
name: 'manifest',
|
|
405
|
+
args: ['manifest'],
|
|
406
|
+
// Manifest describes the CLI itself; exempt (AST-042 FR1).
|
|
407
|
+
skipFieldChecks: true,
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: 'search',
|
|
411
|
+
args: ['search', 'button'],
|
|
412
|
+
jsonFieldAllowlist: [
|
|
413
|
+
'matchCount — stated in section heading "Results for ... (N)"',
|
|
414
|
+
'query — stated in section heading',
|
|
415
|
+
'score — only shown with --verbose',
|
|
416
|
+
'reason — only shown with --verbose',
|
|
417
|
+
'kind — internal domain subtype, not shown in default text',
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'swizzle',
|
|
422
|
+
args: ['swizzle', '--list'],
|
|
423
|
+
skipFieldChecks: true,
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: 'template',
|
|
427
|
+
args: ['template', '--list'],
|
|
428
|
+
// #6265 touches this handler; template id fix deferred.
|
|
429
|
+
skipFieldChecks: true,
|
|
430
|
+
jsonFieldAllowlist: [
|
|
431
|
+
'id — not shown in text records (#6265)',
|
|
432
|
+
'isReady — folded into name as "(WIP)" suffix (#6265)',
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'theme add',
|
|
437
|
+
args: ['theme', 'add', '--list'],
|
|
438
|
+
// Themes are rendered as list items, not records.
|
|
439
|
+
skipFieldChecks: true,
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: 'theme build',
|
|
443
|
+
args: ['theme', 'build', 'nonexistent.ts'],
|
|
444
|
+
errorExpected: true,
|
|
445
|
+
skipFieldChecks: true,
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'theme list',
|
|
449
|
+
args: ['theme', 'list'],
|
|
450
|
+
skipFieldChecks: true,
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: 'theme palette generate',
|
|
454
|
+
args: ['theme', 'palette', 'generate', 'nonexistent.json'],
|
|
455
|
+
errorExpected: true,
|
|
456
|
+
skipFieldChecks: true,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: 'theme targets',
|
|
460
|
+
args: ['theme', 'targets'],
|
|
461
|
+
jsonFieldAllowlist: [
|
|
462
|
+
'filter — null when no filter given; not shown',
|
|
463
|
+
'componentCount — shown in section heading subtitle',
|
|
464
|
+
],
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: 'theme template',
|
|
468
|
+
args: ['theme', 'template'],
|
|
469
|
+
cwd: '__TMP__',
|
|
470
|
+
jsonFieldAllowlist: [
|
|
471
|
+
'written — stated in status token ([ok] / [skip])',
|
|
472
|
+
'path — stated in the "[ok] Wrote ..." line',
|
|
473
|
+
'reason — absent; only in skip envelopes',
|
|
474
|
+
],
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: 'upgrade',
|
|
478
|
+
args: ['upgrade', '--list'],
|
|
479
|
+
cwd: '__TMP__',
|
|
480
|
+
},
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
// ─── Suite ──────────────────────────────────────────────────────────────────
|
|
484
|
+
|
|
485
|
+
describe('text-json-parity', () => {
|
|
486
|
+
/** @type {string[]} */
|
|
487
|
+
let manifestCommands;
|
|
488
|
+
/** @type {string} */
|
|
489
|
+
let tmpDir;
|
|
490
|
+
|
|
491
|
+
beforeAll(async () => {
|
|
492
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-parity-'));
|
|
493
|
+
const {stdout} = await runCli(['manifest', '--json'], {cwd: REPO_ROOT});
|
|
494
|
+
const manifest = JSON.parse(stdout);
|
|
495
|
+
manifestCommands = manifest.data.jsonSupported;
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
afterAll(() => {
|
|
499
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// ── Gate: every JSON-capable command has a parity case ──────────────
|
|
503
|
+
it('every JSON-capable command in the manifest has a parity case', () => {
|
|
504
|
+
const covered = new Set(CASES.map(c => c.name));
|
|
505
|
+
const missing = manifestCommands.filter(cmd => !covered.has(cmd));
|
|
506
|
+
expect(
|
|
507
|
+
missing,
|
|
508
|
+
`Add a parity case for these commands:\n${missing.join('\n')}`,
|
|
509
|
+
).toEqual([]);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('every parity case names a real manifest command', () => {
|
|
513
|
+
const real = new Set(manifestCommands);
|
|
514
|
+
const stale = CASES.filter(c => !real.has(c.name)).map(c => c.name);
|
|
515
|
+
expect(stale).toEqual([]);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// ── Per-command parity checks ──────────────────────────────────────
|
|
519
|
+
for (const tc of CASES) {
|
|
520
|
+
describe(tc.name, () => {
|
|
521
|
+
/** @type {{text: string, json: any, textStatus: number, jsonStatus: number}} */
|
|
522
|
+
let run;
|
|
523
|
+
|
|
524
|
+
beforeAll(async () => {
|
|
525
|
+
let cwd = tc.cwd === '__TMP__' ? tmpDir : (tc.cwd || REPO_ROOT);
|
|
526
|
+
if (tc.cwd === '__TMP__') {
|
|
527
|
+
const sub = path.join(tmpDir, tc.name.replace(/\s+/g, '-'));
|
|
528
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
529
|
+
if (tc.setup) tc.setup(sub);
|
|
530
|
+
cwd = sub;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Run sequentially: runCli uses process.chdir, which is global
|
|
534
|
+
// state and races under Promise.all.
|
|
535
|
+
const textResult = await runCli(tc.args, {cwd});
|
|
536
|
+
const jsonResult = await runCli([...tc.args, '--json'], {cwd});
|
|
537
|
+
|
|
538
|
+
let jsonData;
|
|
539
|
+
try {
|
|
540
|
+
jsonData = JSON.parse(jsonResult.stdout);
|
|
541
|
+
} catch {
|
|
542
|
+
jsonData = null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
run = {
|
|
546
|
+
text: textResult.stdout,
|
|
547
|
+
json: jsonData,
|
|
548
|
+
textStatus: textResult.status,
|
|
549
|
+
jsonStatus: jsonResult.status,
|
|
550
|
+
};
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('JSON output is a valid envelope', () => {
|
|
554
|
+
expect(run.json).not.toBeNull();
|
|
555
|
+
const isSuccess = run.json?.type != null;
|
|
556
|
+
const isError = run.json?.error != null;
|
|
557
|
+
expect(isSuccess || isError).toBe(true);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('exit codes match between text and JSON mode', () => {
|
|
561
|
+
expect(run.textStatus).toBe(run.jsonStatus);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
if (!tc.skipFieldChecks && !tc.errorExpected) {
|
|
565
|
+
it('every text record key exists in the JSON data', () => {
|
|
566
|
+
if (!run.json?.data) return;
|
|
567
|
+
const textKeys = extractRecordKeys(run.text);
|
|
568
|
+
const jsonKeys = jsonLeafKeys(run.json.data);
|
|
569
|
+
if (run.json.meta) {
|
|
570
|
+
for (const k of jsonLeafKeys(run.json.meta)) jsonKeys.add(k);
|
|
571
|
+
}
|
|
572
|
+
const violations = [];
|
|
573
|
+
for (const key of textKeys.keys()) {
|
|
574
|
+
if (!jsonKeys.has(key)) {
|
|
575
|
+
violations.push(key);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
expect(
|
|
579
|
+
violations,
|
|
580
|
+
`Text record keys not in JSON:\n${violations.join(', ')}`,
|
|
581
|
+
).toEqual([]);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('every JSON leaf field appears in the text or is allowlisted', () => {
|
|
585
|
+
if (!run.json?.data) return;
|
|
586
|
+
const textKeys = extractRecordKeys(run.text);
|
|
587
|
+
const jsonKeys = jsonLeafKeys(run.json.data);
|
|
588
|
+
const keyVals = jsonKeyValues(run.json.data);
|
|
589
|
+
const allowedKeys = new Set(
|
|
590
|
+
(tc.jsonFieldAllowlist || []).map(e => e.split(' — ')[0].trim()),
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const missing = [];
|
|
594
|
+
for (const key of jsonKeys) {
|
|
595
|
+
const inRecords = textKeys.has(key);
|
|
596
|
+
// Check for `key:` as a field label at start of line.
|
|
597
|
+
const asLabel = new RegExp(`(?:^|\\n)\\s*${key}:`, 'm').test(run.text);
|
|
598
|
+
// For inline records (no labels): check if THIS KEY's values
|
|
599
|
+
// appear in the text. This catches inline-layout projections
|
|
600
|
+
// where the key name is only in the section subtitle.
|
|
601
|
+
const myVals = keyVals.get(key) ?? new Set();
|
|
602
|
+
const valuesInText = !inRecords && !asLabel && [...myVals].some(v =>
|
|
603
|
+
v.length > 2 && run.text.includes(v),
|
|
604
|
+
);
|
|
605
|
+
const allowed = allowedKeys.has(key);
|
|
606
|
+
if (!inRecords && !asLabel && !valuesInText && !allowed) {
|
|
607
|
+
missing.push(key);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
expect(
|
|
611
|
+
missing,
|
|
612
|
+
`JSON fields not in text and not allowlisted:\n${missing.join(', ')}`,
|
|
613
|
+
).toEqual([]);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('text list items and block content appear in JSON data', () => {
|
|
617
|
+
if (!run.json?.data) return;
|
|
618
|
+
const jsonValues = jsonLeafValues(run.json.data);
|
|
619
|
+
if (run.json.meta) {
|
|
620
|
+
for (const v of jsonLeafValues(run.json.meta)) jsonValues.add(v);
|
|
621
|
+
}
|
|
622
|
+
const items = extractListItems(run.text);
|
|
623
|
+
const textBlocks = extractTextBlocks(run.text);
|
|
624
|
+
const violations = [];
|
|
625
|
+
for (const item of items) {
|
|
626
|
+
// Each list item should match (or be a substring of) some JSON value.
|
|
627
|
+
const found = [...jsonValues].some(
|
|
628
|
+
jv => jv === item || jv.includes(item) || item.includes(jv),
|
|
629
|
+
);
|
|
630
|
+
if (!found) {
|
|
631
|
+
violations.push(`[list] ${item.slice(0, 80)}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
for (const block of textBlocks) {
|
|
635
|
+
// Each text block should contain at least one word that appears in
|
|
636
|
+
// the JSON values. A block with no JSON overlap is a fact the
|
|
637
|
+
// envelope does not carry. Numbers (even short) count because
|
|
638
|
+
// they are data.
|
|
639
|
+
const words = block.split(/\s+/).filter(w =>
|
|
640
|
+
w.length > 3 || /^\d+$/.test(w),
|
|
641
|
+
);
|
|
642
|
+
const anyOverlap = words.some(w =>
|
|
643
|
+
[...jsonValues].some(jv => jv.includes(w)),
|
|
644
|
+
);
|
|
645
|
+
if (!anyOverlap && words.length > 0) {
|
|
646
|
+
violations.push(`[text] ${block.slice(0, 80)}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
expect(
|
|
650
|
+
violations,
|
|
651
|
+
`Text content not found in JSON data:\n${violations.join('\n')}`,
|
|
652
|
+
).toEqual([]);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Stale allowlist check
|
|
657
|
+
if (tc.jsonFieldAllowlist && tc.jsonFieldAllowlist.length > 0) {
|
|
658
|
+
it('allowlist entries are not stale', () => {
|
|
659
|
+
if (!run.json) return;
|
|
660
|
+
const searchTarget = run.json.data ?? run.json;
|
|
661
|
+
const stale = [];
|
|
662
|
+
for (const entry of tc.jsonFieldAllowlist) {
|
|
663
|
+
const key = entry.split(' — ')[0].trim();
|
|
664
|
+
if (!jsonContainsKey(searchTarget, key) && !jsonContainsKey(run.json.meta, key)) {
|
|
665
|
+
stale.push(entry);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
expect(
|
|
669
|
+
stale,
|
|
670
|
+
`Stale allowlist entries — remove them:\n${stale.join('\n')}`,
|
|
671
|
+
).toEqual([]);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// ── Upgrade failure-path regression tests ────────────────────────
|
|
678
|
+
// The upgrade handler silences the logger for --list and replays the error
|
|
679
|
+
// in the catch. These tests prove both list and non-list failures print
|
|
680
|
+
// the error exactly once and exit 1, in both text and --json mode.
|
|
681
|
+
describe('upgrade failure paths', () => {
|
|
682
|
+
it('upgrade --list --registry prints error once on stderr and exits 1 (text)', async () => {
|
|
683
|
+
const sub = path.join(tmpDir, 'upgrade-list-fail');
|
|
684
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
685
|
+
const r = await runCli(['upgrade', '--list', '--registry'], {cwd: sub});
|
|
686
|
+
expect(r.status).toBe(1);
|
|
687
|
+
// The error message should appear exactly once across stdout+stderr.
|
|
688
|
+
const combined = r.stdout + r.stderr;
|
|
689
|
+
const occurrences = combined.split('cannot be used together').length - 1;
|
|
690
|
+
expect(occurrences, 'error should appear exactly once').toBe(1);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it('upgrade --list --registry emits one error envelope in --json mode', async () => {
|
|
694
|
+
const sub = path.join(tmpDir, 'upgrade-list-fail-json');
|
|
695
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
696
|
+
const r = await runCli(['upgrade', '--list', '--registry', '--json'], {cwd: sub});
|
|
697
|
+
expect(r.status).toBe(1);
|
|
698
|
+
const parsed = JSON.parse(r.stdout);
|
|
699
|
+
expect(parsed).toHaveProperty('error');
|
|
700
|
+
expect(parsed).not.toHaveProperty('type');
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('non-list upgrade failure prints error once on stderr and exits 1 (text)', async () => {
|
|
704
|
+
const sub = path.join(tmpDir, 'upgrade-nonlist-fail');
|
|
705
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
706
|
+
const r = await runCli(['upgrade', '--from', 'notsemver'], {cwd: sub});
|
|
707
|
+
expect(r.status).toBe(1);
|
|
708
|
+
// The error should appear exactly once.
|
|
709
|
+
const combined = r.stdout + r.stderr;
|
|
710
|
+
const lines = combined.split('\n').filter(l => l.includes('notsemver') || l.includes('ERR'));
|
|
711
|
+
// At most one line carries the error; zero is OK if the API formats
|
|
712
|
+
// differently, but more than one means duplication.
|
|
713
|
+
expect(lines.length, 'error should not be duplicated').toBeLessThanOrEqual(1);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('non-list upgrade failure emits one error envelope in --json mode', async () => {
|
|
717
|
+
const sub = path.join(tmpDir, 'upgrade-nonlist-fail-json');
|
|
718
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
719
|
+
const r = await runCli(['upgrade', '--from', 'notsemver', '--json'], {cwd: sub});
|
|
720
|
+
expect(r.status).toBe(1);
|
|
721
|
+
const parsed = JSON.parse(r.stdout);
|
|
722
|
+
expect(parsed).toHaveProperty('error');
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
});
|