@astryxdesign/cli 0.6.4 → 0.6.5-canary.01972bc
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 +56 -0
- package/README.md +103 -95
- package/api/build/_adapter.d.mts +36 -2
- package/api/build/_adapter.mjs +41 -10
- package/api/build/build.doc.mjs +8 -3
- package/api/build/build.test.mjs +60 -2
- package/api/build/kit/kit.mjs +109 -26
- package/api/build/kit/rank.d.mts +24 -8
- package/api/build/kit/rank.mjs +277 -97
- package/api/build/kit/rank.test.mjs +231 -48
- package/api/component/_adapter.d.mts +25 -0
- package/api/component/_adapter.mjs +59 -5
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +37 -17
- package/api/component/component.mjs +249 -9
- package/api/component/component.type.d.mts +25 -0
- package/api/component/component.type.mjs +44 -0
- 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 +61 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +147 -8
- package/api/discover/discover.type.mjs +102 -12
- 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 +8 -3
- package/api/docs/_adapter.mjs +14 -6
- package/api/docs/docOverlays.test.mjs +27 -1
- package/api/docs/docs.doc.mjs +2 -2
- package/api/docs/docs.test.mjs +54 -18
- package/api/docs/integration-tree.test.mjs +17 -0
- package/api/docs/integrationDocs.test.mjs +27 -5
- package/api/doctor/doctor.d.mts +8 -3
- package/api/doctor/doctor.doc.mjs +17 -8
- package/api/doctor/doctor.mjs +90 -9
- package/api/doctor/doctor.test.mjs +122 -10
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/error.d.mts +22 -0
- package/api/error.mjs +42 -0
- package/api/gap-report/gap-report.doc.mjs +19 -10
- package/api/hook/hook.doc.mjs +6 -3
- package/api/index.d.mts +1 -0
- package/api/index.mjs +5 -3
- package/api/init/init.doc.mjs +17 -12
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +7 -3
- package/api/integration/add-contribution.test.mjs +3 -3
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +266 -23
- package/api/integration/add-theme.test.mjs +247 -0
- package/api/integration/authoring-checks.mjs +12 -9
- package/api/integration/authoring-checks.type.d.mts +8 -0
- package/api/integration/authoring-checks.type.mjs +7 -3
- package/api/integration/integration-authoring.type.d.mts +4 -1
- package/api/integration/integration-authoring.type.mjs +6 -1
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddTheme.doc.mjs +10 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
- package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
- package/api/integration/pack-check.mjs +92 -10
- package/api/integration/pack-check.test.mjs +140 -1
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/integration/validate-integration.d.mts +4 -2
- package/api/integration/validate-integration.mjs +7 -2
- package/api/integration/validate-integration.test.mjs +55 -0
- package/api/integration/validate-integration.type.d.mts +5 -0
- package/api/integration/validate-integration.type.mjs +5 -1
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/isError.doc.mjs +1 -1
- package/api/layout/expand/expand.mjs +12 -7
- package/api/layout/expand/expand.receipt.test.mjs +74 -0
- package/api/layout/layout.type.d.mts +1 -0
- package/api/layout/layout.type.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -1
- package/api/search/search.d.mts +51 -1
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +299 -17
- package/api/search/search.test.mjs +216 -18
- package/api/swizzle/copy/copy.mjs +66 -3
- package/api/swizzle/swizzle.doc.mjs +11 -5
- package/api/template/copy/copy.mjs +15 -9
- package/api/template/copy/copy.receipt.test.mjs +77 -0
- package/api/template/copy/copy.test.mjs +9 -0
- package/api/template/show/show.mjs +15 -4
- package/api/template/show/show.test.mjs +76 -0
- package/api/template/template-integration.test.mjs +14 -0
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +8 -3
- package/api/template/template.mjs +1 -0
- package/api/template/template.type.d.mts +2 -0
- package/api/template/template.type.mjs +2 -0
- package/api/theme/add/add.mjs +17 -25
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +40 -23
- package/api/theme/build/build.d.mts +24 -0
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +243 -26
- package/api/theme/build/build.project-core.test.mjs +165 -0
- package/api/theme/build/build.rollback.test.mjs +148 -0
- package/api/theme/generateTonalPalette.doc.mjs +1 -2
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +9 -10
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +2 -1
- package/api/theme/themePaletteGenerate.doc.mjs +15 -8
- package/api/theme/themeTargets.doc.mjs +3 -2
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/run/files-changed.test.mjs +111 -0
- package/api/upgrade/run/run.mjs +25 -6
- package/api/upgrade/run/run.test.mjs +45 -1
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/api/upgrade/upgrade.type.d.mts +1 -0
- package/api/upgrade/upgrade.type.mjs +3 -2
- package/assets/codemods/__tests__/runner.test.mjs +3 -1
- package/assets/codemods/file-count.test.mjs +163 -0
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +5 -4
- package/assets/docs/README.md +4 -2
- 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 +5 -16
- package/assets/docs/icons.doc.mjs +3 -21
- package/assets/docs/illustrations.doc.mjs +7 -15
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +130 -82
- package/assets/docs/layout.doc.mjs +133 -77
- 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 +8 -0
- 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 +6 -2
- package/assets/docs/styling.doc.mjs +19 -23
- package/assets/docs/theme.doc.dense.mjs +58 -18
- package/assets/docs/theme.doc.mjs +57 -47
- package/assets/docs/theme.doc.zh.mjs +9 -8
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +389 -8
- 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/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/codemods.doc.mjs +147 -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 +25 -451
- 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 +121 -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 +153 -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 +162 -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 +30 -22
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
- package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
- package/assets/templates/pages/blank/template.doc.mjs +3 -1
- package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
- package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
- package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
- package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
- package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
- package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
- package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
- package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
- package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
- package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
- package/assets/templates/pages/documentation/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
- package/assets/templates/pages/editor/template.doc.mjs +9 -1
- package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
- package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
- package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
- package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
- package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
- package/assets/templates/pages/ide/template.doc.mjs +9 -1
- package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
- package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
- package/assets/templates/pages/library/template.doc.mjs +15 -1
- package/assets/templates/pages/login/template.doc.mjs +10 -1
- package/assets/templates/pages/login-card/template.doc.mjs +11 -1
- package/assets/templates/pages/login-split/template.doc.mjs +10 -1
- package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
- package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
- package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
- package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
- package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/settings/template.doc.mjs +3 -1
- package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
- package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
- package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
- package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
- package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
- package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/table/template.doc.mjs +11 -1
- package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
- package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
- package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
- package/assets/templates/pages/table-page/template.doc.mjs +20 -1
- package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
- package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
- package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
- package/assets/templates/themes/butter/icons.tsx +2 -0
- package/assets/templates/themes/chocolate/icons.tsx +2 -0
- package/assets/templates/themes/gothic/icons.tsx +2 -0
- package/assets/templates/themes/matcha/icons.tsx +2 -0
- package/assets/templates/themes/neutral/icons.tsx +2 -0
- package/assets/templates/themes/stone/icons.tsx +2 -0
- package/assets/templates/themes/y2k/icons.tsx +2 -0
- package/authoring/config/config.doc.mjs +9 -1
- package/authoring/config/parse.d.mts +2 -0
- package/authoring/config/parse.mjs +19 -0
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +11 -0
- 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 +3 -2
- package/authoring/doctypes/_schema.mjs +6 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +4 -2
- package/authoring/doctypes/component/component.doc.mjs +6 -0
- package/authoring/doctypes/component/type.ts +8 -0
- package/authoring/doctypes/reference/reference.doc.mjs +7 -0
- package/authoring/doctypes/reference/type.ts +5 -0
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +1 -0
- package/authoring/doctypes/template/parse.test.mjs +21 -0
- package/authoring/doctypes/template/template.doc.mjs +7 -1
- package/authoring/doctypes/template/type.ts +12 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +10 -0
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +12 -10
- package/clients/cli/commands/component/index.mjs +152 -55
- package/clients/cli/commands/component-batch.test.mjs +341 -0
- package/clients/cli/commands/component-ownership.test.mjs +89 -0
- package/clients/cli/commands/component.doc.mjs +27 -9
- package/clients/cli/commands/discover.doc.mjs +53 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +60 -17
- package/clients/cli/commands/docs.test.mjs +113 -24
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
- package/clients/cli/commands/doctor-integration.test.mjs +53 -0
- package/clients/cli/commands/doctor.doc.mjs +3 -1
- package/clients/cli/commands/doctor.mjs +53 -9
- package/clients/cli/commands/gap-report.doc.mjs +10 -9
- package/clients/cli/commands/init.doc.mjs +9 -6
- package/clients/cli/commands/integration-add.doc.mjs +17 -7
- package/clients/cli/commands/integration-authoring.test.mjs +70 -9
- package/clients/cli/commands/integration-pack.doc.mjs +5 -9
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration-verify.doc.mjs +22 -0
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +76 -43
- package/clients/cli/commands/layout-expand.doc.mjs +3 -1
- package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
- package/clients/cli/commands/layout.mjs +16 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +10 -3
- package/clients/cli/commands/search.mjs +21 -2
- package/clients/cli/commands/search.test.mjs +21 -4
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +19 -5
- package/clients/cli/commands/template.show-media.test.mjs +62 -0
- package/clients/cli/commands/text-json-parity.test.mjs +7 -1
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
- package/clients/cli/commands/theme-palette.doc.mjs +1 -2
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +2 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +14 -0
- package/clients/cli/commands/upgrade.doc.mjs +62 -3
- package/clients/cli/commands/write-failure.test.mjs +175 -0
- package/clients/cli/index.mjs +28 -6
- package/clients/cli/lib/define-command.mjs +28 -4
- package/clients/cli/lib/define-command.test.mjs +54 -0
- package/clients/cli/lib/exit-codes.test.mjs +17 -1
- package/clients/cli/lib/json-shim.mjs +24 -14
- package/clients/cli/lib/manifest.mjs +48 -156
- package/clients/cli/lib/manifest.test.mjs +103 -9
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/agent-docs/agent-docs.test.mjs +3 -2
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
- package/foundation/discovery/cli-self-docs.mjs +16 -2
- package/foundation/discovery/cli-self-docs.test.mjs +20 -0
- package/foundation/discovery/docs-discovery.mjs +5 -1
- package/foundation/discovery/docs-discovery.test.mjs +21 -0
- package/foundation/discovery/docs-section-key.d.mts +1 -1
- package/foundation/discovery/docs-section-key.mjs +1 -1
- package/foundation/discovery/template-adapter.d.mts +14 -0
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
- package/foundation/discovery/template-adapter.mjs +21 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +5 -4
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/tree.d.mts +4 -0
- package/foundation/doc-compiler/tree.mjs +6 -1
- package/foundation/doc-compiler/tree.test.mjs +65 -14
- package/foundation/integrations/cli-requirement.d.mts +75 -11
- package/foundation/integrations/cli-requirement.mjs +120 -23
- package/foundation/integrations/cli-requirement.test.mjs +141 -9
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/integrations/integrations.d.mts +14 -1
- package/foundation/integrations/integrations.mjs +41 -1
- package/foundation/integrations/integrations.test.mjs +31 -0
- package/foundation/response/batch.type.d.mts +33 -0
- package/foundation/response/batch.type.mjs +34 -0
- package/foundation/response/error-codes.doc.mjs +6 -8
- package/foundation/response/error-codes.test.mjs +30 -5
- package/foundation/response/response-types.doc.d.mts +5 -4
- package/foundation/response/response-types.doc.mjs +49 -19
- package/foundation/response/response-types.doc.test.mjs +23 -0
- package/foundation/response/response.doc.mjs +11 -10
- package/package.json +9 -9
- package/assets/docs/tree/integrations.test.mjs +0 -62
- package/assets/docs/tree/writing-docs.doc.mjs +0 -286
|
@@ -243,7 +243,7 @@ describe('integration-contributed topics', () => {
|
|
|
243
243
|
const english = await docs('theme', undefined, {full: true});
|
|
244
244
|
const englishTitles = english.data.sections.map(section => section.title);
|
|
245
245
|
expect(englishTitles).toEqual(
|
|
246
|
-
expect.arrayContaining(['
|
|
246
|
+
expect.arrayContaining(['Available Themes', 'Theme Props']),
|
|
247
247
|
);
|
|
248
248
|
scaffold({
|
|
249
249
|
'theme-internal.doc.mjs': topic({
|
|
@@ -251,9 +251,9 @@ describe('integration-contributed topics', () => {
|
|
|
251
251
|
extends: 'theme',
|
|
252
252
|
sections: [
|
|
253
253
|
{
|
|
254
|
-
id: 'acme-
|
|
255
|
-
title: '
|
|
256
|
-
content: [{type: 'prose', text: 'Acme
|
|
254
|
+
id: 'acme-available-themes',
|
|
255
|
+
title: 'Available Themes',
|
|
256
|
+
content: [{type: 'prose', text: 'Acme themes.'}],
|
|
257
257
|
},
|
|
258
258
|
{
|
|
259
259
|
title: 'Theme Props',
|
|
@@ -271,7 +271,7 @@ describe('integration-contributed topics', () => {
|
|
|
271
271
|
expect(extended.data.sections).toHaveLength(base.data.sections.length);
|
|
272
272
|
expect(
|
|
273
273
|
extended.data.sections.filter(
|
|
274
|
-
section => section.id === 'acme-
|
|
274
|
+
section => section.id === 'acme-available-themes',
|
|
275
275
|
),
|
|
276
276
|
).toHaveLength(1);
|
|
277
277
|
expect(
|
|
@@ -305,6 +305,28 @@ describe('integration-contributed topics', () => {
|
|
|
305
305
|
});
|
|
306
306
|
}, SLOW);
|
|
307
307
|
|
|
308
|
+
it('ranks the topics that match every word of the query first', async () => {
|
|
309
|
+
// Dozens of CLI docs match `integration` alone, by name or in a code
|
|
310
|
+
// tick; the topics that hold both words, this one and the CLI's own
|
|
311
|
+
// troubleshooting guide, must still come first.
|
|
312
|
+
scaffold({
|
|
313
|
+
'troubleshooting.doc.mjs': topic({
|
|
314
|
+
name: 'troubleshooting',
|
|
315
|
+
title: 'Troubleshooting',
|
|
316
|
+
description: 'What to check when an integration does not load.',
|
|
317
|
+
}),
|
|
318
|
+
});
|
|
319
|
+
const {data} = await search('troubleshoot integration', {cwd: tmpDir, type: 'doc'});
|
|
320
|
+
const top = data.results.slice(0, 2).map(result => result.name);
|
|
321
|
+
// This topic, and the CLI's own troubleshooting guide wherever the tree
|
|
322
|
+
// places it.
|
|
323
|
+
expect(top).toContain('troubleshooting');
|
|
324
|
+
expect(
|
|
325
|
+
top.some(name => /^cli\/integrations\/(?:.+\/)?troubleshooting$/.test(name)),
|
|
326
|
+
top.join(', '),
|
|
327
|
+
).toBe(true);
|
|
328
|
+
}, SLOW);
|
|
329
|
+
|
|
308
330
|
it("falls back to the CLI's own topics when the project config is unreadable", async () => {
|
|
309
331
|
scaffold({'deploying.doc.mjs': topic()}, {config: 'export default {integrations: 42};\n'});
|
|
310
332
|
const catalog = await loadDocsCatalog(tmpDir);
|
package/api/doctor/doctor.d.mts
CHANGED
|
@@ -257,15 +257,20 @@ export type DoctorContext = {
|
|
|
257
257
|
* could not be built, when it could not.
|
|
258
258
|
*/
|
|
259
259
|
docsCatalogError?: string | null | undefined;
|
|
260
|
-
/**
|
|
261
|
-
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
262
|
-
*/
|
|
263
260
|
integrationIssues?: {
|
|
264
261
|
package: string;
|
|
265
262
|
code: string;
|
|
266
263
|
severity: "warning" | "error";
|
|
267
264
|
message: string;
|
|
268
265
|
}[] | null | undefined;
|
|
266
|
+
/**
|
|
267
|
+
* installed dependencies whose integration manifest could not be loaded
|
|
268
|
+
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
269
|
+
*/
|
|
270
|
+
autolinkFailures?: {
|
|
271
|
+
spec: string;
|
|
272
|
+
error: string;
|
|
273
|
+
}[] | null | undefined;
|
|
269
274
|
/**
|
|
270
275
|
* - Error thrown while resolving the config
|
|
271
276
|
* path (e.g. multiple config files present), surfaced by checkConfig as a FAIL.
|
|
@@ -13,14 +13,18 @@ export const doc = {
|
|
|
13
13
|
name: 'doctor',
|
|
14
14
|
namespace: 'cli/api',
|
|
15
15
|
displayName: 'doctor()',
|
|
16
|
-
summary:
|
|
16
|
+
summary:
|
|
17
|
+
"Check a project's Astryx setup and get a pass/warn/fail report per check. Use it as a CI gate or before debugging a broken install.",
|
|
17
18
|
description:
|
|
18
|
-
'Runs a series of
|
|
19
|
+
'Runs a series of diagnostics: Node version, ' +
|
|
19
20
|
'@astryxdesign/core install and version alignment with the CLI, installed ' +
|
|
20
21
|
'themes and wiring, astryx.config validity, integrations linked from ' +
|
|
21
|
-
'package.json without a config entry,
|
|
22
|
-
'
|
|
23
|
-
'
|
|
22
|
+
'package.json without a config entry, core peer dependencies, ' +
|
|
23
|
+
'integration provider identity and contribution issues, agent docs, the ' +
|
|
24
|
+
'detected package manager, and the health of the docs the CLI reads (authoring ' +
|
|
25
|
+
'and CLI docs, the docs tree, doc size), and returns a structured ' +
|
|
26
|
+
'report. It only reads (never installs, writes, or mutates), apart from ' +
|
|
27
|
+
"importing astryx.config, which runs that file's top-level code, so it is safe " +
|
|
24
28
|
'as a CI gate and for agents to invoke.',
|
|
25
29
|
importPath: '@astryxdesign/cli/api',
|
|
26
30
|
signature: 'doctor(options?: DoctorOptions): Promise<DoctorResponse>',
|
|
@@ -29,18 +33,23 @@ export const doc = {
|
|
|
29
33
|
{
|
|
30
34
|
name: 'options.cwd',
|
|
31
35
|
type: 'string',
|
|
32
|
-
description:
|
|
36
|
+
description:
|
|
37
|
+
'Directory to diagnose. A missing directory is not an error; it shows up in the checks (e.g. core-installed: fail).',
|
|
38
|
+
default: 'process.cwd()',
|
|
33
39
|
},
|
|
34
40
|
],
|
|
35
41
|
returns: [
|
|
36
42
|
{
|
|
37
43
|
type: 'doctor',
|
|
38
44
|
description:
|
|
39
|
-
'The diagnostic report: `data.checks`, each with a stable id, label, `status` (`pass` | `warn` | `fail` | `info`), a one-line message, and
|
|
45
|
+
'The diagnostic report: `data.checks`, each with a stable id, label, `status` (`pass` | `warn` | `fail` | `info`), a one-line message, and an optional `fix` (always present on `warn` and `fail`; some `info` checks carry one too); plus `data.summary` with counts per status.',
|
|
40
46
|
},
|
|
41
47
|
],
|
|
42
48
|
examples: [
|
|
43
|
-
{
|
|
49
|
+
{
|
|
50
|
+
label: 'Fail a CI step on any failed check',
|
|
51
|
+
code: 'const r = await doctor();\nif (r.data.summary.fail > 0) process.exitCode = 1;',
|
|
52
|
+
},
|
|
44
53
|
{
|
|
45
54
|
label: 'Diagnose a directory',
|
|
46
55
|
code: "await doctor({cwd: '/path/to/app'});",
|
package/api/doctor/doctor.mjs
CHANGED
|
@@ -26,7 +26,11 @@ import * as path from 'node:path';
|
|
|
26
26
|
import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
|
|
27
27
|
import {CLI_ROOT, findCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
|
|
28
28
|
import {explainPackageManager, getCliInvocation} from '../../foundation/env/package-manager.mjs';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
findConfigPath,
|
|
31
|
+
Project,
|
|
32
|
+
providerLedgerOf,
|
|
33
|
+
} from '../../foundation/config/project.mjs';
|
|
30
34
|
import {DocsCatalog} from '../../foundation/discovery/docs-discovery.mjs';
|
|
31
35
|
import {buildDocsIndexData} from '../../foundation/discovery/docs-section-key.mjs';
|
|
32
36
|
import {
|
|
@@ -76,6 +80,8 @@ import {semverCompare, isValidSemver, satisfiesRange} from '../../foundation/env
|
|
|
76
80
|
* @property {string|null} [docsCatalogError] - Why the project's docs catalog
|
|
77
81
|
* could not be built, when it could not.
|
|
78
82
|
* @property {Array<{package: string, code: string, severity: 'warning'|'error', message: string}>|null} [integrationIssues]
|
|
83
|
+
* @property {Array<{spec: string, error: string}>|null} [autolinkFailures]
|
|
84
|
+
* installed dependencies whose integration manifest could not be loaded
|
|
79
85
|
* Combined project-level integration issues, including cross-package template replacement warnings.
|
|
80
86
|
* @property {Error|null} [configError] - Error thrown while resolving the config
|
|
81
87
|
* path (e.g. multiple config files present), surfaced by checkConfig as a FAIL.
|
|
@@ -417,6 +423,9 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
417
423
|
const implicit = ctx.integrations.filter(
|
|
418
424
|
integration => integration.__autolinked,
|
|
419
425
|
);
|
|
426
|
+
// A dependency whose manifest cannot be loaded leaves no loaded record, so
|
|
427
|
+
// without this line doctor would say no dependency ships a manifest at all.
|
|
428
|
+
const unreadable = describeUnreadableManifests(ctx.autolinkFailures ?? []);
|
|
420
429
|
|
|
421
430
|
if (implicit.length === 0) {
|
|
422
431
|
return {
|
|
@@ -424,9 +433,12 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
424
433
|
label,
|
|
425
434
|
status: 'info',
|
|
426
435
|
message:
|
|
427
|
-
ctx.integrations.length > 0
|
|
436
|
+
(ctx.integrations.length > 0
|
|
428
437
|
? 'None — every loaded integration is named in astryx.config.'
|
|
429
|
-
:
|
|
438
|
+
: unreadable
|
|
439
|
+
? 'None loaded.'
|
|
440
|
+
: 'None — no installed dependency ships an astryx.integration.* manifest.') +
|
|
441
|
+
unreadable,
|
|
430
442
|
};
|
|
431
443
|
}
|
|
432
444
|
|
|
@@ -439,11 +451,25 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
439
451
|
integration.__spec && integration.__spec !== integration.name
|
|
440
452
|
? ` (declared as "${integration.__spec}")`
|
|
441
453
|
: '';
|
|
442
|
-
|
|
454
|
+
// A declared root counts only when it exists: the manifest's keys are a
|
|
455
|
+
// claim, and `integration-issues` reports the ones that are not true.
|
|
456
|
+
const declared = ['components', 'templates', 'themes', 'docs', 'codemods'].filter(
|
|
443
457
|
root => integration[/** @type {'components'} */ (root)],
|
|
444
458
|
);
|
|
459
|
+
const missing = declared.filter(root => {
|
|
460
|
+
const dir = integration[/** @type {'components'} */ (root)];
|
|
461
|
+
if (typeof dir !== 'string') return false;
|
|
462
|
+
const base =
|
|
463
|
+
typeof integration.__packageDir === 'string'
|
|
464
|
+
? integration.__packageDir
|
|
465
|
+
: (ctx.cwd ?? process.cwd());
|
|
466
|
+
return !fs.existsSync(path.isAbsolute(dir) ? dir : path.resolve(base, dir));
|
|
467
|
+
});
|
|
468
|
+
const roots = declared.filter(root => !missing.includes(root));
|
|
445
469
|
const contributes = roots.length > 0 ? roots.join(', ') : 'nothing';
|
|
446
|
-
|
|
470
|
+
const absent =
|
|
471
|
+
missing.length > 0 ? ` (declared ${missing.join(', ')} missing on disk)` : '';
|
|
472
|
+
return `${integration.name}${version}${alias} from ${integration.__dependencyField}, contributing ${contributes}${absent}`;
|
|
447
473
|
});
|
|
448
474
|
|
|
449
475
|
const plural = implicit.length === 1 ? '' : 's';
|
|
@@ -453,7 +479,8 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
453
479
|
status: 'info',
|
|
454
480
|
message:
|
|
455
481
|
`${implicit.length} integration${plural} loaded from installed ` +
|
|
456
|
-
`dependencies with no astryx.config entry: ${described.join('; ')}
|
|
482
|
+
`dependencies with no astryx.config entry: ${described.join('; ')}.` +
|
|
483
|
+
unreadable,
|
|
457
484
|
fix:
|
|
458
485
|
'Nothing to fix. Keep these dependencies installed. The CLI links them ' +
|
|
459
486
|
'from package.json, so an unused-dependency check that looks only for ' +
|
|
@@ -462,6 +489,30 @@ export function checkImplicitIntegrations(ctx) {
|
|
|
462
489
|
};
|
|
463
490
|
}
|
|
464
491
|
|
|
492
|
+
/**
|
|
493
|
+
* The sentence `implicit-integrations` adds for dependencies whose manifest
|
|
494
|
+
* could not be loaded, or '' when there are none. Still informational: the
|
|
495
|
+
* package is a dependency's own bug, which `doctor integration validate`
|
|
496
|
+
* diagnoses, but doctor must not report it as absent.
|
|
497
|
+
* @param {Array<{spec: string, error: string}>} failures
|
|
498
|
+
* @returns {string}
|
|
499
|
+
*/
|
|
500
|
+
function describeUnreadableManifests(failures) {
|
|
501
|
+
if (failures.length === 0) return '';
|
|
502
|
+
const one = failures.length === 1;
|
|
503
|
+
const listed = failures
|
|
504
|
+
.map(({spec, error}) => {
|
|
505
|
+
const reason = String(error).split('\n')[0].slice(0, 160);
|
|
506
|
+
return `${spec} (${reason})`;
|
|
507
|
+
})
|
|
508
|
+
.join('; ');
|
|
509
|
+
return (
|
|
510
|
+
` ${failures.length} installed ${one ? 'dependency ships' : 'dependencies ship'} ` +
|
|
511
|
+
`an astryx.integration.* manifest that could not be loaded, so ${one ? 'it contributes' : 'they contribute'} ` +
|
|
512
|
+
`nothing: ${listed}. Run \`astryx doctor integration validate <package>\` for details.`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
465
516
|
/**
|
|
466
517
|
* Check 7 — agent docs exist and contain the Astryx section markers.
|
|
467
518
|
* @param {DoctorContext} ctx
|
|
@@ -738,12 +789,26 @@ export function checkProviderIdentity(ctx) {
|
|
|
738
789
|
integration =>
|
|
739
790
|
integration.providerId != null && integration.__loadError == null,
|
|
740
791
|
).length;
|
|
792
|
+
// An integration that could not be read has no provider ID to check, so the
|
|
793
|
+
// count above is not a complete survey. Say so instead of counting silently.
|
|
794
|
+
const unread = ctx.integrations.filter(
|
|
795
|
+
integration => integration.__loadError != null,
|
|
796
|
+
).length;
|
|
797
|
+
const unreadNote =
|
|
798
|
+
unread === 0
|
|
799
|
+
? ''
|
|
800
|
+
: unread === 1
|
|
801
|
+
? ' 1 loaded integration could not be read, so its provider ID is unknown.'
|
|
802
|
+
: ` ${unread} loaded integrations could not be read, so their provider IDs are unknown.`;
|
|
741
803
|
if (count === 0) {
|
|
742
804
|
return {
|
|
743
805
|
id,
|
|
744
806
|
label,
|
|
745
807
|
status: 'info',
|
|
746
|
-
message:
|
|
808
|
+
message:
|
|
809
|
+
(unread === 0
|
|
810
|
+
? 'None — no loaded integration has a provider identity.'
|
|
811
|
+
: 'No readable integration has a provider identity.') + unreadNote,
|
|
747
812
|
};
|
|
748
813
|
}
|
|
749
814
|
return {
|
|
@@ -751,9 +816,10 @@ export function checkProviderIdentity(ctx) {
|
|
|
751
816
|
label,
|
|
752
817
|
status: 'pass',
|
|
753
818
|
message:
|
|
754
|
-
count === 1
|
|
819
|
+
(count === 1
|
|
755
820
|
? '1 loaded integration has its own provider ID.'
|
|
756
|
-
: `${count} loaded integrations each have their own provider ID
|
|
821
|
+
: `${count} loaded integrations each have their own provider ID.`) +
|
|
822
|
+
unreadNote,
|
|
757
823
|
};
|
|
758
824
|
}
|
|
759
825
|
|
|
@@ -1198,11 +1264,25 @@ export async function runChecks(options = {}) {
|
|
|
1198
1264
|
let docsCatalogError = null;
|
|
1199
1265
|
/** @type {Array<{package: string, code: string, severity: 'warning'|'error', message: string}>|null} */
|
|
1200
1266
|
let integrationIssues = null;
|
|
1267
|
+
/** @type {Array<{spec: string, error: string}>|null} */
|
|
1268
|
+
let autolinkFailures = null;
|
|
1201
1269
|
try {
|
|
1202
1270
|
const project = await Project.load(cwd);
|
|
1203
1271
|
configTheme =
|
|
1204
1272
|
/** @type {{theme?: string}} */ (project.config ?? {}).theme ?? null;
|
|
1205
1273
|
integrations = project.loadedIntegrations;
|
|
1274
|
+
// An installed dependency whose manifest cannot be loaded is kept out of
|
|
1275
|
+
// loadedIntegrations on purpose. The provider ledger still records it.
|
|
1276
|
+
autolinkFailures = [...providerLedgerOf(project).values()]
|
|
1277
|
+
.filter(
|
|
1278
|
+
entry =>
|
|
1279
|
+
entry.outcome === 'load-failed' &&
|
|
1280
|
+
entry.candidate.source === 'autolinked',
|
|
1281
|
+
)
|
|
1282
|
+
.map(entry => ({
|
|
1283
|
+
spec: entry.candidate.spec ?? entry.label,
|
|
1284
|
+
error: entry.error ?? 'its manifest could not be loaded',
|
|
1285
|
+
}));
|
|
1206
1286
|
try {
|
|
1207
1287
|
docsCatalog = await project.docs();
|
|
1208
1288
|
docsCatalogIssues = (await project.issues()).filter(
|
|
@@ -1229,6 +1309,7 @@ export async function runChecks(options = {}) {
|
|
|
1229
1309
|
docsCatalogIssues,
|
|
1230
1310
|
docsCatalogError,
|
|
1231
1311
|
integrationIssues,
|
|
1312
|
+
autolinkFailures,
|
|
1232
1313
|
configError,
|
|
1233
1314
|
};
|
|
1234
1315
|
|
|
@@ -354,16 +354,31 @@ describe('checkImplicitIntegrations', () => {
|
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
it('names the package, the field, and what it contributes', () => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
357
|
+
// Roots count only when they exist, so this one gives them real folders.
|
|
358
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-implicit-'));
|
|
359
|
+
try {
|
|
360
|
+
const dir = name => {
|
|
361
|
+
fs.mkdirSync(path.join(root, name));
|
|
362
|
+
return path.join(root, name);
|
|
363
|
+
};
|
|
364
|
+
const c = checkImplicitIntegrations({
|
|
365
|
+
integrations: [
|
|
366
|
+
autolinked({
|
|
367
|
+
components: dir('components'),
|
|
368
|
+
templates: dir('templates'),
|
|
369
|
+
themes: dir('themes'),
|
|
370
|
+
}),
|
|
371
|
+
],
|
|
372
|
+
});
|
|
373
|
+
expect(c.message).toContain('@acme/widgets@1.0.0');
|
|
374
|
+
expect(c.message).toContain('from dependencies');
|
|
375
|
+
expect(c.message).toContain(
|
|
376
|
+
'contributing components, templates, themes',
|
|
377
|
+
);
|
|
378
|
+
expect(c.message).not.toContain('missing on disk');
|
|
379
|
+
} finally {
|
|
380
|
+
fs.rmSync(root, {recursive: true, force: true});
|
|
381
|
+
}
|
|
367
382
|
});
|
|
368
383
|
|
|
369
384
|
it('names the declared key too when an npm alias makes them differ', () => {
|
|
@@ -906,3 +921,100 @@ describe('checkDocsProgressiveDisclosure languages', () => {
|
|
|
906
921
|
expect(c.message).not.toMatch(/deploying overview:/);
|
|
907
922
|
});
|
|
908
923
|
});
|
|
924
|
+
|
|
925
|
+
describe('doctor says what it could not check', () => {
|
|
926
|
+
const CORE = {
|
|
927
|
+
'node_modules/@astryxdesign/core/package.json': JSON.stringify({
|
|
928
|
+
name: '@astryxdesign/core',
|
|
929
|
+
version: '0.6.3',
|
|
930
|
+
}),
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
/** Installed dependency whose manifest declares roots that do not exist. */
|
|
934
|
+
const dangling = {
|
|
935
|
+
'node_modules/@acme/dangling/package.json': JSON.stringify({
|
|
936
|
+
name: '@acme/dangling',
|
|
937
|
+
version: '2.0.0',
|
|
938
|
+
}),
|
|
939
|
+
'node_modules/@acme/dangling/astryx.integration.mjs':
|
|
940
|
+
"export default {providerId: 'acme-dangling', components: './components', templates: './templates', docs: './docs'};\n",
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
/** Installed dependency whose manifest cannot be parsed at all. */
|
|
944
|
+
const broken = {
|
|
945
|
+
'node_modules/@acme/broken/package.json': JSON.stringify({
|
|
946
|
+
name: '@acme/broken',
|
|
947
|
+
version: '1.0.0',
|
|
948
|
+
}),
|
|
949
|
+
'node_modules/@acme/broken/astryx.integration.mjs':
|
|
950
|
+
'export default { this is not valid javascript ((\n',
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
/** @param {Record<string, string>} extra @param {string[]} deps */
|
|
954
|
+
const project = (extra, deps) =>
|
|
955
|
+
mkProject({
|
|
956
|
+
'package.json': JSON.stringify({
|
|
957
|
+
name: 'consumer',
|
|
958
|
+
version: '1.0.0',
|
|
959
|
+
dependencies: Object.fromEntries(
|
|
960
|
+
['@astryxdesign/core', ...deps].map(d => [d, '1.0.0']),
|
|
961
|
+
),
|
|
962
|
+
}),
|
|
963
|
+
...CORE,
|
|
964
|
+
...extra,
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
// An unparseable manifest is kept out of the loaded set on purpose, and
|
|
968
|
+
// doctor used to report that no installed dependency ships a manifest.
|
|
969
|
+
it('names an installed dependency whose manifest cannot be loaded', async () => {
|
|
970
|
+
const report = (await doctor({cwd: project(broken, ['@acme/broken'])})).data;
|
|
971
|
+
const check = report.checks.find(c => c.id === 'implicit-integrations');
|
|
972
|
+
|
|
973
|
+
expect(check.status).toBe('info');
|
|
974
|
+
expect(check.message).toContain('@acme/broken');
|
|
975
|
+
expect(check.message).toContain('could not be loaded');
|
|
976
|
+
expect(check.message).not.toContain('no installed dependency ships');
|
|
977
|
+
expect(report.summary.fail).toBe(0);
|
|
978
|
+
}, SLOW);
|
|
979
|
+
|
|
980
|
+
it('does not claim contributions from roots that are missing on disk', async () => {
|
|
981
|
+
const report = (await doctor({cwd: project(dangling, ['@acme/dangling'])}))
|
|
982
|
+
.data;
|
|
983
|
+
const implicit = report.checks.find(c => c.id === 'implicit-integrations');
|
|
984
|
+
const issues = report.checks.find(c => c.id === 'integration-issues');
|
|
985
|
+
|
|
986
|
+
expect(implicit.message).toContain('contributing nothing');
|
|
987
|
+
expect(implicit.message).toContain('missing on disk');
|
|
988
|
+
expect(implicit.message).not.toContain('contributing components');
|
|
989
|
+
expect(issues.status).toBe('warn');
|
|
990
|
+
expect(issues.message).toContain('@acme/dangling');
|
|
991
|
+
}, SLOW);
|
|
992
|
+
|
|
993
|
+
it('still says no dependency ships a manifest when none does', async () => {
|
|
994
|
+
const report = (await doctor({cwd: project({}, [])})).data;
|
|
995
|
+
const check = report.checks.find(c => c.id === 'implicit-integrations');
|
|
996
|
+
|
|
997
|
+
expect(check.message).toBe(
|
|
998
|
+
'None — no installed dependency ships an astryx.integration.* manifest.',
|
|
999
|
+
);
|
|
1000
|
+
}, SLOW);
|
|
1001
|
+
|
|
1002
|
+
it('says how many integrations could not be read, rather than counting silently', () => {
|
|
1003
|
+
/** @type {any} */
|
|
1004
|
+
const ctx = {
|
|
1005
|
+
cwd: '/x',
|
|
1006
|
+
nodeVersion: process.versions.node,
|
|
1007
|
+
coreDir: null,
|
|
1008
|
+
configPath: null,
|
|
1009
|
+
configTheme: null,
|
|
1010
|
+
integrations: [
|
|
1011
|
+
{name: '@acme/ok', __spec: '@acme/ok', providerId: 'ok'},
|
|
1012
|
+
{name: '@acme/bad', __spec: '@acme/bad', __loadError: 'boom'},
|
|
1013
|
+
],
|
|
1014
|
+
};
|
|
1015
|
+
const check = checkProviderIdentity(ctx);
|
|
1016
|
+
|
|
1017
|
+
expect(check.message).toContain('1 loaded integration has its own provider ID.');
|
|
1018
|
+
expect(check.message).toContain('could not be read');
|
|
1019
|
+
});
|
|
1020
|
+
});
|
|
@@ -23,7 +23,7 @@ export type DoctorCheck = {
|
|
|
23
23
|
*/
|
|
24
24
|
message: string;
|
|
25
25
|
/**
|
|
26
|
-
* - Actionable remediation
|
|
26
|
+
* - Actionable remediation: always present on 'warn' and 'fail'; some 'info' checks carry one too.
|
|
27
27
|
*/
|
|
28
28
|
fix?: string | undefined;
|
|
29
29
|
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* @property {string} label - Human-readable check name.
|
|
20
20
|
* @property {DoctorStatus} status
|
|
21
21
|
* @property {string} message - One-line result summary.
|
|
22
|
-
* @property {string} [fix] - Actionable remediation
|
|
22
|
+
* @property {string} [fix] - Actionable remediation: always present on 'warn' and 'fail'; some 'info' checks carry one too.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/**
|
package/api/error.d.mts
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* The error for a write that failed on the filesystem: no permission, a
|
|
6
|
+
* read-only mount, a full disk, a path component that is not a directory.
|
|
7
|
+
*
|
|
8
|
+
* A raw Node errno error reached the envelope as
|
|
9
|
+
* `{"error": "EACCES: permission denied, open '/home/you/p/readonly/x.tsx'",
|
|
10
|
+
* "code": "ERR_UNKNOWN"}` — the wrong code (ERR_WRITE_FAILED is in the frozen
|
|
11
|
+
* registry for exactly this) and an absolute host path in the message. This
|
|
12
|
+
* keeps the errno, which is the part that tells you what to fix, and reports
|
|
13
|
+
* the target the way every other Astryx message does: relative to the project.
|
|
14
|
+
*
|
|
15
|
+
* A caller that wrote files before the failure must undo them first and pass
|
|
16
|
+
* the ones it could not restore as `unrestored`, so the message never says
|
|
17
|
+
* nothing was written when something was.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} target absolute path the write was aimed at
|
|
20
|
+
* @param {string} cwd project root, for the relative form
|
|
21
|
+
* @param {unknown} cause the error the filesystem call threw
|
|
22
|
+
* @param {string[]} [unrestored] absolute paths an undo could not put back
|
|
23
|
+
* @returns {AstryxError}
|
|
24
|
+
*/
|
|
25
|
+
export function writeFailed(target: string, cwd: string, cause: unknown, unrestored?: string[]): AstryxError;
|
|
4
26
|
export class AstryxError extends Error {
|
|
5
27
|
/**
|
|
6
28
|
* @param {string} message
|
package/api/error.mjs
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import {ERROR_CODES} from '../foundation/response/error-codes.mjs';
|
|
15
|
+
import * as path from 'node:path';
|
|
15
16
|
|
|
16
17
|
export class AstryxError extends Error {
|
|
17
18
|
/** @type {import('../foundation/response/base').Suggestion[] | undefined} */
|
|
@@ -36,3 +37,44 @@ export class AstryxError extends Error {
|
|
|
36
37
|
if (Array.isArray(suggestions) && suggestions.length) this.suggestions = suggestions;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The error for a write that failed on the filesystem: no permission, a
|
|
43
|
+
* read-only mount, a full disk, a path component that is not a directory.
|
|
44
|
+
*
|
|
45
|
+
* A raw Node errno error reached the envelope as
|
|
46
|
+
* `{"error": "EACCES: permission denied, open '/home/you/p/readonly/x.tsx'",
|
|
47
|
+
* "code": "ERR_UNKNOWN"}` — the wrong code (ERR_WRITE_FAILED is in the frozen
|
|
48
|
+
* registry for exactly this) and an absolute host path in the message. This
|
|
49
|
+
* keeps the errno, which is the part that tells you what to fix, and reports
|
|
50
|
+
* the target the way every other Astryx message does: relative to the project.
|
|
51
|
+
*
|
|
52
|
+
* A caller that wrote files before the failure must undo them first and pass
|
|
53
|
+
* the ones it could not restore as `unrestored`, so the message never says
|
|
54
|
+
* nothing was written when something was.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} target absolute path the write was aimed at
|
|
57
|
+
* @param {string} cwd project root, for the relative form
|
|
58
|
+
* @param {unknown} cause the error the filesystem call threw
|
|
59
|
+
* @param {string[]} [unrestored] absolute paths an undo could not put back
|
|
60
|
+
* @returns {AstryxError}
|
|
61
|
+
*/
|
|
62
|
+
export function writeFailed(target, cwd, cause, unrestored = []) {
|
|
63
|
+
const rel = path.relative(cwd, target) || target;
|
|
64
|
+
const errno =
|
|
65
|
+
typeof (/** @type {any} */ (cause)?.code) === 'string'
|
|
66
|
+
? /** @type {any} */ (cause).code
|
|
67
|
+
: null;
|
|
68
|
+
const why = errno === 'EACCES' || errno === 'EPERM' ? ' (no permission)' : '';
|
|
69
|
+
const outcome =
|
|
70
|
+
unrestored.length === 0
|
|
71
|
+
? 'Nothing was written.'
|
|
72
|
+
: `Earlier writes could not all be undone: ${unrestored
|
|
73
|
+
.map(file => path.relative(cwd, file) || file)
|
|
74
|
+
.join(', ')}.`;
|
|
75
|
+
return new AstryxError(
|
|
76
|
+
`Could not write ${rel}${errno ? `: ${errno}` : ''}${why}. ${outcome}`,
|
|
77
|
+
undefined,
|
|
78
|
+
ERROR_CODES.ERR_WRITE_FAILED,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -12,12 +12,16 @@ export const doc = {
|
|
|
12
12
|
name: 'gapReport',
|
|
13
13
|
namespace: 'cli/api',
|
|
14
14
|
displayName: 'gapReport()',
|
|
15
|
-
summary:
|
|
15
|
+
summary:
|
|
16
|
+
'Report a missing or hard-to-use design-system capability to the package that owns it.',
|
|
16
17
|
description:
|
|
17
|
-
'
|
|
18
|
+
'Sends a gap report to every configured handler: the project config handler first, then each integration handler in config order. ' +
|
|
19
|
+
'Each handler has 30 s to finish, and its output goes to stderr. Public handlers run only with confirmPublic; internal handlers always run. ' +
|
|
20
|
+
"With no handler, it files a GitHub issue for the owning package only with confirmPublic (without it nothing is sent), or returns the package's issues URL when that is not on GitHub. " +
|
|
21
|
+
'The report records whether an agent or a person ran it, and the response lists each handler outcome in order.',
|
|
18
22
|
importPath: '@astryxdesign/cli/api',
|
|
19
23
|
signature:
|
|
20
|
-
'gapReport(component
|
|
24
|
+
'gapReport(component: string | undefined, options?: GapReportOptions): Promise<GapReportCategoriesResponse | GapReportReceiptResponse>',
|
|
21
25
|
keywords: [
|
|
22
26
|
'gap',
|
|
23
27
|
'report',
|
|
@@ -56,13 +60,13 @@ export const doc = {
|
|
|
56
60
|
name: 'options.package',
|
|
57
61
|
type: 'string',
|
|
58
62
|
description:
|
|
59
|
-
'
|
|
63
|
+
'Package that owns the gap: @astryxdesign/core, or a loaded integration by package name or config entry. Overrides automatic owner routing; required when more than one package provides the component.',
|
|
60
64
|
},
|
|
61
65
|
{
|
|
62
66
|
name: 'options.confirmPublic',
|
|
63
67
|
type: 'boolean',
|
|
64
68
|
description:
|
|
65
|
-
'
|
|
69
|
+
'Allow public delivery: public handlers run, and with no handler a GitHub issue is filed through the gh CLI.',
|
|
66
70
|
default: 'false',
|
|
67
71
|
},
|
|
68
72
|
{
|
|
@@ -77,6 +81,7 @@ export const doc = {
|
|
|
77
81
|
type: 'string',
|
|
78
82
|
description:
|
|
79
83
|
'Directory used to load project config and component ownership.',
|
|
84
|
+
default: 'process.cwd()',
|
|
80
85
|
},
|
|
81
86
|
],
|
|
82
87
|
returns: [
|
|
@@ -87,17 +92,21 @@ export const doc = {
|
|
|
87
92
|
{
|
|
88
93
|
type: 'gap-report.file',
|
|
89
94
|
description:
|
|
90
|
-
'
|
|
95
|
+
'Receipt: status (filed, partial, failed, routed_only, consent_required, skipped), package, issuesUrl, ordered deliveries (handlerType, handler, audience, status, url, message), filedCount, and routedOnlyCount.',
|
|
91
96
|
},
|
|
92
97
|
],
|
|
93
98
|
throws: [
|
|
99
|
+
{
|
|
100
|
+
code: 'ERR_MISSING_ARGUMENT',
|
|
101
|
+
when: 'component, category, or reason is missing, blank, or not a string (unless listCategories is true)',
|
|
102
|
+
},
|
|
94
103
|
{
|
|
95
104
|
code: 'ERR_UNKNOWN_CATEGORY',
|
|
96
105
|
when: 'category is not one of the fixed gap-report values',
|
|
97
106
|
},
|
|
98
107
|
{
|
|
99
108
|
code: 'ERR_INVALID_ARGUMENT',
|
|
100
|
-
when: 'a
|
|
109
|
+
when: 'component is over 120 characters, category is over 80, reason is over 2000, or detail is not a string or is over 8000 characters',
|
|
101
110
|
},
|
|
102
111
|
{
|
|
103
112
|
code: 'ERR_AMBIGUOUS_COMPONENT',
|
|
@@ -109,7 +118,7 @@ export const doc = {
|
|
|
109
118
|
},
|
|
110
119
|
{
|
|
111
120
|
code: 'ERR_NOT_FOUND',
|
|
112
|
-
when: 'no handler and no issues URL
|
|
121
|
+
when: 'no report handler is configured and the owning package has no issues URL',
|
|
113
122
|
},
|
|
114
123
|
],
|
|
115
124
|
examples: [
|
|
@@ -122,8 +131,8 @@ export const doc = {
|
|
|
122
131
|
code: "const receipt = await gapReport('Button', {category: 'missing_variant', reason: 'Need a compact size'});",
|
|
123
132
|
},
|
|
124
133
|
{
|
|
125
|
-
label: '
|
|
126
|
-
code: "await gapReport('Button', {category: 'docs_gap', reason: 'Missing keyboard example',
|
|
134
|
+
label: 'Name the owning package',
|
|
135
|
+
code: "const receipt = await gapReport('Button', {category: 'docs_gap', reason: 'Missing keyboard example', package: '@astryxdesign/core'});",
|
|
127
136
|
},
|
|
128
137
|
],
|
|
129
138
|
command: 'gap-report',
|