@astryxdesign/cli 0.6.3 → 0.6.4-canary.06c8fa3
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 +121 -85
- 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 +16 -9
- package/api/build/build.test.mjs +197 -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 +165 -49
- package/api/build/kit/rank.d.mts +44 -0
- package/api/build/kit/rank.mjs +432 -0
- package/api/build/kit/rank.test.mjs +196 -0
- package/api/component/_adapter.d.mts +31 -12
- package/api/component/_adapter.mjs +79 -15
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +36 -13
- 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 +272 -41
- package/api/docs/_adapter.mjs +985 -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/docs.d.mts +10 -3
- package/api/docs/docs.doc.mjs +55 -16
- package/api/docs/docs.mjs +53 -10
- package/api/docs/docs.test.mjs +166 -4
- 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/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +114 -8
- 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 +1 -0
- package/api/doctor/doctor.mjs +635 -7
- package/api/doctor/doctor.test.mjs +732 -11
- package/api/gap-report/gap-report.doc.mjs +8 -4
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- 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 -3
- package/api/index.mjs +5 -4
- package/api/init/init.doc.mjs +6 -1
- 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 +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +179 -7
- package/api/integration/authoring-checks.type.mjs +6 -1
- package/api/integration/integration-authoring.type.d.mts +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 +2 -1
- 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 +105 -0
- package/api/integration/pack-check.mjs +111 -10
- package/api/integration/pack-check.test.mjs +387 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +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 +1 -0
- package/api/json/envelope-types.test.mjs +76 -0
- package/api/json/index.ts +2 -1
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +3 -2
- package/api/search/search-return-type.test.mjs +54 -0
- package/api/search/search.d.mts +62 -11
- package/api/search/search.doc.mjs +8 -2
- package/api/search/search.mjs +471 -83
- package/api/search/search.test.mjs +142 -1
- 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 +2 -1
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/copy/copy.mjs +17 -23
- package/api/template/copy/copy.test.mjs +17 -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 +1008 -3
- package/api/template/template-suffix.test.mjs +41 -21
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +30 -8
- package/api/template/template.mjs +46 -9
- 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 +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/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 +4 -3
- package/api/theme/themeBuild.doc.mjs +8 -4
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +6 -2
- 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 +359 -60
- package/api/upgrade/status/status.mjs +2 -2
- package/api/upgrade/upgrade.doc.mjs +12 -5
- 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 +9 -0
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +2 -2
- package/assets/docs/layout.doc.mjs +1 -1
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +4 -4
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +5 -5
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/tree/component-lookups.doc.mjs +149 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +4 -4
- 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 +31 -8
- 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 +790 -23
- package/authoring/doctypes/_schema.mjs +543 -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 +41 -0
- package/authoring/doctypes/command/command.doc.mjs +5 -4
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +6 -5
- package/authoring/doctypes/component/component.doc.mjs +6 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +6 -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 +48 -6
- package/authoring/doctypes/reference/type.ts +70 -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 +1 -1
- 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 +15 -8
- 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/command-result-coverage.test.mjs +7 -7
- 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 +24 -7
- package/clients/cli/commands/component.test.mjs +19 -0
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- 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 +240 -26
- package/clients/cli/commands/docs.test.mjs +222 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
- package/clients/cli/commands/doctor-integration.test.mjs +90 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +59 -32
- package/clients/cli/commands/doctor.test.mjs +42 -0
- package/clients/cli/commands/gap-report.doc.mjs +17 -6
- 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 +20 -9
- 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 +32 -6
- package/clients/cli/commands/integration-authoring.test.mjs +13 -9
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/interactive-guard.test.mjs +101 -24
- package/clients/cli/commands/json-contract.test.mjs +33 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
- package/clients/cli/commands/search.doc.mjs +7 -4
- package/clients/cli/commands/search.mjs +28 -9
- package/clients/cli/commands/search.test.mjs +75 -0
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +3 -2
- package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
- package/clients/cli/commands/template.doc.mjs +52 -13
- 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 +702 -0
- package/clients/cli/commands/theme-add.doc.mjs +4 -3
- 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 +9 -5
- package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
- package/clients/cli/commands/theme-template.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
- package/clients/cli/commands/upgrade.doc.mjs +22 -10
- 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 +21 -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 +32 -6
- package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
- package/clients/cli/lib/exit-codes.test.mjs +90 -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 +69 -0
- package/clients/cli/lib/manifest.d.ts +2 -0
- package/clients/cli/lib/manifest.mjs +39 -10
- 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 +82 -12
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- 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 +170 -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 +490 -0
- package/foundation/discovery/cli-self-docs.test.mjs +375 -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 +423 -108
- package/foundation/discovery/docs-discovery.test.mjs +365 -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 +775 -84
- 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 +1630 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +291 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +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 +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +606 -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 +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +84 -0
- package/foundation/integrations/contribution-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +3 -2
- package/foundation/integrations/contribution-inventory.mjs +27 -24
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +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 +13 -4
- package/foundation/response/error-codes.mjs +8 -2
- package/foundation/response/error-codes.test.mjs +137 -10
- 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 +5 -1
- package/foundation/response/response-types.doc.mjs +46 -38
- package/foundation/response/response-types.doc.test.mjs +181 -0
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +40 -10
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.d.mts +2 -0
- package/foundation/xle/expand.mjs +6 -5
- package/foundation/xle/expand.test.mjs +54 -0
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -11
- package/api/layout/_adapter.d.mts +0 -34
- package/api/layout/_adapter.mjs +0 -133
- package/api/layout/check/check.d.mts +0 -16
- package/api/layout/check/check.mjs +0 -40
- package/api/layout/expand/expand.d.mts +0 -22
- package/api/layout/expand/expand.mjs +0 -153
- package/api/layout/grammar/grammar.d.mts +0 -13
- package/api/layout/grammar/grammar.mjs +0 -86
- package/api/layout/layout.d.mts +0 -6
- package/api/layout/layout.mjs +0 -17
- package/api/layout/layout.test.mjs +0 -297
- package/api/layout/layout.type.d.mts +0 -89
- package/api/layout/layout.type.mjs +0 -103
- package/api/layout/layoutCheck.doc.d.mts +0 -11
- package/api/layout/layoutCheck.doc.mjs +0 -84
- package/api/layout/layoutExpand.doc.d.mts +0 -11
- package/api/layout/layoutExpand.doc.mjs +0 -106
- package/api/layout/layoutGrammar.doc.d.mts +0 -11
- package/api/layout/layoutGrammar.doc.mjs +0 -56
- package/assets/templates/themes/manifest.json +0 -95
- package/clients/cli/commands/layout-check.doc.mjs +0 -54
- package/clients/cli/commands/layout-expand.doc.mjs +0 -66
- package/clients/cli/commands/layout-grammar.doc.mjs +0 -30
- package/clients/cli/commands/layout.doc.mjs +0 -34
- package/clients/cli/commands/layout.error-codes.test.mjs +0 -66
- package/clients/cli/commands/layout.exit-parity.test.mjs +0 -41
- package/clients/cli/commands/layout.mjs +0 -263
- 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
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The page ranker behind `build`'s START: which page template an idea
|
|
5
|
+
* should start from.
|
|
6
|
+
*
|
|
7
|
+
* @input A free-text idea and the project's ready page templates (id, display
|
|
8
|
+
* name, description, `Family - Variant` category), all read from each
|
|
9
|
+
* template's own descriptor.
|
|
10
|
+
* @output Every template, ranked, with the query terms it matched and whether
|
|
11
|
+
* the idea names its family; `pickStart` turns that into a template or null.
|
|
12
|
+
* @position Beside kit.mjs (api/build/kit/). Search ranks components, docs,
|
|
13
|
+
* blocks and pages against short lookups; this ranks only page templates,
|
|
14
|
+
* against the long descriptions builders actually write ("ops dashboard with
|
|
15
|
+
* a KPI row, a sortable table and a trend chart"). Search's scorer is left
|
|
16
|
+
* alone: its callers depend on its scores.
|
|
17
|
+
*
|
|
18
|
+
* Why a separate ranker. Real `build` queries run to fifteen words or more.
|
|
19
|
+
* Search scores a page on its single strongest term plus a garnish, so one
|
|
20
|
+
* incidental word decides: "status" picks the project-status dashboard,
|
|
21
|
+
* "board" picks the kanban board for a game board. Here every matched term
|
|
22
|
+
* counts, weighted by how rare it is among page templates (inverse document
|
|
23
|
+
* frequency), so the page that answers more of the idea — and its rarer words —
|
|
24
|
+
* wins. Structural signals sit on top:
|
|
25
|
+
*
|
|
26
|
+
* - The head noun. The words before the first "with", ":" or "," name what
|
|
27
|
+
* the page is ("audit dashboard: dense table with …"); a family word there
|
|
28
|
+
* outweighs the same word among the parts.
|
|
29
|
+
* - The container. In "draft history in a modal", the modal is the page's
|
|
30
|
+
* frame, and a template is its frame; container words count double, and a
|
|
31
|
+
* container match is evidence enough to start from. Only an overlay frame
|
|
32
|
+
* counts: "inside a data card" names a component the part lives in, and "in
|
|
33
|
+
* an existing dashboard" names the page being changed.
|
|
34
|
+
* - Phrase heads. In "side-by-side product response", "product" describes the
|
|
35
|
+
* response; it is not a product page. A word that only ever modifies the
|
|
36
|
+
* next one counts half, unless the template names the same pair
|
|
37
|
+
* ("executive summary").
|
|
38
|
+
* - The family base. A template whose id is its family's name (`dashboard`,
|
|
39
|
+
* `settings`) is that family's default; it wins when no variant's own words
|
|
40
|
+
* outweigh it.
|
|
41
|
+
*
|
|
42
|
+
* Family words come from the templates' own ids — a word in the ids of two or
|
|
43
|
+
* more templates of one family, plus the family's name when a template carries
|
|
44
|
+
* it — never from a list kept here or from synonyms, so an integration's
|
|
45
|
+
* templates join the vocabulary by being named like their family, and "a
|
|
46
|
+
* landing page" is not a hero because "landing" is a synonym of "hero".
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import {stem, STOPWORDS, SYNONYMS} from '../../search/search.mjs';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* How much a term counts by where the template names it. The id and category
|
|
53
|
+
* are the author's own label for what the page is; the description's last
|
|
54
|
+
* sentence lists the ideas it serves; its body describes the layout.
|
|
55
|
+
*/
|
|
56
|
+
const FIELD_WEIGHT = {
|
|
57
|
+
id: 3,
|
|
58
|
+
variant: 3,
|
|
59
|
+
family: 2,
|
|
60
|
+
display: 2,
|
|
61
|
+
intent: 2,
|
|
62
|
+
body: 1,
|
|
63
|
+
};
|
|
64
|
+
/** A synonym hit counts at this share of a direct hit. */
|
|
65
|
+
const SYNONYM_SHARE = 0.5;
|
|
66
|
+
/** A container word ("in a modal") counts this many times over. */
|
|
67
|
+
const CONTAINER = 2;
|
|
68
|
+
/** A word that only modifies another ("product" in "product response"). */
|
|
69
|
+
const MODIFIER = 0.5;
|
|
70
|
+
/** Added when the idea's head names the template's family. */
|
|
71
|
+
const HEAD_FAMILY = 12;
|
|
72
|
+
/** Added when the family is named anywhere else in the idea. */
|
|
73
|
+
const BODY_FAMILY = 2;
|
|
74
|
+
/** Added to a family's base template when the idea names that family. */
|
|
75
|
+
const FAMILY_BASE = 2;
|
|
76
|
+
/**
|
|
77
|
+
* The least a start must score, and the evidence it needs: two matched terms,
|
|
78
|
+
* or the idea naming its family. One rare word alone is how a tic-tac-toe game
|
|
79
|
+
* board became a kanban board.
|
|
80
|
+
*/
|
|
81
|
+
const START_SCORE = 10;
|
|
82
|
+
const START_TERMS = 2;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Words search drops that name layout: "side navigation", "side by side".
|
|
86
|
+
*/
|
|
87
|
+
const LAYOUT_WORDS = new Set(['side']);
|
|
88
|
+
|
|
89
|
+
/** Filler that search keeps but that says nothing about a page's layout. */
|
|
90
|
+
const FILLER = new Set([
|
|
91
|
+
'per',
|
|
92
|
+
'each',
|
|
93
|
+
'every',
|
|
94
|
+
'using',
|
|
95
|
+
'via',
|
|
96
|
+
'into',
|
|
97
|
+
'onto',
|
|
98
|
+
'about',
|
|
99
|
+
'above',
|
|
100
|
+
'below',
|
|
101
|
+
'across',
|
|
102
|
+
'within',
|
|
103
|
+
'without',
|
|
104
|
+
'between',
|
|
105
|
+
'plus',
|
|
106
|
+
'also',
|
|
107
|
+
'then',
|
|
108
|
+
'them',
|
|
109
|
+
'they',
|
|
110
|
+
'which',
|
|
111
|
+
'while',
|
|
112
|
+
'when',
|
|
113
|
+
'all',
|
|
114
|
+
'any',
|
|
115
|
+
'new',
|
|
116
|
+
'use',
|
|
117
|
+
'used',
|
|
118
|
+
'show',
|
|
119
|
+
'shows',
|
|
120
|
+
'showing',
|
|
121
|
+
'display',
|
|
122
|
+
'displays',
|
|
123
|
+
'displaying',
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Where an idea's head ends: its first clause, before the parts it lists.
|
|
128
|
+
*/
|
|
129
|
+
const HEAD_END =
|
|
130
|
+
/:|,|;|\(|\s[-\u2013\u2014]\s|\b(?:with|showing|listing|for|that|where|plus|including|containing|featuring|which|to|from|of)\b/i;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Frames a page can live in, above whatever is underneath. A container phrase
|
|
134
|
+
* counts only when it names one of these.
|
|
135
|
+
*/
|
|
136
|
+
const OVERLAY_FRAMES = new Set([
|
|
137
|
+
'modal',
|
|
138
|
+
'dialog',
|
|
139
|
+
'drawer',
|
|
140
|
+
'sheet',
|
|
141
|
+
'popup',
|
|
142
|
+
'overlay',
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
/** A container phrase: "in a modal", "inside the side panel". */
|
|
146
|
+
const CONTAINER_PHRASE =
|
|
147
|
+
/\b(?:in|inside|within)\s+(?:a|an|the)\s+([a-z-]+)(?:\s+([a-z-]+))?/gi;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Where a phrase ends, for finding the word that heads it: punctuation and
|
|
151
|
+
* the words that join phrases.
|
|
152
|
+
*/
|
|
153
|
+
const PHRASE_END =
|
|
154
|
+
/[^a-z0-9\s-]+|\s(?:and|or|with|for|of|to|in|on|at|by|from|the|a|an|plus)\s/;
|
|
155
|
+
|
|
156
|
+
/** @param {string} t */
|
|
157
|
+
const isContentWord = t =>
|
|
158
|
+
t.length >= 2 &&
|
|
159
|
+
(LAYOUT_WORDS.has(t) || (!STOPWORDS.has(t) && !FILLER.has(t)));
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Content terms of a text: lowercase alphanumeric words, stopwords and filler
|
|
163
|
+
* removed, stemmed.
|
|
164
|
+
* @param {string} text
|
|
165
|
+
* @returns {string[]}
|
|
166
|
+
*/
|
|
167
|
+
function terms(text) {
|
|
168
|
+
return (
|
|
169
|
+
String(text)
|
|
170
|
+
.toLowerCase()
|
|
171
|
+
.match(/[a-z0-9]+/g) ?? []
|
|
172
|
+
)
|
|
173
|
+
.filter(isContentWord)
|
|
174
|
+
.map(stem);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The terms of an idea that only ever modify another word, each with the words
|
|
179
|
+
* it modifies: in each phrase the last content word is its head, and the ones
|
|
180
|
+
* before it describe the next.
|
|
181
|
+
* @param {string} query
|
|
182
|
+
* @returns {Map<string, Set<string>>}
|
|
183
|
+
*/
|
|
184
|
+
function modifiersOf(query) {
|
|
185
|
+
const heads = new Set();
|
|
186
|
+
/** @type {Map<string, Set<string>>} */
|
|
187
|
+
const modifiers = new Map();
|
|
188
|
+
for (const phrase of String(query).toLowerCase().split(PHRASE_END)) {
|
|
189
|
+
const words = terms(phrase);
|
|
190
|
+
words.forEach((w, i) => {
|
|
191
|
+
if (i === words.length - 1) heads.add(w);
|
|
192
|
+
else modifiers.set(w, (modifiers.get(w) ?? new Set()).add(words[i + 1]));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
for (const h of heads) modifiers.delete(h);
|
|
196
|
+
return modifiers;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Adjacent term pairs a text names, "a b", for telling a template that names
|
|
201
|
+
* a whole compound ("executive summary") from one that names only its parts.
|
|
202
|
+
* @param {string[]} texts
|
|
203
|
+
* @returns {Set<string>}
|
|
204
|
+
*/
|
|
205
|
+
function pairsOf(texts) {
|
|
206
|
+
const pairs = new Set();
|
|
207
|
+
for (const text of texts) {
|
|
208
|
+
for (const phrase of String(text).toLowerCase().split(PHRASE_END)) {
|
|
209
|
+
const words = terms(phrase);
|
|
210
|
+
for (let i = 1; i < words.length; i++)
|
|
211
|
+
pairs.add(`${words[i - 1]} ${words[i]}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return pairs;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The terms of an idea that name its frame: "modal" in "in a modal". A card, a
|
|
219
|
+
* table or an existing page after "in a" is where a part goes, not a frame.
|
|
220
|
+
* @param {string} query
|
|
221
|
+
* @returns {Set<string>}
|
|
222
|
+
*/
|
|
223
|
+
function containersOf(query) {
|
|
224
|
+
const found = new Set();
|
|
225
|
+
for (const m of String(query).matchAll(CONTAINER_PHRASE)) {
|
|
226
|
+
for (const t of terms(`${m[1]} ${m[2] ?? ''}`)) {
|
|
227
|
+
if (OVERLAY_FRAMES.has(t)) found.add(t);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return found;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** @param {string} text */
|
|
234
|
+
const normalized = text =>
|
|
235
|
+
(
|
|
236
|
+
String(text)
|
|
237
|
+
.toLowerCase()
|
|
238
|
+
.match(/[a-z0-9]+/g) ?? []
|
|
239
|
+
).join(' ');
|
|
240
|
+
|
|
241
|
+
/** Stemmed synonym lookup built from search's vocabulary, both directions. */
|
|
242
|
+
const SYNONYMS_OF = (() => {
|
|
243
|
+
/** @type {Map<string, Set<string>>} */
|
|
244
|
+
const index = new Map();
|
|
245
|
+
/** @param {string} a @param {string} b */
|
|
246
|
+
const add = (a, b) => {
|
|
247
|
+
const set = index.get(a) ?? new Set();
|
|
248
|
+
set.add(b);
|
|
249
|
+
index.set(a, set);
|
|
250
|
+
};
|
|
251
|
+
for (const [key, values] of Object.entries(SYNONYMS)) {
|
|
252
|
+
const k = stem(key);
|
|
253
|
+
for (const value of values) {
|
|
254
|
+
const v = stem(value);
|
|
255
|
+
add(k, v);
|
|
256
|
+
add(v, k);
|
|
257
|
+
for (const other of values) if (other !== value) add(v, stem(other));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return index;
|
|
261
|
+
})();
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @typedef {import('../_adapter.mjs').PageTemplate} PageTemplate
|
|
265
|
+
* @typedef {{name: string, score: number, hits: number, familyNamed: boolean, containerMatched: boolean}} RankedPage
|
|
266
|
+
*/
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Rank page templates against an idea, best first. Ties go to the template
|
|
270
|
+
* with the shorter id (the family's broader page), then by name.
|
|
271
|
+
*
|
|
272
|
+
* @param {string} query
|
|
273
|
+
* @param {PageTemplate[]} pages
|
|
274
|
+
* @returns {RankedPage[]}
|
|
275
|
+
*/
|
|
276
|
+
export function rankPages(query, pages) {
|
|
277
|
+
const docs = pages.map(page => {
|
|
278
|
+
const [family = '', ...variantParts] = page.category.split(' - ');
|
|
279
|
+
const sentences = page.description.trim().split(/(?<=[.!?])\s+/);
|
|
280
|
+
const intent = sentences.length > 1 ? sentences[sentences.length - 1] : '';
|
|
281
|
+
const body =
|
|
282
|
+
sentences.length > 1
|
|
283
|
+
? sentences.slice(0, -1).join(' ')
|
|
284
|
+
: page.description;
|
|
285
|
+
/** @type {Record<keyof typeof FIELD_WEIGHT, string>} */
|
|
286
|
+
const fields = {
|
|
287
|
+
id: page.name.replace(/-/g, ' '),
|
|
288
|
+
variant: variantParts.join(' - '),
|
|
289
|
+
family,
|
|
290
|
+
display: page.displayName,
|
|
291
|
+
intent,
|
|
292
|
+
body,
|
|
293
|
+
};
|
|
294
|
+
/** @type {Map<string, number>} */
|
|
295
|
+
const bag = new Map();
|
|
296
|
+
for (const [field, text] of Object.entries(fields)) {
|
|
297
|
+
const weight =
|
|
298
|
+
FIELD_WEIGHT[/** @type {keyof typeof FIELD_WEIGHT} */ (field)];
|
|
299
|
+
for (const t of terms(text))
|
|
300
|
+
bag.set(t, Math.max(bag.get(t) ?? 0, weight));
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
page,
|
|
304
|
+
family: family.trim(),
|
|
305
|
+
bag,
|
|
306
|
+
pairs: pairsOf(Object.values(fields)),
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Inverse document frequency over page templates: a word every dashboard
|
|
311
|
+
// carries says less than one only the funnel carries.
|
|
312
|
+
/** @type {Map<string, number>} */
|
|
313
|
+
const df = new Map();
|
|
314
|
+
for (const {bag} of docs)
|
|
315
|
+
for (const t of bag.keys()) df.set(t, (df.get(t) ?? 0) + 1);
|
|
316
|
+
const n = docs.length;
|
|
317
|
+
/** @param {string} t */
|
|
318
|
+
const idf = t => {
|
|
319
|
+
const d = df.get(t);
|
|
320
|
+
return d ? Math.log(1 + (n - d + 0.5) / (d + 0.5)) : 0;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// Family words, from the templates' own ids.
|
|
324
|
+
/** @type {Map<string, Map<string, number>>} */
|
|
325
|
+
const idWords = new Map();
|
|
326
|
+
for (const {page, family} of docs) {
|
|
327
|
+
const counts = idWords.get(family) ?? new Map();
|
|
328
|
+
for (const w of new Set(page.name.split('-'))) {
|
|
329
|
+
if (w.length >= 3) counts.set(stem(w), (counts.get(stem(w)) ?? 0) + 1);
|
|
330
|
+
}
|
|
331
|
+
idWords.set(family, counts);
|
|
332
|
+
}
|
|
333
|
+
/** @type {Map<string, string>} */
|
|
334
|
+
const familyOfWord = new Map();
|
|
335
|
+
for (const [family, counts] of idWords) {
|
|
336
|
+
const heads = [...counts].filter(([, c]) => c >= 2).map(([w]) => w);
|
|
337
|
+
const name = normalized(family).split(' ').pop() ?? '';
|
|
338
|
+
if (name.length >= 3 && counts.has(stem(name))) heads.push(stem(name));
|
|
339
|
+
for (const head of heads) {
|
|
340
|
+
if (!familyOfWord.has(head)) familyOfWord.set(head, family);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/** @param {string[]} ts */
|
|
344
|
+
const familiesIn = ts =>
|
|
345
|
+
new Set(ts.filter(t => familyOfWord.has(t)).map(t => familyOfWord.get(t)));
|
|
346
|
+
|
|
347
|
+
const queryTerms = [...new Set(terms(query))];
|
|
348
|
+
const headFamilies = familiesIn(
|
|
349
|
+
terms(String(query).split(HEAD_END)[0] ?? ''),
|
|
350
|
+
);
|
|
351
|
+
const namedFamilies = familiesIn(queryTerms);
|
|
352
|
+
const modifiers = modifiersOf(query);
|
|
353
|
+
const containers = containersOf(query);
|
|
354
|
+
|
|
355
|
+
return docs
|
|
356
|
+
.map(({page, family, bag, pairs}) => {
|
|
357
|
+
let score = 0;
|
|
358
|
+
let hits = 0;
|
|
359
|
+
let containerMatched = false;
|
|
360
|
+
for (const t of queryTerms) {
|
|
361
|
+
// A modifier is discounted where the template names it outright; a
|
|
362
|
+
// synonym hit is already discounted by SYNONYM_SHARE.
|
|
363
|
+
const modified = modifiers.get(t);
|
|
364
|
+
const discounted =
|
|
365
|
+
modified !== undefined &&
|
|
366
|
+
![...modified].some(n => pairs.has(`${t} ${n}`));
|
|
367
|
+
const direct = idf(t) * (bag.get(t) ?? 0) * (discounted ? MODIFIER : 1);
|
|
368
|
+
let viaSynonym = 0;
|
|
369
|
+
for (const s of SYNONYMS_OF.get(t) ?? []) {
|
|
370
|
+
if (queryTerms.includes(s)) continue;
|
|
371
|
+
viaSynonym = Math.max(
|
|
372
|
+
viaSynonym,
|
|
373
|
+
SYNONYM_SHARE * idf(s) * (bag.get(s) ?? 0),
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
const value =
|
|
377
|
+
Math.max(direct, viaSynonym) * (containers.has(t) ? CONTAINER : 1);
|
|
378
|
+
if (value > 0) {
|
|
379
|
+
score += value;
|
|
380
|
+
hits++;
|
|
381
|
+
if (containers.has(t)) containerMatched = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const familyNamed = namedFamilies.has(family);
|
|
385
|
+
if (headFamilies.has(family)) score += HEAD_FAMILY;
|
|
386
|
+
else if (familyNamed) score += BODY_FAMILY;
|
|
387
|
+
if (familyNamed && normalized(page.name) === normalized(family)) {
|
|
388
|
+
score += FAMILY_BASE;
|
|
389
|
+
}
|
|
390
|
+
return {name: page.name, score, hits, familyNamed, containerMatched};
|
|
391
|
+
})
|
|
392
|
+
.sort(
|
|
393
|
+
(a, b) =>
|
|
394
|
+
b.score - a.score ||
|
|
395
|
+
a.name.split('-').length - b.name.split('-').length ||
|
|
396
|
+
a.name.localeCompare(b.name),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The next closest templates after the start, best first: the ones a reader
|
|
402
|
+
* should check the idea against when the start's shape is wrong. Each matched
|
|
403
|
+
* at least one term and scored at least half of what a start needs.
|
|
404
|
+
*
|
|
405
|
+
* @param {RankedPage[]} ranked
|
|
406
|
+
* @param {string} startName
|
|
407
|
+
* @param {number} [count]
|
|
408
|
+
* @returns {RankedPage[]}
|
|
409
|
+
*/
|
|
410
|
+
export function pickAlternatives(ranked, startName, count = 2) {
|
|
411
|
+
return ranked
|
|
412
|
+
.filter(
|
|
413
|
+
r => r.name !== startName && r.hits > 0 && r.score >= START_SCORE / 2,
|
|
414
|
+
)
|
|
415
|
+
.slice(0, count);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* The template to start from, or null when the best one has too little
|
|
420
|
+
* evidence to lead and the page should start from the app shell. Evidence is
|
|
421
|
+
* two matched terms, the idea naming the template's family, or its container.
|
|
422
|
+
*
|
|
423
|
+
* @param {RankedPage[]} ranked
|
|
424
|
+
* @returns {RankedPage | null}
|
|
425
|
+
*/
|
|
426
|
+
export function pickStart(ranked) {
|
|
427
|
+
const top = ranked[0];
|
|
428
|
+
if (!top || top.score < START_SCORE) return null;
|
|
429
|
+
return top.hits >= START_TERMS || top.familyNamed || top.containerMatched
|
|
430
|
+
? top
|
|
431
|
+
: null;
|
|
432
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Tests for build's page ranker: long ideas, head nouns, family bases,
|
|
5
|
+
* rare-word weighting, and family words taken from the templates themselves.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {describe, it, expect} from 'vitest';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import {fileURLToPath} from 'node:url';
|
|
11
|
+
import {pickStart, rankPages} from './rank.mjs';
|
|
12
|
+
import {loadPageTemplates} from '../_adapter.mjs';
|
|
13
|
+
|
|
14
|
+
// api/build/kit/ -> up 5 = repo root (has packages/core and the templates).
|
|
15
|
+
const REPO = path.resolve(
|
|
16
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
17
|
+
'../../../../..',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/** @param {string} name @param {string} category @param {string} description */
|
|
21
|
+
const page = (name, category, description) => ({
|
|
22
|
+
name,
|
|
23
|
+
displayName: name,
|
|
24
|
+
category,
|
|
25
|
+
description,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('rankPages on the shipped page templates', () => {
|
|
29
|
+
/** @param {string} query */
|
|
30
|
+
const start = async query =>
|
|
31
|
+
pickStart(rankPages(query, await loadPageTemplates(REPO)))?.name ?? null;
|
|
32
|
+
|
|
33
|
+
it('starts a long dashboard idea from the family base', async () => {
|
|
34
|
+
expect(
|
|
35
|
+
await start(
|
|
36
|
+
'ops dashboard with a KPI row, a sortable table and a trend chart',
|
|
37
|
+
),
|
|
38
|
+
).toBe('dashboard');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("lets a variant's own words beat the base", async () => {
|
|
42
|
+
expect(
|
|
43
|
+
await start(
|
|
44
|
+
'conversion funnel dashboard with drop-off and retention by cohort',
|
|
45
|
+
),
|
|
46
|
+
).toBe('dashboard-cohort-funnel');
|
|
47
|
+
expect(await start('program milestone tracker with delivery status')).toBe(
|
|
48
|
+
'dashboard-progress',
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('lets the head noun decide between families', async () => {
|
|
53
|
+
// Both ideas mention a table and summary numbers; the head says which
|
|
54
|
+
// page it is.
|
|
55
|
+
expect(
|
|
56
|
+
await start(
|
|
57
|
+
'audit dashboard: dense filterable data table with a KPI stat row',
|
|
58
|
+
),
|
|
59
|
+
).toMatch(/^dashboard/);
|
|
60
|
+
expect(
|
|
61
|
+
await start(
|
|
62
|
+
'a data table of campaigns with spend and revenue, plus summary metrics above it',
|
|
63
|
+
),
|
|
64
|
+
).toMatch(/^table-/);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('does not start from one rare word', async () => {
|
|
68
|
+
// "board" is the kanban board's own word; a game board is not a kanban.
|
|
69
|
+
expect(
|
|
70
|
+
await start('accessible responsive tic-tac-toe game board'),
|
|
71
|
+
).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('rankPages on any catalog', () => {
|
|
76
|
+
it('ranks only the templates it is given', () => {
|
|
77
|
+
const ranked = rankPages('dashboard', [
|
|
78
|
+
page(
|
|
79
|
+
'dashboard',
|
|
80
|
+
'Dashboard - Analytics',
|
|
81
|
+
'Tiles, charts, tables. Dashboard, metrics.',
|
|
82
|
+
),
|
|
83
|
+
]);
|
|
84
|
+
expect(ranked.map(r => r.name)).toEqual(['dashboard']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('takes family words from template ids, so a new family needs no list', () => {
|
|
88
|
+
// An integration's family: its word is in two of its ids, so an idea
|
|
89
|
+
// naming it picks the family, and its base template leads it.
|
|
90
|
+
const catalog = [
|
|
91
|
+
page(
|
|
92
|
+
'dashboard',
|
|
93
|
+
'Dashboard - Analytics',
|
|
94
|
+
'Tiles, charts, tables. Dashboard, metrics.',
|
|
95
|
+
),
|
|
96
|
+
page('widget', 'Widget - Basic', 'One compact card. Widget, tile.'),
|
|
97
|
+
page(
|
|
98
|
+
'widget-grid',
|
|
99
|
+
'Widget - Grid',
|
|
100
|
+
'Many compact cards in a grid. Widget wall.',
|
|
101
|
+
),
|
|
102
|
+
];
|
|
103
|
+
const ranked = rankPages('weather widget with charts', catalog);
|
|
104
|
+
expect(ranked[0].name).toBe('widget');
|
|
105
|
+
expect(ranked[0].familyNamed).toBe(true);
|
|
106
|
+
expect(pickStart(ranked)?.name).toBe('widget');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('weighs a word by how rare it is among page templates', () => {
|
|
110
|
+
const catalog = [
|
|
111
|
+
page('alpha', 'Dashboard - Alpha', 'Status tiles. Status overview.'),
|
|
112
|
+
page('beta', 'Dashboard - Beta', 'Status rows. Status list.'),
|
|
113
|
+
page(
|
|
114
|
+
'gamma',
|
|
115
|
+
'Dashboard - Gamma',
|
|
116
|
+
'Funnel of stages. Funnel conversion.',
|
|
117
|
+
),
|
|
118
|
+
];
|
|
119
|
+
// "status" is on two templates, "funnel" on one: the rare word wins.
|
|
120
|
+
const ranked = rankPages('status funnel', catalog);
|
|
121
|
+
expect(ranked[0].name).toBe('gamma');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('rankPages structure signals', () => {
|
|
126
|
+
const catalog = [
|
|
127
|
+
page(
|
|
128
|
+
'dialog-panel',
|
|
129
|
+
'Settings - Dialog',
|
|
130
|
+
'Preferences inside a modal container. Settings, or modal dialog.',
|
|
131
|
+
),
|
|
132
|
+
page(
|
|
133
|
+
'tree-table',
|
|
134
|
+
'Table - Tree',
|
|
135
|
+
'Nested rows under parents. Tree, nested rows, file browser.',
|
|
136
|
+
),
|
|
137
|
+
page(
|
|
138
|
+
'item-detail',
|
|
139
|
+
'Content - Item Detail',
|
|
140
|
+
'One item with its media. Product, listing, or item detail.',
|
|
141
|
+
),
|
|
142
|
+
page(
|
|
143
|
+
'docs-catalog',
|
|
144
|
+
'Content - Documentation Catalog',
|
|
145
|
+
'A grid of category cards. Documentation, docs, or API index.',
|
|
146
|
+
),
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
it('doubles a container word and lets it carry the start', () => {
|
|
150
|
+
const ranked = rankPages(
|
|
151
|
+
'draft history in a modal with delete row actions',
|
|
152
|
+
catalog,
|
|
153
|
+
);
|
|
154
|
+
expect(ranked[0].name).toBe('dialog-panel');
|
|
155
|
+
expect(ranked[0].containerMatched).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('counts only an overlay frame as a container', () => {
|
|
159
|
+
// A card or an existing page after "in a" is where a part goes.
|
|
160
|
+
for (const idea of [
|
|
161
|
+
'view toggle inside a data card',
|
|
162
|
+
'status filter in an existing dashboard',
|
|
163
|
+
]) {
|
|
164
|
+
expect(rankPages(idea, catalog).every(r => !r.containerMatched)).toBe(
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('halves a word that only modifies the next one', () => {
|
|
171
|
+
// "item" modifies "response" here; "catalog" heads its own phrase.
|
|
172
|
+
const ranked = rankPages(
|
|
173
|
+
'endpoint catalog with side-by-side item response',
|
|
174
|
+
catalog,
|
|
175
|
+
);
|
|
176
|
+
expect(ranked[0].name).toBe('docs-catalog');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('does not treat a synonym as a family name', () => {
|
|
180
|
+
// "landing" is a synonym of "hero" in search, not a family word here.
|
|
181
|
+
const heroes = [
|
|
182
|
+
page(
|
|
183
|
+
'hero-centered',
|
|
184
|
+
'Gallery - Hero',
|
|
185
|
+
'A centered hero. Hero, banner, or landing.',
|
|
186
|
+
),
|
|
187
|
+
page(
|
|
188
|
+
'hero-split',
|
|
189
|
+
'Gallery - Hero Split',
|
|
190
|
+
'A split hero. Hero or splash.',
|
|
191
|
+
),
|
|
192
|
+
];
|
|
193
|
+
const ranked = rankPages('internal landing page', heroes);
|
|
194
|
+
expect(ranked.every(r => !r.familyNamed)).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
});
|