@astryxdesign/cli 0.7.2 → 0.7.3-canary.02f5fe4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -71
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +127 -3
- package/api/build/blocks/blocks.d.mts +92 -0
- package/api/build/blocks/blocks.mjs +528 -0
- package/api/build/blocks/blocks.test.mjs +238 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/build.type.mjs +1 -1
- package/api/build/kit/answer.d.mts +18 -0
- package/api/build/kit/answer.mjs +195 -0
- package/api/build/kit/answer.test.mjs +108 -0
- package/api/build/kit/kind.d.mts +25 -0
- package/api/build/kit/kind.mjs +170 -0
- package/api/build/kit/kit.d.mts +2 -4
- package/api/build/kit/kit.mjs +8 -398
- package/api/build/kit/rank.d.mts +3 -32
- package/api/build/kit/rank.mjs +25 -342
- package/api/build/kit/rank.test.mjs +2 -1
- package/api/build/kit/signals.d.mts +79 -0
- package/api/build/kit/signals.mjs +295 -0
- package/api/build/kit/start.d.mts +32 -0
- package/api/build/kit/start.mjs +163 -0
- package/api/build/kit/words.d.mts +72 -0
- package/api/build/kit/words.mjs +202 -0
- package/api/build/tree.d.mts +20 -0
- package/api/build/tree.mjs +53 -0
- package/api/build/tree.test.mjs +185 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/_adapter.mjs +39 -5
- package/api/component/component-replacement.test.mjs +1 -0
- package/api/component/component.mjs +17 -3
- package/api/component/component.test.mjs +15 -0
- package/api/component/doc-links.test.mjs +2 -2
- package/api/discover/_adapter.d.mts +1 -2
- package/api/discover/_adapter.mjs +42 -17
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/discover/discover.mjs +0 -1
- package/api/docs/_adapter.d.mts +2 -2
- package/api/docs/_adapter.mjs +82 -23
- package/api/docs/docs-homes.test.mjs +127 -0
- package/api/docs/docs.doc.mjs +3 -3
- package/api/docs/docs.test.mjs +35 -19
- package/api/docs/integration-tree.test.mjs +20 -20
- package/api/docs/integrationDocs.test.mjs +1 -1
- package/api/docs/namespace-section.test.mjs +6 -6
- package/api/docs/reference-blocks.test.mjs +2 -2
- package/api/doctor/_adapter.d.mts +33 -13
- package/api/doctor/_adapter.mjs +55 -25
- package/api/doctor/doctor.d.mts +23 -5
- package/api/doctor/doctor.mjs +183 -40
- package/api/doctor/doctor.test.mjs +95 -12
- package/api/hook/hook.mjs +1 -1
- package/api/init/init.doc.mjs +20 -6
- package/api/init/init.test.mjs +18 -2
- package/api/init/init.type.d.mts +3 -3
- package/api/init/init.type.mjs +3 -3
- package/api/init/remove/remove.mjs +17 -4
- package/api/init/remove/remove.test.mjs +114 -2
- package/api/init/run/run.mjs +69 -6
- package/api/integration/_adapter.d.mts +8 -0
- package/api/integration/_adapter.mjs +21 -1
- package/api/integration/authoring-checks.mjs +83 -9
- package/api/integration/authoring-checks.test.mjs +220 -0
- package/api/integration/pack-check.mjs +123 -4
- package/api/integration/pack-check.test.mjs +87 -0
- package/api/integration/validate-integration-case-disk.test.mjs +111 -0
- package/api/integration/validate-integration-fixes.test.mjs +6 -5
- package/api/integration/validate-integration-gaps.test.mjs +1975 -0
- package/api/integration/validate-integration.d.mts +15 -2
- package/api/integration/validate-integration.mjs +298 -39
- package/api/integration/validate-integration.type.d.mts +90 -1
- package/api/integration/validate-integration.type.mjs +67 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +82 -2
- package/api/search/_adapter.mjs +157 -10
- package/api/search/_adapter.test.mjs +173 -0
- package/api/search/_candidate-score.d.mts +40 -0
- package/api/search/_candidate-score.mjs +238 -0
- package/api/search/_cards.json +6098 -0
- package/api/search/_compound.d.mts +50 -0
- package/api/search/_compound.mjs +170 -0
- package/api/search/_doc-searcher.d.mts +4 -0
- package/api/search/_doc-searcher.mjs +23 -0
- package/api/search/_doc-searcher.test.mjs +36 -0
- package/api/search/_doc-searcher.weights.json +1 -0
- package/api/search/_doc-text.d.mts +30 -0
- package/api/search/_doc-text.mjs +87 -0
- package/api/search/_entries.d.mts +63 -0
- package/api/search/_entries.mjs +232 -0
- package/api/search/_entries.test.mjs +48 -0
- package/api/search/_features.d.mts +35 -0
- package/api/search/_features.mjs +155 -0
- package/api/search/_gather-components.d.mts +26 -0
- package/api/search/_gather-components.mjs +284 -0
- package/api/search/_gather-docs.d.mts +20 -0
- package/api/search/_gather-docs.mjs +254 -0
- package/api/search/_gather-hooks.d.mts +12 -0
- package/api/search/_gather-hooks.mjs +52 -0
- package/api/search/_gather-templates.d.mts +25 -0
- package/api/search/_gather-templates.mjs +107 -0
- package/api/search/_lexical.d.mts +135 -0
- package/api/search/_lexical.mjs +351 -0
- package/api/search/_main-searcher.d.mts +19 -0
- package/api/search/_main-searcher.mjs +115 -0
- package/api/search/_main-searcher.test.mjs +106 -0
- package/api/search/_main-searcher.weights.json +1 -0
- package/api/search/_module-doc.d.mts +32 -0
- package/api/search/_module-doc.mjs +78 -0
- package/api/search/_order.d.mts +32 -0
- package/api/search/_order.mjs +162 -0
- package/api/search/_order.test.mjs +72 -0
- package/api/search/_priority.d.mts +50 -0
- package/api/search/_priority.mjs +139 -0
- package/api/search/_query-words.d.mts +25 -0
- package/api/search/_query-words.mjs +92 -0
- package/api/search/_query.d.mts +65 -0
- package/api/search/_query.mjs +176 -0
- package/api/search/_result.d.mts +97 -0
- package/api/search/_result.mjs +150 -0
- package/api/search/_score.d.mts +60 -0
- package/api/search/_score.mjs +283 -0
- package/api/search/_synonyms.d.mts +21 -0
- package/api/search/_synonyms.mjs +67 -0
- package/api/search/_template-searcher.d.mts +4 -0
- package/api/search/_template-searcher.mjs +23 -0
- package/api/search/_template-searcher.test.mjs +40 -0
- package/api/search/_template-searcher.weights.json +1 -0
- package/api/search/_titles.d.mts +33 -0
- package/api/search/_titles.mjs +65 -0
- package/api/search/_words.d.mts +64 -0
- package/api/search/_words.mjs +157 -0
- package/api/search/search.d.mts +22 -305
- package/api/search/search.doc.mjs +8 -6
- package/api/search/search.mjs +56 -2482
- package/api/search/search.test.mjs +14 -14
- package/api/search/search.type.d.mts +6 -1
- package/api/search/search.type.mjs +6 -1
- package/api/search/tree.d.mts +15 -0
- package/api/search/tree.mjs +40 -0
- package/api/search/tree.test.mjs +66 -0
- package/api/search/tree.weights.json +1 -0
- package/api/template/template.mjs +5 -1
- package/api/theme/build/build.packed-old-core.test.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +57 -9
- package/api/upgrade/_adapter.mjs +198 -28
- package/api/upgrade/registry/registry.d.mts +10 -4
- package/api/upgrade/registry/registry.mjs +33 -16
- package/api/upgrade/registry/registry.test.mjs +85 -0
- package/api/upgrade/run/run.d.mts +4 -2
- package/api/upgrade/run/run.mjs +13 -2
- package/api/upgrade/run/run.test.mjs +417 -3
- package/api/upgrade/upgrade.doc.mjs +10 -5
- package/api/upgrade/upgrade.mjs +13 -2
- package/api/upgrade/upgrade.test.mjs +194 -0
- package/api/upgrade/upgrade.type.d.mts +5 -3
- package/api/upgrade/upgrade.type.mjs +8 -6
- package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
- package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
- package/assets/docs/author-a-theme.doc.mjs +2 -2
- package/assets/docs/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/add-a-component.doc.mjs +1 -1
- package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
- package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
- package/assets/docs/tree/block-template.doc.mjs +1 -1
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +2 -2
- package/assets/docs/tree/check-the-props.doc.mjs +1 -1
- package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
- package/assets/docs/tree/checks.doc.mjs +3 -3
- package/assets/docs/tree/ci-and-containers.doc.mjs +13 -0
- package/assets/docs/tree/cli-command.doc.mjs +1 -1
- package/assets/docs/tree/cli.doc.mjs +1 -1
- package/assets/docs/tree/codemods.doc.mjs +1 -1
- package/assets/docs/tree/component-family.doc.mjs +1 -1
- package/assets/docs/tree/component-imports.doc.mjs +1 -1
- package/assets/docs/tree/components.doc.mjs +1 -1
- package/assets/docs/tree/configuration.doc.mjs +1 -1
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
- package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
- package/assets/docs/tree/docs.doc.mjs +1 -1
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
- package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
- package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
- package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
- package/assets/docs/tree/global-integrations.doc.mjs +2 -2
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
- package/assets/docs/tree/help.doc.mjs +2 -2
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/links.doc.mjs +1 -1
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +1 -1
- package/assets/docs/tree/publishing.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/ship.doc.mjs +2 -2
- package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
- package/assets/docs/tree/single-component.doc.mjs +1 -1
- package/assets/docs/tree/start-a-template.doc.mjs +1 -1
- package/assets/docs/tree/subcomponent.doc.mjs +1 -1
- package/assets/docs/tree/template-assets.doc.mjs +1 -1
- package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +1 -1
- package/assets/docs/tree/templates.doc.mjs +1 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
- package/assets/docs/tree/themes.doc.mjs +1 -1
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
- package/assets/docs/tree/upgrading.doc.mjs +1 -1
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
- package/assets/docs/tree/versioning.doc.mjs +5 -1
- package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
- package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
- package/assets/docs/use-integrations.doc.mjs +120 -0
- package/assets/docs/working-with-ai.doc.mjs +6 -2
- package/assets/templates/themes/butter/butterTheme.ts +4 -2
- package/assets/templates/themes/stone/stoneTheme.ts +4 -2
- package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
- package/authoring/debug/parse.d.mts +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/integration/integration.doc.mjs +1 -1
- package/authoring/integration/type.ts +1 -1
- package/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/command-result-coverage.test.mjs +6 -5
- package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
- package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
- package/clients/cli/commands/component.test.mjs +6 -6
- package/clients/cli/commands/docs.doc.mjs +2 -2
- package/clients/cli/commands/docs.mjs +1 -1
- package/clients/cli/commands/docs.test.mjs +14 -14
- package/clients/cli/commands/doctor-integration.test.mjs +49 -1
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +40 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
- package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
- package/clients/cli/commands/init.behavior.test.mjs +230 -1
- package/clients/cli/commands/init.doc.mjs +28 -9
- package/clients/cli/commands/init.flag-help.test.mjs +1 -0
- package/clients/cli/commands/init.mjs +2 -2
- package/clients/cli/commands/integration-check.test.mjs +48 -0
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/manifest.mjs +67 -0
- package/clients/cli/commands/outside-project.test.mjs +101 -1
- package/clients/cli/commands/postinstall.doc.mjs +23 -0
- package/clients/cli/commands/postinstall.mjs +31 -0
- package/clients/cli/commands/setup-nudge.test.mjs +78 -0
- package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +4 -2
- package/clients/cli/commands/upgrade.mjs +9 -2
- package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
- package/clients/cli/index.mjs +54 -94
- package/clients/cli/lib/component-format.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +12 -7
- package/clients/cli/lib/exit-codes.test.mjs +5 -0
- package/clients/cli/lib/manifest.test.mjs +12 -0
- package/clients/cli/lib/welcome-box.mjs +50 -0
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +32 -7
- package/foundation/agent-docs/agent-doc-state.mjs +58 -9
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1017 -105
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +683 -39
- package/foundation/agent-docs/computer-agent-text.d.mts +39 -2
- package/foundation/agent-docs/computer-agent-text.mjs +135 -8
- package/foundation/agent-docs/computer-agent-text.test.mjs +99 -2
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- package/foundation/discovery/authoring-self-docs.d.mts +7 -0
- package/foundation/discovery/authoring-self-docs.mjs +16 -4
- package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
- package/foundation/discovery/authoring-surface.d.mts +1 -1
- package/foundation/discovery/authoring-surface.mjs +2 -2
- package/foundation/discovery/authoring-surface.test.mjs +1 -1
- package/foundation/discovery/cli-self-docs.d.mts +2 -1
- package/foundation/discovery/cli-self-docs.mjs +4 -3
- package/foundation/discovery/cli-self-docs.test.mjs +2 -2
- package/foundation/discovery/component-discovery.d.mts +37 -0
- package/foundation/discovery/component-discovery.mjs +80 -5
- package/foundation/discovery/component-loader.d.mts +1 -1
- package/foundation/discovery/docs-discovery.mjs +14 -1
- package/foundation/discovery/module-exports.d.mts +41 -0
- package/foundation/discovery/module-exports.mjs +253 -0
- package/foundation/discovery/runner-allowlist.d.mts +2 -4
- package/foundation/discovery/runner-allowlist.mjs +2 -4
- package/foundation/discovery/runner-commands.d.mts +77 -0
- package/foundation/discovery/runner-commands.mjs +294 -0
- package/foundation/discovery/runner-commands.test.mjs +128 -0
- package/foundation/discovery/stray-pointers.test.mjs +1 -1
- package/foundation/discovery/template-adapter.mjs +18 -1
- package/foundation/discovery/template-needs.mjs +1 -44
- package/foundation/discovery/theme-discovery.mjs +147 -3
- package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
- package/foundation/doc-compiler/tree.d.mts +32 -6
- package/foundation/doc-compiler/tree.mjs +77 -43
- package/foundation/doc-compiler/tree.test.mjs +7 -7
- package/foundation/fs/paths.d.mts +70 -8
- package/foundation/fs/paths.mjs +556 -38
- package/foundation/fs/paths.test.mjs +430 -54
- package/foundation/fs/read-record.d.mts +18 -0
- package/foundation/fs/read-record.mjs +131 -0
- package/foundation/fs/read-record.test.mjs +77 -0
- package/foundation/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- package/foundation/integrations/cli-peer-version.d.mts +36 -0
- package/foundation/integrations/cli-peer-version.mjs +132 -0
- package/foundation/integrations/cli-peer-version.test.mjs +354 -0
- package/foundation/integrations/computer-integrations.d.mts +6 -3
- package/foundation/integrations/computer-integrations.mjs +38 -9
- package/foundation/integrations/contribution-inventory.d.mts +15 -20
- package/foundation/integrations/contribution-inventory.mjs +41 -5
- package/foundation/integrations/exports-subpaths.d.mts +57 -0
- package/foundation/integrations/exports-subpaths.mjs +113 -0
- package/foundation/integrations/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/public-imports.d.mts +118 -0
- package/foundation/integrations/public-imports.mjs +1769 -0
- package/foundation/integrations/public-imports.test.mjs +1793 -0
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/cache.test.mjs +24 -21
- package/foundation/release/global-state.d.mts +8 -4
- package/foundation/release/global-state.mjs +4 -2
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- package/foundation/release/notices.d.mts +62 -8
- package/foundation/release/notices.mjs +136 -25
- package/foundation/release/notices.test.mjs +21 -0
- package/foundation/release/run-context.mjs +14 -0
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/route/route.d.mts +285 -0
- package/foundation/route/route.mjs +324 -0
- package/foundation/route/route.test.mjs +153 -0
- package/foundation/runtime/hosted-runtime.test.mjs +403 -0
- package/foundation/runtime/locked-versions.d.mts +32 -0
- package/foundation/runtime/locked-versions.mjs +65 -0
- package/foundation/runtime/package-root.d.mts +91 -0
- package/foundation/runtime/package-root.mjs +174 -0
- package/foundation/runtime/package-set.d.mts +62 -0
- package/foundation/runtime/package-set.mjs +130 -0
- package/foundation/runtime/package-set.test.mjs +432 -0
- package/foundation/text/string-utils.d.mts +2 -1
- package/foundation/text/string-utils.mjs +16 -5
- package/foundation/text/suggest-order.d.mts +72 -0
- package/foundation/text/suggest-order.mjs +363 -0
- package/foundation/text/suggest-order.test.mjs +259 -0
- package/host/door-conformance.test.mjs +274 -0
- package/host/index.d.ts +116 -0
- package/host/index.mjs +295 -0
- package/host/index.test.mjs +302 -0
- package/package.json +14 -9
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file build's block picker: ranks every block in the catalog for a request
|
|
5
|
+
* by meaning, from each block's own docs, and keeps build's own count.
|
|
6
|
+
*
|
|
7
|
+
* @input The request, build's signals for it (`kitSignals`: the blocks the kit
|
|
8
|
+
* names, search's block results with their scores and coverage, the block
|
|
9
|
+
* catalog and the components), and the picker weights
|
|
10
|
+
* (`blocks.weights.json` beside this file, read by the adapter): a
|
|
11
|
+
* vocabulary of stemmed words and word pairs with their idf (x1024), two
|
|
12
|
+
* signed byte matrices that map request words and doc words into one space, the
|
|
13
|
+
* lexical weight, the blocks the matrices were fitted with, and the ranking
|
|
14
|
+
* coefficients.
|
|
15
|
+
* @output `pickBlocks(...)`: block names, best first, as many as build's own
|
|
16
|
+
* list, and an entry (real score and reason) for each listed block search
|
|
17
|
+
* did not return.
|
|
18
|
+
* @position api/build/blocks; tree.mjs's main searcher calls it between
|
|
19
|
+
* kitSignals (kit/signals.mjs) and kitAnswer (kit/answer.mjs). Pure: no file,
|
|
20
|
+
* network, or environment access.
|
|
21
|
+
*
|
|
22
|
+
* How it scores. A block is described by its own docs only: its name, display
|
|
23
|
+
* name and description, and its component's name and keywords. The request
|
|
24
|
+
* and each block's docs become weighted word vectors (stemmed content words
|
|
25
|
+
* and adjacent pairs, (1 + ln count) x idf, unit length). Their match is the
|
|
26
|
+
* request's vector through the request matrix, against the docs' vector
|
|
27
|
+
* through the doc matrix, plus the lexical weight times their plain overlap
|
|
28
|
+
* (cosine), so a block the weights were never fitted with, such as a new one,
|
|
29
|
+
* is scored from its docs the same way. The candidates are the best matches,
|
|
30
|
+
* build's own blocks, search's first results, and the best plain overlaps.
|
|
31
|
+
* Each candidate's final score adds the ranking coefficients times: its match
|
|
32
|
+
* relative to the best, its place among the matches, its lead over the
|
|
33
|
+
* second-best match, whether the weights know it, its plain overlap,
|
|
34
|
+
* search's score and share of the request matched and place, whether build
|
|
35
|
+
* names it and where, how strongly the request names its component, and
|
|
36
|
+
* whether it is its component's general example. A block the request writes
|
|
37
|
+
* out by its exact name, and a block whose package is not Core's, keep the
|
|
38
|
+
* slot build gave them; the best of the others fill the remaining slots.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import {STOPWORDS} from '../../search/_query.mjs';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {object} BlockPickerWeights
|
|
45
|
+
* @property {string[]} vocab stemmed words and `a_b` word pairs
|
|
46
|
+
* @property {number[]} idf one per vocab entry, x1024
|
|
47
|
+
* @property {number} dims the width of the shared space
|
|
48
|
+
* @property {string} request base64 bytes, vocab.length x dims, row-major, each a signed weight offset by 128
|
|
49
|
+
* @property {number} requestScale
|
|
50
|
+
* @property {string} doc base64 bytes, vocab.length x dims, row-major, each a signed weight offset by 128
|
|
51
|
+
* @property {number} docScale
|
|
52
|
+
* @property {number} lexical the weight of the plain overlap
|
|
53
|
+
* @property {string[]} known the blocks the matrices were fitted with
|
|
54
|
+
* @property {Record<string, number>} theta the ranking coefficients
|
|
55
|
+
*
|
|
56
|
+
* @typedef {import('../kit/signals.mjs').BlockScore} BlockScore
|
|
57
|
+
* @typedef {{name: string, package?: string, displayName?: string, description?: string, command?: string}} CatalogBlock
|
|
58
|
+
* @typedef {{name: string, keywords?: string[]}} CatalogComponent
|
|
59
|
+
* @typedef {import('../../search/search.type.mjs').SearchResultEntry} SearchResultEntry
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generic English function words, search's stopwords, and words every build
|
|
64
|
+
* request or block shares: never a reason to tie a block to a request.
|
|
65
|
+
*/
|
|
66
|
+
const STOP = new Set([
|
|
67
|
+
...STOPWORDS,
|
|
68
|
+
...(
|
|
69
|
+
'about above across after again against all along already also although ' +
|
|
70
|
+
'always am among amongst an and another any anyone anything anywhere are ' +
|
|
71
|
+
'around as at be because been before behind being below beneath beside ' +
|
|
72
|
+
'besides between beyond both but by can could despite did do does doing ' +
|
|
73
|
+
'down during each either else enough even ever every everyone everything ' +
|
|
74
|
+
'everywhere except few following for from further had has have having he ' +
|
|
75
|
+
'her here hers herself him himself his how however if in inside instead ' +
|
|
76
|
+
'into is it its itself just least less little lot lots many may maybe me ' +
|
|
77
|
+
'might more most much must my myself near neither never no nobody none ' +
|
|
78
|
+
'nor not nothing now nowhere of off often on once one ones only onto or ' +
|
|
79
|
+
'other others ought our ours ourselves out outside over own past per ' +
|
|
80
|
+
'perhaps quite rather same several shall she should since so some someone ' +
|
|
81
|
+
'something sometimes somewhat somewhere still such than that the their ' +
|
|
82
|
+
'theirs them themselves then there these they this those though through ' +
|
|
83
|
+
'throughout thus till to together too toward towards under underneath ' +
|
|
84
|
+
'unless unlike until up upon usually very via was we were what whatever ' +
|
|
85
|
+
'when whenever where whereas whether which whichever while who whoever ' +
|
|
86
|
+
'whom whomever whose why will with within without would yet you your ' +
|
|
87
|
+
'yours yourself yourselves'
|
|
88
|
+
).split(' '),
|
|
89
|
+
...(
|
|
90
|
+
'a an the and or of for to in on with by at from as is are be this that ' +
|
|
91
|
+
'it its my our your their me we you i into via per each all any some page ' +
|
|
92
|
+
'app screen ui view build make create need want like show shows showing ' +
|
|
93
|
+
'using use one two new add list item items basic simple showcase example ' +
|
|
94
|
+
'default variant'
|
|
95
|
+
).split(' '),
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Words after which the rest of a request is where the new piece goes, not
|
|
100
|
+
* the piece itself ("a filter bar ABOVE the table").
|
|
101
|
+
*/
|
|
102
|
+
const CONTEXT = new Set(
|
|
103
|
+
(
|
|
104
|
+
'in on inside within into above below under beneath next beside to for ' +
|
|
105
|
+
'with and from of near over across along around after before at'
|
|
106
|
+
).split(' '),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
/** How many of each kind of candidate the picker weighs. */
|
|
110
|
+
const POOL = {matches: 60, searched: 60, overlaps: 20};
|
|
111
|
+
|
|
112
|
+
/** How many matches carry a place; the rest share the last one's match. */
|
|
113
|
+
const TOP = 80;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A word with a plural or verb ending removed (words over four letters):
|
|
117
|
+
* "-ies" becomes "-y"; "-es" goes only after s, x, z, ch, or sh; otherwise a
|
|
118
|
+
* final "s" goes, but never after another "s"; then "-ing" or "-ed".
|
|
119
|
+
* @param {string} w
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
export function stemOf(w) {
|
|
123
|
+
if (w.length <= 4) return w;
|
|
124
|
+
if (w.endsWith('ies')) return `${w.slice(0, -3)}y`;
|
|
125
|
+
if (/(?:s|x|z|ch|sh)es$/.test(w)) return w.slice(0, -2);
|
|
126
|
+
if (w.endsWith('s') && !w.endsWith('ss')) return w.slice(0, -1);
|
|
127
|
+
if (w.endsWith('ing')) return w.slice(0, -3);
|
|
128
|
+
if (w.endsWith('ed')) return w.slice(0, -2);
|
|
129
|
+
return w;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* An identifier's words, lowercase: "SideNavNestedItems" -> side, nav,
|
|
134
|
+
* nested, items.
|
|
135
|
+
* @param {string} text
|
|
136
|
+
* @returns {string[]}
|
|
137
|
+
*/
|
|
138
|
+
export function wordsOf(text) {
|
|
139
|
+
return String(text)
|
|
140
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
141
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.split(/[^a-z0-9]+/)
|
|
144
|
+
.filter(Boolean);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A text's stemmed content words, then its adjacent pairs as `a_b`.
|
|
149
|
+
* @param {string} text
|
|
150
|
+
* @returns {string[]}
|
|
151
|
+
*/
|
|
152
|
+
export function termsOf(text) {
|
|
153
|
+
const words = (
|
|
154
|
+
String(text)
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.match(/[a-z0-9]{2,}/g) ?? []
|
|
157
|
+
)
|
|
158
|
+
.filter(w => !STOP.has(w))
|
|
159
|
+
.map(stemOf);
|
|
160
|
+
const out = [...words];
|
|
161
|
+
for (let i = 0; i + 1 < words.length; i++)
|
|
162
|
+
out.push(`${words[i]}_${words[i + 1]}`);
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The weights, decoded once per weights object.
|
|
168
|
+
* @type {WeakMap<BlockPickerWeights, {index: Map<string, number>, idf: Float64Array, request: Int8Array, doc: Int8Array}>}
|
|
169
|
+
*/
|
|
170
|
+
const decoded = new WeakMap();
|
|
171
|
+
|
|
172
|
+
/** @param {BlockPickerWeights} weights */
|
|
173
|
+
function decode(weights) {
|
|
174
|
+
let d = decoded.get(weights);
|
|
175
|
+
if (!d) {
|
|
176
|
+
// Each byte is a signed weight stored offset by 128.
|
|
177
|
+
const bytes = (/** @type {string} */ s) =>
|
|
178
|
+
Int8Array.from(Buffer.from(s, 'base64'), b => b - 128);
|
|
179
|
+
d = {
|
|
180
|
+
index: new Map(weights.vocab.map((w, i) => [w, i])),
|
|
181
|
+
idf: Float64Array.from(weights.idf, x => x / 1024),
|
|
182
|
+
request: bytes(weights.request),
|
|
183
|
+
doc: bytes(weights.doc),
|
|
184
|
+
};
|
|
185
|
+
decoded.set(weights, d);
|
|
186
|
+
}
|
|
187
|
+
return d;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* A text's sparse unit vector over the vocabulary: (1 + ln count) x idf.
|
|
192
|
+
* @param {string[]} terms
|
|
193
|
+
* @param {ReturnType<typeof decode>} d
|
|
194
|
+
* @returns {Map<number, number>}
|
|
195
|
+
*/
|
|
196
|
+
function vectorOf(terms, d) {
|
|
197
|
+
/** @type {Map<number, number>} */
|
|
198
|
+
const counts = new Map();
|
|
199
|
+
for (const t of terms) {
|
|
200
|
+
const i = d.index.get(t);
|
|
201
|
+
if (i !== undefined) counts.set(i, (counts.get(i) ?? 0) + 1);
|
|
202
|
+
}
|
|
203
|
+
/** @type {Map<number, number>} */
|
|
204
|
+
const v = new Map();
|
|
205
|
+
let sq = 0;
|
|
206
|
+
for (const [i, c] of counts) {
|
|
207
|
+
const x = (1 + Math.log(c)) * d.idf[i];
|
|
208
|
+
v.set(i, x);
|
|
209
|
+
sq += x * x;
|
|
210
|
+
}
|
|
211
|
+
const norm = Math.sqrt(sq) || 1;
|
|
212
|
+
for (const [i, x] of v) v.set(i, x / norm);
|
|
213
|
+
return v;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* A sparse vector through a signed byte matrix.
|
|
218
|
+
* @param {Map<number, number>} v
|
|
219
|
+
* @param {Int8Array} m
|
|
220
|
+
* @param {number} scale
|
|
221
|
+
* @param {number} dims
|
|
222
|
+
*/
|
|
223
|
+
function project(v, m, scale, dims) {
|
|
224
|
+
const out = new Float64Array(dims);
|
|
225
|
+
for (const [i, x] of v) {
|
|
226
|
+
const row = i * dims;
|
|
227
|
+
for (let k = 0; k < dims; k++) out[k] += x * m[row + k];
|
|
228
|
+
}
|
|
229
|
+
for (let k = 0; k < dims; k++) out[k] *= scale;
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Each block's component: the longest component name its name starts with,
|
|
235
|
+
* else the display name's part before " — ", without spaces.
|
|
236
|
+
* @param {CatalogBlock} block
|
|
237
|
+
* @param {string[]} componentNames longest first
|
|
238
|
+
*/
|
|
239
|
+
function componentOf(block, componentNames) {
|
|
240
|
+
for (const c of componentNames) if (block.name.startsWith(c)) return c;
|
|
241
|
+
const display = block.displayName ?? block.name;
|
|
242
|
+
return display.split(' — ')[0].replace(/ /g, '');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The catalog, read once per weights and catalog contents: each block's
|
|
247
|
+
* component, docs vector and projection, and each component's name phrases.
|
|
248
|
+
* @type {WeakMap<BlockPickerWeights, {key: string, blocks: Map<string, {component: string, vector: Map<number, number>, projected: Float64Array}>, phrases: Map<string, [string[], number][]>}>}
|
|
249
|
+
*/
|
|
250
|
+
const catalogs = new WeakMap();
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {CatalogBlock[]} catalog
|
|
254
|
+
* @param {CatalogComponent[]} components
|
|
255
|
+
* @param {BlockPickerWeights} weights
|
|
256
|
+
*/
|
|
257
|
+
function readCatalog(catalog, components, weights) {
|
|
258
|
+
const key = JSON.stringify([
|
|
259
|
+
catalog.map(b => [b.name, b.displayName, b.description]),
|
|
260
|
+
components.map(c => [c.name, c.keywords]),
|
|
261
|
+
]);
|
|
262
|
+
const cached = catalogs.get(weights);
|
|
263
|
+
if (cached && cached.key === key) return cached;
|
|
264
|
+
const d = decode(weights);
|
|
265
|
+
const names = components.map(c => c.name).sort((a, b) => b.length - a.length);
|
|
266
|
+
const keywords = new Map(components.map(c => [c.name, c.keywords ?? []]));
|
|
267
|
+
/** @type {Map<string, {component: string, vector: Map<number, number>, projected: Float64Array}>} */
|
|
268
|
+
const blocks = new Map();
|
|
269
|
+
for (const b of catalog) {
|
|
270
|
+
if (blocks.has(b.name)) continue;
|
|
271
|
+
const component = componentOf(b, names);
|
|
272
|
+
const text = [
|
|
273
|
+
wordsOf(b.name).join(' '),
|
|
274
|
+
b.displayName ?? '',
|
|
275
|
+
b.description ?? '',
|
|
276
|
+
wordsOf(component).join(' '),
|
|
277
|
+
(keywords.get(component) ?? []).join(' '),
|
|
278
|
+
].join(' ');
|
|
279
|
+
const vector = vectorOf(termsOf(text), d);
|
|
280
|
+
blocks.set(b.name, {
|
|
281
|
+
component,
|
|
282
|
+
vector,
|
|
283
|
+
projected: project(vector, d.doc, weights.docScale, weights.dims),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// A component is named by its own words, or by a keyword at most three
|
|
287
|
+
// components share (weighted by how few share it).
|
|
288
|
+
/** @type {Map<string, number>} */
|
|
289
|
+
const shared = new Map();
|
|
290
|
+
for (const c of components)
|
|
291
|
+
for (const k of new Set((c.keywords ?? []).map(k => k.replace(/-/g, ' '))))
|
|
292
|
+
shared.set(k, (shared.get(k) ?? 0) + 1);
|
|
293
|
+
const phrase = (/** @type {string} */ s) =>
|
|
294
|
+
s.toLowerCase().split(/\s+/).filter(Boolean).map(stemOf);
|
|
295
|
+
/** @type {Map<string, [string[], number][]>} */
|
|
296
|
+
const phrases = new Map();
|
|
297
|
+
const withBlocks = new Set([...blocks.values()].map(b => b.component));
|
|
298
|
+
for (const c of withBlocks) {
|
|
299
|
+
/** @type {[string[], number][]} */
|
|
300
|
+
const list = [[phrase(wordsOf(c).join(' ')), 3]];
|
|
301
|
+
for (const k of (keywords.get(c) ?? []).map(k => k.replace(/-/g, ' ')))
|
|
302
|
+
if ((shared.get(k) ?? 0) <= 3)
|
|
303
|
+
list.push([phrase(k), 1 / (shared.get(k) ?? 1)]);
|
|
304
|
+
phrases.set(c, list);
|
|
305
|
+
}
|
|
306
|
+
const read = {key, blocks, phrases};
|
|
307
|
+
catalogs.set(weights, read);
|
|
308
|
+
return read;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* How strongly the request names each component: its best phrase, longer
|
|
313
|
+
* phrases and earlier ones stronger, and half as strong once the request has
|
|
314
|
+
* moved on to where the piece goes.
|
|
315
|
+
* @param {string} request
|
|
316
|
+
* @param {Map<string, [string[], number][]>} phrases
|
|
317
|
+
* @returns {Map<string, number>}
|
|
318
|
+
*/
|
|
319
|
+
function namedComponents(request, phrases) {
|
|
320
|
+
const raw =
|
|
321
|
+
String(request)
|
|
322
|
+
.toLowerCase()
|
|
323
|
+
.match(/[a-z0-9]+/g) ?? [];
|
|
324
|
+
const stems = raw.map(stemOf);
|
|
325
|
+
let context = raw.findIndex((w, k) => k > 0 && CONTEXT.has(w));
|
|
326
|
+
if (context < 0) context = raw.length;
|
|
327
|
+
/** @type {Map<string, number>} */
|
|
328
|
+
const out = new Map();
|
|
329
|
+
for (const [c, list] of phrases) {
|
|
330
|
+
let best = 0;
|
|
331
|
+
for (const [p, w] of list) {
|
|
332
|
+
if (p.length === 0) continue;
|
|
333
|
+
let at = -1;
|
|
334
|
+
for (let i = 0; i + p.length <= stems.length && at < 0; i++)
|
|
335
|
+
if (p.every((x, k) => stems[i + k] === x)) at = i;
|
|
336
|
+
if (at < 0) continue;
|
|
337
|
+
const s =
|
|
338
|
+
w *
|
|
339
|
+
(1 + 0.3 * (p.length - 1)) *
|
|
340
|
+
(at < context ? 1 : 0.5) *
|
|
341
|
+
(1 - 0.01 * at);
|
|
342
|
+
if (s > best) best = s;
|
|
343
|
+
}
|
|
344
|
+
if (best) out.set(c, best);
|
|
345
|
+
}
|
|
346
|
+
return out;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Whether the request writes out the block's exact name (its words joined).
|
|
351
|
+
* @param {string[]} words the request's identifier words
|
|
352
|
+
* @param {string} block
|
|
353
|
+
*/
|
|
354
|
+
function writesName(words, block) {
|
|
355
|
+
const name = block.toLowerCase();
|
|
356
|
+
const length = wordsOf(block).length;
|
|
357
|
+
for (let i = 0; i < words.length; i++) {
|
|
358
|
+
let run = '';
|
|
359
|
+
for (let n = 0; n < length && i + n < words.length; n++) {
|
|
360
|
+
run += words[i + n];
|
|
361
|
+
if (run === name) return true;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The blocks build names for a request, ranked by the picker.
|
|
369
|
+
* @param {string} request
|
|
370
|
+
* @param {{kitBlocks: string[], searched: BlockScore[], foreign: string[], catalog: CatalogBlock[], components: CatalogComponent[]}} signals
|
|
371
|
+
* @param {BlockPickerWeights} weights
|
|
372
|
+
* @returns {{names: string[], entries: Map<string, SearchResultEntry>}} as
|
|
373
|
+
* many names as build's own list, best first; an entry for each listed
|
|
374
|
+
* block search did not return
|
|
375
|
+
*/
|
|
376
|
+
export function pickBlocks(request, signals, weights) {
|
|
377
|
+
const own = signals.kitBlocks;
|
|
378
|
+
/** @type {Map<string, SearchResultEntry>} */
|
|
379
|
+
const entries = new Map();
|
|
380
|
+
if (own.length === 0) return {names: [], entries};
|
|
381
|
+
const d = decode(weights);
|
|
382
|
+
const cat = readCatalog(signals.catalog, signals.components, weights);
|
|
383
|
+
const q = vectorOf(termsOf(request), d);
|
|
384
|
+
const eq = project(q, d.request, weights.requestScale, weights.dims);
|
|
385
|
+
const dot = (/** @type {Map<number, number>} */ v) => {
|
|
386
|
+
let s = 0;
|
|
387
|
+
for (const [i, x] of q) {
|
|
388
|
+
const y = v.get(i);
|
|
389
|
+
if (y !== undefined) s += x * y;
|
|
390
|
+
}
|
|
391
|
+
return s;
|
|
392
|
+
};
|
|
393
|
+
/** @type {Map<string, {match: number, overlap: number}>} */
|
|
394
|
+
const scored = new Map();
|
|
395
|
+
for (const [name, b] of cat.blocks) {
|
|
396
|
+
let s = 0;
|
|
397
|
+
for (let k = 0; k < weights.dims; k++) s += eq[k] * b.projected[k];
|
|
398
|
+
const overlap = dot(b.vector);
|
|
399
|
+
scored.set(name, {match: s + weights.lexical * overlap, overlap});
|
|
400
|
+
}
|
|
401
|
+
const get = (/** @type {string} */ b) =>
|
|
402
|
+
scored.get(b) ?? {match: -Infinity, overlap: 0};
|
|
403
|
+
const ranked = [...scored.keys()].sort((a, b) => get(b).match - get(a).match);
|
|
404
|
+
const top = ranked.slice(0, TOP);
|
|
405
|
+
const best = top.length ? get(top[0]).match : 0;
|
|
406
|
+
const second = top.length > 1 ? get(top[1]).match : 0;
|
|
407
|
+
const floor = top.length ? get(top[top.length - 1]).match : 0;
|
|
408
|
+
const place = new Map(top.map((b, k) => [b, k]));
|
|
409
|
+
/** @type {Map<string, [BlockScore, number]>} */
|
|
410
|
+
const found = new Map();
|
|
411
|
+
signals.searched.forEach((s, k) => {
|
|
412
|
+
if (!found.has(s[0])) found.set(s[0], [s, k]);
|
|
413
|
+
});
|
|
414
|
+
const overlaps = [...scored.keys()]
|
|
415
|
+
.filter(b => get(b).overlap > 0)
|
|
416
|
+
.sort((a, b) => get(b).overlap - get(a).overlap || (a < b ? -1 : 1));
|
|
417
|
+
const pool = [
|
|
418
|
+
...new Set([
|
|
419
|
+
...own,
|
|
420
|
+
...top.slice(0, POOL.matches),
|
|
421
|
+
...signals.searched.slice(0, POOL.searched).map(s => s[0]),
|
|
422
|
+
...overlaps.slice(0, POOL.overlaps),
|
|
423
|
+
]),
|
|
424
|
+
].filter(b => scored.has(b) || found.has(b));
|
|
425
|
+
const known = new Set(weights.known);
|
|
426
|
+
const named = namedComponents(request, cat.phrases);
|
|
427
|
+
const t = weights.theta;
|
|
428
|
+
const final = pool.map((b, pos) => {
|
|
429
|
+
const m = place.has(b) ? get(b).match : floor;
|
|
430
|
+
const f = found.get(b);
|
|
431
|
+
const s = f?.[0];
|
|
432
|
+
const component = cat.blocks.get(b)?.component;
|
|
433
|
+
const score =
|
|
434
|
+
t.g * (m - best) +
|
|
435
|
+
t.grank * (place.get(b) ?? 99) +
|
|
436
|
+
t.gmargin * (m - second) +
|
|
437
|
+
t.seen * (known.has(b) ? 1 : 0) +
|
|
438
|
+
t.lex * get(b).overlap +
|
|
439
|
+
t.sscore * (s ? s[1] / 100 : 0) +
|
|
440
|
+
t.scov * (s ? (s[2] ?? 0) / Math.max(s[3] ?? 0, 1) : 0) +
|
|
441
|
+
t.srank * (f ? f[1] : 300) +
|
|
442
|
+
t.inkit * (own.includes(b) ? 1 : 0) +
|
|
443
|
+
t.kitpos * (own.includes(b) ? own.indexOf(b) : 9) +
|
|
444
|
+
t.mention * (component ? (named.get(component) ?? 0) : 0) +
|
|
445
|
+
t.show * (/Showcase|Basic/.test(b) ? 1 : 0);
|
|
446
|
+
return {b, pos, score};
|
|
447
|
+
});
|
|
448
|
+
final.sort((a, b) => b.score - a.score || a.pos - b.pos);
|
|
449
|
+
const foreign = new Set(signals.foreign);
|
|
450
|
+
const words = wordsOf(request);
|
|
451
|
+
const keeps = own.map(b => foreign.has(b) || writesName(words, b));
|
|
452
|
+
const kept = new Set(own.filter((_, k) => keeps[k]));
|
|
453
|
+
const order = [
|
|
454
|
+
...final.map(x => x.b).filter(b => !kept.has(b) && !foreign.has(b)),
|
|
455
|
+
...own.filter(b => !kept.has(b)),
|
|
456
|
+
];
|
|
457
|
+
/** @type {string[]} */
|
|
458
|
+
const out = [];
|
|
459
|
+
let next = 0;
|
|
460
|
+
for (let k = 0; k < own.length; k++) {
|
|
461
|
+
if (keeps[k]) {
|
|
462
|
+
out.push(own[k]);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
while (out.includes(order[next])) next++;
|
|
466
|
+
out.push(order[next++]);
|
|
467
|
+
}
|
|
468
|
+
// A listed block search did not return gets the picker's own entry: its
|
|
469
|
+
// score (100 for the best candidate, less by how far it trails, never
|
|
470
|
+
// below 1) and the request words that tie it to its docs.
|
|
471
|
+
const max = Math.max(...final.map(x => x.score));
|
|
472
|
+
const byName = new Map(signals.catalog.map(b => [b.name, b]));
|
|
473
|
+
for (const name of out) {
|
|
474
|
+
if (found.has(name) || own.includes(name)) continue;
|
|
475
|
+
const block = byName.get(name);
|
|
476
|
+
const x = final.find(f => f.b === name);
|
|
477
|
+
if (!block || !x) continue;
|
|
478
|
+
entries.set(name, {
|
|
479
|
+
domain: 'template',
|
|
480
|
+
name,
|
|
481
|
+
package: block.package ?? '',
|
|
482
|
+
score: Math.max(1, Math.round(100 * Math.exp(x.score - max))),
|
|
483
|
+
reason: reasonFor(request, cat.blocks.get(name), d, weights),
|
|
484
|
+
description: block.description ?? '',
|
|
485
|
+
displayName: block.displayName,
|
|
486
|
+
kind: 'block',
|
|
487
|
+
command: block.command ?? '',
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
return {names: out, entries};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Why a block fits: the request words that tie it to its docs most.
|
|
495
|
+
* @param {string} request
|
|
496
|
+
* @param {{vector: Map<number, number>, projected: Float64Array} | undefined} block
|
|
497
|
+
* @param {ReturnType<typeof decode>} d
|
|
498
|
+
* @param {BlockPickerWeights} weights
|
|
499
|
+
*/
|
|
500
|
+
function reasonFor(request, block, d, weights) {
|
|
501
|
+
if (!block) return 'fits by meaning';
|
|
502
|
+
const raw =
|
|
503
|
+
String(request)
|
|
504
|
+
.toLowerCase()
|
|
505
|
+
.match(/[a-z0-9]{2,}/g) ?? [];
|
|
506
|
+
/** @type {Map<string, string>} */
|
|
507
|
+
const wordOf = new Map();
|
|
508
|
+
for (const w of raw)
|
|
509
|
+
if (!STOP.has(w) && !wordOf.has(stemOf(w))) wordOf.set(stemOf(w), w);
|
|
510
|
+
const q = vectorOf(termsOf(request), d);
|
|
511
|
+
/** @type {[string, number][]} */
|
|
512
|
+
const parts = [];
|
|
513
|
+
for (const [i, x] of q) {
|
|
514
|
+
const term = weights.vocab[i];
|
|
515
|
+
if (term.includes('_')) continue;
|
|
516
|
+
let s = 0;
|
|
517
|
+
const row = i * weights.dims;
|
|
518
|
+
for (let k = 0; k < weights.dims; k++)
|
|
519
|
+
s += d.request[row + k] * block.projected[k];
|
|
520
|
+
s =
|
|
521
|
+
x * s * weights.requestScale +
|
|
522
|
+
weights.lexical * x * (block.vector.get(i) ?? 0);
|
|
523
|
+
if (s > 0) parts.push([wordOf.get(term) ?? term, s]);
|
|
524
|
+
}
|
|
525
|
+
parts.sort((a, b) => b[1] - a[1]);
|
|
526
|
+
const top = parts.slice(0, 3).map(p => p[0]);
|
|
527
|
+
return top.length ? `fits by meaning: ${top.join(', ')}` : 'fits by meaning';
|
|
528
|
+
}
|