@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
package/api/search/search.mjs
CHANGED
|
@@ -17,21 +17,43 @@
|
|
|
17
17
|
* component fuzzy resolver in lib/string-utils.mjs:
|
|
18
18
|
*
|
|
19
19
|
* 100 exact name match
|
|
20
|
+
* 95 name is the term's plural or stem form ("buttons" -> Button)
|
|
20
21
|
* 90 exact keyword match
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* 88 keyword is the term's plural or stem form
|
|
23
|
+
* 80 name Levenshtein distance 1 (one-word lookups, words of 5+ letters)
|
|
24
|
+
* 70 keyword word prefix / distance 1 (distance: one-word lookups, 5+ letters)
|
|
25
|
+
* 60 name word prefix (>=4 chars, >=50% coverage)
|
|
24
26
|
* 60 exact weak-keyword match
|
|
25
27
|
* 50 description / prose mentions the term
|
|
26
28
|
* 45 usage guidance mentions the term
|
|
27
|
-
* 40 name Levenshtein distance 2
|
|
28
|
-
* 40 weak-keyword
|
|
29
|
-
* 30 keyword Levenshtein distance 2
|
|
30
|
-
* 20 name Levenshtein distance 3
|
|
29
|
+
* 40 name Levenshtein distance 2 (one-word lookups, 8+ letters)
|
|
30
|
+
* 40 weak-keyword word prefix
|
|
31
|
+
* 30 keyword Levenshtein distance 2 (one-word lookups, 8+ letters)
|
|
32
|
+
* 20 name Levenshtein distance 3 (one-word lookups, 11+ letters)
|
|
31
33
|
*
|
|
32
34
|
* Name + keyword signals always outweigh description/prose, so an exact match
|
|
33
35
|
* sorts above an incidental mention.
|
|
34
36
|
*
|
|
37
|
+
* A term matches inside a name or keyword only at the start of one of its
|
|
38
|
+
* words: "dash" finds "dashboard" and "input" finds "TextInput", but "file"
|
|
39
|
+
* does not find "profile". Edit distance is typo tolerance, so it applies only
|
|
40
|
+
* to a one-word lookup, where a typo is the likely explanation, and only to
|
|
41
|
+
* words long enough that one edit rarely makes another real word. In a
|
|
42
|
+
* sentence, a near miss is usually a different word: "site" is not "side",
|
|
43
|
+
* "cable" is not "table".
|
|
44
|
+
*
|
|
45
|
+
* A multi-word query has a reserved top tier (see {@link scoreQuery}): the
|
|
46
|
+
* whole query as a candidate's name or keyword (190-200), then the whole query
|
|
47
|
+
* as a phrase inside a doc's title or one of its headings (170), then a whole
|
|
48
|
+
* title of two words or more inside the query (160-169), then a candidate that
|
|
49
|
+
* matches every word of the query, at least one of them by name or keyword
|
|
50
|
+
* (151-159). Below those sits everything else: a partial match, or every word
|
|
51
|
+
* matched only in prose or through the components a page renders. A section titled "Light/Dark Mode" answers `dark
|
|
52
|
+
* mode` better than any doc that merely names `mode` in code, however exactly;
|
|
53
|
+
* "Dark mode" answers `how do I add dark mode`; and a guide whose title and
|
|
54
|
+
* description hold both words of `troubleshoot integration` answers it better
|
|
55
|
+
* than a doc named `integration`.
|
|
56
|
+
*
|
|
35
57
|
* Description and guidance are separate tiers on purpose. A component's own
|
|
36
58
|
* one-line description saying "notification" is a claim about what it IS; the
|
|
37
59
|
* same word inside another component's best-practice advice is a passing
|
|
@@ -50,11 +72,11 @@
|
|
|
50
72
|
* queries they have nothing to do with.
|
|
51
73
|
*/
|
|
52
74
|
|
|
53
|
-
import {
|
|
75
|
+
import {readDocView} from '../../foundation/doc-compiler/read.mjs';
|
|
54
76
|
import {findCoreDir} from '../../foundation/fs/paths.mjs';
|
|
55
77
|
import {
|
|
56
78
|
discoverComponents,
|
|
57
|
-
|
|
79
|
+
discoverValidIntegrationComponents,
|
|
58
80
|
findComponentReadme,
|
|
59
81
|
resolveImportPath,
|
|
60
82
|
resolveIntegrationImportPath,
|
|
@@ -66,7 +88,20 @@ import {
|
|
|
66
88
|
import {loadIntegrationsSafely} from '../component/_adapter.mjs';
|
|
67
89
|
import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
68
90
|
import {discoverTemplates, extractComponents} from '../template/template.mjs';
|
|
69
|
-
import {
|
|
91
|
+
import {templateLookupIds} from '../../foundation/discovery/template-adapter.mjs';
|
|
92
|
+
import {
|
|
93
|
+
guideEntry,
|
|
94
|
+
loadDocsCatalog,
|
|
95
|
+
lowerTopic,
|
|
96
|
+
projectTree,
|
|
97
|
+
holdsOwnName,
|
|
98
|
+
} from '../docs/_adapter.mjs';
|
|
99
|
+
import {unlinkText} from '../../foundation/doc-compiler/links.mjs';
|
|
100
|
+
import {nodeView} from '../docs/node/node.mjs';
|
|
101
|
+
import {
|
|
102
|
+
sectionKey,
|
|
103
|
+
sectionSummary,
|
|
104
|
+
} from '../../foundation/discovery/docs-section-key.mjs';
|
|
70
105
|
import {AstryxError} from '../error.mjs';
|
|
71
106
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
72
107
|
import {setResultCoverage} from './coverage.mjs';
|
|
@@ -82,19 +117,33 @@ import {setResultCoverage} from './coverage.mjs';
|
|
|
82
117
|
* @property {string} [description]
|
|
83
118
|
* @property {string[]} [prose]
|
|
84
119
|
* @property {string[]} [guidance]
|
|
120
|
+
* @property {string[]} [titles] - A doc's title and the headings inside it:
|
|
121
|
+
* the lines a reader scans to pick it. The whole query standing in one of
|
|
122
|
+
* them, or one of them standing whole in the query, is a top-tier match.
|
|
85
123
|
* @property {string} [_import]
|
|
86
124
|
* @property {string} [_title]
|
|
125
|
+
* @property {string} [_topic] - A doc result's topic or docs-tree route.
|
|
126
|
+
* @property {string} [_section] - A doc result's section key, when it is one section.
|
|
127
|
+
* @property {string} [_command] - The command that reads exactly this doc part.
|
|
128
|
+
* @property {string} [_parent] - The command that opens the level above a doc
|
|
129
|
+
* part: its topic's section list, or the docs-tree namespace it sits in.
|
|
130
|
+
* @property {string} [_package] - The npm package that authored a docs-tree
|
|
131
|
+
* doc part. Flat topics carry none: an extension's sections can come from
|
|
132
|
+
* another package.
|
|
87
133
|
* @property {string} [_displayName]
|
|
88
134
|
* @property {'page'|'block'} [_kind]
|
|
135
|
+
* @property {string} [_resultName]
|
|
136
|
+
* @property {string} [_commandName]
|
|
89
137
|
*/
|
|
90
138
|
|
|
91
139
|
/**
|
|
92
140
|
* Synonym / intent map: product-language terms an agent is likely to type,
|
|
93
141
|
* expanded to the catalog's vocabulary so oblique queries still rank. Keys and
|
|
94
142
|
* values are matched bidirectionally (typing any value also pulls in the key
|
|
95
|
-
* and its siblings). Lowercase, single words or short phrases.
|
|
143
|
+
* and its siblings). Lowercase, single words or short phrases. Exported for
|
|
144
|
+
* `build`, whose page ranker expands a query with the same vocabulary.
|
|
96
145
|
*/
|
|
97
|
-
const SYNONYMS = {
|
|
146
|
+
export const SYNONYMS = {
|
|
98
147
|
dashboard: [
|
|
99
148
|
'overview',
|
|
100
149
|
'analytics',
|
|
@@ -166,14 +215,90 @@ export function stem(w) {
|
|
|
166
215
|
return s;
|
|
167
216
|
}
|
|
168
217
|
|
|
218
|
+
/**
|
|
219
|
+
* The forms of a word that count as the same word: itself, its stem, and its
|
|
220
|
+
* singular when it ends in a plural suffix — so "tables" is "table",
|
|
221
|
+
* "statuses" is "status", and "filtering" is "filter".
|
|
222
|
+
* @param {string} w - Lowercase word.
|
|
223
|
+
* @returns {Set<string>}
|
|
224
|
+
*/
|
|
225
|
+
function wordForms(w) {
|
|
226
|
+
const forms = new Set([w, stem(w)]);
|
|
227
|
+
if (w.length > 3 && w.endsWith('s')) forms.add(w.slice(0, -1));
|
|
228
|
+
if (w.length > 4 && w.endsWith('es')) forms.add(w.slice(0, -2));
|
|
229
|
+
if (w.length > 4 && w.endsWith('ies')) forms.add(w.slice(0, -3) + 'y');
|
|
230
|
+
return forms;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Whether two lowercase words are the same word, up to plural and stem form.
|
|
235
|
+
* @param {string} a
|
|
236
|
+
* @param {string} b
|
|
237
|
+
* @returns {boolean}
|
|
238
|
+
*/
|
|
239
|
+
export function sameWord(a, b) {
|
|
240
|
+
if (a === b) return true;
|
|
241
|
+
const forms = wordForms(a);
|
|
242
|
+
for (const f of wordForms(b)) if (forms.has(f)) return true;
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The lowercase words of a name or keyword: split at non-alphanumerics and at
|
|
248
|
+
* camelCase boundaries, so "TextInput" is ["text", "input"] and
|
|
249
|
+
* "Dashboard - Analytics" is ["dashboard", "analytics"].
|
|
250
|
+
* @param {string} text
|
|
251
|
+
* @returns {string[]}
|
|
252
|
+
*/
|
|
253
|
+
function wordsOf(text) {
|
|
254
|
+
return String(text)
|
|
255
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
256
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
|
|
257
|
+
.toLowerCase()
|
|
258
|
+
.split(/[^a-z0-9]+/)
|
|
259
|
+
.filter(Boolean);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Whether a term is found inside a name or keyword: it is one of its words, or
|
|
264
|
+
* the start of one (a truncation), and covers at least half of the whole
|
|
265
|
+
* string. Four letters minimum, so a short term never matches by accident.
|
|
266
|
+
* @param {string} term - Lowercase term.
|
|
267
|
+
* @param {string} text - The name or keyword as authored.
|
|
268
|
+
* @returns {boolean}
|
|
269
|
+
*/
|
|
270
|
+
function startsAWordOf(term, text) {
|
|
271
|
+
if (term.length < 4) return false;
|
|
272
|
+
if (term.length / String(text).length < 0.5) return false;
|
|
273
|
+
return wordsOf(text).some(w => w.startsWith(term) || sameWord(term, w));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The fewest letters both words need before an edit distance counts as a
|
|
278
|
+
* typo, by distance. Below them, one edit usually makes a different word.
|
|
279
|
+
*/
|
|
280
|
+
const TYPO_MIN_LENGTH = {1: 5, 2: 8, 3: 11};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {string} a
|
|
284
|
+
* @param {string} b
|
|
285
|
+
* @param {number} dist
|
|
286
|
+
*/
|
|
287
|
+
const isTypo = (a, b, dist) =>
|
|
288
|
+
dist > 0 &&
|
|
289
|
+
dist <= 3 &&
|
|
290
|
+
Math.min(a.length, b.length) >=
|
|
291
|
+
TYPO_MIN_LENGTH[/** @type {1 | 2 | 3} */ (dist)];
|
|
292
|
+
|
|
169
293
|
/** Valid domain filters for `--type`. */
|
|
170
294
|
export const SEARCH_DOMAINS = ['component', 'hook', 'doc', 'template'];
|
|
171
295
|
|
|
172
296
|
/**
|
|
173
297
|
* Filler words stripped from multi-word queries so natural-language phrasing
|
|
174
298
|
* ("a page where you can see business stats") ranks on its content words.
|
|
299
|
+
* Exported for `build`, whose page ranker strips the same words.
|
|
175
300
|
*/
|
|
176
|
-
const STOPWORDS = new Set([
|
|
301
|
+
export const STOPWORDS = new Set([
|
|
177
302
|
'a',
|
|
178
303
|
'an',
|
|
179
304
|
'the',
|
|
@@ -285,14 +410,15 @@ const MIN_TOKEN_SCORE = 50;
|
|
|
285
410
|
* (synonym hits are discounted so a direct hit always wins).
|
|
286
411
|
* @param {string} tok
|
|
287
412
|
* @param {Candidate} candidate
|
|
413
|
+
* @param {{fuzzy?: boolean}} [opts]
|
|
288
414
|
* @returns {{score: number, reason: string} | null}
|
|
289
415
|
*/
|
|
290
|
-
function bestForToken(tok, candidate) {
|
|
291
|
-
let best = scoreCandidate(tok, candidate);
|
|
416
|
+
function bestForToken(tok, candidate, opts = {}) {
|
|
417
|
+
let best = scoreCandidate(tok, candidate, opts);
|
|
292
418
|
const syns = SYNONYM_INDEX.get(tok);
|
|
293
419
|
if (syns) {
|
|
294
420
|
for (const s of syns) {
|
|
295
|
-
const h = scoreCandidate(s, candidate);
|
|
421
|
+
const h = scoreCandidate(s, candidate, opts);
|
|
296
422
|
if (h) {
|
|
297
423
|
const score = Math.round(h.score * 0.85);
|
|
298
424
|
if (!best || score > best.score)
|
|
@@ -303,6 +429,120 @@ function bestForToken(tok, candidate) {
|
|
|
303
429
|
return best;
|
|
304
430
|
}
|
|
305
431
|
|
|
432
|
+
/**
|
|
433
|
+
* The score of a whole-query phrase inside a doc's title or heading: a keyword
|
|
434
|
+
* substring hit (70) promoted by the same 100 as the exact tier. Below an
|
|
435
|
+
* exact name or keyword (190-200), above the token-sum path (~151 at most).
|
|
436
|
+
*/
|
|
437
|
+
const TITLE_PHRASE_SCORE = 170;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* The score of a whole title inside a longer query, before its coverage bonus:
|
|
441
|
+
* one step below {@link TITLE_PHRASE_SCORE}. The bonus (one per query term the
|
|
442
|
+
* candidate matches, at most 9) orders the sections that share a common title,
|
|
443
|
+
* so "best practices for spacing" puts Spacing's Best Practices first.
|
|
444
|
+
*/
|
|
445
|
+
const TITLE_IN_QUERY_SCORE = 160;
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* The score of a candidate that matches every content word of a multi-word
|
|
449
|
+
* query, before a bonus of up to 8 for how strong its strongest match is: just
|
|
450
|
+
* above anything that matches only some of the words. The token-sum path tops
|
|
451
|
+
* out near 150 for a partial match (a 100 on one word, the per-word bonus, and
|
|
452
|
+
* the coverage term), so an AND-match with one keyword-strength hit (see
|
|
453
|
+
* {@link STRONG_TOKEN_SCORE}) always outranks an OR-match, and stays below the
|
|
454
|
+
* title tiers.
|
|
455
|
+
*/
|
|
456
|
+
const FULL_COVERAGE_SCORE = 151;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* The strongest single-word hit an every-word match needs to take that tier: a
|
|
460
|
+
* keyword substring. Two passing mentions in prose, or the components a page
|
|
461
|
+
* happens to render, are breadth, not relevance; they stay on the token sum,
|
|
462
|
+
* below an exact name or keyword hit on one of the words.
|
|
463
|
+
*/
|
|
464
|
+
const STRONG_TOKEN_SCORE = 70;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* The words of a title or query, lowercased, without punctuation or code ticks.
|
|
468
|
+
* @param {string} text
|
|
469
|
+
* @returns {string[]}
|
|
470
|
+
*/
|
|
471
|
+
function phraseWords(text) {
|
|
472
|
+
return unlinkText(text).toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Whether two words are the same word, allowing a plural on either side, so
|
|
477
|
+
* `data attributes selector` still reads "Data attribute selectors".
|
|
478
|
+
* @param {string} a
|
|
479
|
+
* @param {string} b
|
|
480
|
+
*/
|
|
481
|
+
function samePhraseWord(a, b) {
|
|
482
|
+
return (
|
|
483
|
+
a === b ||
|
|
484
|
+
`${a}s` === b ||
|
|
485
|
+
`${b}s` === a ||
|
|
486
|
+
`${a}es` === b ||
|
|
487
|
+
`${b}es` === a
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Whether `plural` is the plural of `word`: `integrations` of `integration`,
|
|
493
|
+
* `boxes` of `box`. `es` only follows s, x, z, ch, or sh, so `notes` is not a
|
|
494
|
+
* plural of `not`.
|
|
495
|
+
* @param {string} plural
|
|
496
|
+
* @param {string} word
|
|
497
|
+
*/
|
|
498
|
+
function pluralOf(plural, word) {
|
|
499
|
+
if (word.length < 3) return false;
|
|
500
|
+
if (plural === `${word}s`) return true;
|
|
501
|
+
return /(?:s|x|z|ch|sh)$/.test(word) && plural === `${word}es`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* The first title or heading that holds every word of the query, in order and
|
|
506
|
+
* side by side, or null.
|
|
507
|
+
* @param {string} term - Lowercased full query.
|
|
508
|
+
* @param {string[] | undefined} titles
|
|
509
|
+
* @returns {string | null}
|
|
510
|
+
*/
|
|
511
|
+
export function headingWithPhrase(term, titles) {
|
|
512
|
+
const query = phraseWords(term);
|
|
513
|
+
if (query.length < 2 || !titles) return null;
|
|
514
|
+
for (const title of titles) {
|
|
515
|
+
const words = phraseWords(String(title ?? ''));
|
|
516
|
+
for (let i = 0; i + query.length <= words.length; i++) {
|
|
517
|
+
if (query.every((word, j) => samePhraseWord(words[i + j], word)))
|
|
518
|
+
return title;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* The first title or heading of two words or more that the query holds whole,
|
|
526
|
+
* in order and side by side, or null. A question such as "how do I add dark
|
|
527
|
+
* mode" names the "Dark mode" section outright, around words no title has.
|
|
528
|
+
* @param {string} term - Lowercased full query.
|
|
529
|
+
* @param {string[] | undefined} titles
|
|
530
|
+
* @returns {string | null}
|
|
531
|
+
*/
|
|
532
|
+
export function titleInQuery(term, titles) {
|
|
533
|
+
const query = phraseWords(term);
|
|
534
|
+
if (!titles) return null;
|
|
535
|
+
for (const title of titles) {
|
|
536
|
+
const words = phraseWords(String(title ?? ''));
|
|
537
|
+
if (words.length < 2 || words.length > query.length) continue;
|
|
538
|
+
for (let i = 0; i + words.length <= query.length; i++) {
|
|
539
|
+
if (words.every((word, j) => samePhraseWord(query[i + j], word)))
|
|
540
|
+
return title;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
|
|
306
546
|
/**
|
|
307
547
|
* @param {string} term - Lowercased full query.
|
|
308
548
|
* @param {string[]} tokens - Content tokens from tokenizeQuery(term).
|
|
@@ -322,17 +562,26 @@ export function scoreQuery(term, tokens, candidate) {
|
|
|
322
562
|
matched: total,
|
|
323
563
|
total,
|
|
324
564
|
});
|
|
325
|
-
|
|
565
|
+
// Typo tolerance is for one-word lookups. In a multi-word query a near miss
|
|
566
|
+
// is usually a different word, not a typo.
|
|
567
|
+
const fuzzy = tokens.length <= 1;
|
|
568
|
+
const full = scoreCandidate(term, candidate, {fuzzy});
|
|
569
|
+
// A query of several words keeps its phrase tiers below even when stopwords
|
|
570
|
+
// leave one content word: "make an integration" is still the phrase an
|
|
571
|
+
// author declares as a keyword, and "build an integration" still names a
|
|
572
|
+
// title outright, though each tokenizes to `integration` alone.
|
|
573
|
+
const phrase = phraseWords(term).length >= 2;
|
|
326
574
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
575
|
+
/** 0–1 content tokens: whole-phrase fuzzy matching (typo tolerance for
|
|
576
|
+
* single words), but if stopwords left exactly one DIFFERENT token (e.g.
|
|
577
|
+
* "pricing page" → "pricing"), score that token too and take the stronger. */
|
|
578
|
+
const fewTokens = () => {
|
|
331
579
|
const single =
|
|
332
|
-
tokens.length === 1 ? bestForToken(tokens[0], candidate) : null;
|
|
580
|
+
tokens.length === 1 ? bestForToken(tokens[0], candidate, {fuzzy}) : null;
|
|
333
581
|
if (full && (!single || full.score >= single.score)) return asFull(full);
|
|
334
582
|
return single ? asFull(single) : null;
|
|
335
|
-
}
|
|
583
|
+
};
|
|
584
|
+
if (tokens.length <= 1 && !phrase) return fewTokens();
|
|
336
585
|
|
|
337
586
|
// The full (untokenized) query matching a candidate's name or a declared
|
|
338
587
|
// keyword VERBATIM — full.score 90 or 100, the only two scoreCandidate
|
|
@@ -349,6 +598,22 @@ export function scoreQuery(term, tokens, candidate) {
|
|
|
349
598
|
return asFull({score: full.score + 100, reason: full.reason});
|
|
350
599
|
}
|
|
351
600
|
|
|
601
|
+
// The whole query standing as a phrase in a doc's title or one of its
|
|
602
|
+
// headings is the next tier down, and still above the token-sum path. The
|
|
603
|
+
// reader named what the section is about, in order: `dark mode` is the
|
|
604
|
+
// "Light/Dark Mode" section. Without this, the title scores a keyword
|
|
605
|
+
// substring (70) and loses to a doc that happens to name `mode` exactly in
|
|
606
|
+
// a code tick (90 on one token, 98 with coverage), so API enum docs outrank
|
|
607
|
+
// the guide section.
|
|
608
|
+
const heading = headingWithPhrase(term, candidate.titles);
|
|
609
|
+
if (heading != null) {
|
|
610
|
+
return asFull({
|
|
611
|
+
score: TITLE_PHRASE_SCORE,
|
|
612
|
+
reason: `title "${heading}" holds the whole query`,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
if (tokens.length <= 1) return fewTokens();
|
|
616
|
+
|
|
352
617
|
// Multi-word natural language: score each content token, counting only
|
|
353
618
|
// strong hits, then reward coverage so candidates matching more terms win.
|
|
354
619
|
let strongest = 0;
|
|
@@ -356,15 +621,49 @@ export function scoreQuery(term, tokens, candidate) {
|
|
|
356
621
|
/** @type {string[]} */
|
|
357
622
|
const hitTerms = [];
|
|
358
623
|
for (const tok of tokens) {
|
|
359
|
-
const h = bestForToken(tok, candidate);
|
|
624
|
+
const h = bestForToken(tok, candidate, {fuzzy});
|
|
360
625
|
if (h && h.score >= MIN_TOKEN_SCORE) {
|
|
361
626
|
if (h.score > strongest) strongest = h.score;
|
|
362
627
|
matched++;
|
|
363
628
|
hitTerms.push(tok);
|
|
364
629
|
}
|
|
365
630
|
}
|
|
631
|
+
// The reverse of the title tier, a step lower: the query holds a whole title
|
|
632
|
+
// of two words or more, so the reader asked a question around the section's
|
|
633
|
+
// name ("how do I add dark mode"). Coverage breaks ties between sections
|
|
634
|
+
// that share a title such as "Best Practices".
|
|
635
|
+
const named = titleInQuery(term, candidate.titles);
|
|
636
|
+
if (named != null) {
|
|
637
|
+
return {
|
|
638
|
+
score: TITLE_IN_QUERY_SCORE + Math.min(matched, 9),
|
|
639
|
+
reason: `the query names the title "${named}"`,
|
|
640
|
+
matched,
|
|
641
|
+
total,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
366
644
|
if (matched === 0) return full ? asFull(full) : null;
|
|
367
645
|
|
|
646
|
+
const reason = `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`;
|
|
647
|
+
|
|
648
|
+
// Every word matched is its own tier. Summed per word, a doc that matches
|
|
649
|
+
// both words of `troubleshoot integration` in its title and description
|
|
650
|
+
// (50 + bonus + coverage = 77) lost to thirty docs that each match
|
|
651
|
+
// `integration` alone, by name or in a code tick (98-108). The reader asked for
|
|
652
|
+
// both; a candidate that has both comes first, ordered among its peers by
|
|
653
|
+
// how strong its strongest match is. It needs one keyword-strength hit:
|
|
654
|
+
// every word mentioned in prose, or rendered by a page, is breadth, and
|
|
655
|
+
// stays on the token sum below an exact hit on one word.
|
|
656
|
+
if (matched === tokens.length && strongest >= STRONG_TOKEN_SCORE) {
|
|
657
|
+
return {
|
|
658
|
+
score:
|
|
659
|
+
FULL_COVERAGE_SCORE +
|
|
660
|
+
Math.floor((strongest - MIN_TOKEN_SCORE) / 6.25),
|
|
661
|
+
reason,
|
|
662
|
+
matched,
|
|
663
|
+
total,
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
368
667
|
// Base the score on the STRONGEST concept that matched, plus a bonus per
|
|
369
668
|
// additional matched concept and a coverage term.
|
|
370
669
|
//
|
|
@@ -386,14 +685,8 @@ export function scoreQuery(term, tokens, candidate) {
|
|
|
386
685
|
const tokenScore = Math.round(
|
|
387
686
|
strongest + Math.min(matched - 1, 3) * 12 + coverage * 15,
|
|
388
687
|
);
|
|
389
|
-
|
|
390
688
|
if (full && full.score >= tokenScore) return asFull(full);
|
|
391
|
-
return {
|
|
392
|
-
score: tokenScore,
|
|
393
|
-
reason: `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`,
|
|
394
|
-
matched,
|
|
395
|
-
total,
|
|
396
|
-
};
|
|
689
|
+
return {score: tokenScore, reason, matched, total};
|
|
397
690
|
}
|
|
398
691
|
|
|
399
692
|
/**
|
|
@@ -404,23 +697,28 @@ export function scoreQuery(term, tokens, candidate) {
|
|
|
404
697
|
* @param {string} term - Lowercased search term.
|
|
405
698
|
* @param {object} candidate
|
|
406
699
|
* @param {string} candidate.name - Primary identifier (component/hook name, topic, template name).
|
|
700
|
+
* @param {string} [candidate.domain] - A component, hook, or template name
|
|
701
|
+
* also matches typed as words: `command palette` is CommandPalette.
|
|
407
702
|
* @param {string[]} [candidate.keywords] - Authored intent (componentsUsed, category words).
|
|
408
703
|
* @param {string[]} [candidate.weakKeywords] - Derived signal (components a page renders).
|
|
409
704
|
* @param {string} [candidate.description]
|
|
410
705
|
* @param {string[]} [candidate.prose] - Extra free-text blobs (doc section text, best practices).
|
|
411
706
|
* @param {string[]} [candidate.guidance] - Usage guidance (features, best practices) — scored a tier below description.
|
|
707
|
+
* @param {{fuzzy?: boolean}} [opts] - `fuzzy`: allow edit-distance (typo) matches. Default true; multi-word queries pass false.
|
|
412
708
|
* @returns {{score: number, reason: string} | null}
|
|
413
709
|
*/
|
|
414
710
|
export function scoreCandidate(
|
|
415
711
|
term,
|
|
416
712
|
{
|
|
417
713
|
name,
|
|
714
|
+
domain,
|
|
418
715
|
keywords = [],
|
|
419
716
|
weakKeywords = [],
|
|
420
717
|
description = '',
|
|
421
718
|
prose = [],
|
|
422
719
|
guidance = [],
|
|
423
720
|
},
|
|
721
|
+
{fuzzy = true} = {},
|
|
424
722
|
) {
|
|
425
723
|
let best = 0;
|
|
426
724
|
let reason = '';
|
|
@@ -436,25 +734,41 @@ export function scoreCandidate(
|
|
|
436
734
|
};
|
|
437
735
|
|
|
438
736
|
const nameLower = name.toLowerCase();
|
|
737
|
+
// A placed guide's name is its route, and the route's last segment is its
|
|
738
|
+
// name too, as a flat topic's is: `codemods` is cli/integrations/codemods.
|
|
739
|
+
const leafLower = nameLower.slice(nameLower.lastIndexOf('/') + 1);
|
|
439
740
|
|
|
440
741
|
// ── Name signals ────────────────────────────────────────────────
|
|
441
|
-
|
|
742
|
+
// A plural of the name is the name: `integration` is the `integrations`
|
|
743
|
+
// guides, `tab` the `tabs` doc.
|
|
744
|
+
// A component, hook, or template name typed as words is its name:
|
|
745
|
+
// `command palette` is CommandPalette. A doc's name is a route or key,
|
|
746
|
+
// matched as written.
|
|
747
|
+
const spelled =
|
|
748
|
+
domain !== 'doc' &&
|
|
749
|
+
!/[\s_-]/.test(nameLower) &&
|
|
750
|
+
nameLower === term.replace(/\s+/g, '');
|
|
751
|
+
if (nameLower === term || leafLower === term || spelled) {
|
|
442
752
|
consider(100, 'exact name');
|
|
753
|
+
} else if (pluralOf(nameLower, term) || pluralOf(term, nameLower)) {
|
|
754
|
+
// One point under the exact spelling, so the doc named `tokens` still
|
|
755
|
+
// outranks the Token component for `tokens`.
|
|
756
|
+
consider(99, 'plural of the name');
|
|
443
757
|
} else {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
758
|
+
if (sameWord(term, nameLower)) consider(95, `name "${name}"`);
|
|
759
|
+
// The term is a word of the name, or starts one: "input" in TextInput.
|
|
760
|
+
else if (startsAWordOf(term, name)) {
|
|
761
|
+
consider(60, `name contains "${term}"`);
|
|
762
|
+
}
|
|
763
|
+
if (fuzzy) {
|
|
764
|
+
const dist = levenshteinDistance(term, nameLower);
|
|
765
|
+
if (isTypo(term, nameLower, dist)) {
|
|
766
|
+
consider(
|
|
767
|
+
dist === 1 ? 80 : dist === 2 ? 40 : 20,
|
|
768
|
+
`similar name (distance ${dist})`,
|
|
769
|
+
);
|
|
770
|
+
}
|
|
453
771
|
}
|
|
454
|
-
const dist = levenshteinDistance(term, nameLower);
|
|
455
|
-
if (dist === 1) consider(80, `similar name (distance ${dist})`);
|
|
456
|
-
else if (dist === 2) consider(40, `similar name (distance ${dist})`);
|
|
457
|
-
else if (dist === 3) consider(20, `similar name (distance ${dist})`);
|
|
458
772
|
}
|
|
459
773
|
|
|
460
774
|
// ── Keyword signals ─────────────────────────────────────────────
|
|
@@ -464,14 +778,17 @@ export function scoreCandidate(
|
|
|
464
778
|
consider(90, `keyword "${kw}"`);
|
|
465
779
|
continue;
|
|
466
780
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
781
|
+
if (sameWord(term, kwLower)) {
|
|
782
|
+
consider(88, `keyword "${kw}"`);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
if (startsAWordOf(term, kw)) consider(70, `keyword "${kw}"`);
|
|
786
|
+
if (fuzzy) {
|
|
787
|
+
const dist = levenshteinDistance(term, kwLower);
|
|
788
|
+
if (isTypo(term, kwLower, dist) && dist <= 2) {
|
|
789
|
+
consider(dist === 1 ? 70 : 30, `keyword "${kw}" (distance ${dist})`);
|
|
790
|
+
}
|
|
471
791
|
}
|
|
472
|
-
const dist = levenshteinDistance(term, kwLower);
|
|
473
|
-
if (dist === 1) consider(70, `keyword "${kw}" (distance ${dist})`);
|
|
474
|
-
else if (dist === 2) consider(30, `keyword "${kw}" (distance ${dist})`);
|
|
475
792
|
}
|
|
476
793
|
|
|
477
794
|
// ── Weak keyword signals (derived, not authored) ─────────────────
|
|
@@ -481,15 +798,11 @@ export function scoreCandidate(
|
|
|
481
798
|
// No Levenshtein tier — fuzzy matching a derived signal is pure noise.
|
|
482
799
|
for (const kw of weakKeywords) {
|
|
483
800
|
const kwLower = String(kw).toLowerCase();
|
|
484
|
-
if (kwLower === term) {
|
|
801
|
+
if (kwLower === term || sameWord(term, kwLower)) {
|
|
485
802
|
consider(60, `renders ${kw}`);
|
|
486
803
|
continue;
|
|
487
804
|
}
|
|
488
|
-
|
|
489
|
-
const l = term.length < kwLower.length ? kwLower : term;
|
|
490
|
-
if (s.length >= 4 && l.includes(s) && s.length / l.length >= 0.5) {
|
|
491
|
-
consider(40, `renders ${kw}`);
|
|
492
|
-
}
|
|
805
|
+
if (startsAWordOf(term, kw)) consider(40, `renders ${kw}`);
|
|
493
806
|
}
|
|
494
807
|
|
|
495
808
|
// ── Prose / description / guidance signals (stem-tolerant whole word) ──
|
|
@@ -528,16 +841,26 @@ export function scoreCandidate(
|
|
|
528
841
|
}
|
|
529
842
|
|
|
530
843
|
/**
|
|
531
|
-
*
|
|
844
|
+
* A component or hook doc, compiled, or null when it cannot be read.
|
|
532
845
|
* @param {string} docPath
|
|
533
846
|
* @param {string} [exportName]
|
|
847
|
+
* @param {'components' | 'hooks'} [root]
|
|
534
848
|
* @returns {Promise<any>}
|
|
535
849
|
*/
|
|
536
|
-
async function loadModuleDoc(
|
|
850
|
+
async function loadModuleDoc(
|
|
851
|
+
docPath,
|
|
852
|
+
exportName = 'docs',
|
|
853
|
+
root = 'components',
|
|
854
|
+
) {
|
|
537
855
|
try {
|
|
538
|
-
const mod = await import(pathToFileURL(docPath).href);
|
|
539
856
|
// Support both the stamped default export and the legacy named export.
|
|
540
|
-
return
|
|
857
|
+
return (
|
|
858
|
+
(await readDocView(docPath, {
|
|
859
|
+
root,
|
|
860
|
+
loader: 'native',
|
|
861
|
+
exports: ['default', exportName],
|
|
862
|
+
})) ?? null
|
|
863
|
+
);
|
|
541
864
|
} catch {
|
|
542
865
|
return null;
|
|
543
866
|
}
|
|
@@ -637,7 +960,8 @@ async function gatherIntegrationComponents(cwd) {
|
|
|
637
960
|
/** @type {Candidate[]} */
|
|
638
961
|
const candidates = [];
|
|
639
962
|
for (const integration of loadedIntegrations) {
|
|
640
|
-
|
|
963
|
+
const {components} = await discoverValidIntegrationComponents(integration);
|
|
964
|
+
for (const rec of components) {
|
|
641
965
|
const doc = await loadModuleDoc(rec.docPath);
|
|
642
966
|
candidates.push({
|
|
643
967
|
domain: 'component',
|
|
@@ -701,7 +1025,7 @@ async function gatherHooks(coreDir) {
|
|
|
701
1025
|
let description = '';
|
|
702
1026
|
let importPath = '@astryxdesign/core/hooks';
|
|
703
1027
|
if (docPath) {
|
|
704
|
-
const doc = await loadModuleDoc(docPath);
|
|
1028
|
+
const doc = await loadModuleDoc(docPath, 'docs', 'hooks');
|
|
705
1029
|
if (doc) {
|
|
706
1030
|
keywords = Array.isArray(doc.keywords) ? doc.keywords : [];
|
|
707
1031
|
description = doc.usage?.description || doc.description || '';
|
|
@@ -720,7 +1044,11 @@ async function gatherHooks(coreDir) {
|
|
|
720
1044
|
}
|
|
721
1045
|
|
|
722
1046
|
/**
|
|
723
|
-
* Build doc
|
|
1047
|
+
* Build doc candidates at the grain a reader reads them: each section of a
|
|
1048
|
+
* topic, whose command reads just that section; each topic as a whole, whose
|
|
1049
|
+
* command lists its sections; and each docs-tree node by its route. The tree's
|
|
1050
|
+
* guides split into sections like topics, and its typed docs also match by
|
|
1051
|
+
* their own name, so `assertResponse` finds `cli/api/functions/assert-response`.
|
|
724
1052
|
*
|
|
725
1053
|
* Reads the project's catalog rather than the CLI's own docs directory, so a
|
|
726
1054
|
* topic an integration contributed (or replaced) is searchable exactly like a
|
|
@@ -732,44 +1060,287 @@ async function gatherHooks(coreDir) {
|
|
|
732
1060
|
async function gatherDocs(cwd) {
|
|
733
1061
|
/** @type {Candidate[]} */
|
|
734
1062
|
const candidates = [];
|
|
735
|
-
let
|
|
1063
|
+
let catalog;
|
|
736
1064
|
try {
|
|
737
|
-
|
|
1065
|
+
catalog = await loadDocsCatalog(cwd);
|
|
738
1066
|
} catch {
|
|
739
1067
|
return candidates;
|
|
740
1068
|
}
|
|
741
|
-
|
|
742
|
-
|
|
1069
|
+
let tree = null;
|
|
1070
|
+
try {
|
|
1071
|
+
tree = await projectTree(catalog);
|
|
1072
|
+
} catch {
|
|
1073
|
+
// `astryx doctor` reports a tree that fails to build; search still
|
|
1074
|
+
// indexes the topics.
|
|
1075
|
+
}
|
|
1076
|
+
for (const entry of catalog.entries()) {
|
|
1077
|
+
// A topic whose name opens another doc (spec:AST-046 FR11) is not
|
|
1078
|
+
// offered: every hit's command must open the hit.
|
|
1079
|
+
if (tree && !holdsOwnName(tree, catalog, entry)) continue;
|
|
1080
|
+
let lowered = null;
|
|
743
1081
|
try {
|
|
744
|
-
|
|
1082
|
+
lowered = await lowerTopic(catalog, entry);
|
|
745
1083
|
} catch {
|
|
746
1084
|
// A topic that cannot be loaded is reported by the commands that own
|
|
747
1085
|
// integration issues; search just cannot index it.
|
|
748
1086
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
1087
|
+
const doc = lowered?.doc ?? null;
|
|
1088
|
+
// A flat topic lives in the Unorganized level; its hits say so, and name
|
|
1089
|
+
// the package each section came from.
|
|
1090
|
+
const home = tree?.get(entry.name);
|
|
1091
|
+
const placed = home?.ref?.flatTopic === entry.name ? home : null;
|
|
1092
|
+
/** @type {Map<string, string>} */
|
|
1093
|
+
const packages = new Map([
|
|
1094
|
+
[entry.providerId ?? entry.package, entry.package],
|
|
1095
|
+
...entry.extensions.map(
|
|
1096
|
+
ext => /** @type {[string, string]} */ ([ext.providerId ?? ext.package, ext.package]),
|
|
1097
|
+
),
|
|
1098
|
+
]);
|
|
1099
|
+
candidates.push(
|
|
1100
|
+
...topicCandidates(
|
|
1101
|
+
entry.name,
|
|
1102
|
+
doc,
|
|
1103
|
+
entry.title,
|
|
1104
|
+
'',
|
|
1105
|
+
placed && tree
|
|
1106
|
+
? [
|
|
1107
|
+
...tree.ancestors(placed).map(a => a.title),
|
|
1108
|
+
doc?.title || entry.title || entry.name,
|
|
1109
|
+
].join(' › ')
|
|
1110
|
+
: undefined,
|
|
1111
|
+
placed ? `astryx docs ${placed.parent}` : undefined,
|
|
1112
|
+
entry.package,
|
|
1113
|
+
key =>
|
|
1114
|
+
packages.get(lowered?.sectionProviders?.[key] ?? '') ?? entry.package,
|
|
1115
|
+
),
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (tree == null) return candidates;
|
|
1119
|
+
for (const node of tree.nodes.values()) {
|
|
1120
|
+
// A flat topic is indexed above, as a topic.
|
|
1121
|
+
if (node.ref?.flatTopic) continue;
|
|
1122
|
+
// A tree hit names where it lives: its ancestors' titles, then its own.
|
|
1123
|
+
const path = [...tree.ancestors(node).map(a => a.title), node.title];
|
|
1124
|
+
if (node.kind === 'generic') {
|
|
1125
|
+
let doc = null;
|
|
1126
|
+
try {
|
|
1127
|
+
doc = (await lowerTopic(catalog, guideEntry(node))).doc;
|
|
1128
|
+
} catch {
|
|
1129
|
+
// As above: the owning commands report it.
|
|
759
1130
|
}
|
|
1131
|
+
candidates.push(
|
|
1132
|
+
...topicCandidates(
|
|
1133
|
+
node.route,
|
|
1134
|
+
doc,
|
|
1135
|
+
node.title,
|
|
1136
|
+
node.summary,
|
|
1137
|
+
path.join(' › '),
|
|
1138
|
+
node.parent == null ? undefined : `astryx docs ${node.parent}`,
|
|
1139
|
+
node.provider,
|
|
1140
|
+
),
|
|
1141
|
+
);
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
const selfDoc = /** @type {any} */ (node.ref)?.selfDoc;
|
|
1145
|
+
// A typed doc's content is what `astryx docs <route>` prints. The first
|
|
1146
|
+
// column of its tables names what the doc defines (an error code, an
|
|
1147
|
+
// option, a parameter), so each is a keyword the doc answers to.
|
|
1148
|
+
/** @type {any[]} */
|
|
1149
|
+
const content = (await nodeView(catalog, tree, node)).content ?? [];
|
|
1150
|
+
/** @type {string[]} */
|
|
1151
|
+
const defined = [];
|
|
1152
|
+
for (const block of content) {
|
|
1153
|
+
if (block.type !== 'table' || !Array.isArray(block.rows)) continue;
|
|
1154
|
+
for (const row of block.rows)
|
|
1155
|
+
if (row[0] != null) defined.push(plain(row[0]));
|
|
760
1156
|
}
|
|
761
1157
|
candidates.push({
|
|
762
1158
|
domain: 'doc',
|
|
763
|
-
name:
|
|
764
|
-
keywords: [
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1159
|
+
name: node.name,
|
|
1160
|
+
keywords: [
|
|
1161
|
+
node.route.slice(node.route.lastIndexOf('/') + 1),
|
|
1162
|
+
...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
|
|
1163
|
+
// A namespace doc's own keywords, which it declares for search.
|
|
1164
|
+
...(Array.isArray(node.keywords) ? node.keywords : []),
|
|
1165
|
+
...defined,
|
|
1166
|
+
...codeTerms({content}),
|
|
1167
|
+
],
|
|
1168
|
+
description: node.summary || '',
|
|
1169
|
+
prose: sectionProse({title: node.title, content}),
|
|
1170
|
+
titles: [node.title],
|
|
1171
|
+
_topic: node.route,
|
|
1172
|
+
_title: path.join(' › '),
|
|
1173
|
+
_command: `astryx docs ${node.route}`,
|
|
1174
|
+
_parent: node.parent == null ? 'astryx docs' : `astryx docs ${node.parent}`,
|
|
1175
|
+
_package: node.provider,
|
|
768
1176
|
});
|
|
769
1177
|
}
|
|
770
1178
|
return candidates;
|
|
771
1179
|
}
|
|
772
1180
|
|
|
1181
|
+
/**
|
|
1182
|
+
* The words one section says: its prose, headings, and list items.
|
|
1183
|
+
* @param {any} section
|
|
1184
|
+
* @returns {string[]}
|
|
1185
|
+
*/
|
|
1186
|
+
function sectionProse(section) {
|
|
1187
|
+
/** @type {string[]} */
|
|
1188
|
+
const prose = [];
|
|
1189
|
+
if (section?.title) prose.push(section.title);
|
|
1190
|
+
for (const block of section?.content || []) {
|
|
1191
|
+
if ((block.type === 'prose' || block.type === 'heading') && block.text) {
|
|
1192
|
+
prose.push(block.text);
|
|
1193
|
+
} else if (block.type === 'list' && Array.isArray(block.items)) {
|
|
1194
|
+
for (const item of block.items) {
|
|
1195
|
+
const text = typeof item === 'string' ? item : item?.text;
|
|
1196
|
+
if (typeof text === 'string') prose.push(text);
|
|
1197
|
+
}
|
|
1198
|
+
} else if (block.type === 'table' && Array.isArray(block.rows)) {
|
|
1199
|
+
for (const row of block.rows) prose.push(row.map(plain).join(' '));
|
|
1200
|
+
} else if (block.type === 'code' && typeof block.code === 'string') {
|
|
1201
|
+
prose.push([block.label, block.code].filter(Boolean).join(' '));
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
return prose;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* The identifiers a doc part names in code ticks (`token-ref`,
|
|
1209
|
+
* `ERR_UNKNOWN_SECTION`). Each is a keyword: a reader who types one exactly
|
|
1210
|
+
* wants the part that defines or explains it.
|
|
1211
|
+
* @param {any} part - a section, or `{content}` of a typed doc
|
|
1212
|
+
* @returns {string[]}
|
|
1213
|
+
*/
|
|
1214
|
+
function codeTerms(part) {
|
|
1215
|
+
/** @type {Set<string>} */
|
|
1216
|
+
const terms = new Set();
|
|
1217
|
+
/** @param {unknown} text */
|
|
1218
|
+
const scan = text => {
|
|
1219
|
+
for (const m of String(text ?? '').matchAll(/`([^`\s]{2,40})`/g)) {
|
|
1220
|
+
terms.add(m[1]);
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
for (const block of part?.content || []) {
|
|
1224
|
+
if (block.type === 'prose') scan(block.text);
|
|
1225
|
+
else if (block.type === 'list' && Array.isArray(block.items)) {
|
|
1226
|
+
for (const item of block.items) {
|
|
1227
|
+
scan(typeof item === 'string' ? item : item?.text);
|
|
1228
|
+
}
|
|
1229
|
+
} else if (block.type === 'table' && Array.isArray(block.rows)) {
|
|
1230
|
+
for (const row of block.rows) for (const cell of row) scan(cell);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return [...terms];
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* The headings inside a section. Each names a subsection, so a query that
|
|
1238
|
+
* names one should find the section as surely as one that names its title.
|
|
1239
|
+
* @param {any} section
|
|
1240
|
+
* @returns {string[]}
|
|
1241
|
+
*/
|
|
1242
|
+
function headings(section) {
|
|
1243
|
+
return (section?.content || [])
|
|
1244
|
+
.filter(
|
|
1245
|
+
(/** @type {any} */ block) => block.type === 'heading' && block.text,
|
|
1246
|
+
)
|
|
1247
|
+
.map((/** @type {any} */ block) => String(block.text));
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* A table cell as plain words, without its code ticks.
|
|
1252
|
+
* @param {unknown} cell
|
|
1253
|
+
* @returns {string}
|
|
1254
|
+
*/
|
|
1255
|
+
function plain(cell) {
|
|
1256
|
+
return unlinkText(String(cell ?? '')).replaceAll('`', '');
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* The candidates one topic yields: the topic itself, and one per section when
|
|
1261
|
+
* it has more than one. A topic's command lists its sections, and a section's
|
|
1262
|
+
* command reads only that section, so a hit never costs a whole-topic read.
|
|
1263
|
+
* @param {string} name - the topic name, or a placed guide's route
|
|
1264
|
+
* @param {any} doc - the lowered topic, or null when it did not load
|
|
1265
|
+
* @param {string} [title]
|
|
1266
|
+
* @param {string} [summary]
|
|
1267
|
+
* @param {string} [path] - where the topic lives in the docs tree, as titles
|
|
1268
|
+
* joined by ` › `; a flat topic is its own title
|
|
1269
|
+
* @param {string} [parent] - the command that opens the level above the
|
|
1270
|
+
* topic: its namespace, or the Unorganized level for a flat topic
|
|
1271
|
+
* @param {string} [pkg] - the npm package that authored the topic
|
|
1272
|
+
* @param {(key: string) => string} [sectionPackage] - the npm package a
|
|
1273
|
+
* section came from: an extension's section names the extension's package
|
|
1274
|
+
* @returns {Candidate[]}
|
|
1275
|
+
*/
|
|
1276
|
+
function topicCandidates(
|
|
1277
|
+
name,
|
|
1278
|
+
doc,
|
|
1279
|
+
title,
|
|
1280
|
+
summary = '',
|
|
1281
|
+
path,
|
|
1282
|
+
parent,
|
|
1283
|
+
pkg,
|
|
1284
|
+
sectionPackage,
|
|
1285
|
+
) {
|
|
1286
|
+
/** @type {any[]} */
|
|
1287
|
+
const sections = doc?.sections ?? [];
|
|
1288
|
+
const docTitle = path || doc?.title || title || name;
|
|
1289
|
+
const split = sections.length > 1;
|
|
1290
|
+
// A placed guide also answers to its last route segment's words:
|
|
1291
|
+
// `quick start` is cli/integrations/quick-start.
|
|
1292
|
+
const leaf = name.slice(name.lastIndexOf('/') + 1);
|
|
1293
|
+
/** @type {Candidate[]} */
|
|
1294
|
+
const out = [
|
|
1295
|
+
{
|
|
1296
|
+
domain: 'doc',
|
|
1297
|
+
name,
|
|
1298
|
+
keywords: [
|
|
1299
|
+
...(leaf !== name ? [leaf.replaceAll('-', ' ')] : []),
|
|
1300
|
+
...(doc?.title || title ? [doc?.title || title] : []),
|
|
1301
|
+
...(Array.isArray(doc?.keywords) ? doc.keywords : []),
|
|
1302
|
+
],
|
|
1303
|
+
description: doc?.description || summary,
|
|
1304
|
+
prose: split
|
|
1305
|
+
? sections.map(section => section.title).filter(Boolean)
|
|
1306
|
+
: sections.flatMap(sectionProse),
|
|
1307
|
+
titles: [
|
|
1308
|
+
doc?.title || title || name,
|
|
1309
|
+
// A topic read whole answers for the headings inside it.
|
|
1310
|
+
...(split ? [] : sections.flatMap(s => [s.title, ...headings(s)])),
|
|
1311
|
+
].filter(Boolean),
|
|
1312
|
+
_topic: name,
|
|
1313
|
+
_title: docTitle,
|
|
1314
|
+
_command: split ? `astryx docs ${name} --index` : `astryx docs ${name}`,
|
|
1315
|
+
...(parent ? {_parent: parent} : {}),
|
|
1316
|
+
...(pkg ? {_package: pkg} : {}),
|
|
1317
|
+
},
|
|
1318
|
+
];
|
|
1319
|
+
if (!split) return out;
|
|
1320
|
+
for (const section of sections) {
|
|
1321
|
+
const key = sectionKey(section);
|
|
1322
|
+
out.push({
|
|
1323
|
+
domain: 'doc',
|
|
1324
|
+
name: key,
|
|
1325
|
+
keywords: [
|
|
1326
|
+
...(section.title ? [section.title] : []),
|
|
1327
|
+
...headings(section),
|
|
1328
|
+
...codeTerms(section),
|
|
1329
|
+
],
|
|
1330
|
+
description: sectionSummary(section),
|
|
1331
|
+
prose: sectionProse(section),
|
|
1332
|
+
titles: [section.title, ...headings(section)].filter(Boolean),
|
|
1333
|
+
_topic: name,
|
|
1334
|
+
_section: key,
|
|
1335
|
+
_title: `${docTitle} › ${section.title}`,
|
|
1336
|
+
_command: `astryx docs ${name} ${key}`,
|
|
1337
|
+
_parent: `astryx docs ${name} --index`,
|
|
1338
|
+
...((sectionPackage?.(key) ?? pkg) ? {_package: sectionPackage?.(key) ?? pkg} : {}),
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
return out;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
773
1344
|
/**
|
|
774
1345
|
* Build template candidates (page + block) from the template discovery API.
|
|
775
1346
|
* @param {string} cwd
|
|
@@ -783,6 +1354,13 @@ async function gatherTemplates(cwd) {
|
|
|
783
1354
|
return [];
|
|
784
1355
|
}
|
|
785
1356
|
return templates.map(t => {
|
|
1357
|
+
// A replacement's target is its canonical unqualified lookup id. Keep the
|
|
1358
|
+
// integration-owned id as a keyword and response label, but score and print
|
|
1359
|
+
// commands against the id that `template()` resolves back to this entry.
|
|
1360
|
+
// This matters for replacement chains: one replacement's own id can be the
|
|
1361
|
+
// target of another, so using that shadowed id as the command would select
|
|
1362
|
+
// the other template.
|
|
1363
|
+
const commandName = t.replaces ?? t.dirName;
|
|
786
1364
|
// Blocks ship an authored componentsUsed; page templates don't, so derive
|
|
787
1365
|
// them from the source. Category words (e.g. "Dashboard - Analytics") are
|
|
788
1366
|
// strong intent signal for pages, which otherwise only index on name +
|
|
@@ -795,6 +1373,7 @@ async function gatherTemplates(cwd) {
|
|
|
795
1373
|
const keywords = Array.isArray(t.componentsUsed)
|
|
796
1374
|
? [...t.componentsUsed]
|
|
797
1375
|
: [];
|
|
1376
|
+
keywords.push(...templateLookupIds(t).filter(id => id !== commandName));
|
|
798
1377
|
/** @type {string[]} */
|
|
799
1378
|
let weakKeywords = [];
|
|
800
1379
|
if (t.type === 'page') {
|
|
@@ -810,12 +1389,14 @@ async function gatherTemplates(cwd) {
|
|
|
810
1389
|
}
|
|
811
1390
|
return {
|
|
812
1391
|
domain: 'template',
|
|
813
|
-
name:
|
|
1392
|
+
name: commandName,
|
|
814
1393
|
keywords,
|
|
815
1394
|
weakKeywords,
|
|
816
1395
|
description: t.description || '',
|
|
817
1396
|
_displayName: t.name,
|
|
818
1397
|
_kind: t.type, // 'page' | 'block'
|
|
1398
|
+
_resultName: t.dirName,
|
|
1399
|
+
_commandName: commandName,
|
|
819
1400
|
};
|
|
820
1401
|
});
|
|
821
1402
|
}
|
|
@@ -834,7 +1415,7 @@ async function gatherTemplates(cwd) {
|
|
|
834
1415
|
function toResult(c, score, reason, matchedTerms, queryTerms) {
|
|
835
1416
|
const base = {
|
|
836
1417
|
domain: c.domain,
|
|
837
|
-
name: c.name,
|
|
1418
|
+
name: c._resultName ?? c.name,
|
|
838
1419
|
score,
|
|
839
1420
|
reason,
|
|
840
1421
|
description: c.description || '',
|
|
@@ -856,10 +1437,16 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
|
|
|
856
1437
|
};
|
|
857
1438
|
break;
|
|
858
1439
|
case 'doc':
|
|
1440
|
+
// A doc result names its topic or route, plus the section when the
|
|
1441
|
+
// hit is one section; its command reads exactly that part.
|
|
859
1442
|
result = {
|
|
860
1443
|
...base,
|
|
1444
|
+
name: c._topic ?? c.name,
|
|
1445
|
+
...(c._section ? {section: c._section} : {}),
|
|
861
1446
|
title: c._title,
|
|
862
|
-
command: `astryx docs ${c.name}`,
|
|
1447
|
+
command: c._command ?? `astryx docs ${c.name}`,
|
|
1448
|
+
...(c._parent ? {parent: c._parent} : {}),
|
|
1449
|
+
...(c._package ? {package: c._package} : {}),
|
|
863
1450
|
};
|
|
864
1451
|
break;
|
|
865
1452
|
case 'template':
|
|
@@ -867,7 +1454,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
|
|
|
867
1454
|
...base,
|
|
868
1455
|
displayName: c._displayName,
|
|
869
1456
|
kind: c._kind,
|
|
870
|
-
command: `astryx template ${c.name}`,
|
|
1457
|
+
command: `astryx template ${c._commandName ?? c.name} --type ${c._kind}`,
|
|
871
1458
|
};
|
|
872
1459
|
break;
|
|
873
1460
|
default:
|
|
@@ -884,7 +1471,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
|
|
|
884
1471
|
* @param {string} [options.cwd]
|
|
885
1472
|
* @param {'component'|'hook'|'doc'|'template'} [options.type] - Restrict to one domain.
|
|
886
1473
|
* @param {number} [options.limit] - Max results (default 20).
|
|
887
|
-
* @returns {Promise<
|
|
1474
|
+
* @returns {Promise<import('./search.type.mjs').SearchResponse>}
|
|
888
1475
|
*/
|
|
889
1476
|
export async function search(query, options = {}) {
|
|
890
1477
|
const {cwd = process.cwd(), type, limit = 20} = options;
|
|
@@ -919,17 +1506,27 @@ export async function search(query, options = {}) {
|
|
|
919
1506
|
const term = String(query).trim().toLowerCase();
|
|
920
1507
|
const tokens = tokenizeQuery(term);
|
|
921
1508
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1509
|
+
// `astryx docs` reads docs without @astryxdesign/core, so a docs-only
|
|
1510
|
+
// search must too. Every other domain reads core: asked for by name, it is
|
|
1511
|
+
// an error without core; an open search then covers the docs alone.
|
|
1512
|
+
const docsOnly = type === 'doc';
|
|
1513
|
+
const coreDir = docsOnly ? null : findCoreDir(cwd);
|
|
1514
|
+
if (type && !docsOnly && !coreDir) {
|
|
1515
|
+
throw new AstryxError(
|
|
1516
|
+
'Could not find @astryxdesign/core package',
|
|
1517
|
+
undefined,
|
|
1518
|
+
ERROR_CODES.ERR_CORE_NOT_FOUND,
|
|
1519
|
+
);
|
|
925
1520
|
}
|
|
926
1521
|
|
|
927
1522
|
// Gather candidates from each requested domain in parallel.
|
|
928
1523
|
/** @param {string} d */
|
|
929
|
-
const wants = d => !type || type === d;
|
|
1524
|
+
const wants = d => (!type && (coreDir != null || d === 'doc')) || type === d;
|
|
930
1525
|
const [components, hooks, docTopics, templates] = await Promise.all([
|
|
931
|
-
wants('component')
|
|
932
|
-
|
|
1526
|
+
wants('component')
|
|
1527
|
+
? gatherComponents(/** @type {string} */ (coreDir), cwd)
|
|
1528
|
+
: [],
|
|
1529
|
+
wants('hook') ? gatherHooks(/** @type {string} */ (coreDir)) : [],
|
|
933
1530
|
wants('doc') ? gatherDocs(cwd) : [],
|
|
934
1531
|
wants('template') ? gatherTemplates(cwd) : [],
|
|
935
1532
|
]);
|
|
@@ -971,7 +1568,10 @@ export async function search(query, options = {}) {
|
|
|
971
1568
|
data: {
|
|
972
1569
|
query: String(query).trim(),
|
|
973
1570
|
matchCount: scored.length,
|
|
974
|
-
|
|
1571
|
+
// toResult gives every domain its command and domain fields.
|
|
1572
|
+
results: /** @type {import('./search.type.mjs').SearchResultEntry[]} */ (
|
|
1573
|
+
limited
|
|
1574
|
+
),
|
|
975
1575
|
},
|
|
976
1576
|
};
|
|
977
1577
|
}
|