@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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON (§11.5): no whitespace; object keys sorted by their UTF-8
|
|
3
|
+
* bytes; members whose value is `undefined` omitted (an optional key absent is
|
|
4
|
+
* absent, never `null`; a JSON `null` value stays `null`); strings escaped only
|
|
5
|
+
* as JSON requires — `"`, `\`, and U+0000–U+001F (`\b \f \n \r \t`, else
|
|
6
|
+
* `\u00xx` lower-case) — everything else literal UTF-8; a number that is
|
|
7
|
+
* integral with |n| < 2^53 printed as a plain integer (`-0` as `0`), any other
|
|
8
|
+
* finite number by ECMAScript `Number::toString` (shortest round-trip digits);
|
|
9
|
+
* `true`/`false`/`null`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function canonicalJson(v: unknown): string;
|
|
12
|
+
/** A `plan` / `apply` / `execute` request as the caller gave it: `select`, `effects`, `expect` (each only when present). */
|
|
13
|
+
export interface FixRequest {
|
|
14
|
+
select?: unknown;
|
|
15
|
+
effects: unknown;
|
|
16
|
+
expect?: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** The `fix` for a request to the SAME tool: the corrected arguments, canonical JSON. */
|
|
19
|
+
export declare function fixRequest(req: FixRequest): string;
|
|
20
|
+
/** The `fix` naming another tool: `<tool> <canonical args>`. */
|
|
21
|
+
export declare function fixTool(tool: string, args: unknown): string;
|
|
22
|
+
/** The selector that names exactly one row: the request narrowed to `from <root> where id == "<id>"` with `expect.max: 1` (the fix of a cross-hit collision or an ambiguous target under a selector). */
|
|
23
|
+
export declare function narrowedTo(root: "docs" | "blocks" | "nodes", id: string): string;
|
|
24
|
+
/** The root a row id belongs to, by its prefix (`d_` / `b_` / `n_`). */
|
|
25
|
+
export declare function rootOfId(id: string): "docs" | "blocks" | "nodes";
|
|
26
|
+
/** `value` with the member at `path` (keys and array indices) replaced; a copy, the input untouched. */
|
|
27
|
+
export declare function withMember(value: unknown, path: readonly string[], replacement: unknown): unknown;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { MutationError } from "./tree.js";
|
|
2
|
+
/** The argument-shape error of the surface (`effects.ts` `effectInvalid`), spelled here to avoid the module cycle. */
|
|
3
|
+
function effectInvalid(message) {
|
|
4
|
+
return new MutationError("filter_invalid", message, { reason: message, hint: "arguments" });
|
|
5
|
+
}
|
|
6
|
+
// Canonical JSON (spec/mutate §11.5): the text a plan's digest is taken over,
|
|
7
|
+
// and since 3.3 the text every error `fix` and every `summary` value is spelled
|
|
8
|
+
// in — one serialization both engines produce byte for byte. Kept apart from
|
|
9
|
+
// `plan.ts` so the compile (`select.ts`) and the lowering (`effects.ts`) can use
|
|
10
|
+
// it without a module cycle.
|
|
11
|
+
function cmpUtf8(a, b) {
|
|
12
|
+
return Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
// A lone surrogate has no UTF-8 encoding: it cannot be digested (nor sent as JSON text).
|
|
15
|
+
function isWellFormed(s) {
|
|
16
|
+
for (let i = 0; i < s.length; i++) {
|
|
17
|
+
const c = s.charCodeAt(i);
|
|
18
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
19
|
+
const n = s.charCodeAt(i + 1);
|
|
20
|
+
if (!(n >= 0xdc00 && n <= 0xdfff))
|
|
21
|
+
return false;
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
function canonicalString(s) {
|
|
30
|
+
if (!isWellFormed(s))
|
|
31
|
+
throw effectInvalid("strings must be well-formed Unicode (a lone surrogate cannot be digested)");
|
|
32
|
+
let out = '"';
|
|
33
|
+
for (let i = 0; i < s.length; i++) {
|
|
34
|
+
const c = s.charCodeAt(i);
|
|
35
|
+
switch (c) {
|
|
36
|
+
case 0x22:
|
|
37
|
+
out += '\\"';
|
|
38
|
+
break;
|
|
39
|
+
case 0x5c:
|
|
40
|
+
out += "\\\\";
|
|
41
|
+
break;
|
|
42
|
+
case 0x08:
|
|
43
|
+
out += "\\b";
|
|
44
|
+
break;
|
|
45
|
+
case 0x0c:
|
|
46
|
+
out += "\\f";
|
|
47
|
+
break;
|
|
48
|
+
case 0x0a:
|
|
49
|
+
out += "\\n";
|
|
50
|
+
break;
|
|
51
|
+
case 0x0d:
|
|
52
|
+
out += "\\r";
|
|
53
|
+
break;
|
|
54
|
+
case 0x09:
|
|
55
|
+
out += "\\t";
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
out += c < 0x20 ? `\\u${c.toString(16).padStart(4, "0")}` : s[i];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out + '"';
|
|
62
|
+
}
|
|
63
|
+
function canonicalNumber(n) {
|
|
64
|
+
if (!Number.isFinite(n))
|
|
65
|
+
throw effectInvalid("a number in a plan must be finite");
|
|
66
|
+
if (Number.isInteger(n) && Math.abs(n) < 2 ** 53)
|
|
67
|
+
return String(n === 0 ? 0 : n);
|
|
68
|
+
return String(n);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Canonical JSON (§11.5): no whitespace; object keys sorted by their UTF-8
|
|
72
|
+
* bytes; members whose value is `undefined` omitted (an optional key absent is
|
|
73
|
+
* absent, never `null`; a JSON `null` value stays `null`); strings escaped only
|
|
74
|
+
* as JSON requires — `"`, `\`, and U+0000–U+001F (`\b \f \n \r \t`, else
|
|
75
|
+
* `\u00xx` lower-case) — everything else literal UTF-8; a number that is
|
|
76
|
+
* integral with |n| < 2^53 printed as a plain integer (`-0` as `0`), any other
|
|
77
|
+
* finite number by ECMAScript `Number::toString` (shortest round-trip digits);
|
|
78
|
+
* `true`/`false`/`null`.
|
|
79
|
+
*/
|
|
80
|
+
export function canonicalJson(v) {
|
|
81
|
+
if (v === null)
|
|
82
|
+
return "null";
|
|
83
|
+
switch (typeof v) {
|
|
84
|
+
case "boolean":
|
|
85
|
+
return v ? "true" : "false";
|
|
86
|
+
case "number":
|
|
87
|
+
return canonicalNumber(v);
|
|
88
|
+
case "string":
|
|
89
|
+
return canonicalString(v);
|
|
90
|
+
case "object": {
|
|
91
|
+
if (Array.isArray(v))
|
|
92
|
+
return `[${v.map((x) => canonicalJson(x === undefined ? null : x)).join(",")}]`;
|
|
93
|
+
const o = v;
|
|
94
|
+
const keys = Object.keys(o).filter((k) => o[k] !== undefined).sort(cmpUtf8);
|
|
95
|
+
return `{${keys.map((k) => `${canonicalString(k)}:${canonicalJson(o[k])}`).join(",")}}`;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
throw effectInvalid(`a ${typeof v} cannot be digested`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** The `fix` for a request to the SAME tool: the corrected arguments, canonical JSON. */
|
|
102
|
+
export function fixRequest(req) {
|
|
103
|
+
return canonicalJson({
|
|
104
|
+
...(req.select !== undefined ? { select: req.select } : {}),
|
|
105
|
+
effects: req.effects,
|
|
106
|
+
...(req.expect !== undefined ? { expect: req.expect } : {}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** The `fix` naming another tool: `<tool> <canonical args>`. */
|
|
110
|
+
export function fixTool(tool, args) {
|
|
111
|
+
return `${tool} ${canonicalJson(args)}`;
|
|
112
|
+
}
|
|
113
|
+
/** The selector that names exactly one row: the request narrowed to `from <root> where id == "<id>"` with `expect.max: 1` (the fix of a cross-hit collision or an ambiguous target under a selector). */
|
|
114
|
+
export function narrowedTo(root, id) {
|
|
115
|
+
return `from ${root} where id == ${JSON.stringify(id)}`;
|
|
116
|
+
}
|
|
117
|
+
/** The root a row id belongs to, by its prefix (`d_` / `b_` / `n_`). */
|
|
118
|
+
export function rootOfId(id) {
|
|
119
|
+
if (id.startsWith("d_"))
|
|
120
|
+
return "docs";
|
|
121
|
+
if (id.startsWith("n_"))
|
|
122
|
+
return "nodes";
|
|
123
|
+
return "blocks";
|
|
124
|
+
}
|
|
125
|
+
/** `value` with the member at `path` (keys and array indices) replaced; a copy, the input untouched. */
|
|
126
|
+
export function withMember(value, path, replacement) {
|
|
127
|
+
if (path.length === 0)
|
|
128
|
+
return replacement;
|
|
129
|
+
const [head, ...rest] = path;
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
const copy = [...value];
|
|
132
|
+
copy[Number(head)] = withMember(copy[Number(head)], rest, replacement);
|
|
133
|
+
return copy;
|
|
134
|
+
}
|
|
135
|
+
const o = (typeof value === "object" && value !== null ? value : {});
|
|
136
|
+
return { ...o, [head]: withMember(o[head], rest, replacement) };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=canonical.js.map
|
|
@@ -8,7 +8,10 @@ import { type LinkRepairCount, type RetargetHit } from "./macros.js";
|
|
|
8
8
|
import type { At, Expect } from "./ops.js";
|
|
9
9
|
import type { IdentityPreview } from "./opset.js";
|
|
10
10
|
import { MutationError } from "./tree.js";
|
|
11
|
-
|
|
11
|
+
import { type FixRequest } from "./canonical.js";
|
|
12
|
+
/** What an effect addresses. `section` is a derived range (ADR-005), never a stored id.
|
|
13
|
+
* `symbol` (3.2, §11.8 "Sugar"): a bare `"$new.n"` target whose kind — document or block — the
|
|
14
|
+
* symbol table decides when the effect is resolved; it never reaches a resolved effect. */
|
|
12
15
|
export type EffectTarget = {
|
|
13
16
|
kind: "document";
|
|
14
17
|
id?: string;
|
|
@@ -22,6 +25,9 @@ export type EffectTarget = {
|
|
|
22
25
|
} | {
|
|
23
26
|
kind: "section";
|
|
24
27
|
heading_id: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "symbol";
|
|
30
|
+
id: string;
|
|
25
31
|
};
|
|
26
32
|
export type EffectAt = At;
|
|
27
33
|
/** Guards: the kernel's two CAS fields plus the document's `current_rev` at plan time. */
|
|
@@ -167,12 +173,46 @@ export type ResolvedEffect = {
|
|
|
167
173
|
}[];
|
|
168
174
|
path_glob?: string;
|
|
169
175
|
};
|
|
170
|
-
/** The argument-shape error of the surface, thrown from the planner: `filter_invalid { reason, hint: "arguments" }
|
|
171
|
-
export declare function effectInvalid(message: string): MutationError;
|
|
172
|
-
/**
|
|
173
|
-
|
|
176
|
+
/** The argument-shape error of the surface, thrown from the planner: `filter_invalid { reason, hint: "arguments" }` — with a `fix` (3.4, §8) when the shape check can spell the corrected call. */
|
|
177
|
+
export declare function effectInvalid(message: string, fix?: string): MutationError;
|
|
178
|
+
/**
|
|
179
|
+
* What the shape check needs to spell a `fix` for the shape errors of §8
|
|
180
|
+
* (3.4): the request as given (the corrected call is that request with the
|
|
181
|
+
* offending effect rewritten — the effect at `effects[i]` AS WRITTEN, before
|
|
182
|
+
* any desugaring) and a live-row lookup for the one fix that needs the store:
|
|
183
|
+
* `parentOf(block id)` → the block's parent id (`null` at the top level) and
|
|
184
|
+
* its document's path (storage form), or `null` when no live block has the id
|
|
185
|
+
* (then there is no fix). The planner's compile (`select.ts`) supplies it; a
|
|
186
|
+
* library call without it gets the same errors with no `fix`.
|
|
187
|
+
*/
|
|
188
|
+
export interface ShapeFixContext {
|
|
189
|
+
request: FixRequest;
|
|
190
|
+
parentOf: (blockId: string) => {
|
|
191
|
+
parent: string | null;
|
|
192
|
+
path: string;
|
|
193
|
+
} | null;
|
|
194
|
+
}
|
|
195
|
+
/** The `-> ` leader (3.2, §11.8 "Sugar"): a string beginning exactly so is an OQX expression, the rest of the string. */
|
|
196
|
+
export declare const EXPRESSION_LEADER = "-> ";
|
|
197
|
+
/** A path-looking string: contains `/` or ends `.md` (the bare document target of §11.8 "Sugar"). */
|
|
198
|
+
export declare function looksLikePath(s: string): boolean;
|
|
199
|
+
/** A bare id of the three target kinds (§11.8 "Sugar"). */
|
|
200
|
+
export declare function isTargetId(s: string): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* A bare string where a target object is expected (3.2, §11.8 "Sugar"): a
|
|
203
|
+
* path-looking string → `{ kind: "document", path }`; a `b_`/`d_`/`n_` id →
|
|
204
|
+
* that kind; `$new.n` → `{ kind: "symbol", id }` (its kind is the symbol
|
|
205
|
+
* table's at resolution); a `#` locator → `filter_invalid` with a `fix`;
|
|
206
|
+
* anything else is no target. A `-> ` leader never reaches here from the
|
|
207
|
+
* planner (`select.ts` desugars it first); as a library input it is refused
|
|
208
|
+
* the same way, naming the planner.
|
|
209
|
+
*/
|
|
210
|
+
export declare function bareTarget(ref: string, here: string): EffectTarget;
|
|
211
|
+
export declare function validateTarget(v: unknown, here: string, kinds: readonly EffectTarget["kind"][]): EffectTarget;
|
|
212
|
+
/** The shape check of one effect (§11.1). `i` names it in the message (`effects[i]`); `fix` (3.4) lets the shape errors of §8 carry their corrected call. */
|
|
213
|
+
export declare function validateEffect(v: unknown, i: number, fix?: ShapeFixContext): Effect;
|
|
174
214
|
/** The whole list; a non-array or an empty list is a shape error too. */
|
|
175
|
-
export declare function validateEffects(v: unknown): Effect[];
|
|
215
|
+
export declare function validateEffects(v: unknown, fix?: ShapeFixContext): Effect[];
|
|
176
216
|
export declare const FRONTMATTER_FIELD: RegExp;
|
|
177
217
|
/** The focus a conflict names: `{ kind, target, field? }`. */
|
|
178
218
|
export interface Focus {
|
|
@@ -304,6 +344,8 @@ export interface LoweringContext {
|
|
|
304
344
|
canonical: (v: unknown) => string;
|
|
305
345
|
/** the resolved effects kept (dropped duplicates excluded) */
|
|
306
346
|
resolved: ResolvedEffect[];
|
|
347
|
+
/** `preview.summary` (3.3, §11.9): one line in words per resolved effect, in order */
|
|
348
|
+
summary: string[];
|
|
307
349
|
}
|
|
308
350
|
/** Resolve a document target: an existing document's `{ id, path }`, or a `$new.n` document this plan creates. */
|
|
309
351
|
export declare function resolveDocumentTarget(ctx: LoweringContext, t: Extract<EffectTarget, {
|
|
@@ -311,8 +353,16 @@ export declare function resolveDocumentTarget(ctx: LoweringContext, t: Extract<E
|
|
|
311
353
|
}>): Extract<ResolvedTarget, {
|
|
312
354
|
kind: "document";
|
|
313
355
|
}>;
|
|
314
|
-
/**
|
|
315
|
-
export declare function
|
|
356
|
+
/** An existing document's `{ id, path }` for a literal document target (`doc_missing`; `filter_invalid` when `id` and `path` disagree). */
|
|
357
|
+
export declare function findDocumentTarget(store: Store, repoId: string, t: Extract<EffectTarget, {
|
|
358
|
+
kind: "document";
|
|
359
|
+
}>): Extract<ResolvedTarget, {
|
|
360
|
+
kind: "document";
|
|
361
|
+
}>;
|
|
362
|
+
/** `1 block` / `2 blocks` (an `s` on every noun of the table). */
|
|
363
|
+
export declare function plural(n: number, noun: string): string;
|
|
364
|
+
/** Resolve one validated effect's targets (§11.4): documents to `{ id, path }`, defaults made explicit; `i` names the effect in messages. */
|
|
365
|
+
export declare function resolveEffect(ctx: LoweringContext, e: Effect, i?: number): ResolvedEffect;
|
|
316
366
|
/** Shift a kernel op's `$n.ids[i]` placeholders by `offset` (an opset planned alone, spliced into a longer op list). */
|
|
317
367
|
export declare function shiftPlaceholders(op: Op, offset: number): Op;
|
|
318
368
|
/**
|