@omgbase/core 0.15.0 → 0.21.0
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/dist/src/core/ingest.d.ts +2 -0
- package/dist/src/core/ingest.js +13 -0
- package/dist/src/core/read/document.d.ts +9 -8
- package/dist/src/core/read/document.js +0 -2
- package/dist/src/core/store/gc.d.ts +13 -1
- package/dist/src/core/store/gc.js +24 -5
- package/dist/src/core/store/size.d.ts +79 -0
- package/dist/src/core/store/size.js +232 -0
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +5 -1
- package/dist/src/mcp/arguments.d.ts +10 -2
- package/dist/src/mcp/arguments.js +37 -3
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/reference.d.ts +1 -1
- package/dist/src/mcp/reference.js +63 -10
- package/dist/src/mcp/server.d.ts +34 -1
- package/dist/src/mcp/server.js +158 -46
- package/dist/src/mutate/canonical.d.ts +27 -0
- package/dist/src/mutate/canonical.js +138 -0
- package/dist/src/mutate/effects.d.ts +58 -8
- package/dist/src/mutate/effects.js +312 -43
- package/dist/src/mutate/known-ids.d.ts +19 -0
- package/dist/src/mutate/known-ids.js +47 -7
- package/dist/src/mutate/ops.d.ts +1 -0
- package/dist/src/mutate/ops.js +20 -1
- package/dist/src/mutate/plan.d.ts +26 -16
- package/dist/src/mutate/plan.js +152 -112
- package/dist/src/mutate/select.d.ts +78 -0
- package/dist/src/mutate/select.js +903 -0
- package/dist/src/mutate/tree.d.ts +2 -2
- package/dist/src/oqx-js/context.d.ts +9 -2
- package/dist/src/oqx-js/context.js +74 -28
- package/dist/src/oqx-js/names.d.ts +40 -0
- package/dist/src/oqx-js/names.js +347 -0
- package/dist/src/oqx-js/planner.d.ts +3 -3
- package/dist/src/oqx-js/planner.js +17 -9
- package/dist/src/oqx-js/root-row.d.ts +15 -4
- package/dist/src/oqx-js/root-row.js +18 -10
- package/dist/src/oqx-js/run.d.ts +45 -1
- package/dist/src/oqx-js/run.js +114 -32
- package/dist/src/oqx-js/sql/translate.js +1 -1
- package/dist/src/spec-versions.d.ts +5 -5
- package/dist/src/spec-versions.js +4 -4
- package/dist/src/sync/observe.js +9 -3
- package/dist/src/version.d.ts +7 -2
- package/dist/src/version.js +5 -2
- package/package.json +2 -2
|
@@ -15,9 +15,9 @@ export declare function rootTarget(source: Expr): Target | null;
|
|
|
15
15
|
* • a bare reserved docs basename (`id path updated_at hash raw`): as
|
|
16
16
|
* an `ident` at the ROOT scope when the target is `docs`, or as `doc.<name>`
|
|
17
17
|
* on any target at any depth (the reach-through row is a doc)
|
|
18
|
-
* • a bare
|
|
19
|
-
* property: as an `ident` at the ROOT scope when it is not a relation
|
|
20
|
-
* the target's rows (`blocks` on blocks, `nodes` on nodes, any on links),
|
|
18
|
+
* • a bare root name (`docs blocks nodes links revisions`, root-row.ts) read
|
|
19
|
+
* as a property: as an `ident` at the ROOT scope when it is not a relation
|
|
20
|
+
* of the target's rows (`blocks` on blocks, `nodes` on nodes, any on links),
|
|
21
21
|
* as an `ident` at any depth below (the rows there are unknown), or as
|
|
22
22
|
* `<head>.<name>` unless the head is a reach-through whose row has that
|
|
23
23
|
* relation (`doc.nodes`, `doc.blocks`, `block.nodes`, `section.blocks`)
|
|
@@ -14,15 +14,16 @@
|
|
|
14
14
|
// `[]` planned, `filter_invalid` in memory), so when any residual conjunct could
|
|
15
15
|
// raise — a function or method call, a `single` block, a `^`-escaped name, or a
|
|
16
16
|
// bare reserved docs basename — the WHOLE query runs unplanned.
|
|
17
|
-
import { ROWS_ROOT, partitionPushable, residualQuery } from "@omgbase/oqx";
|
|
17
|
+
import { ROWS_ROOT, isRegexOp, partitionPushable, residualQuery } from "@omgbase/oqx";
|
|
18
18
|
import { makeStoreContext, tagRows } from "./context.js";
|
|
19
19
|
import { translatePredicate } from "./sql/translate.js";
|
|
20
20
|
import { ALIAS, COLS, FROM, ORDER, guards } from "./sql/scan.js";
|
|
21
|
-
import { REACH_THROUGH, TARGETS, isTargetRelationOf } from "./root-row.js";
|
|
21
|
+
import { REACH_THROUGH, ROOTS, TARGETS, isTargetRelationOf } from "./root-row.js";
|
|
22
22
|
// The root collection a query scans, if it is a bare `docs|blocks|nodes|links`
|
|
23
23
|
// source at the root scope (else null — not a pushable shape; a nested `^docs`
|
|
24
|
-
// is a block's receiver, never the query's source).
|
|
25
|
-
//
|
|
24
|
+
// is a block's receiver, never the query's source). `revisions` (4.7) is a root
|
|
25
|
+
// the planner never plans — it is not a `Target` here — so a revisions scan
|
|
26
|
+
// always runs in memory.
|
|
26
27
|
export function rootTarget(source) {
|
|
27
28
|
if (source.kind === "ident" && TARGETS.has(source.name))
|
|
28
29
|
return source.name;
|
|
@@ -41,9 +42,9 @@ export function rootTarget(source) {
|
|
|
41
42
|
* • a bare reserved docs basename (`id path updated_at hash raw`): as
|
|
42
43
|
* an `ident` at the ROOT scope when the target is `docs`, or as `doc.<name>`
|
|
43
44
|
* on any target at any depth (the reach-through row is a doc)
|
|
44
|
-
* • a bare
|
|
45
|
-
* property: as an `ident` at the ROOT scope when it is not a relation
|
|
46
|
-
* the target's rows (`blocks` on blocks, `nodes` on nodes, any on links),
|
|
45
|
+
* • a bare root name (`docs blocks nodes links revisions`, root-row.ts) read
|
|
46
|
+
* as a property: as an `ident` at the ROOT scope when it is not a relation
|
|
47
|
+
* of the target's rows (`blocks` on blocks, `nodes` on nodes, any on links),
|
|
47
48
|
* as an `ident` at any depth below (the rows there are unknown), or as
|
|
48
49
|
* `<head>.<name>` unless the head is a reach-through whose row has that
|
|
49
50
|
* relation (`doc.nodes`, `doc.blocks`, `block.nodes`, `section.blocks`)
|
|
@@ -107,9 +108,9 @@ function exprMayRaise(e, target, root) {
|
|
|
107
108
|
case "call": return true;
|
|
108
109
|
case "outer": return true;
|
|
109
110
|
case "ident":
|
|
110
|
-
return
|
|
111
|
+
return ROOTS.has(e.name) && (!root || !isTargetRelationOf(target, e.name));
|
|
111
112
|
case "member":
|
|
112
|
-
if (
|
|
113
|
+
if (ROOTS.has(e.name) && !(e.recv.kind === "ident" && isTargetRelationOf(REACH_THROUGH[e.recv.name], e.name)))
|
|
113
114
|
return true;
|
|
114
115
|
return exprMayRaise(e.recv, target, root);
|
|
115
116
|
case "unary": return exprMayRaise(e.expr, target, root);
|
|
@@ -117,7 +118,10 @@ function exprMayRaise(e, target, root) {
|
|
|
117
118
|
// read anything its block reads.
|
|
118
119
|
case "required": return true;
|
|
119
120
|
case "op": return opMayRaise(e, target, root);
|
|
121
|
+
// A regex operator (oqx 0.22) is a `matches` call: an invalid pattern
|
|
122
|
+
// raises, exactly as the call would.
|
|
120
123
|
case "binary":
|
|
124
|
+
return isRegexOp(e.op) || exprMayRaise(e.left, target, root) || exprMayRaise(e.right, target, root);
|
|
121
125
|
case "logical":
|
|
122
126
|
case "in":
|
|
123
127
|
return exprMayRaise(e.left, target, root) || exprMayRaise(e.right, target, root);
|
|
@@ -125,6 +129,10 @@ function exprMayRaise(e, target, root) {
|
|
|
125
129
|
return (e.lo !== null && exprMayRaise(e.lo, target, root)) || (e.hi !== null && exprMayRaise(e.hi, target, root));
|
|
126
130
|
// A tuple (oqx 0.20) is never pushed down; it raises iff an element does.
|
|
127
131
|
case "tuple": return e.items.some((x) => exprMayRaise(x, target, root));
|
|
132
|
+
// A template literal (oqx 0.21) is never pushed down; interpolating a list or
|
|
133
|
+
// a record is an eval error only the data decides, so with any interpolation
|
|
134
|
+
// it may raise.
|
|
135
|
+
case "template": return e.parts.some((p) => typeof p !== "string");
|
|
128
136
|
case "lit":
|
|
129
137
|
case "binding": return false;
|
|
130
138
|
}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
export type Target = "docs" | "blocks" | "nodes" | "links";
|
|
2
|
+
/** Every root collection (spec/surface §1.1): the four row kinds the
|
|
3
|
+
* unknown-names pass judges (`TARGETS`, names.ts) plus `revisions` (4.7), the
|
|
4
|
+
* history target — one row per revision of the repository's documents. */
|
|
5
|
+
export type Root = Target | "revisions";
|
|
6
|
+
/** The four row kinds whose names the unknown-names pass judges (names.ts keys
|
|
7
|
+
* its vocabulary tables by them); `revisions` joins when that pass learns the
|
|
8
|
+
* kind. Every OTHER root-collection test uses `ROOTS`. */
|
|
2
9
|
export declare const TARGETS: ReadonlySet<string>;
|
|
3
|
-
/** The
|
|
4
|
-
export declare const
|
|
10
|
+
/** The root collections, in the root object's entry order (§1.1). */
|
|
11
|
+
export declare const ROOT_NAMES: readonly Root[];
|
|
12
|
+
export declare const ROOTS: ReadonlySet<string>;
|
|
13
|
+
/** The root names that are a relation of each kind's rows (§1.2): `revisions`
|
|
14
|
+
* on a document is its revision list (4.7). */
|
|
15
|
+
export declare const TARGET_RELATIONS: Readonly<Record<Root, ReadonlySet<string>>>;
|
|
5
16
|
/** The reach-through heads and the target of the row they reach (`doc.x`, `block.x`, `section.x`, and a link's `dst.x`). */
|
|
6
17
|
export declare const REACH_THROUGH: Readonly<Record<string, Target>>;
|
|
7
18
|
export declare const REPO_REMOVED_MESSAGE = "`$repo` was removed in surface 2.0 \u2014 reach the repository's collections through the root row: `^docs` from a top-level row (one caret per enclosing block, or the absolute `0^docs`), and the repository id as `^id` / `0^id`";
|
|
@@ -19,10 +30,10 @@ export declare function bareTargetMessage(name: string, depth: number | null): s
|
|
|
19
30
|
export declare function pastRootMessage(name: string, levels: number): string;
|
|
20
31
|
/**
|
|
21
32
|
* The unknown-target error (3.0 name pass): a top-level `from <name>` whose name
|
|
22
|
-
* is not one of the
|
|
33
|
+
* is not one of the five collections would read an absent root and return an
|
|
23
34
|
* empty result — refused instead, naming the collections; `edges`, the links
|
|
24
35
|
* target's name before 3.0, gets its new name spelled out.
|
|
25
36
|
*/
|
|
26
37
|
export declare function unknownTargetMessage(name: string): string;
|
|
27
38
|
/** Whether `name`, read as `<head>.<name>` or bare on a row of `target`, is one of that row's relations named after a target. */
|
|
28
|
-
export declare function isTargetRelationOf(target:
|
|
39
|
+
export declare function isTargetRelationOf(target: Root | undefined, name: string): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The root row (spec/surface §1.1, 2.0; spec/oqx 0.18). The repository is OQX's
|
|
2
2
|
// root row: from a top-level row `^docs` reaches the documents (one caret per
|
|
3
3
|
// enclosing block, or the absolute `0^docs`), `^id` the repository id, and the
|
|
4
|
-
// root object (`^$it`) exposes the
|
|
4
|
+
// root object (`^$it`) exposes the five collections (`revisions` since 4.7). Two spellings are refused
|
|
5
5
|
// as `filter_invalid`, with the messages below, in both engines:
|
|
6
6
|
//
|
|
7
7
|
// • `$repo` (removed in 2.0) anywhere — bare, as a member head (`$repo.docs`),
|
|
@@ -11,22 +11,30 @@
|
|
|
11
11
|
// • a bare target name at scope depth ≥ 1 (inside a block, or in a top-level
|
|
12
12
|
// row's clauses), where a bare name reads a property of the current row,
|
|
13
13
|
// which has none: `docs` and `links` are no row's relation, so they are
|
|
14
|
-
// refused statically by the same walk; `blocks` and `
|
|
15
|
-
// of some rows (`docs.blocks`, `docs.nodes`, `blocks.nodes`,
|
|
16
|
-
// `section.blocks`), so the context raises the same error
|
|
17
|
-
// row that lacks the relation (context.ts). A frontmatter key named after a
|
|
14
|
+
// refused statically by the same walk; `blocks`, `nodes` and `revisions`
|
|
15
|
+
// ARE relations of some rows (`docs.blocks`, `docs.nodes`, `blocks.nodes`,
|
|
16
|
+
// `section.blocks`, `docs.revisions`), so the context raises the same error
|
|
17
|
+
// at read time on a row that lacks the relation (context.ts). A frontmatter key named after a
|
|
18
18
|
// target is unreachable by bare name (use `frontmatter.<k>`).
|
|
19
|
+
/** The four row kinds whose names the unknown-names pass judges (names.ts keys
|
|
20
|
+
* its vocabulary tables by them); `revisions` joins when that pass learns the
|
|
21
|
+
* kind. Every OTHER root-collection test uses `ROOTS`. */
|
|
19
22
|
export const TARGETS = new Set(["docs", "blocks", "nodes", "links"]);
|
|
20
|
-
/** The
|
|
23
|
+
/** The root collections, in the root object's entry order (§1.1). */
|
|
24
|
+
export const ROOT_NAMES = ["docs", "blocks", "nodes", "links", "revisions"];
|
|
25
|
+
export const ROOTS = new Set(ROOT_NAMES);
|
|
26
|
+
/** The root names that are a relation of each kind's rows (§1.2): `revisions`
|
|
27
|
+
* on a document is its revision list (4.7). */
|
|
21
28
|
export const TARGET_RELATIONS = {
|
|
22
|
-
docs: new Set(["nodes", "blocks"]),
|
|
29
|
+
docs: new Set(["nodes", "blocks", "revisions"]),
|
|
23
30
|
blocks: new Set(["nodes"]),
|
|
24
31
|
nodes: new Set(["blocks"]),
|
|
25
32
|
links: new Set(),
|
|
33
|
+
revisions: new Set(),
|
|
26
34
|
};
|
|
27
35
|
/** The reach-through heads and the target of the row they reach (`doc.x`, `block.x`, `section.x`, and a link's `dst.x`). */
|
|
28
36
|
export const REACH_THROUGH = { doc: "docs", block: "blocks", section: "nodes", dst: "docs" };
|
|
29
|
-
const NOUN = { docs: "documents", blocks: "blocks", nodes: "nodes", links: "links" };
|
|
37
|
+
const NOUN = { docs: "documents", blocks: "blocks", nodes: "nodes", links: "links", revisions: "revisions" };
|
|
30
38
|
export const REPO_REMOVED_MESSAGE = "`$repo` was removed in surface 2.0 — reach the repository's collections through the root row: `^docs` from a top-level row (one caret per enclosing block, or the absolute `0^docs`), and the repository id as `^id` / `0^id`";
|
|
31
39
|
/**
|
|
32
40
|
* The bare-target error. `depth` is the scope depth the name was read at when
|
|
@@ -50,13 +58,13 @@ export function pastRootMessage(name, levels) {
|
|
|
50
58
|
}
|
|
51
59
|
/**
|
|
52
60
|
* The unknown-target error (3.0 name pass): a top-level `from <name>` whose name
|
|
53
|
-
* is not one of the
|
|
61
|
+
* is not one of the five collections would read an absent root and return an
|
|
54
62
|
* empty result — refused instead, naming the collections; `edges`, the links
|
|
55
63
|
* target's name before 3.0, gets its new name spelled out.
|
|
56
64
|
*/
|
|
57
65
|
export function unknownTargetMessage(name) {
|
|
58
66
|
const was = name === "edges" ? " (`edges` was renamed `links` in surface 3.0)" : "";
|
|
59
|
-
return `\`${name}\` is not a target — the repository's collections are
|
|
67
|
+
return `\`${name}\` is not a target — the repository's collections are ${ROOT_NAMES.map((r) => `\`${r}\``).join(", ")}${was}`;
|
|
60
68
|
}
|
|
61
69
|
/** Whether `name`, read as `<head>.<name>` or bare on a row of `target`, is one of that row's relations named after a target. */
|
|
62
70
|
export function isTargetRelationOf(target, name) {
|
package/dist/src/oqx-js/run.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { Query, Expr } from "@omgbase/oqx";
|
|
1
|
+
import type { Query, Expr, SelectItem } from "@omgbase/oqx";
|
|
2
|
+
import { type UnknownNames } from "./names.js";
|
|
2
3
|
import type { Store } from "../core/store/store.js";
|
|
4
|
+
import { FilterInvalid } from "../search/cel/parser.js";
|
|
3
5
|
import type { SemanticVec } from "../search/cel/compile.js";
|
|
4
6
|
export type OqxConsumer = "collect" | "count" | "exists" | "none" | "first" | "single";
|
|
5
7
|
export type EmbedQuery = (text: string) => Promise<{
|
|
@@ -18,6 +20,10 @@ export interface OqxHit {
|
|
|
18
20
|
[k: string]: unknown;
|
|
19
21
|
}
|
|
20
22
|
export interface OqxResult {
|
|
23
|
+
/** `{ id, path, ...projection }` per hit — or, under `OqxOptions.bare`, the
|
|
24
|
+
* projection alone (`id`/`path` present only when the query projected them;
|
|
25
|
+
* typed as `OqxHit` for the one hit shape every consumer handles, a bare
|
|
26
|
+
* caller knows it asked). */
|
|
21
27
|
hits: OqxHit[];
|
|
22
28
|
truncated: boolean;
|
|
23
29
|
cursor: string | null;
|
|
@@ -31,19 +37,57 @@ export interface OqxResult {
|
|
|
31
37
|
* empty). Paged/deduped exactly like hits — `truncated`/`cursor` apply. For
|
|
32
38
|
* first/single it holds zero or one value. */
|
|
33
39
|
values?: unknown[];
|
|
40
|
+
/** Unknown names (spec/surface §1.5, 4.6): one line per name the query reads
|
|
41
|
+
* that is neither a field of its row kind nor a property any object of that
|
|
42
|
+
* kind in the repository carries — the rows are still returned. First-
|
|
43
|
+
* reference order, deduplicated; absent when there is none (never `[]`).
|
|
44
|
+
* Last member of the result, after everything above. */
|
|
45
|
+
hints?: string[];
|
|
34
46
|
}
|
|
35
47
|
export interface OqxOptions {
|
|
36
48
|
limit?: number;
|
|
37
49
|
cursor?: string | null;
|
|
50
|
+
/** Return each hit as its PROJECTION only — no automatic `id`/`path`
|
|
51
|
+
* members (spec/surface §1.4, 4.2): `select title, era from docs` yields
|
|
52
|
+
* `{ title, era }` rows. Needs a `select` (without a projection a hit is
|
|
53
|
+
* only its envelope) and is refused with `values` (already the bare-values
|
|
54
|
+
* form); both are `filter_invalid` before the engine runs. A projected
|
|
55
|
+
* `id`/`path` is the intrinsic as everywhere (`select id` → `{ id }`), and
|
|
56
|
+
* with no envelope there is nothing to collide with (`select path: layer`
|
|
57
|
+
* is fine). Paging, `distinct`, `truncated` and `cursor` are unchanged —
|
|
58
|
+
* the runner keeps identity internally and strips the envelope last. The
|
|
59
|
+
* scalar consumers (`count`/`exists`/`none`) have no hits and ignore it. */
|
|
60
|
+
bare?: boolean;
|
|
38
61
|
semanticVectors?: Map<string, SemanticVec>;
|
|
39
62
|
/** Force the pure in-memory engine (skip tier-3 pushdown). Internal — used by
|
|
40
63
|
* the differential conformance suite to prove planned == in-memory. */
|
|
41
64
|
plan?: boolean;
|
|
65
|
+
/** What a tier-3 name on an open kind does (spec/surface §1.5, 4.6; a tier-3
|
|
66
|
+
* name on `links` is `filter_invalid` either way): `"hint"` records it in
|
|
67
|
+
* `hints` and runs the query — the default of the source entry (`oqxRun`,
|
|
68
|
+
* every read); `"refuse"` throws `filter_invalid` before any row is read —
|
|
69
|
+
* the default of the AST entry (`oqxRunQuery`), whose caller is the write
|
|
70
|
+
* selector compiler, where a name no row carries would select every row. */
|
|
71
|
+
unknownNames?: UnknownNames;
|
|
42
72
|
}
|
|
43
73
|
export declare function rootPathLiterals(e: Expr): Expr;
|
|
44
74
|
export declare function checkRootSpellings(q: Query): void;
|
|
45
75
|
export declare function collectSemanticPhrases(source: string): string[];
|
|
76
|
+
export declare function reservedAlias(items: readonly SelectItem[]): FilterInvalid | null;
|
|
77
|
+
export declare const BARE_NEEDS_SELECT_MESSAGE = "`bare` needs a `select` \u2014 without a projection a hit is only its envelope";
|
|
78
|
+
export declare const BARE_WITH_VALUES_MESSAGE = "`bare` is redundant with `values` \u2014 `values` is already the bare-values form; drop one";
|
|
46
79
|
export declare function oqxRun(store: Store, repoId: string, source: string, opts?: OqxOptions): OqxResult;
|
|
80
|
+
/**
|
|
81
|
+
* The AST entry (4.1): run an already parsed query — the source path above
|
|
82
|
+
* after `parse`, and the selector compiler (`mutate/select.ts`), which appends
|
|
83
|
+
* its slot items to the RAW tree so a user alias a template reads is inlined
|
|
84
|
+
* by the alias resolution here. Errors are the library's own (`OqxError`);
|
|
85
|
+
* `oqxRun` normalizes them, a caller of this entry does the same. Unknown
|
|
86
|
+
* names (4.6) default to `"refuse"` here: this entry's caller is the write
|
|
87
|
+
* selector, where a name no row carries would select every row; the source
|
|
88
|
+
* entry passes `"hint"`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function oqxRunQuery(store: Store, repoId: string, raw: Query, opts?: OqxOptions): OqxResult;
|
|
47
91
|
export declare function oqxRunAsync(store: Store, repoId: string, source: string, opts?: OqxOptions, embedQuery?: EmbedQuery): Promise<OqxResult>;
|
|
48
92
|
/** Parse an OQX source into the `@omgbase/oqx` AST (re-exported for callers). */
|
|
49
93
|
export declare function parseOqx(source: string): Query;
|
package/dist/src/oqx-js/run.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
// in-tree compiler, so every caller and the corpus are unchanged.
|
|
7
7
|
import { parse, PlannedEngine, InMemoryEngine, OqxError, resolveAliases, transform, visit, build, semantics } from "@omgbase/oqx";
|
|
8
8
|
import { makeStoreContext, rowRef } from "./context.js";
|
|
9
|
-
import { SQLiteQueryPlanner
|
|
10
|
-
import { REPO_REMOVED_MESSAGE,
|
|
9
|
+
import { SQLiteQueryPlanner } from "./planner.js";
|
|
10
|
+
import { REPO_REMOVED_MESSAGE, ROOTS, bareTargetMessage, pastRootMessage, unknownTargetMessage } from "./root-row.js";
|
|
11
|
+
import { checkNames } from "./names.js";
|
|
11
12
|
import { FilterInvalid } from "../search/cel/parser.js";
|
|
12
13
|
import { float32ToBlob } from "../core/vec.js";
|
|
13
14
|
import { encodeCursor as encodeKeyset, decodePathCursor } from "../core/cursor.js";
|
|
@@ -72,8 +73,8 @@ export function rootPathLiterals(e) {
|
|
|
72
73
|
export function checkRootSpellings(q) {
|
|
73
74
|
// A top-level `from <name>` that is no collection (`from edges`, the links
|
|
74
75
|
// target's name before 3.0) would scan an absent root and come back empty:
|
|
75
|
-
// refused, naming the
|
|
76
|
-
if (q.source.kind === "ident" && !
|
|
76
|
+
// refused, naming the five collections (root-row.ts).
|
|
77
|
+
if (q.source.kind === "ident" && !ROOTS.has(q.source.name))
|
|
77
78
|
throw new FilterInvalid(unknownTargetMessage(q.source.name), "OQX");
|
|
78
79
|
visit(q, {
|
|
79
80
|
enter(node, ctx) {
|
|
@@ -84,7 +85,7 @@ export function checkRootSpellings(q) {
|
|
|
84
85
|
if (node.kind === "ident" && ctx.depth >= 1 && (node.name === "docs" || node.name === "links")) {
|
|
85
86
|
throw new FilterInvalid(bareTargetMessage(node.name, ctx.depth), "OQX");
|
|
86
87
|
}
|
|
87
|
-
if (node.kind === "outer" &&
|
|
88
|
+
if (node.kind === "outer" && ROOTS.has(node.name) && node.levels > ctx.depth) {
|
|
88
89
|
throw new FilterInvalid(pastRootMessage(node.name, node.levels), "OQX");
|
|
89
90
|
}
|
|
90
91
|
},
|
|
@@ -128,6 +129,21 @@ export function collectSemanticPhrases(source) {
|
|
|
128
129
|
}
|
|
129
130
|
const ID_ITEM = build.field("__oqx_id", build.ident("id"));
|
|
130
131
|
const PATH_ITEM = build.field("__oqx_path", build.ident("path"));
|
|
132
|
+
// The runner's own projection names (`__oqx_id`, `__oqx_path`, `__oqx_self`,
|
|
133
|
+
// `__oqx_value`) and the selector compiler's slots (`__omg_sel_<t>_<slot>`,
|
|
134
|
+
// mutate/select.ts) share two reserved prefixes. A user alias under either would
|
|
135
|
+
// shadow an injected item (`select __oqx_id: 1` overwrote the hit's `id` through
|
|
136
|
+
// 4.0) or collide with a slot, so it is refused before anything runs — over the
|
|
137
|
+
// query's own top-level items, the only ones the runner injects beside.
|
|
138
|
+
const RESERVED_ALIAS_PREFIXES = ["__oqx_", "__omg_"];
|
|
139
|
+
export function reservedAlias(items) {
|
|
140
|
+
for (const item of items) {
|
|
141
|
+
const prefix = RESERVED_ALIAS_PREFIXES.find((p) => item.name.startsWith(p));
|
|
142
|
+
if (prefix !== undefined)
|
|
143
|
+
return new FilterInvalid(`projection \`${item.name}\` uses the reserved prefix \`${prefix}\` — choose another alias`, "OQX");
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
131
147
|
// Result projections may not overwrite the envelope (spec/surface §1.4, 3.0):
|
|
132
148
|
// a projection whose output key is `id` or `path` is allowed only when it IS
|
|
133
149
|
// the envelope's own selection — the bare attribute of the same name, which
|
|
@@ -150,8 +166,9 @@ function envelopeCollision(items) {
|
|
|
150
166
|
return null;
|
|
151
167
|
}
|
|
152
168
|
// A hit is a store row (spec/surface §1.4, 1.5). The engine's top-level rows
|
|
153
|
-
// are store rows by construction when the source is a bare root scan
|
|
154
|
-
//
|
|
169
|
+
// are store rows by construction when the source is a bare root scan (one of
|
|
170
|
+
// the five collections, `revisions` included — the planner's `rootTarget` is
|
|
171
|
+
// narrower: the four it can plan) and nothing re-projects them; a `follow` destination (`follow before` over a
|
|
155
172
|
// frontmatter list of paths), a `from E` re-projection or any other source can
|
|
156
173
|
// reach a scalar, which the injected `id`/`path` reads would render as the
|
|
157
174
|
// junk hit `{ id: "undefined", path: "" }`. For those queries the row itself is
|
|
@@ -162,7 +179,7 @@ function envelopeCollision(items) {
|
|
|
162
179
|
const SELF_KEY = "__oqx_self";
|
|
163
180
|
const SELF_ITEM = build.field(SELF_KEY, build.ident("$it"));
|
|
164
181
|
function mayReachNonRows(q) {
|
|
165
|
-
return q.follow !== null || q.from.length > 0 ||
|
|
182
|
+
return q.follow !== null || q.from.length > 0 || !(q.source.kind === "ident" && ROOTS.has(q.source.name));
|
|
166
183
|
}
|
|
167
184
|
function describeValue(v) {
|
|
168
185
|
if (v === null || v === undefined)
|
|
@@ -180,7 +197,7 @@ function describeValue(v) {
|
|
|
180
197
|
return "an object";
|
|
181
198
|
}
|
|
182
199
|
function notAStoreRow(v) {
|
|
183
|
-
return new FilterInvalid(`a hit must be a document, block, node or
|
|
200
|
+
return new FilterInvalid(`a hit must be a document, block, node, link or revision row — the query reached ${describeValue(v)}; to follow document references held in a property use refs(<field>)`, "OQX");
|
|
184
201
|
}
|
|
185
202
|
// The reserved key a top-level `values` projection's single item is renamed to,
|
|
186
203
|
// so it rides through id/path injection, keyset paging, and distinct as an
|
|
@@ -213,33 +230,56 @@ function renderValue(v) {
|
|
|
213
230
|
out[k] = renderValue(x);
|
|
214
231
|
return out;
|
|
215
232
|
}
|
|
216
|
-
function
|
|
233
|
+
function shapeRow(row) {
|
|
217
234
|
const o = row;
|
|
218
235
|
const { __oqx_id, __oqx_path, __oqx_self, ...rest } = o;
|
|
219
236
|
if (SELF_KEY in o && !rowRef(__oqx_self))
|
|
220
237
|
throw notAStoreRow(__oqx_self);
|
|
221
|
-
|
|
222
|
-
// them in place (the same value, read by the same attribute).
|
|
223
|
-
return { id: String(__oqx_id), path: String(__oqx_path ?? ""), ...renderValue(rest) };
|
|
238
|
+
return { id: String(__oqx_id), path: String(__oqx_path ?? ""), proj: renderValue(rest) };
|
|
224
239
|
}
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
// The envelope keys lead; a user projection of `id`/`path` collapses onto
|
|
241
|
+
// them in place (the same value, read by the same attribute).
|
|
242
|
+
function toHit(s) {
|
|
243
|
+
return { id: s.id, path: s.path, ...s.proj };
|
|
244
|
+
}
|
|
245
|
+
// `bare` (4.2): the projection alone. A projected `id`/`path` is an ordinary
|
|
246
|
+
// column here — the intrinsic the attribute reads, under the key the query
|
|
247
|
+
// gave it — and nothing is injected beside it.
|
|
248
|
+
function toBareHit(s) {
|
|
249
|
+
return s.proj;
|
|
250
|
+
}
|
|
251
|
+
// Top-level `select distinct`: dedup hits by their USER projection — every
|
|
252
|
+
// field except the envelope's `id`/`path`, which a projection of the attribute
|
|
253
|
+
// collapses onto (so `select distinct id` dedups on nothing but the empty
|
|
254
|
+
// projection, as it always has); under `bare` there is no envelope, so a
|
|
255
|
+
// projected `id`/`path` IS part of the projection and counts. Keeps the first
|
|
256
|
+
// (so the retained hit carries a real identity). Order-preserving.
|
|
257
|
+
function dedupByProjection(rows, bare) {
|
|
229
258
|
const seen = new Set();
|
|
230
259
|
const out = [];
|
|
231
|
-
for (const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
void _path;
|
|
235
|
-
const key = JSON.stringify(Object.keys(proj).sort().map((k) => [k, proj[k]]));
|
|
260
|
+
for (const s of rows) {
|
|
261
|
+
const keys = Object.keys(s.proj).filter((k) => bare || !ENVELOPE_KEYS.has(k)).sort();
|
|
262
|
+
const key = JSON.stringify(keys.map((k) => [k, s.proj[k]]));
|
|
236
263
|
if (seen.has(key))
|
|
237
264
|
continue;
|
|
238
265
|
seen.add(key);
|
|
239
|
-
out.push(
|
|
266
|
+
out.push(s);
|
|
240
267
|
}
|
|
241
268
|
return out;
|
|
242
269
|
}
|
|
270
|
+
// `bare` is examined where hits are built (spec/surface §1.4, 4.2): without a
|
|
271
|
+
// projection a hit is only its envelope, and `values` is already the
|
|
272
|
+
// bare-values form — both refused before the engine runs, over the resolved
|
|
273
|
+
// query, so the error never depends on the rows.
|
|
274
|
+
export const BARE_NEEDS_SELECT_MESSAGE = "`bare` needs a `select` — without a projection a hit is only its envelope";
|
|
275
|
+
export const BARE_WITH_VALUES_MESSAGE = "`bare` is redundant with `values` — `values` is already the bare-values form; drop one";
|
|
276
|
+
function bareMisuse(parsed) {
|
|
277
|
+
if (parsed.values)
|
|
278
|
+
return new FilterInvalid(BARE_WITH_VALUES_MESSAGE, "OQX");
|
|
279
|
+
if (parsed.select.length === 0)
|
|
280
|
+
return new FilterInvalid(BARE_NEEDS_SELECT_MESSAGE, "OQX");
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
243
283
|
export function oqxRun(store, repoId, source, opts = {}) {
|
|
244
284
|
try {
|
|
245
285
|
return oqxRunInner(store, repoId, source, opts);
|
|
@@ -252,10 +292,40 @@ export function oqxRun(store, repoId, source, opts = {}) {
|
|
|
252
292
|
}
|
|
253
293
|
}
|
|
254
294
|
function oqxRunInner(store, repoId, source, opts) {
|
|
255
|
-
// The query's `select` aliases are resolved HERE, once, before the runner
|
|
256
|
-
// renames/injects items (an engine evaluates the query it is given).
|
|
257
295
|
const raw = parse(source);
|
|
296
|
+
const reserved = reservedAlias(raw.select);
|
|
297
|
+
if (reserved)
|
|
298
|
+
throw reserved;
|
|
299
|
+
// The source entry is every read: a tier-3 name on an open kind is a hint.
|
|
300
|
+
return oqxRunQuery(store, repoId, raw, { unknownNames: "hint", ...opts });
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* The AST entry (4.1): run an already parsed query — the source path above
|
|
304
|
+
* after `parse`, and the selector compiler (`mutate/select.ts`), which appends
|
|
305
|
+
* its slot items to the RAW tree so a user alias a template reads is inlined
|
|
306
|
+
* by the alias resolution here. Errors are the library's own (`OqxError`);
|
|
307
|
+
* `oqxRun` normalizes them, a caller of this entry does the same. Unknown
|
|
308
|
+
* names (4.6) default to `"refuse"` here: this entry's caller is the write
|
|
309
|
+
* selector, where a name no row carries would select every row; the source
|
|
310
|
+
* entry passes `"hint"`.
|
|
311
|
+
*/
|
|
312
|
+
export function oqxRunQuery(store, repoId, raw, opts = {}) {
|
|
258
313
|
checkRootSpellings(raw);
|
|
314
|
+
// Unknown names (spec/surface §1.5): judged over the RAW tree — before alias
|
|
315
|
+
// resolution and the runner's own items — so an alias is never mistaken for
|
|
316
|
+
// a row property and the verdict never depends on the rows. The selector
|
|
317
|
+
// compiler's slot items (`__omg_sel_*`, the templates' expressions) are not
|
|
318
|
+
// the selector: an expression reading an absent property there is
|
|
319
|
+
// spec/mutate's `target_missing` / absent value, not an unknown name.
|
|
320
|
+
const own = { ...raw, select: raw.select.filter((it) => !RESERVED_ALIAS_PREFIXES.some((p) => it.name.startsWith(p))) };
|
|
321
|
+
const hints = checkNames(store, repoId, own, opts.unknownNames ?? "refuse");
|
|
322
|
+
const result = runResolved(store, repoId, raw, opts);
|
|
323
|
+
// `hints` is the last member, present only when there is something to say.
|
|
324
|
+
return hints.length === 0 ? result : { ...result, hints };
|
|
325
|
+
}
|
|
326
|
+
function runResolved(store, repoId, raw, opts) {
|
|
327
|
+
// The query's `select` aliases are resolved HERE, once, before the runner
|
|
328
|
+
// renames/injects items (an engine evaluates the query it is given).
|
|
259
329
|
const parsed = rewriteQuery(resolveAliases(raw));
|
|
260
330
|
const consumer = parsed.consumer;
|
|
261
331
|
const ctxOpts = opts.semanticVectors ? { semanticVectors: opts.semanticVectors } : {};
|
|
@@ -289,11 +359,21 @@ function oqxRunInner(store, repoId, source, opts) {
|
|
|
289
359
|
// the final page below and returned as `values` with `hits` empty.
|
|
290
360
|
const topValues = parsed.values;
|
|
291
361
|
const userSelect = topValues ? [{ ...parsed.select[0], name: VALUE_KEY }] : parsed.select;
|
|
292
|
-
|
|
362
|
+
// `bare` (4.2): the hits are the projection alone — refused without one and
|
|
363
|
+
// with `values`; with no envelope there is no collision to refuse either, a
|
|
364
|
+
// projected `id`/`path` is simply a column.
|
|
365
|
+
const bare = opts.bare === true;
|
|
366
|
+
if (bare) {
|
|
367
|
+
const misuse = bareMisuse(parsed);
|
|
368
|
+
if (misuse)
|
|
369
|
+
throw misuse;
|
|
370
|
+
}
|
|
371
|
+
else if (!topValues) {
|
|
293
372
|
const collision = envelopeCollision(userSelect);
|
|
294
373
|
if (collision)
|
|
295
374
|
throw collision;
|
|
296
375
|
}
|
|
376
|
+
const render = bare ? toBareHit : toHit;
|
|
297
377
|
// On the collect path a top-level `limit`/`offset` is ALSO taken out of the
|
|
298
378
|
// engine query and applied here, after the runner's own distinct — the engine
|
|
299
379
|
// would otherwise bound the raw rows before dedup (`select distinct type
|
|
@@ -307,13 +387,13 @@ function oqxRunInner(store, repoId, source, opts) {
|
|
|
307
387
|
if (consumer === "first" || consumer === "single") {
|
|
308
388
|
const row = res.consumer === "first" || res.consumer === "single" ? res.row : null;
|
|
309
389
|
if (topValues)
|
|
310
|
-
return { hits: [], truncated: false, cursor: null, consumer, values: row == null ? [] : [
|
|
311
|
-
return { hits: row == null ? [] : [
|
|
390
|
+
return { hits: [], truncated: false, cursor: null, consumer, values: row == null ? [] : [shapeRow(row).proj[VALUE_KEY]] };
|
|
391
|
+
return { hits: row == null ? [] : [render(shapeRow(row))], truncated: false, cursor: null, consumer };
|
|
312
392
|
}
|
|
313
393
|
// collect: keyset pagination on (path, id) when the order is the default.
|
|
314
|
-
let rows = (res.consumer === "collect" ? res.rows : []).map(
|
|
394
|
+
let rows = (res.consumer === "collect" ? res.rows : []).map(shapeRow);
|
|
315
395
|
if (topDistinct)
|
|
316
|
-
rows =
|
|
396
|
+
rows = dedupByProjection(rows, bare);
|
|
317
397
|
// The query's own bound defines the result SET; the `limit`/`cursor` options
|
|
318
398
|
// then page within it.
|
|
319
399
|
const offset = constBound(topOffset, "offset") ?? 0;
|
|
@@ -331,9 +411,11 @@ function oqxRunInner(store, repoId, source, opts) {
|
|
|
331
411
|
page = page.slice(0, cap);
|
|
332
412
|
const last = page[page.length - 1];
|
|
333
413
|
const cursor = truncated && last && !custom ? encodeCursor(last.path, last.id) : null;
|
|
414
|
+
// The envelope is stripped LAST (`bare`): the page and the cursor above ran
|
|
415
|
+
// on identity, so a bare result pages exactly like the enveloped one.
|
|
334
416
|
if (topValues)
|
|
335
|
-
return { hits: [], truncated, cursor, consumer: "collect", values: page.map((
|
|
336
|
-
return { hits: page, truncated, cursor, consumer: "collect" };
|
|
417
|
+
return { hits: [], truncated, cursor, consumer: "collect", values: page.map((s) => s.proj[VALUE_KEY]) };
|
|
418
|
+
return { hits: page.map(render), truncated, cursor, consumer: "collect" };
|
|
337
419
|
}
|
|
338
420
|
export async function oqxRunAsync(store, repoId, source, opts = {}, embedQuery) {
|
|
339
421
|
const phrases = collectSemanticPhrases(source);
|
|
@@ -53,7 +53,7 @@ import { TARGETS } from "../root-row.js";
|
|
|
53
53
|
* `translate.test.ts` asserts this set against the store context.
|
|
54
54
|
*/
|
|
55
55
|
export const NON_PROPERTY_NAMES = {
|
|
56
|
-
docs: new Set(["doc", "nodes", "blocks", "out_links", "in_links", "data", "frontmatter", "inline"]),
|
|
56
|
+
docs: new Set(["doc", "nodes", "blocks", "out_links", "in_links", "revisions", "data", "frontmatter", "inline"]),
|
|
57
57
|
blocks: new Set(["block", "doc", "children", "nodes", "out_links", "section", "attrs"]),
|
|
58
58
|
nodes: new Set(["section", "doc", "block", "blocks", "subsections", "children", "attrs"]),
|
|
59
59
|
links: new Set(["doc", "dst"]),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export declare const SPEC_VERSIONS: {
|
|
2
|
-
readonly oqx: "0.
|
|
2
|
+
readonly oqx: "0.23";
|
|
3
3
|
readonly format: "0.2";
|
|
4
4
|
readonly reconcile: "2.3";
|
|
5
|
-
readonly store: "14.
|
|
5
|
+
readonly store: "14.2";
|
|
6
6
|
readonly properties: "1.1";
|
|
7
7
|
readonly graph: "1.1";
|
|
8
8
|
readonly search: "2.0";
|
|
9
|
-
readonly mutate: "3.
|
|
9
|
+
readonly mutate: "3.5";
|
|
10
10
|
readonly sync: "2.2";
|
|
11
|
-
readonly surface: "4.
|
|
12
|
-
readonly cli: "4.
|
|
11
|
+
readonly surface: "4.7";
|
|
12
|
+
readonly cli: "4.5";
|
|
13
13
|
};
|
|
14
14
|
export type SpecName = keyof typeof SPEC_VERSIONS;
|
|
@@ -13,13 +13,13 @@ export const SPEC_VERSIONS = {
|
|
|
13
13
|
oqx: LANGUAGE_VERSION,
|
|
14
14
|
format: "0.2",
|
|
15
15
|
reconcile: "2.3",
|
|
16
|
-
store: "14.
|
|
16
|
+
store: "14.2",
|
|
17
17
|
properties: "1.1",
|
|
18
18
|
graph: "1.1",
|
|
19
19
|
search: "2.0",
|
|
20
|
-
mutate: "3.
|
|
20
|
+
mutate: "3.5",
|
|
21
21
|
sync: "2.2",
|
|
22
|
-
surface: "4.
|
|
23
|
-
cli: "4.
|
|
22
|
+
surface: "4.7",
|
|
23
|
+
cli: "4.5",
|
|
24
24
|
};
|
|
25
25
|
//# sourceMappingURL=spec-versions.js.map
|
package/dist/src/sync/observe.js
CHANGED
|
@@ -189,9 +189,15 @@ function commitPrepared(store, repoId, prepared, ts, oldHashHex, newHashHex) {
|
|
|
189
189
|
const conflicted = hasConflictMarkers(content);
|
|
190
190
|
const resolveIds = makeReconcilingResolver(store, repoId, { ts, path, prepared });
|
|
191
191
|
const res = ingestFile(store, repoId, path, content, { ts, origin: "observed", resolveIds, parsed: prepared.tree, conflicted });
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
// The outcome counts every verdict the matcher produced, `same` included —
|
|
193
|
+
// it describes what the matcher saw; the `dispositions` table describes
|
|
194
|
+
// what changed (spec/store §5.4 step 10, 14.2: `same` is not persisted), so
|
|
195
|
+
// the counts come from the in-memory result, not the table. Kinds in
|
|
196
|
+
// bytewise order, as the table query listed them through 14.1.
|
|
197
|
+
const counts = new Map();
|
|
198
|
+
for (const d of prepared.result.dispositions)
|
|
199
|
+
counts.set(d.kind, (counts.get(d.kind) ?? 0) + 1);
|
|
200
|
+
const rows = [...counts].map(([kind, n]) => ({ kind, n })).sort((a, b) => (a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0));
|
|
195
201
|
return {
|
|
196
202
|
docId: res.docId,
|
|
197
203
|
path,
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Store } from "./core/store/store.js";
|
|
2
2
|
import { SPEC_VERSIONS } from "./spec-versions.js";
|
|
3
|
+
import type { ToolsProfile } from "./mcp/server.js";
|
|
3
4
|
export interface HostInfo {
|
|
4
5
|
/** the serving binary's own version — `omgbase`'s package.json (what `--version` prints) */
|
|
5
6
|
version: string;
|
|
@@ -25,6 +26,8 @@ export interface VersionInfo {
|
|
|
25
26
|
runtime: string;
|
|
26
27
|
commit: string | null;
|
|
27
28
|
built: string | null;
|
|
29
|
+
/** the catalog profile the server serves (spec/surface §4 "Profiles", 4.3): `omg version` itself reports `full` */
|
|
30
|
+
profile: ToolsProfile;
|
|
28
31
|
}
|
|
29
32
|
/** The omgbase packages the reference binary is built from (spec/surface §4). */
|
|
30
33
|
export declare const COMPONENT_PACKAGES: readonly ["omgbase", "@omgbase/core", "@omgbase/oqx", "@omgbase/sync", "@omgbase/fs-adapter"];
|
|
@@ -34,6 +37,8 @@ export declare function nearestPackageVersion(fromUrl: string, name: string): st
|
|
|
34
37
|
export declare function coreVersion(): string;
|
|
35
38
|
/**
|
|
36
39
|
* The `version` result. `store` supplies `schema`; `host` the binary's own
|
|
37
|
-
* version, build info and resolution anchor (see the module comment)
|
|
40
|
+
* version, build info and resolution anchor (see the module comment);
|
|
41
|
+
* `profile` the catalog profile served (the MCP server's `--tools`; the CLI's
|
|
42
|
+
* own `version` verb is the full library and says `full`).
|
|
38
43
|
*/
|
|
39
|
-
export declare function versionInfo(store?: Store | null, host?: HostInfo): VersionInfo;
|
|
44
|
+
export declare function versionInfo(store?: Store | null, host?: HostInfo, profile?: ToolsProfile): VersionInfo;
|
package/dist/src/version.js
CHANGED
|
@@ -73,9 +73,11 @@ export function coreVersion() {
|
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* The `version` result. `store` supplies `schema`; `host` the binary's own
|
|
76
|
-
* version, build info and resolution anchor (see the module comment)
|
|
76
|
+
* version, build info and resolution anchor (see the module comment);
|
|
77
|
+
* `profile` the catalog profile served (the MCP server's `--tools`; the CLI's
|
|
78
|
+
* own `version` verb is the full library and says `full`).
|
|
77
79
|
*/
|
|
78
|
-
export function versionInfo(store, host) {
|
|
80
|
+
export function versionInfo(store, host, profile = "full") {
|
|
79
81
|
const anchors = [...(host?.resolveFrom ? [host.resolveFrom] : []), import.meta.url];
|
|
80
82
|
const components = {};
|
|
81
83
|
for (const name of COMPONENT_PACKAGES) {
|
|
@@ -98,6 +100,7 @@ export function versionInfo(store, host) {
|
|
|
98
100
|
runtime: `node ${process.versions.node}`,
|
|
99
101
|
commit: host?.commit ?? null,
|
|
100
102
|
built: host?.built ?? null,
|
|
103
|
+
profile,
|
|
101
104
|
};
|
|
102
105
|
}
|
|
103
106
|
//# sourceMappingURL=version.js.map
|