@libredb/libredb 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/lens/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,MAAM,EAAiC,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkChD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAC/B,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC;AAEnC,6EAA6E;AAC7E,MAAM,UAAU,SAAS,CAAC,GAAQ;IAChC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAQ,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,SAAS,CAAC,CAAwB,EAAE,CAAwB;IACnE,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,oDAAoD;IAC9E,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,CAAC,kDAAkD;IAC9F,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,+BAA+B;IACjG,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,4CAA4C;IAC7F,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,CAAgB,CAAC;QAC9B,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC3C,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,IAAI,GAAG,CAAiC,CAAC;IAC/C,MAAM,IAAI,GAAG,CAAiC,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,KAAK,CAAC,KAAK,CAChB,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAC5F,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,QAAa,EAAE,SAAc;IACnD,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACzF,CAAC;AAED;;;;;;GAMG;AACH,SAAS,KAAK,CAAC,UAAkB,EAAE,EAAU;IAC3C,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,UAAU,IAAI,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAwCD;;;;GAIG;AACH,MAAM,UAAU,GAAG,CAAC,KAAY,EAAE,UAAkB;IAClD,8EAA8E;IAC9E,8EAA8E;IAC9E,+EAA+E;IAC/E,2EAA2E;IAC3E,+EAA+E;IAC/E,MAAM,MAAM,GAAG,GAAG,UAAU,GAAG,CAAC;IAChC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAExD,2EAA2E;IAC3E,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,wBAAwB;IACxB,MAAM,IAAI,GAAG,CAAC,IAAgC,EAAoB,EAAE,CAClE,MAAM,CAAC,GAAG,EAAE,CACV,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE;QACpB,MAAM,IAAI,GAAe,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CACH,CAAC;IAEJ,OAAO;QACL,GAAG,CAAC,EAAE,EAAE,QAAQ;YACd,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE;gBACpB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACxB,CAAC;QACD,GAAG,CAAC,EAAE;YACJ,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE;gBAC3B,MAAM,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC5C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,EAAE;YACP,yEAAyE;YACzE,0EAA0E;YAC1E,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE;gBACpC,MAAM,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBAChC,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;gBACxC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACb,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC;QACD,GAAG;YACD,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,SAAS;YACZ,OAAO,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * lens/kv.ts — the key-value lens, LibreDB's proof lens.
3
+ *
4
+ * The kernel (core.ts) is already an ordered byte key-value store, so this lens
5
+ * is the thinnest one possible: it does not add storage, transactions, or
6
+ * recovery — it only puts an ergonomic face on what the kernel already does.
7
+ * That makes it the honest proof of the architecture (DESIGN.md section 6):
8
+ * "here is the ordered KV core, usable directly."
9
+ *
10
+ * What the lens adds, and nothing more:
11
+ * - STRING ergonomics. The kernel speaks raw bytes on purpose; this lens
12
+ * encodes string keys/values as UTF-8 on the way in and decodes on the way
13
+ * out, so it reads like the `Map<string, string>` it means to replace.
14
+ * - The SHARED ENVELOPE. Reads return a {@link Result}, writes return a
15
+ * {@link WriteResult} (lens/types.ts), the shapes every later lens reuses.
16
+ *
17
+ * Each operation runs in its own transaction (auto-commit), so the lens behaves
18
+ * like a durable map: a write is atomic and, on a file-backed database, fsync'd
19
+ * before it returns. Multi-key atomicity is a conscious omission for the proof
20
+ * lens — a caller that needs it reaches the kernel's `transact` directly. The
21
+ * lens is a view: it depends only on the {@link Store} seam (`transact`), never
22
+ * on the store's lifecycle; whoever opened the store closes it.
23
+ */
24
+ import { type Result, type WriteResult } from "./types.ts";
25
+ import type { Store } from "../adapter/store.ts";
26
+ /** One key/value pair from a range scan, decoded to strings. The kv-lens
27
+ * counterpart of the kernel's byte-level {@link import("../core.ts").Entry}. */
28
+ export interface KvEntry {
29
+ readonly key: string;
30
+ readonly value: string;
31
+ }
32
+ /**
33
+ * The key-value lens surface: a durable, ordered, string-keyed map.
34
+ *
35
+ * Keys are ordered by the UTF-8 byte order the kernel sorts on, so `range`
36
+ * yields entries in ascending key order over the half-open interval
37
+ * `[start, end)` — `start` included, `end` excluded.
38
+ */
39
+ export interface Kv {
40
+ /** The value stored for `key`, or `undefined` if it is not set. */
41
+ get(key: string): string | undefined;
42
+ /** Store `value` under `key`, overwriting any existing value. Always reports
43
+ * one changed entry. */
44
+ set(key: string, value: string): WriteResult;
45
+ /** Remove `key`. Reports one changed entry if it existed, zero if it did not. */
46
+ delete(key: string): WriteResult;
47
+ /** Scan `[start, end)` in ascending key order. The {@link Result} is lazy and
48
+ * re-iterable: each pass re-runs the scan against the current state. */
49
+ range(start: string, end: string): Result<KvEntry>;
50
+ /** Scan every key beginning with `prefix`, in ascending key order — the
51
+ * canonical ordered-KV query. Throws if `prefix` is empty (it has no finite
52
+ * upper bound; use {@link range} to scan an explicit interval). The
53
+ * {@link Result} is lazy and re-iterable, like {@link range}. */
54
+ prefix(prefix: string): Result<KvEntry>;
55
+ }
56
+ /** Build a {@link Kv} lens over a {@link Store} (the kernel's `Database`
57
+ * satisfies it, as does any object that can run a transaction). */
58
+ export declare function kv(store: Store): Kv;
59
+ //# sourceMappingURL=kv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../../src/lens/kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAU,KAAK,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD;gFACgF;AAChF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,EAAE;IACjB,mEAAmE;IACnE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC;4BACwB;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7C,iFAAiF;IACjF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;IACjC;4EACwE;IACxE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnD;;;qEAGiE;IACjE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;CACzC;AAOD;mEACmE;AACnE,wBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,EAAE,CAmCnC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * lens/kv.ts — the key-value lens, LibreDB's proof lens.
3
+ *
4
+ * The kernel (core.ts) is already an ordered byte key-value store, so this lens
5
+ * is the thinnest one possible: it does not add storage, transactions, or
6
+ * recovery — it only puts an ergonomic face on what the kernel already does.
7
+ * That makes it the honest proof of the architecture (DESIGN.md section 6):
8
+ * "here is the ordered KV core, usable directly."
9
+ *
10
+ * What the lens adds, and nothing more:
11
+ * - STRING ergonomics. The kernel speaks raw bytes on purpose; this lens
12
+ * encodes string keys/values as UTF-8 on the way in and decodes on the way
13
+ * out, so it reads like the `Map<string, string>` it means to replace.
14
+ * - The SHARED ENVELOPE. Reads return a {@link Result}, writes return a
15
+ * {@link WriteResult} (lens/types.ts), the shapes every later lens reuses.
16
+ *
17
+ * Each operation runs in its own transaction (auto-commit), so the lens behaves
18
+ * like a durable map: a write is atomic and, on a file-backed database, fsync'd
19
+ * before it returns. Multi-key atomicity is a conscious omission for the proof
20
+ * lens — a caller that needs it reaches the kernel's `transact` directly. The
21
+ * lens is a view: it depends only on the {@link Store} seam (`transact`), never
22
+ * on the store's lifecycle; whoever opened the store closes it.
23
+ */
24
+ import { result } from "./types.js";
25
+ import { prefixRange } from "../query/range.js";
26
+ const utf8 = new TextEncoder();
27
+ const fromUtf8 = new TextDecoder();
28
+ const encode = (s) => utf8.encode(s);
29
+ const decode = (b) => fromUtf8.decode(b);
30
+ /** Build a {@link Kv} lens over a {@link Store} (the kernel's `Database`
31
+ * satisfies it, as does any object that can run a transaction). */
32
+ export function kv(store) {
33
+ return {
34
+ get(key) {
35
+ return store.transact((tx) => {
36
+ const value = tx.get(encode(key));
37
+ return value === undefined ? undefined : decode(value);
38
+ });
39
+ },
40
+ set(key, value) {
41
+ store.transact((tx) => {
42
+ tx.set(encode(key), encode(value));
43
+ });
44
+ return { changed: 1 };
45
+ },
46
+ delete(key) {
47
+ const changed = store.transact((tx) => {
48
+ const k = encode(key);
49
+ const existed = tx.get(k) !== undefined;
50
+ tx.delete(k);
51
+ return existed ? 1 : 0;
52
+ });
53
+ return { changed };
54
+ },
55
+ range(start, end) {
56
+ return scan(store, encode(start), encode(end));
57
+ },
58
+ prefix(p) {
59
+ // prefixRange computes the [start, end) bounds on bytes so they agree with
60
+ // the kernel's order, and rejects a prefix with no finite end (an empty
61
+ // string). It runs at call time, so a bad prefix fails here, not on a
62
+ // later iteration; the scan itself stays lazy.
63
+ const { start, end } = prefixRange(encode(p));
64
+ return scan(store, start, end);
65
+ },
66
+ };
67
+ }
68
+ /**
69
+ * A lazy, re-iterable {@link Result} over the kernel's half-open `[start, end)`
70
+ * byte scan, decoded to string {@link KvEntry} rows. Shared by `range` and
71
+ * `prefix` so the decode-and-materialize step has one definition.
72
+ *
73
+ * The thunk runs on each iteration (laziness + re-iterability come from
74
+ * result()), so every pass observes the current committed state. Rows are
75
+ * materialized inside the read transaction because the kernel's range is only
76
+ * valid within the transaction body; nothing runs until the Result is iterated.
77
+ */
78
+ function scan(store, start, end) {
79
+ return result(() => store.transact((tx) => {
80
+ const rows = [];
81
+ for (const entry of tx.getRange(start, end)) {
82
+ rows.push({ key: decode(entry.key), value: decode(entry.value) });
83
+ }
84
+ return rows;
85
+ }));
86
+ }
87
+ //# sourceMappingURL=kv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv.js","sourceRoot":"","sources":["../../src/lens/kv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ import { type Result, type WriteResult } from "./types.ts";
2
+ import type { Store } from "../adapter/store.ts";
3
+ /**
4
+ * A column's declared type. These are the JSON shapes a relational column can
5
+ * hold (DESIGN.md section 6.2): the three scalars plus a nested object. Arrays
6
+ * and `null` are deliberately not column types in v1 — `"object"` means a plain
7
+ * JSON object, and nullable/optional columns are an honest deferral.
8
+ */
9
+ export type ColumnType = "string" | "number" | "boolean" | "object";
10
+ /**
11
+ * A table schema: the primary-key column name and the declared columns with
12
+ * their types. `primaryKey` must name a declared column whose type is `"string"`
13
+ * (it becomes the kernel key `<table>:<pk>`), checked when the {@link table}
14
+ * handle is built.
15
+ */
16
+ export interface TableSchema {
17
+ readonly primaryKey: string;
18
+ readonly columns: {
19
+ readonly [name: string]: ColumnType;
20
+ };
21
+ }
22
+ /**
23
+ * A table row: a JSON object whose fields are the schema's columns. The value
24
+ * type mirrors {@link ColumnType} (string/number/boolean/object). A row is
25
+ * validated against the schema on insert before it is stored.
26
+ */
27
+ export type Row = {
28
+ [column: string]: string | number | boolean | object;
29
+ };
30
+ /**
31
+ * A lazy, chainable query over a table's rows.
32
+ *
33
+ * A Query IS a {@link Result} (it is iterable and has `toArray`), so it can be
34
+ * consumed directly; it additionally offers `where` and `select`, each returning
35
+ * a new Query so operations compose (`table.where(...).select(...)`). Like every
36
+ * lens read it stays lazy and re-iterable: nothing runs until iterated, and each
37
+ * pass re-runs the underlying table scan against current state.
38
+ */
39
+ export interface Query extends Result<Row> {
40
+ /** Keep only the rows whose top-level fields all equal `predicate`'s (deep
41
+ * structural equality, type-sensitive — the same matcher the document lens
42
+ * uses). Multiple predicate fields are an implicit AND; an empty predicate
43
+ * keeps every row. Chained `where`s narrow further (also an AND). */
44
+ where(predicate: Row): Query;
45
+ /** Project each row down to only the named `columns`. A requested column that
46
+ * a row does not have is simply omitted from that row's projection. On a joined
47
+ * result the columns are the qualified `table.column` names, which `select`
48
+ * matches literally — so projecting `"users.id"` works the same as any column. */
49
+ select(...columns: string[]): Query;
50
+ /**
51
+ * Inner equi-join this query's rows against `other`'s on `this[leftField]`
52
+ * deeply equal to `other[rightField]`, returning a chainable {@link Query} of
53
+ * the matched, combined rows. Each result row carries every column of both
54
+ * sides, qualified as `table.column` (e.g. `users.id`, `orders.total`), so the
55
+ * two sides never collide and `select`/`where` can name a column unambiguously.
56
+ *
57
+ * Inner means unmatched rows on either side are dropped; one left key matching
58
+ * several right rows fans out to several result rows (and vice versa). Joining
59
+ * on a key that a row lacks never matches that row (a missing key is not a join
60
+ * value), so it cannot produce a cross product by accident.
61
+ *
62
+ * Cost: O(n*m) — a nested loop over this query's `n` rows and `other`'s `m`
63
+ * rows, the honest minimal join with no indexes (DESIGN.md section 6.2).
64
+ */
65
+ join(other: Table, leftField: string, rightField: string): Query;
66
+ }
67
+ /**
68
+ * A handle on one typed table over a {@link Store}.
69
+ *
70
+ * Like the other lenses, the handle is a view: it depends only on the `Store`
71
+ * seam and never touches lifecycle. `insert` auto-commits in its own kernel
72
+ * transaction (a file-backed write is fsync'd before it returns). Read-side
73
+ * operations are added in later phases.
74
+ */
75
+ export interface Table {
76
+ /** This table's name — the qualifier its columns take in a join
77
+ * (`<name>.<column>`). A join reads it off the right-hand table. */
78
+ readonly name: string;
79
+ /**
80
+ * Validate `row` against the schema and store it under `<table>:<pk>`,
81
+ * overwriting any existing row at that primary key. Throws if the row is
82
+ * invalid (missing/wrong-typed column, or an unknown field). Reports one
83
+ * changed entry.
84
+ */
85
+ insert(row: Row): WriteResult;
86
+ /** The row stored under primary key `pk`, or `undefined` if none exists. The
87
+ * returned row is the whole stored object, including the primary-key column. */
88
+ get(pk: string): Row | undefined;
89
+ /** Remove the row at primary key `pk`. Reports one changed entry if it
90
+ * existed, zero if it did not. */
91
+ delete(pk: string): WriteResult;
92
+ /** Every row in the table, in ascending primary-key (byte) order, as a
93
+ * chainable {@link Query}. Each row is the whole stored object (the primary key
94
+ * is a declared column, so it is part of the row — there is no separate id).
95
+ * The Query is lazy and re-iterable: each pass re-runs the scan against current
96
+ * state. The scan sees only this table's rows — the `<table>:` byte prefix is a
97
+ * sound boundary, so a sibling whose name shares a prefix (`users` vs `users2`)
98
+ * is never included. */
99
+ all(): Query;
100
+ /** The rows matching `predicate` (top-level field equality). Sugar for
101
+ * `all().where(predicate)`; returns a chainable {@link Query}. */
102
+ where(predicate: Row): Query;
103
+ /** Every row projected to only `columns`. Sugar for `all().select(...columns)`;
104
+ * returns a chainable {@link Query}. */
105
+ select(...columns: string[]): Query;
106
+ /** Inner equi-join every row of this table against `other` on
107
+ * `this[leftField]` equal to `other[rightField]`. Sugar for
108
+ * `all().join(other, leftField, rightField)`; returns a chainable {@link Query}
109
+ * of qualified `table.column` rows. */
110
+ join(other: Table, leftField: string, rightField: string): Query;
111
+ }
112
+ /**
113
+ * Build a {@link Table} handle for `name` with `schema` over a {@link Store}
114
+ * (the kernel's `Database` satisfies it). The schema is validated immediately:
115
+ * the `primaryKey` must name a declared column whose type is `"string"`, since
116
+ * the primary key becomes the string kernel key.
117
+ */
118
+ export declare function table(store: Store, name: string, schema: TableSchema): Table;
119
+ //# sourceMappingURL=relational.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relational.d.ts","sourceRoot":"","sources":["../../src/lens/relational.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAU,KAAK,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAC3D;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,GAAG;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AA0C3E;;;;;;;;GAQG;AACH,MAAM,WAAW,KAAM,SAAQ,MAAM,CAAC,GAAG,CAAC;IACxC;;;yEAGqE;IACrE,KAAK,CAAC,SAAS,EAAE,GAAG,GAAG,KAAK,CAAC;IAC7B;;;sFAGkF;IAClF,MAAM,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACpC;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;CAClE;AAuGD;;;;;;;GAOG;AACH,MAAM,WAAW,KAAK;IACpB;wEACoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAAC;IAC9B;oFACgF;IAChF,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;IACjC;sCACkC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC;;;;;;4BAMwB;IACxB,GAAG,IAAI,KAAK,CAAC;IACb;sEACkE;IAClE,KAAK,CAAC,SAAS,EAAE,GAAG,GAAG,KAAK,CAAC;IAC7B;4CACwC;IACxC,MAAM,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACpC;;;2CAGuC;IACvC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,KAAK,CAoD5E"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * lens/relational.ts — the relational lens, LibreDB's reach lens.
3
+ *
4
+ * This is a deliberately minimal *relational view*, NOT a SQL engine (DESIGN.md
5
+ * section 6.2): there is no SQL text, lexer, or planner. A table is a typed,
6
+ * schema-validated collection of rows stored as JSON objects under a
7
+ * `<table>:<pk>` kernel key — the exact storage scheme the document lens uses,
8
+ * reusing its JSON codec. The relational value the lens adds over the document
9
+ * lens is the *schema*: declared columns with declared types, enforced at insert.
10
+ *
11
+ * Schema validation lives here; row STORAGE is delegated to the document lens.
12
+ * Because a validated row is just a JSON object stored at `<table>:<pk>` — the
13
+ * exact key the document lens uses for `<collection>:<id>` — a table is literally
14
+ * a schema-validated document collection. The relational lens holds a
15
+ * {@link doc} handle for the same name and reads/writes rows through it, so it
16
+ * inherits the document lens's codec and its collection-isolation-safe prefix
17
+ * scan for free (a sibling like `users2` never leaks into `users`).
18
+ *
19
+ * On top of the schema/row vocabulary, the {@link table} handle, insert-time
20
+ * validation, and by-pk CRUD (`get`/`delete`/`all`), this lens adds a small
21
+ * chainable query surface: `where` (top-level field-equality filter, reusing the
22
+ * document lens's matcher), `select` (column projection), and `join` (inner
23
+ * equi-join via nested loop, producing rows with columns qualified as
24
+ * `table.column`). The kernel is unchanged.
25
+ */
26
+ import { doc, matches } from "./document.js";
27
+ import { result } from "./types.js";
28
+ /** Whether `value` matches the declared `type`. `"object"` is a plain JSON
29
+ * object — `null` (typeof `"object"`) and arrays are rejected, since v1 has
30
+ * neither nullable columns nor an array column type. */
31
+ function matchesType(value, type) {
32
+ switch (type) {
33
+ case "string":
34
+ return typeof value === "string";
35
+ case "number":
36
+ return typeof value === "number";
37
+ case "boolean":
38
+ return typeof value === "boolean";
39
+ case "object":
40
+ return typeof value === "object" && value !== null && !Array.isArray(value);
41
+ }
42
+ }
43
+ /**
44
+ * Validate `row` against `schema`, throwing on the first violation (DESIGN.md
45
+ * section 6.2: declared columns required, types checked, unknown fields
46
+ * rejected). Strict by design — there is no silent coercion or dropping.
47
+ */
48
+ function validateRow(schema, row) {
49
+ // Unknown fields: every row key must be a declared column.
50
+ for (const key of Object.keys(row)) {
51
+ if (!Object.prototype.hasOwnProperty.call(schema.columns, key)) {
52
+ throw new Error(`unknown column "${key}" (not declared in the table schema)`);
53
+ }
54
+ }
55
+ // Declared columns: each must be present and match its declared type. A
56
+ // missing or null value fails here (no nullable/optional columns in v1).
57
+ for (const [name, type] of Object.entries(schema.columns)) {
58
+ if (!Object.prototype.hasOwnProperty.call(row, name)) {
59
+ throw new Error(`missing required column "${name}"`);
60
+ }
61
+ if (!matchesType(row[name], type)) {
62
+ throw new Error(`column "${name}" expected ${type}, got ${typeof row[name]}`);
63
+ }
64
+ }
65
+ }
66
+ /** Whether `row` matches `predicate` by the document lens's field-equality
67
+ * matcher. The casts are the same Row/Doc type-system artifact `table` uses
68
+ * elsewhere (their value unions overlap but neither is a subtype of the other);
69
+ * the runtime values are plain JSON. */
70
+ function rowMatches(row, predicate) {
71
+ return matches(row, predicate);
72
+ }
73
+ /** A row containing only `columns` that are actually present on `row`. A row's
74
+ * values are always defined (it comes from JSON, which has no `undefined`), so an
75
+ * absent column reads as `undefined` and is omitted from the projection. */
76
+ function project(row, columns) {
77
+ const out = {};
78
+ for (const column of columns) {
79
+ const value = row[column];
80
+ if (value !== undefined) {
81
+ out[column] = value;
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ /** Whether a left row joins to a right row: `left[leftField]` deeply equals
87
+ * `right[rightField]`. A join match is literally "the left row matches a
88
+ * predicate built from the right row's join value", so it reuses the document
89
+ * lens's `matches` for the exact same deep, type-sensitive equality `where` uses.
90
+ * A field a row lacks reads as `undefined`, which is never a join value (a row
91
+ * from JSON never holds `undefined`), so a missing key on either side never
92
+ * matches — an inner join, not an accidental cross product. */
93
+ function joinKeyEqual(left, leftField, right, rightField) {
94
+ const rightValue = right[rightField];
95
+ if (left[leftField] === undefined || rightValue === undefined)
96
+ return false;
97
+ return matches(left, { [leftField]: rightValue });
98
+ }
99
+ /** Combine a matched left/right pair into one row whose columns are qualified by
100
+ * their table name (`<table>.<column>`), so columns of the same name on the two
101
+ * sides stay distinct and a later `select`/`where` can name either unambiguously. */
102
+ function qualify(leftName, left, rightName, right) {
103
+ const out = {};
104
+ for (const [column, value] of Object.entries(left))
105
+ out[`${leftName}.${column}`] = value;
106
+ for (const [column, value] of Object.entries(right))
107
+ out[`${rightName}.${column}`] = value;
108
+ return out;
109
+ }
110
+ /** The inner equi-join itself: a nested loop pairing each left row with each
111
+ * right row that shares the join key, emitting qualified rows in left-major order
112
+ * (every match for the first left row, then the second, ...). O(n*m). */
113
+ function joinRows(leftName, leftRows, other, leftField, rightField) {
114
+ const rightRows = other.all().toArray();
115
+ const out = [];
116
+ for (const left of leftRows) {
117
+ for (const right of rightRows) {
118
+ if (joinKeyEqual(left, leftField, right, rightField)) {
119
+ out.push(qualify(leftName, left, other.name, right));
120
+ }
121
+ }
122
+ }
123
+ return out;
124
+ }
125
+ /**
126
+ * Wrap a lazy row source as a {@link Query}. `where`/`select`/`join` build new
127
+ * Queries over the materialized rows of this one, so they re-run the source each
128
+ * pass (laziness and re-iterability carry through the whole chain). All filtering,
129
+ * projection, and joining happen in-engine over the rows — there are no indexes,
130
+ * so a `where` is O(n) in the table size and a `join` is O(n*m), the same cost as
131
+ * the underlying scans.
132
+ *
133
+ * `name` is the qualifier the rows belong to — the table name. It is carried so
134
+ * a later `join` can prefix this side's columns as `<name>.<column>`; `where` and
135
+ * `select` do not use it (they match/project by literal column name), so it rides
136
+ * through them unchanged.
137
+ */
138
+ function query(name, source) {
139
+ const base = result(source);
140
+ return {
141
+ [Symbol.iterator]() {
142
+ return base[Symbol.iterator]();
143
+ },
144
+ toArray() {
145
+ return base.toArray();
146
+ },
147
+ where(predicate) {
148
+ return query(name, () => base.toArray().filter((row) => rowMatches(row, predicate)));
149
+ },
150
+ select(...columns) {
151
+ return query(name, () => base.toArray().map((row) => project(row, columns)));
152
+ },
153
+ join(other, leftField, rightField) {
154
+ return query(name, () => joinRows(name, base.toArray(), other, leftField, rightField));
155
+ },
156
+ };
157
+ }
158
+ /**
159
+ * Build a {@link Table} handle for `name` with `schema` over a {@link Store}
160
+ * (the kernel's `Database` satisfies it). The schema is validated immediately:
161
+ * the `primaryKey` must name a declared column whose type is `"string"`, since
162
+ * the primary key becomes the string kernel key.
163
+ */
164
+ export function table(store, name, schema) {
165
+ const pkType = schema.columns[schema.primaryKey];
166
+ if (pkType === undefined) {
167
+ throw new Error(`primaryKey "${schema.primaryKey}" is not a declared column`);
168
+ }
169
+ if (pkType !== "string") {
170
+ throw new Error(`primaryKey "${schema.primaryKey}" must be a string column, but is ${pkType}`);
171
+ }
172
+ // A table is a schema-validated document collection: rows are stored, read,
173
+ // deleted, and scanned through the document lens at `<table>:<pk>`. This is
174
+ // what reuses the document codec and the collection-isolation-safe prefix scan;
175
+ // a row is a Doc whose fields happen to be the declared columns. The Row/Doc
176
+ // casts are type-system artifacts (their value unions overlap but neither is a
177
+ // subtype of the other); validation guarantees what crosses the boundary.
178
+ const rows = doc(store, name);
179
+ // Every read starts from the full table scan re-wrapped as a chainable Query:
180
+ // drop the document lens's DocEntry id wrapper (the pk lives in the row), then
181
+ // let where/select compose over it. Each call rebuilds the Query so laziness
182
+ // and re-iterability carry through from the document scan.
183
+ const allRows = () => query(name, () => rows.all().toArray().map((entry) => entry.doc));
184
+ return {
185
+ name,
186
+ insert(row) {
187
+ validateRow(schema, row);
188
+ // The primary-key column is validated as a string above and required, so
189
+ // this read is a checked string, safe as the document id.
190
+ const pk = row[schema.primaryKey];
191
+ return rows.put(pk, row);
192
+ },
193
+ get(pk) {
194
+ return rows.get(pk);
195
+ },
196
+ delete(pk) {
197
+ return rows.delete(pk);
198
+ },
199
+ all() {
200
+ return allRows();
201
+ },
202
+ where(predicate) {
203
+ return allRows().where(predicate);
204
+ },
205
+ select(...columns) {
206
+ return allRows().select(...columns);
207
+ },
208
+ join(other, leftField, rightField) {
209
+ return allRows().join(other, leftField, rightField);
210
+ },
211
+ };
212
+ }
213
+ //# sourceMappingURL=relational.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relational.js","sourceRoot":"","sources":["../../src/lens/relational.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * lens/types.ts — the shared query/result shape every lens reuses.
3
+ *
4
+ * A lens (kv, document, relational) is a typed view over the kernel in core.ts.
5
+ * Lenses differ wildly in HOW you ask for data — a kv key range, a document
6
+ * filter, a relational SELECT — and that is on purpose: DESIGN.md section 2 (and
7
+ * the founding brainstorm in HANDOFF.md) reject a single unified query
8
+ * *language* as a lowest-common-denominator trap. So what lenses share is not
9
+ * the query syntax but the RESULT envelope: every read hands back a
10
+ * {@link Result}, every write reports a {@link WriteResult}. Pinning these two
11
+ * shapes once lets later lenses — and the universal tooling above them (Studio,
12
+ * Platform) — consume any lens uniformly without flattening their differences.
13
+ *
14
+ * This file is an edge, not the kernel: it holds no durability logic, only the
15
+ * vocabulary the lenses speak.
16
+ */
17
+ /**
18
+ * What every lens read returns: a lazy, typed, re-iterable sequence of rows.
19
+ *
20
+ * `Row` is the lens's own unit — a kv {@link import("../core.ts").Entry}, a
21
+ * document, a relational tuple — so the shape is shared without erasing what
22
+ * each lens actually yields.
23
+ *
24
+ * Lazy and re-iterable are deliberate. The kernel's range scan is itself lazy
25
+ * (`Iterable<Entry>`), so a Result must not force a large scan to materialize
26
+ * just to be wrapped. Re-iterable means a Result behaves like the query it
27
+ * stands for: iterating it twice runs the read twice, instead of the classic
28
+ * generator footgun where the second pass silently yields nothing. Because of
29
+ * that, the Result *is* the deferred query — there is no separate query object
30
+ * to define.
31
+ */
32
+ export interface Result<Row> extends Iterable<Row> {
33
+ /** Materialize every row into a fresh array. Convenience over iterating; the
34
+ * returned array is the caller's to keep and mutate. */
35
+ toArray(): Row[];
36
+ }
37
+ /**
38
+ * The outcome of a lens write.
39
+ *
40
+ * `changed` is the number of stored entries the write created, overwrote, or
41
+ * removed. It is uniform across lenses (a kv set is 1, a delete of an absent
42
+ * key is 0, a relational UPDATE is its matched-row count) and keeps writes
43
+ * visible by default (DESIGN.md principle 2): a caller can always see what a
44
+ * write actually did.
45
+ */
46
+ export interface WriteResult {
47
+ readonly changed: number;
48
+ }
49
+ /**
50
+ * Build a {@link Result} from a source of rows.
51
+ *
52
+ * `source` is a thunk, not an iterable, so the read is deferred and repeatable:
53
+ * nothing runs until the Result is iterated, and each iteration calls `source`
54
+ * again for a fresh pass. Every lens constructs its reads through this one
55
+ * helper so "what a Result is" has a single definition.
56
+ */
57
+ export declare function result<Row>(source: () => Iterable<Row>): Result<Row>;
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lens/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,MAAM,CAAC,GAAG,CAAE,SAAQ,QAAQ,CAAC,GAAG,CAAC;IAChD;4DACwD;IACxD,OAAO,IAAI,GAAG,EAAE,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CASpE"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * lens/types.ts — the shared query/result shape every lens reuses.
3
+ *
4
+ * A lens (kv, document, relational) is a typed view over the kernel in core.ts.
5
+ * Lenses differ wildly in HOW you ask for data — a kv key range, a document
6
+ * filter, a relational SELECT — and that is on purpose: DESIGN.md section 2 (and
7
+ * the founding brainstorm in HANDOFF.md) reject a single unified query
8
+ * *language* as a lowest-common-denominator trap. So what lenses share is not
9
+ * the query syntax but the RESULT envelope: every read hands back a
10
+ * {@link Result}, every write reports a {@link WriteResult}. Pinning these two
11
+ * shapes once lets later lenses — and the universal tooling above them (Studio,
12
+ * Platform) — consume any lens uniformly without flattening their differences.
13
+ *
14
+ * This file is an edge, not the kernel: it holds no durability logic, only the
15
+ * vocabulary the lenses speak.
16
+ */
17
+ /**
18
+ * Build a {@link Result} from a source of rows.
19
+ *
20
+ * `source` is a thunk, not an iterable, so the read is deferred and repeatable:
21
+ * nothing runs until the Result is iterated, and each iteration calls `source`
22
+ * again for a fresh pass. Every lens constructs its reads through this one
23
+ * helper so "what a Result is" has a single definition.
24
+ */
25
+ export function result(source) {
26
+ return {
27
+ [Symbol.iterator]() {
28
+ return source()[Symbol.iterator]();
29
+ },
30
+ toArray() {
31
+ return [...source()];
32
+ },
33
+ };
34
+ }
35
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lens/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAoCH;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAM,MAA2B;IACrD,OAAO;QACL,CAAC,MAAM,CAAC,QAAQ,CAAC;YACf,OAAO,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,CAAC;QACD,OAAO;YACL,OAAO,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}