@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.
Files changed (47) hide show
  1. package/dist/src/core/ingest.d.ts +2 -0
  2. package/dist/src/core/ingest.js +13 -0
  3. package/dist/src/core/read/document.d.ts +9 -8
  4. package/dist/src/core/read/document.js +0 -2
  5. package/dist/src/core/store/gc.d.ts +13 -1
  6. package/dist/src/core/store/gc.js +24 -5
  7. package/dist/src/core/store/size.d.ts +79 -0
  8. package/dist/src/core/store/size.js +232 -0
  9. package/dist/src/index.d.ts +4 -1
  10. package/dist/src/index.js +5 -1
  11. package/dist/src/mcp/arguments.d.ts +10 -2
  12. package/dist/src/mcp/arguments.js +37 -3
  13. package/dist/src/mcp/errors.d.ts +1 -1
  14. package/dist/src/mcp/reference.d.ts +1 -1
  15. package/dist/src/mcp/reference.js +63 -10
  16. package/dist/src/mcp/server.d.ts +34 -1
  17. package/dist/src/mcp/server.js +158 -46
  18. package/dist/src/mutate/canonical.d.ts +27 -0
  19. package/dist/src/mutate/canonical.js +138 -0
  20. package/dist/src/mutate/effects.d.ts +58 -8
  21. package/dist/src/mutate/effects.js +312 -43
  22. package/dist/src/mutate/known-ids.d.ts +19 -0
  23. package/dist/src/mutate/known-ids.js +47 -7
  24. package/dist/src/mutate/ops.d.ts +1 -0
  25. package/dist/src/mutate/ops.js +20 -1
  26. package/dist/src/mutate/plan.d.ts +26 -16
  27. package/dist/src/mutate/plan.js +152 -112
  28. package/dist/src/mutate/select.d.ts +78 -0
  29. package/dist/src/mutate/select.js +903 -0
  30. package/dist/src/mutate/tree.d.ts +2 -2
  31. package/dist/src/oqx-js/context.d.ts +9 -2
  32. package/dist/src/oqx-js/context.js +74 -28
  33. package/dist/src/oqx-js/names.d.ts +40 -0
  34. package/dist/src/oqx-js/names.js +347 -0
  35. package/dist/src/oqx-js/planner.d.ts +3 -3
  36. package/dist/src/oqx-js/planner.js +17 -9
  37. package/dist/src/oqx-js/root-row.d.ts +15 -4
  38. package/dist/src/oqx-js/root-row.js +18 -10
  39. package/dist/src/oqx-js/run.d.ts +45 -1
  40. package/dist/src/oqx-js/run.js +114 -32
  41. package/dist/src/oqx-js/sql/translate.js +1 -1
  42. package/dist/src/spec-versions.d.ts +5 -5
  43. package/dist/src/spec-versions.js +4 -4
  44. package/dist/src/sync/observe.js +9 -3
  45. package/dist/src/version.d.ts +7 -2
  46. package/dist/src/version.js +5 -2
  47. 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
- /** What an effect addresses. `section` is a derived range (ADR-005), never a stored id. */
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
- /** The shape check of one effect (§11.1). `i` names it in the message (`effects[i]`). */
173
- export declare function validateEffect(v: unknown, i: number): Effect;
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
- /** Resolve one validated effect's targets (§11.4): documents to `{ id, path }`, defaults made explicit. */
315
- export declare function resolveEffect(ctx: LoweringContext, e: Effect): ResolvedEffect;
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
  /**