@astryxdesign/cli 0.7.2 → 0.7.3-canary.06872b4
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 +99 -66
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +115 -3
- package/api/build/blocks/blocks.d.mts +42 -0
- package/api/build/blocks/blocks.mjs +169 -0
- package/api/build/blocks/blocks.test.mjs +108 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/kit/answer.d.mts +14 -0
- package/api/build/kit/answer.mjs +85 -0
- package/api/build/kit/answer.test.mjs +38 -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 +60 -0
- package/api/build/kit/signals.mjs +284 -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 +52 -0
- package/api/build/tree.test.mjs +177 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/component.mjs +16 -2
- package/api/component/component.test.mjs +15 -0
- package/api/discover/_adapter.mjs +36 -9
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/docs/_adapter.mjs +17 -5
- package/api/docs/docs.test.mjs +7 -1
- package/api/doctor/doctor.mjs +86 -12
- package/api/doctor/doctor.test.mjs +28 -3
- 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/authoring-checks.mjs +83 -9
- 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-gaps.test.mjs +1964 -0
- package/api/integration/validate-integration.d.mts +11 -2
- package/api/integration/validate-integration.mjs +226 -32
- package/api/integration/validate-integration.type.d.mts +58 -1
- package/api/integration/validate-integration.type.mjs +45 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +66 -2
- package/api/search/_adapter.mjs +118 -10
- 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 +52 -0
- package/api/search/_entries.mjs +158 -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 +7 -5
- package/api/search/search.mjs +53 -2483
- 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/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/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +16 -37
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- 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/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/commands/doctor.mjs +30 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- 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/outside-project.test.mjs +2 -1
- package/clients/cli/commands/setup-nudge.test.mjs +61 -0
- 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 +19 -1
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
- package/foundation/agent-docs/agent-doc-state.mjs +35 -8
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1010 -99
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +678 -35
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- 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/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 +8 -0
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- 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/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- 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/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- 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/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
package/api/build/build.mjs
CHANGED
|
@@ -4,20 +4,27 @@
|
|
|
4
4
|
* @file build API — dispatcher + barrel for the "assemble a page" assistant.
|
|
5
5
|
*
|
|
6
6
|
* `build()` with no query signals the workflow playbook (`build.help`). With a
|
|
7
|
-
* query it runs
|
|
8
|
-
* KIT (`build.kit`). Each shape is projected by its own leaf; this file only
|
|
7
|
+
* query it runs build's tree (tree.mjs), whose searchers answer with a
|
|
8
|
+
* composition KIT (`build.kit`). Each shape is projected by its own leaf; this file only
|
|
9
9
|
* routes to the leaves and re-exports them, keeping the SAME `build` export so
|
|
10
10
|
* api/index.mjs and the CLI consumer stay unchanged.
|
|
11
11
|
*
|
|
12
12
|
* no query → build.help (api/build/help/help.mjs)
|
|
13
|
-
* a query → build.kit (api/build/kit/
|
|
13
|
+
* a query → build.kit (api/build/tree.mjs: kit/signals.mjs, the block
|
|
14
|
+
* picker in blocks/blocks.mjs, then kit/answer.mjs)
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import {AstryxError} from '../error.mjs';
|
|
17
18
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
18
19
|
import {buildHelp} from './help/help.mjs';
|
|
19
20
|
import {buildKit} from './kit/kit.mjs';
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
loadBlockPicker,
|
|
23
|
+
loadTreeWeights,
|
|
24
|
+
withListingCache,
|
|
25
|
+
} from './_adapter.mjs';
|
|
26
|
+
import {runTree} from '../../foundation/route/route.mjs';
|
|
27
|
+
import {BUILD_TREE} from './tree.mjs';
|
|
21
28
|
|
|
22
29
|
// Barrel: surface the leaves alongside the dispatcher for the fractal shape.
|
|
23
30
|
export {buildHelp, buildKit};
|
|
@@ -34,7 +41,10 @@ export async function build(query, options = {}) {
|
|
|
34
41
|
if (!query || !String(query).trim()) {
|
|
35
42
|
return buildHelp();
|
|
36
43
|
}
|
|
37
|
-
if (
|
|
44
|
+
if (
|
|
45
|
+
options.limit != null &&
|
|
46
|
+
(!Number.isInteger(options.limit) || options.limit <= 0)
|
|
47
|
+
) {
|
|
38
48
|
throw new AstryxError(
|
|
39
49
|
`Invalid limit "${options.limit}". Must be a positive integer.`,
|
|
40
50
|
undefined,
|
|
@@ -43,5 +53,14 @@ export async function build(query, options = {}) {
|
|
|
43
53
|
}
|
|
44
54
|
// The kit resolves every candidate's source and docs by walking the same
|
|
45
55
|
// tree; read each directory once for this call.
|
|
46
|
-
return withListingCache(
|
|
56
|
+
return withListingCache(
|
|
57
|
+
async () =>
|
|
58
|
+
/** @type {import('./build.type.mjs').BuildKitResponse} */ (
|
|
59
|
+
await runTree(
|
|
60
|
+
BUILD_TREE,
|
|
61
|
+
{query, options},
|
|
62
|
+
{weights: loadTreeWeights(), blocks: loadBlockPicker()},
|
|
63
|
+
)
|
|
64
|
+
),
|
|
65
|
+
);
|
|
47
66
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The kit for a request. `blocks` re-orders the blocks build names itself:
|
|
6
|
+
* each keeps build's own entry (its score and reason), and a name build did
|
|
7
|
+
* not give is dropped, so the kit never lists more blocks than build's own.
|
|
8
|
+
* Blocks of the same name from two packages are both kept.
|
|
9
|
+
*
|
|
10
|
+
* @param {import('./signals.mjs').KitSignals} signals
|
|
11
|
+
* @param {string[]} [blocks] block names; build's own when omitted
|
|
12
|
+
* @returns {import('../build.type.mjs').BuildKitResponse}
|
|
13
|
+
*/
|
|
14
|
+
export function kitAnswer(signals: import("./signals.mjs").KitSignals, blocks?: string[]): import("../build.type.mjs").BuildKitResponse;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file The build.kit response, from a request's signals (signals.mjs) and the
|
|
5
|
+
* blocks to name. With build's own blocks it is the kit build has always
|
|
6
|
+
* given; another list changes only the blocks, never their count.
|
|
7
|
+
* @input {@link import('./signals.mjs').KitSignals} and block names
|
|
8
|
+
* @output the build.kit response
|
|
9
|
+
* @position api/build/kit, beside signals.mjs; the main searcher in tree.mjs
|
|
10
|
+
* calls it with the block picker's list.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {FOUNDATION, FRAME} from './signals.mjs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fewer offerable results than this and the kit says how to look further.
|
|
17
|
+
*
|
|
18
|
+
* Three is the point below which a kit stops being a starting point. An agent
|
|
19
|
+
* that reads a near-empty kit does not conclude "my wording was wrong" — it
|
|
20
|
+
* concludes the package has nothing and falls back on its own memory of what
|
|
21
|
+
* Astryx contains, which is exactly the failure `build` exists to prevent.
|
|
22
|
+
*/
|
|
23
|
+
const THIN_KIT = 3;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The kit for a request. `blocks` re-orders the blocks build names itself:
|
|
27
|
+
* each keeps build's own entry (its score and reason), and a name build did
|
|
28
|
+
* not give is dropped, so the kit never lists more blocks than build's own.
|
|
29
|
+
* Blocks of the same name from two packages are both kept.
|
|
30
|
+
*
|
|
31
|
+
* @param {import('./signals.mjs').KitSignals} signals
|
|
32
|
+
* @param {string[]} [blocks] block names; build's own when omitted
|
|
33
|
+
* @returns {import('../build.type.mjs').BuildKitResponse}
|
|
34
|
+
*/
|
|
35
|
+
export function kitAnswer(signals, blocks = signals.kitBlocks) {
|
|
36
|
+
// Entries by name, in build's order: two packages can ship a block with the
|
|
37
|
+
// same name, and each name in `blocks` takes the next entry of that name, so
|
|
38
|
+
// both stay listed.
|
|
39
|
+
/** @type {Map<string, import('../build.type.mjs').BuildKitResponse['data']['blocks']>} */
|
|
40
|
+
const own = new Map();
|
|
41
|
+
for (const b of signals.blocks)
|
|
42
|
+
own.set(b.name, [...(own.get(b.name) ?? []), b]);
|
|
43
|
+
const named = blocks.flatMap(name => {
|
|
44
|
+
const entry = own.get(name)?.shift();
|
|
45
|
+
return entry ? [entry] : [];
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// What to try when the kit comes back thin. Keyword search over a design
|
|
49
|
+
// system misses in a predictable way — the reader's words and the package's
|
|
50
|
+
// often do not overlap — so name the two commands that browse rather than
|
|
51
|
+
// search, and say plainly that this is not semantic matching.
|
|
52
|
+
//
|
|
53
|
+
// STRUCTURED, not prose: `commands` are bare subcommands, because the API
|
|
54
|
+
// cannot know how the caller invokes the CLI. Baking `astryx component
|
|
55
|
+
// --list` into the text hands a pnpm-workspace reader a command that does
|
|
56
|
+
// not resolve — the same defect `getCliInvocation` exists to prevent, and
|
|
57
|
+
// the renderer applies it. A JSON caller gets the parts, not a sentence.
|
|
58
|
+
const hint =
|
|
59
|
+
signals.pages.length + named.length + signals.domain.length < THIN_KIT
|
|
60
|
+
? {
|
|
61
|
+
reason:
|
|
62
|
+
'Few matches. This is keyword search, not semantic — try other wordings.',
|
|
63
|
+
commands: ['component --list', 'template --list'],
|
|
64
|
+
}
|
|
65
|
+
: undefined;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
type: 'build.kit',
|
|
69
|
+
data: {
|
|
70
|
+
query: signals.query,
|
|
71
|
+
// Distinguishes "search found nothing" from a weak-but-non-empty result
|
|
72
|
+
// set. Either way the kit still names a template to start from.
|
|
73
|
+
hasResults: signals.matchCount > 0,
|
|
74
|
+
matchCount: signals.matchCount,
|
|
75
|
+
directMatch: signals.directMatch,
|
|
76
|
+
start: signals.start,
|
|
77
|
+
pages: signals.pages,
|
|
78
|
+
blocks: named,
|
|
79
|
+
domain: signals.domain,
|
|
80
|
+
frame: FRAME,
|
|
81
|
+
foundation: FOUNDATION,
|
|
82
|
+
hint,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect} from 'vitest';
|
|
4
|
+
import {kitAnswer} from './answer.mjs';
|
|
5
|
+
|
|
6
|
+
/** @param {any[]} blocks */
|
|
7
|
+
const signals = blocks =>
|
|
8
|
+
/** @type {any} */ ({
|
|
9
|
+
query: 'q',
|
|
10
|
+
matchCount: blocks.length,
|
|
11
|
+
directMatch: false,
|
|
12
|
+
start: null,
|
|
13
|
+
pages: [],
|
|
14
|
+
blocks,
|
|
15
|
+
kitBlocks: blocks.map(b => b.name),
|
|
16
|
+
domain: [],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('kitAnswer', () => {
|
|
20
|
+
const a = {name: 'Hero', package: '@a/one', score: 90, reason: 'name'};
|
|
21
|
+
const b = {name: 'Hero', package: '@b/two', score: 80, reason: 'name'};
|
|
22
|
+
const c = {name: 'Footer', package: '@a/one', score: 70, reason: 'keyword'};
|
|
23
|
+
|
|
24
|
+
it('keeps blocks of the same name from two packages', () => {
|
|
25
|
+
const kit = kitAnswer(signals([a, b, c]));
|
|
26
|
+
expect(kit.data.blocks).toEqual([a, b, c]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('keeps both when the picker re-orders them', () => {
|
|
30
|
+
const kit = kitAnswer(signals([a, b, c]), ['Footer', 'Hero', 'Hero']);
|
|
31
|
+
expect(kit.data.blocks).toEqual([c, a, b]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('never lists a block build did not give, or more than it gave', () => {
|
|
35
|
+
const kit = kitAnswer(signals([a, c]), ['Hero', 'Hero', 'Nav', 'Footer']);
|
|
36
|
+
expect(kit.data.blocks).toEqual([a, c]);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
|
+
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What an idea asks for (spec:AST-048/FR3): a whole `page`; a `part` of one,
|
|
6
|
+
* when its head noun names one of the system's components and it lists fewer
|
|
7
|
+
* than PAGE_PIECES pieces; or an `edit` of a page the builder already has.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} query
|
|
10
|
+
* @param {PageTemplate[]} pages
|
|
11
|
+
* @param {ComponentWords[]} components
|
|
12
|
+
* @returns {IdeaKind}
|
|
13
|
+
*/
|
|
14
|
+
export function ideaKind(query: string, pages: PageTemplate[], components: ComponentWords[]): IdeaKind;
|
|
15
|
+
/**
|
|
16
|
+
* Whether an idea asks for a new page (see `asksNewPage`), from the idea and
|
|
17
|
+
* the project's page templates.
|
|
18
|
+
* @param {string} query
|
|
19
|
+
* @param {PageTemplate[]} pages
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
export function asksForNewPage(query: string, pages: PageTemplate[]): boolean;
|
|
23
|
+
export type PageTemplate = import("./words.mjs").PageTemplate;
|
|
24
|
+
export type ComponentWords = import("./words.mjs").ComponentWords;
|
|
25
|
+
export type IdeaKind = import("./words.mjs").IdeaKind;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Whether a build idea asks for a page, a part of a page, or a change to
|
|
5
|
+
* a page the builder already has.
|
|
6
|
+
*
|
|
7
|
+
* Parts (spec:AST-048/FR3). "A date range picker" asks for a part of a page,
|
|
8
|
+
* not a page, and the system's own components say what a part is: an idea
|
|
9
|
+
* whose head noun is a word of a component's name or keywords, Core's or an
|
|
10
|
+
* integration's, asks for that part, unless the noun is a family word ("a data
|
|
11
|
+
* table") or the idea lists a page's worth of pieces. Words search drops as
|
|
12
|
+
* stopwords ("page", "screen", "app", "view") name no component, so "a calendar
|
|
13
|
+
* page" asks for a page and "a calendar" for a part. A part starts from the
|
|
14
|
+
* base template of the family the idea names ("an empty state for a settings
|
|
15
|
+
* page"), else from the app shell. An idea that changes a page or part the
|
|
16
|
+
* builder already has starts from the app shell too: that page is the
|
|
17
|
+
* builder's to keep (FR9), and no template scaffolds it. "Existing" marks one
|
|
18
|
+
* when its phrase names a page family or a component ("add a column to the
|
|
19
|
+
* existing table"), unless the idea asks for a new page ("a new dashboard like
|
|
20
|
+
* the existing dashboard", "clone the existing dashboard as a new page"). A
|
|
21
|
+
* bare "existing" phrase that names a page ("existing reports dashboard with a
|
|
22
|
+
* date filter") reads as the builder's own page.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {stem} from '../../search/search.mjs';
|
|
26
|
+
import {
|
|
27
|
+
HEAD_END,
|
|
28
|
+
CONTAINER_PHRASE,
|
|
29
|
+
PHRASE_END,
|
|
30
|
+
terms,
|
|
31
|
+
containersOf,
|
|
32
|
+
familyWordsOf,
|
|
33
|
+
} from './words.mjs';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {import('./words.mjs').PageTemplate} PageTemplate
|
|
37
|
+
* @typedef {import('./words.mjs').ComponentWords} ComponentWords
|
|
38
|
+
* @typedef {import('./words.mjs').IdeaKind} IdeaKind
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* How many listed pieces make an idea a page: a part is one thing with a detail
|
|
43
|
+
* or two ("a status pill with a tooltip"), while three pieces, two commas
|
|
44
|
+
* apart, describe a page.
|
|
45
|
+
*/
|
|
46
|
+
const PAGE_PIECES = 3;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The word that marks a page or part the builder already has, when its phrase
|
|
50
|
+
* names one (see `changesExistingPage`).
|
|
51
|
+
*/
|
|
52
|
+
const EXISTING = /\bexisting\b/i;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The word that marks an idea asking for a new page ("a new dashboard", "as a
|
|
56
|
+
* new page"), which is never a change to one the builder has.
|
|
57
|
+
*/
|
|
58
|
+
const NEW = /\bnew\b/;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* What an idea asks for (spec:AST-048/FR3): a whole `page`; a `part` of one,
|
|
62
|
+
* when its head noun names one of the system's components and it lists fewer
|
|
63
|
+
* than PAGE_PIECES pieces; or an `edit` of a page the builder already has.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} query
|
|
66
|
+
* @param {PageTemplate[]} pages
|
|
67
|
+
* @param {ComponentWords[]} components
|
|
68
|
+
* @returns {IdeaKind}
|
|
69
|
+
*/
|
|
70
|
+
export function ideaKind(query, pages, components) {
|
|
71
|
+
const text = String(query);
|
|
72
|
+
const familyWords = familyWordsOf(pages);
|
|
73
|
+
if (changesExistingPage(text, familyWords, components)) return 'edit';
|
|
74
|
+
// The head's last word, page words included: "calendar" is the noun of "a
|
|
75
|
+
// calendar", "page" the noun of "a calendar page". An overlay frame names
|
|
76
|
+
// where the thing lives, not the thing: "drafts" is the noun of "drafts in
|
|
77
|
+
// a modal".
|
|
78
|
+
const head = (text.split(HEAD_END)[0] ?? '').replace(CONTAINER_PHRASE, m =>
|
|
79
|
+
containersOf(m).size > 0 ? ' ' : m,
|
|
80
|
+
);
|
|
81
|
+
const words = (head.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter(
|
|
82
|
+
w => w.length >= 2,
|
|
83
|
+
);
|
|
84
|
+
const noun = stem(words[words.length - 1] ?? '');
|
|
85
|
+
if (familyWords.has(noun)) return 'page';
|
|
86
|
+
const pieces = (text.match(/,/g) ?? []).length + 1;
|
|
87
|
+
if (pieces >= PAGE_PIECES) return 'page';
|
|
88
|
+
return components.some(c => componentTerms(c).includes(noun))
|
|
89
|
+
? 'part'
|
|
90
|
+
: 'page';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether an idea changes a page or part the builder already has: "existing"
|
|
95
|
+
* in the same phrase as a word that names a family of page templates or one of
|
|
96
|
+
* the system's components ("the existing incidents table", "the existing
|
|
97
|
+
* banner"). A new page "inspired by the existing one", or "existing users" on a
|
|
98
|
+
* new page, names neither, and an idea that asks for a new page is no change
|
|
99
|
+
* whatever it names.
|
|
100
|
+
* @param {string} text
|
|
101
|
+
* @param {Map<string, string>} familyWords
|
|
102
|
+
* @param {ComponentWords[]} components
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
function changesExistingPage(text, familyWords, components) {
|
|
106
|
+
const componentWords = new Set(components.flatMap(nameTerms));
|
|
107
|
+
const phrases = text.toLowerCase().split(PHRASE_END);
|
|
108
|
+
if (asksNewPage(phrases, familyWords)) return false;
|
|
109
|
+
return phrases.some(phrase => {
|
|
110
|
+
const at = phrase.search(EXISTING);
|
|
111
|
+
return (
|
|
112
|
+
at >= 0 &&
|
|
113
|
+
terms(phrase.slice(at)).some(
|
|
114
|
+
t => familyWords.has(t) || componentWords.has(t),
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether an idea asks for a new page: "new" before a word that names a family
|
|
122
|
+
* of page templates ("a new dashboard"), or before only words search drops,
|
|
123
|
+
* such as "page" ("as a new page"). "A new column" asks for a part.
|
|
124
|
+
* @param {string[]} phrases
|
|
125
|
+
* @param {Map<string, string>} familyWords
|
|
126
|
+
* @returns {boolean}
|
|
127
|
+
*/
|
|
128
|
+
function asksNewPage(phrases, familyWords) {
|
|
129
|
+
return phrases.some(phrase => {
|
|
130
|
+
const at = phrase.search(NEW);
|
|
131
|
+
if (at < 0) return false;
|
|
132
|
+
const after = terms(phrase.slice(at + 'new'.length));
|
|
133
|
+
return after.length === 0 || familyWords.has(after[0]);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Whether an idea asks for a new page (see `asksNewPage`), from the idea and
|
|
139
|
+
* the project's page templates.
|
|
140
|
+
* @param {string} query
|
|
141
|
+
* @param {PageTemplate[]} pages
|
|
142
|
+
* @returns {boolean}
|
|
143
|
+
*/
|
|
144
|
+
export function asksForNewPage(query, pages) {
|
|
145
|
+
return asksNewPage(
|
|
146
|
+
String(query).toLowerCase().split(PHRASE_END),
|
|
147
|
+
familyWordsOf(pages),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The words of a component's name: "DateRangeInput" is date, range, input.
|
|
153
|
+
* @param {ComponentWords} component
|
|
154
|
+
* @returns {string[]}
|
|
155
|
+
*/
|
|
156
|
+
function nameTerms(component) {
|
|
157
|
+
return terms(component.name.replace(/([a-z0-9])([A-Z])/g, '$1 $2'));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The terms a component answers to: the words of its name and of its keywords.
|
|
162
|
+
* @param {ComponentWords} component
|
|
163
|
+
* @returns {string[]}
|
|
164
|
+
*/
|
|
165
|
+
function componentTerms(component) {
|
|
166
|
+
return [
|
|
167
|
+
...nameTerms(component),
|
|
168
|
+
...component.keywords.flatMap(keyword => terms(keyword)),
|
|
169
|
+
];
|
|
170
|
+
}
|
package/api/build/kit/kit.d.mts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* The page template to start from, and the kit around it
|
|
5
|
+
* The page template to start from, and the kit around it, with the blocks
|
|
6
|
+
* build names itself.
|
|
6
7
|
*
|
|
7
8
|
* @param {string} query what you're building (e.g. "analytics dashboard")
|
|
8
9
|
* @param {{cwd?: string, type?: import('../../search/search.type.mjs').SearchDomain, limit?: number}} [options]
|
|
@@ -13,6 +14,3 @@ export function buildKit(query: string, options?: {
|
|
|
13
14
|
type?: import("../../search/search.type.mjs").SearchDomain;
|
|
14
15
|
limit?: number;
|
|
15
16
|
}): Promise<import("../build.type.mjs").BuildKitResponse>;
|
|
16
|
-
export type SearchResultEntry = import("../../search/search.type.mjs").SearchResultEntry;
|
|
17
|
-
export type BuildStart = import("../build.type.mjs").BuildStart;
|
|
18
|
-
export type PageTemplate = import("../_adapter.mjs").PageTemplate;
|