@omgbase/core 0.28.0 → 0.29.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/read/document.d.ts +11 -10
- package/dist/src/core/read/document.js +12 -11
- package/dist/src/core/read/nodes.d.ts +10 -10
- package/dist/src/core/read/nodes.js +11 -11
- package/dist/src/core/read/reader.d.ts +37 -24
- package/dist/src/core/read/reader.js +44 -31
- package/dist/src/graph/history.d.ts +25 -22
- package/dist/src/graph/history.js +17 -17
- package/dist/src/graph/inbound-links.d.ts +7 -7
- package/dist/src/graph/inbound-links.js +7 -7
- package/dist/src/graph/link-health.d.ts +9 -9
- package/dist/src/graph/link-health.js +9 -9
- package/dist/src/graph/links.d.ts +3 -3
- package/dist/src/graph/links.js +4 -4
- package/dist/src/mcp/arguments.d.ts +10 -3
- package/dist/src/mcp/arguments.js +32 -9
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/graph.d.ts +22 -22
- package/dist/src/mcp/graph.js +53 -46
- package/dist/src/mcp/reference.d.ts +29 -1
- package/dist/src/mcp/reference.js +350 -222
- package/dist/src/mcp/server.d.ts +9 -0
- package/dist/src/mcp/server.js +169 -107
- package/dist/src/mutate/apply.d.ts +6 -6
- package/dist/src/mutate/apply.js +1 -1
- package/dist/src/mutate/canonical.d.ts +1 -1
- package/dist/src/mutate/canonical.js +2 -2
- package/dist/src/mutate/commit.d.ts +3 -3
- package/dist/src/mutate/commit.js +4 -4
- package/dist/src/mutate/docs.d.ts +1 -1
- package/dist/src/mutate/docs.js +8 -8
- package/dist/src/mutate/effects.d.ts +17 -17
- package/dist/src/mutate/effects.js +113 -113
- package/dist/src/mutate/macros.d.ts +5 -5
- package/dist/src/mutate/macros.js +1 -1
- package/dist/src/mutate/materialize.js +2 -2
- package/dist/src/mutate/ops.js +2 -2
- package/dist/src/mutate/opset.d.ts +7 -7
- package/dist/src/mutate/plan-update.js +2 -2
- package/dist/src/mutate/plan.d.ts +8 -8
- package/dist/src/mutate/plan.js +25 -25
- package/dist/src/mutate/select.d.ts +4 -4
- package/dist/src/mutate/select.js +52 -52
- package/dist/src/mutate/sugar.d.ts +10 -10
- package/dist/src/mutate/sugar.js +21 -21
- package/dist/src/oqx-js/context.d.ts +8 -4
- package/dist/src/oqx-js/context.js +206 -189
- package/dist/src/oqx-js/names.d.ts +28 -17
- package/dist/src/oqx-js/names.js +115 -78
- package/dist/src/oqx-js/planner.d.ts +4 -8
- package/dist/src/oqx-js/planner.js +6 -10
- package/dist/src/oqx-js/root-row.d.ts +6 -5
- package/dist/src/oqx-js/root-row.js +15 -16
- package/dist/src/oqx-js/run.d.ts +18 -17
- package/dist/src/oqx-js/run.js +57 -54
- package/dist/src/oqx-js/sql/translate.d.ts +5 -5
- package/dist/src/oqx-js/sql/translate.js +162 -161
- package/dist/src/oqx-js/store-index.d.ts +1 -1
- package/dist/src/oqx-js/store-index.js +28 -30
- package/dist/src/search/resolve.d.ts +2 -2
- package/dist/src/search/resolve.js +1 -1
- package/dist/src/search/rrf.js +2 -2
- package/dist/src/search/text.d.ts +5 -5
- package/dist/src/search/text.js +2 -2
- package/dist/src/spec-versions.d.ts +6 -6
- package/dist/src/spec-versions.js +6 -6
- package/dist/src/surface-paths.d.ts +18 -17
- package/dist/src/surface-paths.js +23 -21
- package/dist/src/sync/admin.d.ts +2 -2
- package/dist/src/sync/admin.js +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Query, ReferenceStep } from "@omgbase/oqx";
|
|
2
2
|
import { type Kind } from "./context.js";
|
|
3
3
|
import type { Store } from "../core/store/store.js";
|
|
4
|
-
/** What to do with
|
|
4
|
+
/** What to do with an unknown bare name on an open kind: record it in `hints` (every read) or refuse the query (a write selector). */
|
|
5
5
|
export type UnknownNames = "hint" | "refuse";
|
|
6
6
|
/** The relations of each kind and the kind of the rows they yield (spec/surface §1.2). */
|
|
7
7
|
export declare const RELATIONS: Readonly<Record<Kind, Readonly<Record<string, Kind>>>>;
|
|
8
|
-
/** The
|
|
8
|
+
/** The reach-through handles of each kind and the kind of the row they reach (§1.2; the self aliases are gone in 5.0 — `$it` is the self). */
|
|
9
9
|
export declare const HANDLES: Readonly<Record<Kind, Readonly<Record<string, Kind>>>>;
|
|
10
|
+
/** Every engine name of a kind, in the §1.2 table's order: attributes, reach-throughs, source handles (docs), relations. */
|
|
11
|
+
export declare function engineNames(target: Kind): string[];
|
|
10
12
|
/** The vocabulary of a kind in a repository (cached on the head commit; the SQL runs only when a name is not intrinsic). */
|
|
11
13
|
export declare function vocabulary(store: Store, repoId: string, target: Kind): {
|
|
12
14
|
present: boolean;
|
|
@@ -17,24 +19,33 @@ export declare function kindOfScope(steps: readonly ReferenceStep[]): Kind | nul
|
|
|
17
19
|
/** Optimal string alignment distance (Damerau–Levenshtein with adjacent transpositions, each edit 1). */
|
|
18
20
|
export declare function editDistance(a: string, b: string): number;
|
|
19
21
|
/**
|
|
20
|
-
* The nearest match of `name` among `candidates` (§1.5)
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* The nearest match of `name` among `candidates` (§1.5). First, the one hint
|
|
23
|
+
* the name rule makes certain: a candidate that is exactly `name` with the
|
|
24
|
+
* engine prefix added or removed (`id` → `_id`, `_layer` → `layer`) wins
|
|
25
|
+
* outright, whatever its length. Otherwise case-insensitive and **prefix-blind**
|
|
26
|
+
* — the `_` is a declaration, not spelling, so `commited` is one edit from
|
|
27
|
+
* `_commit` and `titel` one from `_title` as from `title`: a candidate
|
|
28
|
+
* qualifies when its distance is ≤ 2, or when one of the two is a substring of
|
|
29
|
+
* the other and the shorter has at least three characters; the lowest distance
|
|
30
|
+
* wins, ties broken by the shorter candidate (so the bare authored `title`
|
|
31
|
+
* beats `_title` when both are one edit away), then alphabetically. Null when
|
|
32
|
+
* nothing qualifies.
|
|
25
33
|
*/
|
|
26
34
|
export declare function nearest(name: string, candidates: readonly string[]): string | null;
|
|
27
|
-
/** The error of
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
/** The error of an unknown engine name: `` `_target` is not an engine name of links (engine names: _id, _path, …); did you mean `_dst_path`? ``
|
|
36
|
+
* — the nearest match drawn from the engine names and the authored keys, so a `_layer` written for the authored `layer` is pointed at it. */
|
|
37
|
+
export declare function engineNameErrorMessage(store: Store, repoId: string, target: Kind, name: string): string;
|
|
38
|
+
/** The error of a bare name on a closed kind: `` `target` reads an authored property, and a link carries none (engine names: _id, _path, …); did you mean `_dst_path`? ``. */
|
|
39
|
+
export declare function closedKindErrorMessage(target: Kind, name: string, handle?: string): string;
|
|
40
|
+
/** The hint of an open kind: `` no block in this repository carries a property `preview`; did you mean `_text`? ``.
|
|
41
|
+
* Under a source handle the name is shown with it — `` no document in this repository carries a property `x` (`_data.x`); did you mean `y`? `` —
|
|
42
|
+
* and the nearest match is drawn from the authored keys alone. */
|
|
33
43
|
export declare function propertyHintMessage(store: Store, repoId: string, target: Kind, name: string, handle?: string): string;
|
|
34
44
|
/**
|
|
35
|
-
* Judge every name the query reads. Returns the hints (one per
|
|
36
|
-
* of an open kind, first-reference order, deduplicated) under `"hint"`;
|
|
37
|
-
* `FilterInvalid` for
|
|
38
|
-
*
|
|
45
|
+
* Judge every name the query reads. Returns the hints (one per unknown bare
|
|
46
|
+
* name of an open kind, first-reference order, deduplicated) under `"hint"`;
|
|
47
|
+
* throws `FilterInvalid` for an unknown engine name (`_x`) and for a bare name
|
|
48
|
+
* on a closed kind in either mode, and for an unknown bare name on an open kind
|
|
49
|
+
* under `"refuse"`.
|
|
39
50
|
*/
|
|
40
51
|
export declare function checkNames(store: Store, repoId: string, raw: Query, mode: UnknownNames): string[];
|
package/dist/src/oqx-js/names.js
CHANGED
|
@@ -1,52 +1,63 @@
|
|
|
1
|
-
// Unknown names (spec/surface §1.5, 4.6). A query that
|
|
2
|
-
// orders by a name no row of the scope's kind can carry —
|
|
3
|
-
// from links` (links have `
|
|
4
|
-
// to run silently: an absent column in every row, a
|
|
5
|
-
// every row. This pass judges every name the query
|
|
6
|
-
// `references`, AST.md §5b) against the
|
|
7
|
-
// from, statically — before any row is read, so the verdict never depends
|
|
8
|
-
// the query's own filter — and either records a hint or refuses the query.
|
|
1
|
+
// Unknown names (spec/surface §1.5, 4.6; simplified in 5.0). A query that
|
|
2
|
+
// projects, filters or orders by a name no row of the scope's kind can carry —
|
|
3
|
+
// `select _path, target from links` (links have `_dst_path`), `where preview !=
|
|
4
|
+
// ""` on blocks — used to run silently: an absent column in every row, a
|
|
5
|
+
// predicate true or false of every row. This pass judges every name the query
|
|
6
|
+
// reads (`@omgbase/oqx`'s `references`, AST.md §5b) against the row kind it is
|
|
7
|
+
// read from, statically — before any row is read, so the verdict never depends
|
|
8
|
+
// on the query's own filter — and either records a hint or refuses the query.
|
|
9
9
|
//
|
|
10
|
-
// The
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// the
|
|
25
|
-
//
|
|
10
|
+
// The name rule (§1.2, 5.0) makes the judgement two-sided:
|
|
11
|
+
// • an UNDERSCORE-prefixed name is an engine name — an attribute, a
|
|
12
|
+
// reach-through, a source handle or a relation of the kind, a CLOSED set —
|
|
13
|
+
// so a `_x` that is none of them is `filter_invalid` naming the kind's
|
|
14
|
+
// engine names (the one loud error of this pass; both modes);
|
|
15
|
+
// • a BARE name is the author's — on a document a frontmatter or inline key,
|
|
16
|
+
// on a block or node a key of its `attrs` — and is judged against the
|
|
17
|
+
// VOCABULARY of the kind in this repository: a name at least one live object
|
|
18
|
+
// carries is known (however partial: `stage` on three processes out of
|
|
19
|
+
// twenty documents is the data model working); a name no object carries is
|
|
20
|
+
// the complaint — on a read the result carries one hint per offending name
|
|
21
|
+
// and the rows still come back; a write selector (the AST entry,
|
|
22
|
+
// `unknownNames: "refuse"`) is refused, since a name no row carries would
|
|
23
|
+
// select every row. The hint's nearest match is drawn from the authored keys
|
|
24
|
+
// AND the engine names, so the one hint worth keeping survives: a bare
|
|
25
|
+
// `title` / `id` / `path` / `tags` / `format` that spells an engine attribute
|
|
26
|
+
// without its prefix says "did you mean `_title`?". A `links` or `revisions`
|
|
27
|
+
// row carries no authored data at all, so a bare name there is
|
|
28
|
+
// `filter_invalid` either way (closed row shape). A repository with no live
|
|
29
|
+
// object of an open kind has no vocabulary: the check is skipped for that
|
|
30
|
+
// kind. The vocabulary is read once per (repository, kind) and cached on the
|
|
31
|
+
// repository's head commit (`MAX(commits.seq)`), the one thing every
|
|
32
|
+
// property or attrs change moves.
|
|
26
33
|
import { references } from "@omgbase/oqx";
|
|
27
|
-
import { ATTRIBUTES, SOURCE_HANDLES } from "./context.js";
|
|
34
|
+
import { ATTRIBUTES, REACH_THROUGHS, SOURCE_HANDLES } from "./context.js";
|
|
28
35
|
import { ROOTS } from "./root-row.js";
|
|
29
36
|
import { FROM, guards } from "./sql/scan.js";
|
|
30
37
|
import { FilterInvalid } from "../search/cel/parser.js";
|
|
31
38
|
/** The relations of each kind and the kind of the rows they yield (spec/surface §1.2). */
|
|
32
39
|
export const RELATIONS = {
|
|
33
|
-
docs: {
|
|
34
|
-
blocks: {
|
|
35
|
-
nodes: {
|
|
40
|
+
docs: { _blocks: "blocks", _nodes: "nodes", _out_links: "links", _in_links: "links", _revisions: "revisions" },
|
|
41
|
+
blocks: { _children: "blocks", _nodes: "nodes", _out_links: "links", _section: "nodes" },
|
|
42
|
+
nodes: { _blocks: "blocks", _subsections: "nodes", _children: "nodes" },
|
|
36
43
|
links: {},
|
|
37
44
|
revisions: {},
|
|
38
45
|
};
|
|
39
|
-
/** The
|
|
46
|
+
/** The reach-through handles of each kind and the kind of the row they reach (§1.2; the self aliases are gone in 5.0 — `$it` is the self). */
|
|
40
47
|
export const HANDLES = {
|
|
41
|
-
docs: {
|
|
42
|
-
blocks: {
|
|
43
|
-
nodes: {
|
|
44
|
-
links: {
|
|
45
|
-
revisions: {
|
|
48
|
+
docs: {},
|
|
49
|
+
blocks: { _doc: "docs" },
|
|
50
|
+
nodes: { _doc: "docs", _block: "blocks" },
|
|
51
|
+
links: { _doc: "docs", _dst: "docs", _block: "blocks" },
|
|
52
|
+
revisions: { _doc: "docs" },
|
|
46
53
|
};
|
|
47
54
|
/** The kinds whose row shape is closed: no authored data reaches a row by bare name. */
|
|
48
55
|
const CLOSED = new Set(["links", "revisions"]);
|
|
49
56
|
const NOUN = { docs: "document", blocks: "block", nodes: "node", links: "link", revisions: "revision" };
|
|
57
|
+
/** Every engine name of a kind, in the §1.2 table's order: attributes, reach-throughs, source handles (docs), relations. */
|
|
58
|
+
export function engineNames(target) {
|
|
59
|
+
return [...ATTRIBUTES[target], ...REACH_THROUGHS[target], ...(target === "docs" ? [...SOURCE_HANDLES] : []), ...Object.keys(RELATIONS[target])];
|
|
60
|
+
}
|
|
50
61
|
const CACHE = new WeakMap();
|
|
51
62
|
function headSeq(store, repoId) {
|
|
52
63
|
const row = store.db.prepare("SELECT COALESCE(MAX(seq), 0) AS seq FROM commits WHERE repo_id = ?").get(repoId);
|
|
@@ -65,7 +76,7 @@ function repoCache(store, repoId) {
|
|
|
65
76
|
/** The authored vocabulary of a kind: docs — every live frontmatter or inline
|
|
66
77
|
* key (the merged read a bare name performs); blocks and nodes — every key a
|
|
67
78
|
* live row's `attrs` holds (the bare-name read of those kinds). Computed keys
|
|
68
|
-
* (`$title`, `$tags`) are the
|
|
79
|
+
* (`$title`, `$tags`) are the attributes `_title`/`_tags`, not authored keys. */
|
|
69
80
|
function authoredKeys(store, repoId, target) {
|
|
70
81
|
let sql;
|
|
71
82
|
switch (target) {
|
|
@@ -150,7 +161,7 @@ function dotted(e) {
|
|
|
150
161
|
const inner = dotted(e.recv);
|
|
151
162
|
return inner === null ? null : { head: inner.head, rest: [...inner.rest, e.name] };
|
|
152
163
|
}
|
|
153
|
-
/** Follow a dotted path from a row kind through
|
|
164
|
+
/** Follow a dotted path from a row kind through reach-throughs and relations (`_doc._blocks` on a link → blocks). */
|
|
154
165
|
function kindOfPath(on, path) {
|
|
155
166
|
let cur = on;
|
|
156
167
|
for (let i = 0; i < path.length; i++) {
|
|
@@ -216,15 +227,19 @@ export function kindOfScope(steps) {
|
|
|
216
227
|
/** Judge a dotted name read on rows of `target`; null = known. */
|
|
217
228
|
function judge(store, repoId, target, path) {
|
|
218
229
|
const [head, ...rest] = path;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
if (head.startsWith("_")) {
|
|
231
|
+
// An engine attribute: what follows it (`_attrs.checked`, `_tags.size`) is the value's own navigation.
|
|
232
|
+
if (ATTRIBUTES[target].has(head))
|
|
233
|
+
return null;
|
|
234
|
+
// The source handles reach the authored data under them.
|
|
235
|
+
if (target === "docs" && SOURCE_HANDLES.has(head))
|
|
236
|
+
return rest.length === 0 ? null : judgeAuthored(store, repoId, "docs", rest, head);
|
|
237
|
+
const via = HANDLES[target][head] ?? RELATIONS[target][head];
|
|
238
|
+
if (via !== undefined)
|
|
239
|
+
return rest.length === 0 ? null : judge(store, repoId, via, rest);
|
|
240
|
+
// Not an engine name of this kind: the closed set is the whole set.
|
|
241
|
+
return { target, name: head, why: "engine" };
|
|
242
|
+
}
|
|
228
243
|
// A bare target name on a row is the context's own loud error (root-row.ts); not doubled here.
|
|
229
244
|
if (ROOTS.has(head))
|
|
230
245
|
return null;
|
|
@@ -233,22 +248,15 @@ function judge(store, repoId, target, path) {
|
|
|
233
248
|
function judgeAuthored(store, repoId, target, path, handle) {
|
|
234
249
|
const name = path.join(".");
|
|
235
250
|
if (CLOSED.has(target))
|
|
236
|
-
return { target, name, closed:
|
|
251
|
+
return { target, name, why: "closed", ...(handle !== undefined ? { handle } : {}) };
|
|
237
252
|
const v = vocabularyOf(store, repoId, target);
|
|
238
253
|
if (!v.present)
|
|
239
254
|
return null; // no object of the kind: no vocabulary to judge by
|
|
240
255
|
if (v.keys.has(name))
|
|
241
256
|
return null;
|
|
242
|
-
return { target, name,
|
|
257
|
+
return { target, name, why: "authored", ...(handle !== undefined ? { handle } : {}) };
|
|
243
258
|
}
|
|
244
259
|
// ---- messages --------------------------------------------------------------
|
|
245
|
-
/** The names a kind knows, for the nearest match: attributes, handles, relations, the authored keys. */
|
|
246
|
-
function candidates(store, repoId, target) {
|
|
247
|
-
const out = [...ATTRIBUTES[target], ...Object.keys(HANDLES[target]), ...(target === "docs" ? [...SOURCE_HANDLES] : []), ...Object.keys(RELATIONS[target])];
|
|
248
|
-
if (!CLOSED.has(target))
|
|
249
|
-
out.push(...vocabularyOf(store, repoId, target).list);
|
|
250
|
-
return out;
|
|
251
|
-
}
|
|
252
260
|
/** Optimal string alignment distance (Damerau–Levenshtein with adjacent transpositions, each edit 1). */
|
|
253
261
|
export function editDistance(a, b) {
|
|
254
262
|
const s = Array.from(a), t = Array.from(b);
|
|
@@ -270,21 +278,35 @@ export function editDistance(a, b) {
|
|
|
270
278
|
return d[m][n];
|
|
271
279
|
}
|
|
272
280
|
/**
|
|
273
|
-
* The nearest match of `name` among `candidates` (§1.5)
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
281
|
+
* The nearest match of `name` among `candidates` (§1.5). First, the one hint
|
|
282
|
+
* the name rule makes certain: a candidate that is exactly `name` with the
|
|
283
|
+
* engine prefix added or removed (`id` → `_id`, `_layer` → `layer`) wins
|
|
284
|
+
* outright, whatever its length. Otherwise case-insensitive and **prefix-blind**
|
|
285
|
+
* — the `_` is a declaration, not spelling, so `commited` is one edit from
|
|
286
|
+
* `_commit` and `titel` one from `_title` as from `title`: a candidate
|
|
287
|
+
* qualifies when its distance is ≤ 2, or when one of the two is a substring of
|
|
288
|
+
* the other and the shorter has at least three characters; the lowest distance
|
|
289
|
+
* wins, ties broken by the shorter candidate (so the bare authored `title`
|
|
290
|
+
* beats `_title` when both are one edit away), then alphabetically. Null when
|
|
291
|
+
* nothing qualifies.
|
|
278
292
|
*/
|
|
279
293
|
export function nearest(name, candidates) {
|
|
280
|
-
const
|
|
294
|
+
const lower = name.toLowerCase();
|
|
295
|
+
const prefixed = `_${lower}`;
|
|
296
|
+
const unprefixed = lower.startsWith("_") ? lower.slice(1) : null;
|
|
297
|
+
for (const c of candidates) {
|
|
298
|
+
const hay = c.toLowerCase();
|
|
299
|
+
if (hay === prefixed || (unprefixed !== null && hay === unprefixed))
|
|
300
|
+
return c;
|
|
301
|
+
}
|
|
302
|
+
const needle = lower.replace(/^_/, "");
|
|
281
303
|
// A name under three characters is within distance 2 of nearly anything, so
|
|
282
304
|
// only a substring match (which needs three characters anyway) could qualify.
|
|
283
305
|
if (needle.length < 3)
|
|
284
306
|
return null;
|
|
285
307
|
let best = null;
|
|
286
308
|
for (const c of new Set(candidates)) {
|
|
287
|
-
const hay = c.toLowerCase();
|
|
309
|
+
const hay = c.toLowerCase().replace(/^_/, "");
|
|
288
310
|
if (hay === needle)
|
|
289
311
|
continue;
|
|
290
312
|
const d = editDistance(needle, hay);
|
|
@@ -294,9 +316,9 @@ export function nearest(name, candidates) {
|
|
|
294
316
|
if (!(d <= 2 || sub))
|
|
295
317
|
continue;
|
|
296
318
|
// One typo (distance 1) wins first; then a substring match outranks a
|
|
297
|
-
// distance-2 match (`
|
|
298
|
-
// `
|
|
299
|
-
// then alphabetical.
|
|
319
|
+
// distance-2 match (`_via` → `_via_id`, not `_id`; `_dst_pth` → `_dst_path`,
|
|
320
|
+
// not `_dst`); within a class the lowest distance, then the shorter
|
|
321
|
+
// candidate, then alphabetical.
|
|
300
322
|
const rank = (x) => (x.d === 1 ? 0 : x.sub ? 1 : 2);
|
|
301
323
|
const better = best === null ||
|
|
302
324
|
rank({ d, sub }) < rank(best) ||
|
|
@@ -310,26 +332,39 @@ function suggestion(name, candidates) {
|
|
|
310
332
|
const c = nearest(name, candidates);
|
|
311
333
|
return c === null ? "" : `; did you mean \`${c}\`?`;
|
|
312
334
|
}
|
|
313
|
-
/** The
|
|
314
|
-
|
|
315
|
-
|
|
335
|
+
/** The candidates of a bare name: the kind's engine names (so `title` finds `_title`) and its authored keys. */
|
|
336
|
+
function candidates(store, repoId, target) {
|
|
337
|
+
const out = engineNames(target);
|
|
338
|
+
if (!CLOSED.has(target))
|
|
339
|
+
out.push(...vocabularyOf(store, repoId, target).list);
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
/** The error of an unknown engine name: `` `_target` is not an engine name of links (engine names: _id, _path, …); did you mean `_dst_path`? ``
|
|
343
|
+
* — the nearest match drawn from the engine names and the authored keys, so a `_layer` written for the authored `layer` is pointed at it. */
|
|
344
|
+
export function engineNameErrorMessage(store, repoId, target, name) {
|
|
345
|
+
return `\`${name}\` is not an engine name of ${target} (engine names: ${engineNames(target).join(", ")})${suggestion(name, candidates(store, repoId, target))}`;
|
|
346
|
+
}
|
|
347
|
+
/** The error of a bare name on a closed kind: `` `target` reads an authored property, and a link carries none (engine names: _id, _path, …); did you mean `_dst_path`? ``. */
|
|
348
|
+
export function closedKindErrorMessage(target, name, handle) {
|
|
349
|
+
const shown = handle !== undefined ? ` (\`${handle}.${name}\`)` : "";
|
|
350
|
+
return `\`${name}\`${shown} reads an authored property, and a ${NOUN[target]} carries none (engine names: ${engineNames(target).join(", ")})${suggestion(name, engineNames(target))}`;
|
|
316
351
|
}
|
|
317
|
-
/** The hint of an open kind: ``
|
|
318
|
-
* Under a source handle the name is
|
|
319
|
-
*
|
|
320
|
-
* the nearest match drawn from the authored keys alone. */
|
|
352
|
+
/** The hint of an open kind: `` no block in this repository carries a property `preview`; did you mean `_text`? ``.
|
|
353
|
+
* Under a source handle the name is shown with it — `` no document in this repository carries a property `x` (`_data.x`); did you mean `y`? `` —
|
|
354
|
+
* and the nearest match is drawn from the authored keys alone. */
|
|
321
355
|
export function propertyHintMessage(store, repoId, target, name, handle) {
|
|
322
356
|
if (handle !== undefined) {
|
|
323
357
|
return `no ${NOUN[target]} in this repository carries a property \`${name}\` (\`${handle}.${name}\`)${suggestion(name, vocabularyOf(store, repoId, target).list)}`;
|
|
324
358
|
}
|
|
325
|
-
return
|
|
359
|
+
return `no ${NOUN[target]} in this repository carries a property \`${name}\`${suggestion(name, candidates(store, repoId, target))}`;
|
|
326
360
|
}
|
|
327
361
|
// ---- the pass --------------------------------------------------------------
|
|
328
362
|
/**
|
|
329
|
-
* Judge every name the query reads. Returns the hints (one per
|
|
330
|
-
* of an open kind, first-reference order, deduplicated) under `"hint"`;
|
|
331
|
-
* `FilterInvalid` for
|
|
332
|
-
*
|
|
363
|
+
* Judge every name the query reads. Returns the hints (one per unknown bare
|
|
364
|
+
* name of an open kind, first-reference order, deduplicated) under `"hint"`;
|
|
365
|
+
* throws `FilterInvalid` for an unknown engine name (`_x`) and for a bare name
|
|
366
|
+
* on a closed kind in either mode, and for an unknown bare name on an open kind
|
|
367
|
+
* under `"refuse"`.
|
|
333
368
|
*/
|
|
334
369
|
export function checkNames(store, repoId, raw, mode) {
|
|
335
370
|
const hints = [];
|
|
@@ -343,8 +378,10 @@ export function checkNames(store, repoId, raw, mode) {
|
|
|
343
378
|
const verdict = judge(store, repoId, kind, ref.name.split("."));
|
|
344
379
|
if (verdict === null)
|
|
345
380
|
continue;
|
|
346
|
-
if (verdict.
|
|
347
|
-
throw new FilterInvalid(
|
|
381
|
+
if (verdict.why === "engine")
|
|
382
|
+
throw new FilterInvalid(engineNameErrorMessage(store, repoId, verdict.target, verdict.name), "OQX");
|
|
383
|
+
if (verdict.why === "closed")
|
|
384
|
+
throw new FilterInvalid(closedKindErrorMessage(verdict.target, verdict.name, verdict.handle), "OQX");
|
|
348
385
|
const message = propertyHintMessage(store, repoId, verdict.target, verdict.name, verdict.handle);
|
|
349
386
|
if (mode === "refuse")
|
|
350
387
|
throw new FilterInvalid(message, "OQX");
|
|
@@ -12,15 +12,11 @@ export declare function rootTarget(source: Expr): Target | null;
|
|
|
12
12
|
* • any `call` node (a function or method: unknown name, bad regex, wrong target…)
|
|
13
13
|
* • any nested block whose consumer is `single` (raises on 0 or >1 rows)
|
|
14
14
|
* • any `^`-escaped name: an `outer` reference or a `^name:` lift
|
|
15
|
-
* • a bare reserved docs basename (`id path updated_at hash raw`): as
|
|
16
|
-
* an `ident` at the ROOT scope when the target is `docs`, or as `doc.<name>`
|
|
17
|
-
* on any target at any depth (the reach-through row is a doc)
|
|
18
15
|
* • a bare root name (`docs blocks nodes links revisions`, root-row.ts) read
|
|
19
|
-
* as a property: as an `ident` at
|
|
20
|
-
*
|
|
21
|
-
* as
|
|
22
|
-
*
|
|
23
|
-
* relation (`doc.nodes`, `doc.blocks`, `block.nodes`, `section.blocks`)
|
|
16
|
+
* as a property: as an `ident` at any depth (since 5.0 a bare root name is
|
|
17
|
+
* never a relation — the relations are `_blocks`, `_nodes`, `_revisions`),
|
|
18
|
+
* or as `<head>.<name>` (`_doc.blocks`, `_block.nodes`: the loud error of a
|
|
19
|
+
* bare target name on the reached row)
|
|
24
20
|
*
|
|
25
21
|
* A residual made only of comparisons, logical operators, `in`/ranges, `!`,
|
|
26
22
|
* literals, bindings and plain reads (including exists/none/count/collect/first
|
|
@@ -39,15 +39,11 @@ export function rootTarget(source) {
|
|
|
39
39
|
* • any `call` node (a function or method: unknown name, bad regex, wrong target…)
|
|
40
40
|
* • any nested block whose consumer is `single` (raises on 0 or >1 rows)
|
|
41
41
|
* • any `^`-escaped name: an `outer` reference or a `^name:` lift
|
|
42
|
-
* • a bare reserved docs basename (`id path updated_at hash raw`): as
|
|
43
|
-
* an `ident` at the ROOT scope when the target is `docs`, or as `doc.<name>`
|
|
44
|
-
* on any target at any depth (the reach-through row is a doc)
|
|
45
42
|
* • a bare root name (`docs blocks nodes links revisions`, root-row.ts) read
|
|
46
|
-
* as a property: as an `ident` at
|
|
47
|
-
*
|
|
48
|
-
* as
|
|
49
|
-
*
|
|
50
|
-
* relation (`doc.nodes`, `doc.blocks`, `block.nodes`, `section.blocks`)
|
|
43
|
+
* as a property: as an `ident` at any depth (since 5.0 a bare root name is
|
|
44
|
+
* never a relation — the relations are `_blocks`, `_nodes`, `_revisions`),
|
|
45
|
+
* or as `<head>.<name>` (`_doc.blocks`, `_block.nodes`: the loud error of a
|
|
46
|
+
* bare target name on the reached row)
|
|
51
47
|
*
|
|
52
48
|
* A residual made only of comparisons, logical operators, `in`/ranges, `!`,
|
|
53
49
|
* literals, bindings and plain reads (including exists/none/count/collect/first
|
|
@@ -70,8 +66,8 @@ function opMayRaise(op, target, root) {
|
|
|
70
66
|
return true;
|
|
71
67
|
return exprMayRaise(op.receiver, target, root) || subMayRaise(op.sub, target);
|
|
72
68
|
}
|
|
73
|
-
// Inside a block the rows are a different scope (a relation's rows)
|
|
74
|
-
//
|
|
69
|
+
// Inside a block the rows are a different scope (a relation's rows); `root` is
|
|
70
|
+
// false there, so a bare root name raises whatever the target.
|
|
75
71
|
function subMayRaise(s, target) {
|
|
76
72
|
if (s.from.some((e) => exprMayRaise(e, target, false)))
|
|
77
73
|
return true;
|
|
@@ -10,12 +10,13 @@ export declare const TARGETS: ReadonlySet<string>;
|
|
|
10
10
|
/** The root collections, in the root object's entry order (§1.1). */
|
|
11
11
|
export declare const ROOT_NAMES: readonly Root[];
|
|
12
12
|
export declare const ROOTS: ReadonlySet<string>;
|
|
13
|
-
/** The root names
|
|
14
|
-
* on a document is its revision list (4.7).
|
|
13
|
+
/** The root names, as relations of each kind's rows (§1.2, `_`-prefixed since
|
|
14
|
+
* 5.0): `_revisions` on a document is its revision list (4.7). A BARE root name
|
|
15
|
+
* on a row is never a relation (it is the loud error below). */
|
|
15
16
|
export declare const TARGET_RELATIONS: Readonly<Record<Root, ReadonlySet<string>>>;
|
|
16
|
-
/** The reach-through heads and the target of the row they reach (`
|
|
17
|
+
/** The reach-through heads and the target of the row they reach (`_doc.x`, `_block.x`, and a link's `_dst.x`; spec/surface §1.2, 5.0 — the self aliases are gone). */
|
|
17
18
|
export declare const REACH_THROUGH: Readonly<Record<string, Target>>;
|
|
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 `^
|
|
19
|
+
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
20
|
/**
|
|
20
21
|
* The bare-target error. `depth` is the scope depth the name was read at when
|
|
21
22
|
* known statically (the carets in the hint count it); `null` when raised at read
|
|
@@ -35,5 +36,5 @@ export declare function pastRootMessage(name: string, levels: number): string;
|
|
|
35
36
|
* target's name before 3.0, gets its new name spelled out.
|
|
36
37
|
*/
|
|
37
38
|
export declare function unknownTargetMessage(name: string): string;
|
|
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. */
|
|
39
|
+
/** Whether `name`, read as `<head>.<name>` or bare on a row of `target`, is one of that row's relations named after a target (`_blocks`, `_nodes`, `_revisions`). */
|
|
39
40
|
export declare function isTargetRelationOf(target: Root | undefined, name: string): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
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
|
-
// enclosing block, or the absolute `0^docs`), `^
|
|
3
|
+
// enclosing block, or the absolute `0^docs`), `^_id` the repository id, and the
|
|
4
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
|
//
|
|
@@ -10,12 +10,10 @@
|
|
|
10
10
|
// `checkRootSpellings`);
|
|
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
|
-
// which has none
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// at read time on a row that lacks the relation (context.ts). A frontmatter key named after a
|
|
18
|
-
// target is unreachable by bare name (use `frontmatter.<k>`).
|
|
13
|
+
// which has none — refused statically for every root name since 5.0 (the
|
|
14
|
+
// relations are `_blocks`, `_nodes`, `_revisions`: a bare `blocks` is never
|
|
15
|
+
// one), and by the context at read time should one reach it (context.ts). A frontmatter key named after a
|
|
16
|
+
// target is unreachable by bare name (use `_frontmatter.<k>`).
|
|
19
17
|
/** The four row kinds whose names the unknown-names pass judges (names.ts keys
|
|
20
18
|
* its vocabulary tables by them); `revisions` joins when that pass learns the
|
|
21
19
|
* kind. Every OTHER root-collection test uses `ROOTS`. */
|
|
@@ -23,19 +21,20 @@ export const TARGETS = new Set(["docs", "blocks", "nodes", "links"]);
|
|
|
23
21
|
/** The root collections, in the root object's entry order (§1.1). */
|
|
24
22
|
export const ROOT_NAMES = ["docs", "blocks", "nodes", "links", "revisions"];
|
|
25
23
|
export const ROOTS = new Set(ROOT_NAMES);
|
|
26
|
-
/** The root names
|
|
27
|
-
* on a document is its revision list (4.7).
|
|
24
|
+
/** The root names, as relations of each kind's rows (§1.2, `_`-prefixed since
|
|
25
|
+
* 5.0): `_revisions` on a document is its revision list (4.7). A BARE root name
|
|
26
|
+
* on a row is never a relation (it is the loud error below). */
|
|
28
27
|
export const TARGET_RELATIONS = {
|
|
29
|
-
docs: new Set(["
|
|
30
|
-
blocks: new Set(["
|
|
31
|
-
nodes: new Set(["
|
|
28
|
+
docs: new Set(["_nodes", "_blocks", "_revisions"]),
|
|
29
|
+
blocks: new Set(["_nodes"]),
|
|
30
|
+
nodes: new Set(["_blocks"]),
|
|
32
31
|
links: new Set(),
|
|
33
32
|
revisions: new Set(),
|
|
34
33
|
};
|
|
35
|
-
/** The reach-through heads and the target of the row they reach (`
|
|
36
|
-
export const REACH_THROUGH = {
|
|
34
|
+
/** The reach-through heads and the target of the row they reach (`_doc.x`, `_block.x`, and a link's `_dst.x`; spec/surface §1.2, 5.0 — the self aliases are gone). */
|
|
35
|
+
export const REACH_THROUGH = { _doc: "docs", _block: "blocks", _dst: "docs" };
|
|
37
36
|
const NOUN = { docs: "documents", blocks: "blocks", nodes: "nodes", links: "links", revisions: "revisions" };
|
|
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 `^
|
|
37
|
+
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`";
|
|
39
38
|
/**
|
|
40
39
|
* The bare-target error. `depth` is the scope depth the name was read at when
|
|
41
40
|
* known statically (the carets in the hint count it); `null` when raised at read
|
|
@@ -66,7 +65,7 @@ export function unknownTargetMessage(name) {
|
|
|
66
65
|
const was = name === "edges" ? " (`edges` was renamed `links` in surface 3.0)" : "";
|
|
67
66
|
return `\`${name}\` is not a target — the repository's collections are ${ROOT_NAMES.map((r) => `\`${r}\``).join(", ")}${was}`;
|
|
68
67
|
}
|
|
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. */
|
|
68
|
+
/** Whether `name`, read as `<head>.<name>` or bare on a row of `target`, is one of that row's relations named after a target (`_blocks`, `_nodes`, `_revisions`). */
|
|
70
69
|
export function isTargetRelationOf(target, name) {
|
|
71
70
|
return target !== undefined && TARGET_RELATIONS[target].has(name);
|
|
72
71
|
}
|
package/dist/src/oqx-js/run.d.ts
CHANGED
|
@@ -8,20 +8,20 @@ export type EmbedQuery = (text: string) => Promise<{
|
|
|
8
8
|
model: string;
|
|
9
9
|
vec: Float32Array;
|
|
10
10
|
}>;
|
|
11
|
-
/** A query hit (spec/surface §1.4,
|
|
12
|
-
*
|
|
11
|
+
/** A query hit (spec/surface §1.4, 5.0): the row's identity under the SAME
|
|
12
|
+
* engine names the expression language reads — `_id`, `_path` — plus whatever
|
|
13
13
|
* `select` projected. A key copied from a hit is valid in the next `where` with
|
|
14
|
-
* no translation (`where
|
|
15
|
-
* envelope, and any other projection whose output key would be `
|
|
16
|
-
* must be aliased (`envelopeCollision`). */
|
|
14
|
+
* no translation (`where _id == "<hit._id>"`); a `select _id, _path` is simply
|
|
15
|
+
* the envelope, and any other projection whose output key would be `_id` or
|
|
16
|
+
* `_path` must be aliased (`envelopeCollision`). */
|
|
17
17
|
export interface OqxHit {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
_id: string;
|
|
19
|
+
_path: string;
|
|
20
20
|
[k: string]: unknown;
|
|
21
21
|
}
|
|
22
22
|
export interface OqxResult {
|
|
23
|
-
/** `{
|
|
24
|
-
* projection alone (`
|
|
23
|
+
/** `{ _id, _path, ...projection }` per hit — or, under `OqxOptions.bare`, the
|
|
24
|
+
* projection alone (`_id`/`_path` present only when the query projected them;
|
|
25
25
|
* typed as `OqxHit` for the one hit shape every consumer handles, a bare
|
|
26
26
|
* caller knows it asked). */
|
|
27
27
|
hits: OqxHit[];
|
|
@@ -47,16 +47,17 @@ export interface OqxResult {
|
|
|
47
47
|
export interface OqxOptions {
|
|
48
48
|
limit?: number;
|
|
49
49
|
cursor?: string | null;
|
|
50
|
-
/** Return each hit as its PROJECTION only — no automatic `
|
|
51
|
-
* members (spec/surface §1.4, 4.2): `select
|
|
52
|
-
* `{
|
|
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
53
|
* only its envelope) and is refused with `values` (already the bare-values
|
|
54
54
|
* form); both are `filter_invalid` before the engine runs. A projected
|
|
55
|
-
* `
|
|
56
|
-
* with no envelope there is nothing to collide with (`select
|
|
57
|
-
* is fine). Paging, `distinct`, `truncated` and `cursor` are
|
|
58
|
-
* the runner keeps identity internally and strips the envelope
|
|
59
|
-
* scalar consumers (`count`/`exists`/`none`) have no hits and
|
|
55
|
+
* `_id`/`_path` is the attribute as everywhere (`select _id` → `{ _id }`),
|
|
56
|
+
* and with no envelope there is nothing to collide with (`select _path:
|
|
57
|
+
* layer` is fine). Paging, `distinct`, `truncated` and `cursor` are
|
|
58
|
+
* unchanged — the runner keeps identity internally and strips the envelope
|
|
59
|
+
* last. The scalar consumers (`count`/`exists`/`none`) have no hits and
|
|
60
|
+
* ignore it. */
|
|
60
61
|
bare?: boolean;
|
|
61
62
|
semanticVectors?: Map<string, SemanticVec>;
|
|
62
63
|
/** Force the pure in-memory engine (skip tier-3 pushdown). Internal — used by
|