@minnowdb/core 0.2.1 → 0.4.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/README.md +23 -32
- package/dist/block-format/block.d.ts +32 -13
- package/dist/block-format/block.js +179 -55
- package/dist/block-format/checksum.d.ts +2 -1
- package/dist/block-format/checksum.js +5 -2
- package/dist/block-format/codecs.d.ts +7 -2
- package/dist/block-format/codecs.js +53 -12
- package/dist/block-format/column.d.ts +3 -2
- package/dist/block-format/column.js +96 -30
- package/dist/block-format/index.d.ts +2 -2
- package/dist/block-format/index.js +2 -2
- package/dist/block-format/physical.d.ts +8 -2
- package/dist/block-format/physical.js +14 -6
- package/dist/block-format/types.d.ts +7 -3
- package/dist/block-format/types.js +0 -1
- package/dist/block-format/unicode.d.ts +11 -0
- package/dist/block-format/unicode.js +46 -0
- package/dist/date-value.d.ts +15 -0
- package/dist/date-value.js +64 -0
- package/dist/engine/artifact-cache.d.ts +2 -1
- package/dist/engine/artifact-cache.js +5 -1
- package/dist/engine/batch.d.ts +11 -3
- package/dist/engine/batch.js +17 -7
- package/dist/engine/buffered-writer.d.ts +2 -1
- package/dist/engine/buffered-writer.js +34 -9
- package/dist/engine/cache-limits.d.ts +25 -0
- package/dist/engine/cache-limits.js +25 -0
- package/dist/engine/catalog.d.ts +24 -5
- package/dist/engine/catalog.js +34 -7
- package/dist/engine/client.d.ts +29 -7
- package/dist/engine/client.js +216 -40
- package/dist/engine/database.d.ts +224 -39
- package/dist/engine/database.js +7475 -1539
- package/dist/engine/defaults.d.ts +4 -8
- package/dist/engine/defaults.js +46 -22
- package/dist/engine/errors.d.ts +19 -1
- package/dist/engine/errors.js +32 -2
- package/dist/engine/fts.d.ts +0 -6
- package/dist/engine/fts.js +41 -14
- package/dist/engine/group-index.d.ts +0 -1
- package/dist/engine/group-index.js +6 -11
- package/dist/engine/index.d.ts +16 -10
- package/dist/engine/index.js +12 -9
- package/dist/engine/join-index.d.ts +0 -1
- package/dist/engine/join-index.js +2 -2
- package/dist/engine/keyed-live.d.ts +57 -0
- package/dist/engine/keyed-live.js +226 -0
- package/dist/engine/live-api.d.ts +4 -0
- package/dist/engine/live-api.js +4 -0
- package/dist/engine/live.d.ts +43 -26
- package/dist/engine/live.js +504 -126
- package/dist/engine/memory.d.ts +2 -1
- package/dist/engine/memory.js +3 -2
- package/dist/engine/optimizer.d.ts +0 -1
- package/dist/engine/optimizer.js +262 -27
- package/dist/engine/query-api.d.ts +3 -0
- package/dist/engine/query-api.js +3 -0
- package/dist/engine/query-cache.d.ts +1 -2
- package/dist/engine/query-cache.js +7 -5
- package/dist/engine/query.d.ts +134 -29
- package/dist/engine/query.js +1668 -372
- package/dist/engine/result-wire.d.ts +0 -1
- package/dist/engine/result-wire.js +2 -2
- package/dist/engine/schema-wire.d.ts +6 -3
- package/dist/engine/schema-wire.js +40 -34
- package/dist/engine/schema.d.ts +156 -80
- package/dist/engine/schema.js +570 -108
- package/dist/engine/sort-keys.d.ts +4 -4
- package/dist/engine/sort-keys.js +44 -29
- package/dist/engine/sql-domains.d.ts +31 -0
- package/dist/engine/sql-domains.js +585 -0
- package/dist/engine/sql-driver.d.ts +19 -0
- package/dist/engine/sql-driver.js +1 -0
- package/dist/engine/sql-json.d.ts +7 -9
- package/dist/engine/sql-json.js +75 -15
- package/dist/engine/sql-semantics.d.ts +8 -3
- package/dist/engine/sql-semantics.js +458 -30
- package/dist/engine/typed-live.d.ts +67 -0
- package/dist/engine/typed-live.js +349 -0
- package/dist/engine/vector.d.ts +12 -2
- package/dist/engine/vector.js +635 -119
- package/dist/engine/worker-host.d.ts +9 -2
- package/dist/engine/worker-host.js +620 -152
- package/dist/engine/worker.d.ts +0 -1
- package/dist/engine/worker.js +3 -3
- package/dist/engine/write-block-planner.d.ts +18 -0
- package/dist/engine/write-block-planner.js +112 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/plan/index.d.ts +3 -4
- package/dist/plan/index.js +3 -4
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/indexeddb.d.ts +99 -51
- package/dist/storage/indexeddb.js +13448 -2660
- package/dist/storage/memory.d.ts +64 -57
- package/dist/storage/memory.js +1042 -128
- package/dist/storage/opfs/files.d.ts +18 -7
- package/dist/storage/opfs/files.js +115 -28
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +1 -1
- package/dist/storage/opfs/leader.d.ts +409 -75
- package/dist/storage/opfs/leader.js +4621 -650
- package/dist/storage/opfs/rpc.d.ts +15 -3
- package/dist/storage/opfs/rpc.js +224 -2
- package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
- package/dist/storage/opfs/snapshot-ledger.js +281 -0
- package/dist/storage/opfs/store.d.ts +33 -99
- package/dist/storage/opfs/store.js +453 -364
- package/dist/storage/persistence.d.ts +37 -0
- package/dist/storage/persistence.js +78 -0
- package/dist/storage/snapshot-stream.d.ts +24 -0
- package/dist/storage/snapshot-stream.js +897 -0
- package/dist/storage/snapshot.d.ts +9 -85
- package/dist/storage/snapshot.js +33 -265
- package/dist/storage/toolkit/extents.d.ts +41 -7
- package/dist/storage/toolkit/extents.js +402 -39
- package/dist/storage/toolkit/index.d.ts +4 -5
- package/dist/storage/toolkit/index.js +28 -4
- package/dist/storage/toolkit/record-core.d.ts +182 -55
- package/dist/storage/toolkit/record-core.js +6158 -1331
- package/dist/storage/toolkit/sync-file.d.ts +10 -1
- package/dist/storage/toolkit/sync-file.js +50 -2
- package/dist/storage/toolkit/wal.d.ts +23 -5
- package/dist/storage/toolkit/wal.js +89 -27
- package/dist/storage/toolkit/wire.d.ts +15 -2
- package/dist/storage/toolkit/wire.js +199 -10
- package/dist/storage/types.d.ts +1439 -204
- package/dist/storage/types.js +1745 -141
- package/dist/testing/block-store-conformance.d.ts +0 -1
- package/dist/testing/block-store-conformance.js +1009 -119
- package/dist/testing/index.d.ts +46 -26
- package/dist/testing/index.js +112 -57
- package/dist/testing/opfs-shim.d.ts +13 -3
- package/dist/testing/opfs-shim.js +40 -6
- package/dist/testing/simulator.d.ts +97 -0
- package/dist/testing/simulator.js +591 -0
- package/dist/testing/sqllogictest.d.ts +89 -0
- package/dist/testing/sqllogictest.js +434 -0
- package/dist/transactions/index.d.ts +92 -16
- package/dist/transactions/index.js +1074 -236
- package/dist/worker-protocol/index.d.ts +3 -2
- package/dist/worker-protocol/index.js +2 -5
- package/package.json +53 -3
- package/postgres-feature-profile.json +223 -0
- package/sql-feature-matrix.json +172 -252
- package/dist/block-format/block.d.ts.map +0 -1
- package/dist/block-format/block.js.map +0 -1
- package/dist/block-format/checksum.d.ts.map +0 -1
- package/dist/block-format/checksum.js.map +0 -1
- package/dist/block-format/codecs.d.ts.map +0 -1
- package/dist/block-format/codecs.js.map +0 -1
- package/dist/block-format/column.d.ts.map +0 -1
- package/dist/block-format/column.js.map +0 -1
- package/dist/block-format/index.d.ts.map +0 -1
- package/dist/block-format/index.js.map +0 -1
- package/dist/block-format/physical.d.ts.map +0 -1
- package/dist/block-format/physical.js.map +0 -1
- package/dist/block-format/types.d.ts.map +0 -1
- package/dist/block-format/types.js.map +0 -1
- package/dist/engine/artifact-cache.d.ts.map +0 -1
- package/dist/engine/artifact-cache.js.map +0 -1
- package/dist/engine/batch.d.ts.map +0 -1
- package/dist/engine/batch.js.map +0 -1
- package/dist/engine/buffered-writer.d.ts.map +0 -1
- package/dist/engine/buffered-writer.js.map +0 -1
- package/dist/engine/catalog.d.ts.map +0 -1
- package/dist/engine/catalog.js.map +0 -1
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/coordinator.d.ts +0 -17
- package/dist/engine/coordinator.d.ts.map +0 -1
- package/dist/engine/coordinator.js +0 -60
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/engine/database.d.ts.map +0 -1
- package/dist/engine/database.js.map +0 -1
- package/dist/engine/defaults.d.ts.map +0 -1
- package/dist/engine/defaults.js.map +0 -1
- package/dist/engine/errors.d.ts.map +0 -1
- package/dist/engine/errors.js.map +0 -1
- package/dist/engine/fts.d.ts.map +0 -1
- package/dist/engine/fts.js.map +0 -1
- package/dist/engine/group-index.d.ts.map +0 -1
- package/dist/engine/group-index.js.map +0 -1
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/join-index.d.ts.map +0 -1
- package/dist/engine/join-index.js.map +0 -1
- package/dist/engine/live.d.ts.map +0 -1
- package/dist/engine/live.js.map +0 -1
- package/dist/engine/memory.d.ts.map +0 -1
- package/dist/engine/memory.js.map +0 -1
- package/dist/engine/optimizer.d.ts.map +0 -1
- package/dist/engine/optimizer.js.map +0 -1
- package/dist/engine/query-cache.d.ts.map +0 -1
- package/dist/engine/query-cache.js.map +0 -1
- package/dist/engine/query.d.ts.map +0 -1
- package/dist/engine/query.js.map +0 -1
- package/dist/engine/result-wire.d.ts.map +0 -1
- package/dist/engine/result-wire.js.map +0 -1
- package/dist/engine/schema-wire.d.ts.map +0 -1
- package/dist/engine/schema-wire.js.map +0 -1
- package/dist/engine/schema.d.ts.map +0 -1
- package/dist/engine/schema.js.map +0 -1
- package/dist/engine/sort-keys.d.ts.map +0 -1
- package/dist/engine/sort-keys.js.map +0 -1
- package/dist/engine/sql-json.d.ts.map +0 -1
- package/dist/engine/sql-json.js.map +0 -1
- package/dist/engine/sql-semantics.d.ts.map +0 -1
- package/dist/engine/sql-semantics.js.map +0 -1
- package/dist/engine/vector.d.ts.map +0 -1
- package/dist/engine/vector.js.map +0 -1
- package/dist/engine/worker-host.d.ts.map +0 -1
- package/dist/engine/worker-host.js.map +0 -1
- package/dist/engine/worker.d.ts.map +0 -1
- package/dist/engine/worker.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js.map +0 -1
- package/dist/storage/fixture-shape.d.ts +0 -42
- package/dist/storage/fixture-shape.d.ts.map +0 -1
- package/dist/storage/fixture-shape.js +0 -146
- package/dist/storage/fixture-shape.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/indexeddb.d.ts.map +0 -1
- package/dist/storage/indexeddb.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/opfs/files.d.ts.map +0 -1
- package/dist/storage/opfs/files.js.map +0 -1
- package/dist/storage/opfs/index.d.ts.map +0 -1
- package/dist/storage/opfs/index.js.map +0 -1
- package/dist/storage/opfs/leader.d.ts.map +0 -1
- package/dist/storage/opfs/leader.js.map +0 -1
- package/dist/storage/opfs/rpc.d.ts.map +0 -1
- package/dist/storage/opfs/rpc.js.map +0 -1
- package/dist/storage/opfs/store.d.ts.map +0 -1
- package/dist/storage/opfs/store.js.map +0 -1
- package/dist/storage/snapshot.d.ts.map +0 -1
- package/dist/storage/snapshot.js.map +0 -1
- package/dist/storage/toolkit/extents.d.ts.map +0 -1
- package/dist/storage/toolkit/extents.js.map +0 -1
- package/dist/storage/toolkit/index.d.ts.map +0 -1
- package/dist/storage/toolkit/index.js.map +0 -1
- package/dist/storage/toolkit/record-core.d.ts.map +0 -1
- package/dist/storage/toolkit/record-core.js.map +0 -1
- package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
- package/dist/storage/toolkit/sync-file.js.map +0 -1
- package/dist/storage/toolkit/wal.d.ts.map +0 -1
- package/dist/storage/toolkit/wal.js.map +0 -1
- package/dist/storage/toolkit/wire.d.ts.map +0 -1
- package/dist/storage/toolkit/wire.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/testing/block-store-conformance.d.ts.map +0 -1
- package/dist/testing/block-store-conformance.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/opfs-shim.d.ts.map +0 -1
- package/dist/testing/opfs-shim.js.map +0 -1
- package/dist/testing/seeds.d.ts +0 -11
- package/dist/testing/seeds.d.ts.map +0 -1
- package/dist/testing/seeds.js +0 -50
- package/dist/testing/seeds.js.map +0 -1
- package/dist/transactions/index.d.ts.map +0 -1
- package/dist/transactions/index.js.map +0 -1
- package/dist/worker-protocol/index.d.ts.map +0 -1
- package/dist/worker-protocol/index.js.map +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { dateMilliseconds } from "../date-value.js";
|
|
2
|
+
function sameValue(left, right) {
|
|
3
|
+
if (Object.is(left, right))
|
|
4
|
+
return true;
|
|
5
|
+
if (left instanceof Date || right instanceof Date) {
|
|
6
|
+
return (left instanceof Date &&
|
|
7
|
+
right instanceof Date &&
|
|
8
|
+
Object.is(dateMilliseconds(left), dateMilliseconds(right)));
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
11
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length)
|
|
12
|
+
return false;
|
|
13
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
14
|
+
if (!sameValue(left[index], right[index]))
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const leftRecord = left;
|
|
23
|
+
const rightRecord = right;
|
|
24
|
+
const leftKeys = Object.keys(leftRecord);
|
|
25
|
+
const rightKeys = Object.keys(rightRecord);
|
|
26
|
+
if (leftKeys.length !== rightKeys.length)
|
|
27
|
+
return false;
|
|
28
|
+
for (let index = 0; index < leftKeys.length; index += 1) {
|
|
29
|
+
const key = leftKeys[index];
|
|
30
|
+
if (key === undefined || key !== rightKeys[index])
|
|
31
|
+
return false;
|
|
32
|
+
if (!sameValue(leftRecord[key], rightRecord[key]))
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function keyToken(value, name) {
|
|
38
|
+
if (typeof value === "string")
|
|
39
|
+
return `s:${String(value.length)}:${value}`;
|
|
40
|
+
if (typeof value === "boolean")
|
|
41
|
+
return value ? "b:1" : "b:0";
|
|
42
|
+
if (typeof value === "number") {
|
|
43
|
+
if (Number.isNaN(value))
|
|
44
|
+
return "n:NaN";
|
|
45
|
+
if (Object.is(value, -0))
|
|
46
|
+
return "n:-0";
|
|
47
|
+
return `n:${String(value)}`;
|
|
48
|
+
}
|
|
49
|
+
if (value instanceof Date) {
|
|
50
|
+
const time = dateMilliseconds(value);
|
|
51
|
+
if (!Number.isFinite(time)) {
|
|
52
|
+
throw new TypeError(`Live query key ${String(name)} must be a valid Date`);
|
|
53
|
+
}
|
|
54
|
+
return `d:${String(time)}`;
|
|
55
|
+
}
|
|
56
|
+
throw new TypeError(`Live query key ${String(name)} must be a non-null string, number, boolean, or Date`);
|
|
57
|
+
}
|
|
58
|
+
function indexRows(rows, key) {
|
|
59
|
+
const indexed = new Map();
|
|
60
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
61
|
+
const row = rows[index];
|
|
62
|
+
if (row === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
const token = keyToken(row[key], key);
|
|
65
|
+
if (indexed.has(token)) {
|
|
66
|
+
throw new TypeError(`Live query key ${String(key)} is not unique: duplicate at row ${String(index)}`);
|
|
67
|
+
}
|
|
68
|
+
indexed.set(token, { row, index });
|
|
69
|
+
}
|
|
70
|
+
return indexed;
|
|
71
|
+
}
|
|
72
|
+
function diffRows(previousRows, rows, key) {
|
|
73
|
+
const previous = indexRows(previousRows, key);
|
|
74
|
+
const current = indexRows(rows, key);
|
|
75
|
+
const changes = [];
|
|
76
|
+
for (const [token, old] of previous) {
|
|
77
|
+
if (current.has(token))
|
|
78
|
+
continue;
|
|
79
|
+
changes.push({ type: "delete", key: old.row[key], previous: old.row, index: old.index });
|
|
80
|
+
}
|
|
81
|
+
for (const [token, next] of current) {
|
|
82
|
+
const old = previous.get(token);
|
|
83
|
+
if (old === undefined) {
|
|
84
|
+
changes.push({ type: "insert", row: next.row, index: next.index });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!sameValue(old.row, next.row)) {
|
|
88
|
+
changes.push({ type: "update", row: next.row, previous: old.row, index: next.index });
|
|
89
|
+
}
|
|
90
|
+
if (old.index !== next.index) {
|
|
91
|
+
changes.push({
|
|
92
|
+
type: "move",
|
|
93
|
+
key: next.row[key],
|
|
94
|
+
row: next.row,
|
|
95
|
+
from: old.index,
|
|
96
|
+
to: next.index,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return changes;
|
|
101
|
+
}
|
|
102
|
+
/** Keyed exact diffs over a typed live query, with optional bounded-window enforcement. */
|
|
103
|
+
export class KeyedLiveQuery {
|
|
104
|
+
#source;
|
|
105
|
+
#key;
|
|
106
|
+
#maxRows;
|
|
107
|
+
#listeners = new Set();
|
|
108
|
+
#sourceUnsubscribe;
|
|
109
|
+
#rows = [];
|
|
110
|
+
#snapshot = { status: "loading", rows: [] };
|
|
111
|
+
#hasReadySnapshot = false;
|
|
112
|
+
#closed = false;
|
|
113
|
+
constructor(source, options) {
|
|
114
|
+
if (options.maxRows !== undefined &&
|
|
115
|
+
(!Number.isSafeInteger(options.maxRows) || options.maxRows <= 0)) {
|
|
116
|
+
throw new RangeError("Live query window maxRows must be a positive whole number");
|
|
117
|
+
}
|
|
118
|
+
this.#source = source;
|
|
119
|
+
this.#key = options.key;
|
|
120
|
+
this.#maxRows = options.maxRows;
|
|
121
|
+
}
|
|
122
|
+
getSnapshot = () => this.#snapshot;
|
|
123
|
+
subscribe = (listener) => {
|
|
124
|
+
if (this.#closed)
|
|
125
|
+
throw new Error("Keyed live query is closed");
|
|
126
|
+
const registered = () => listener();
|
|
127
|
+
this.#listeners.add(registered);
|
|
128
|
+
if (this.#listeners.size === 1) {
|
|
129
|
+
this.#sourceUnsubscribe = this.#source.subscribe(() => this.#accept(this.#source.getSnapshot()));
|
|
130
|
+
this.#accept(this.#source.getSnapshot());
|
|
131
|
+
}
|
|
132
|
+
let subscribed = true;
|
|
133
|
+
return () => {
|
|
134
|
+
if (!subscribed)
|
|
135
|
+
return;
|
|
136
|
+
subscribed = false;
|
|
137
|
+
this.#listeners.delete(registered);
|
|
138
|
+
if (this.#listeners.size === 0) {
|
|
139
|
+
this.#sourceUnsubscribe?.();
|
|
140
|
+
this.#sourceUnsubscribe = undefined;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
async refresh() {
|
|
145
|
+
if (this.#closed)
|
|
146
|
+
throw new Error("Keyed live query is closed");
|
|
147
|
+
await this.#source.refresh();
|
|
148
|
+
this.#accept(this.#source.getSnapshot());
|
|
149
|
+
}
|
|
150
|
+
close() {
|
|
151
|
+
if (this.#closed)
|
|
152
|
+
return;
|
|
153
|
+
this.#closed = true;
|
|
154
|
+
this.#sourceUnsubscribe?.();
|
|
155
|
+
this.#sourceUnsubscribe = undefined;
|
|
156
|
+
this.#source.close();
|
|
157
|
+
this.#emit();
|
|
158
|
+
this.#listeners.clear();
|
|
159
|
+
}
|
|
160
|
+
#accept(snapshot) {
|
|
161
|
+
if (this.#closed)
|
|
162
|
+
return;
|
|
163
|
+
if (snapshot.status === "loading") {
|
|
164
|
+
if (this.#snapshot.status === "loading")
|
|
165
|
+
return;
|
|
166
|
+
this.#snapshot = { status: "loading", rows: this.#rows };
|
|
167
|
+
this.#emit();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (snapshot.status === "error") {
|
|
171
|
+
this.#snapshot = {
|
|
172
|
+
status: "error",
|
|
173
|
+
rows: this.#rows,
|
|
174
|
+
error: snapshot.error,
|
|
175
|
+
version: snapshot.version,
|
|
176
|
+
};
|
|
177
|
+
this.#emit();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
if (this.#maxRows !== undefined && snapshot.rows.length > this.#maxRows) {
|
|
182
|
+
throw new RangeError(`Live query window returned ${String(snapshot.rows.length)} rows; maximum is ${String(this.#maxRows)}`);
|
|
183
|
+
}
|
|
184
|
+
const initial = !this.#hasReadySnapshot;
|
|
185
|
+
let changes;
|
|
186
|
+
if (initial) {
|
|
187
|
+
// Initial snapshots still validate uniqueness even though no previous index is needed.
|
|
188
|
+
indexRows(snapshot.rows, this.#key);
|
|
189
|
+
changes = snapshot.rows.map((row, index) => ({ type: "insert", row, index }));
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// diffRows builds each old/new key index exactly once and validates duplicates as it goes.
|
|
193
|
+
changes = diffRows(this.#rows, snapshot.rows, this.#key);
|
|
194
|
+
}
|
|
195
|
+
this.#rows = snapshot.rows;
|
|
196
|
+
this.#hasReadySnapshot = true;
|
|
197
|
+
this.#snapshot = {
|
|
198
|
+
status: "ready",
|
|
199
|
+
rows: snapshot.rows,
|
|
200
|
+
changes: Object.freeze(changes),
|
|
201
|
+
initial,
|
|
202
|
+
version: snapshot.version,
|
|
203
|
+
};
|
|
204
|
+
this.#emit();
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
this.#snapshot = {
|
|
208
|
+
status: "error",
|
|
209
|
+
rows: this.#rows,
|
|
210
|
+
error,
|
|
211
|
+
version: snapshot.version,
|
|
212
|
+
};
|
|
213
|
+
this.#emit();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
#emit() {
|
|
217
|
+
for (const listener of [...this.#listeners]) {
|
|
218
|
+
try {
|
|
219
|
+
listener();
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Isolate external-store listeners.
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
package/dist/engine/live.d.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { type Manifest, type StoragePage } from "../storage/
|
|
1
|
+
import { type CatalogProbe, type Manifest, type StoragePage } from "../storage/types.js";
|
|
2
2
|
import { type CompiledQuery, type QueryResult, type QueryValue } from "./query.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* a refresh but can never produce a stale result, and a manifest without a change set (written
|
|
9
|
-
* before tracking, or outside a transaction) conservatively affects every subscription.
|
|
4
|
+
* Live-query invalidation is a hint-driven cache-coherence problem, not a notification problem.
|
|
5
|
+
* Every hint path converges on the store's durable manifest/catalog probe; persisted per-commit
|
|
6
|
+
* table sets then decide which prepared queries may be stale. Hints may be lost, duplicated, or
|
|
7
|
+
* reordered without changing correctness.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* digests suppress the notification entirely.
|
|
9
|
+
* Equal statements share one dependency record and one execution per sweep. Results retain an
|
|
10
|
+
* exact private snapshot: a 32-bit digest is only a fast inequality check, never proof of equality.
|
|
14
11
|
*/
|
|
15
12
|
export interface LiveQueryHintChannel {
|
|
16
13
|
postMessage(message: unknown): void;
|
|
@@ -20,22 +17,43 @@ export interface LiveQueryHintChannel {
|
|
|
20
17
|
}
|
|
21
18
|
export interface LiveQuerySetOptions {
|
|
22
19
|
readonly channel?: LiveQueryHintChannel;
|
|
23
|
-
/**
|
|
24
|
-
* Convenience form of `channel`: the set creates (and owns, closing it with the set) a
|
|
25
|
-
* BroadcastChannel of this name when the platform provides one. Ignored when `channel` is
|
|
26
|
-
* given or BroadcastChannel is unavailable.
|
|
27
|
-
*/
|
|
20
|
+
/** Creates and owns a BroadcastChannel of this name when the platform provides one. */
|
|
28
21
|
readonly channelName?: string;
|
|
29
22
|
readonly pollIntervalMs?: number;
|
|
23
|
+
/** Maximum distinct query groups retained by this set. Defaults to 256. */
|
|
24
|
+
readonly maxGroups?: number;
|
|
25
|
+
/** Maximum result/observer subscriptions retained by this set. Defaults to 1,024. */
|
|
26
|
+
readonly maxSubscriptions?: number;
|
|
30
27
|
/** Called once when the set closes; the owner uses this to drop its reference. */
|
|
31
28
|
readonly onClosed?: () => void;
|
|
32
29
|
}
|
|
30
|
+
export declare const DEFAULT_LIVE_QUERY_MAX_GROUPS = 256;
|
|
31
|
+
export declare const DEFAULT_LIVE_QUERY_MAX_SUBSCRIPTIONS = 1024;
|
|
32
|
+
export declare const MAX_LIVE_QUERY_GROUPS = 4096;
|
|
33
|
+
export declare const MAX_LIVE_QUERY_SUBSCRIPTIONS = 16384;
|
|
34
|
+
export declare const MAX_LIVE_QUERY_SETS_PER_DATABASE = 256;
|
|
35
|
+
export declare class LiveQueryLimitError extends Error {
|
|
36
|
+
readonly resource: "set" | "group" | "subscription";
|
|
37
|
+
readonly limit: number;
|
|
38
|
+
readonly name = "LiveQueryLimitError";
|
|
39
|
+
constructor(resource: "set" | "group" | "subscription", limit: number);
|
|
40
|
+
}
|
|
33
41
|
export interface LiveQuerySubscribeOptions {
|
|
34
42
|
onChange(result: QueryResult): void;
|
|
35
43
|
onError?(error: unknown): void;
|
|
36
44
|
/** Called once when the subscription ends because the subscription or its set closed. */
|
|
37
45
|
onComplete?(): void;
|
|
38
46
|
}
|
|
47
|
+
export interface LiveQueryInvalidation {
|
|
48
|
+
readonly manifestVersion: number | null;
|
|
49
|
+
readonly catalogEpoch: number;
|
|
50
|
+
readonly initial: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface LiveQueryObserveOptions {
|
|
53
|
+
onInvalidate(invalidation: LiveQueryInvalidation): void;
|
|
54
|
+
onError?(error: unknown): void;
|
|
55
|
+
onComplete?(): void;
|
|
56
|
+
}
|
|
39
57
|
export interface LiveQuerySubscription {
|
|
40
58
|
readonly dependencyTableIds: readonly string[];
|
|
41
59
|
close(): void;
|
|
@@ -49,6 +67,10 @@ export interface LiveQueryStats {
|
|
|
49
67
|
/** Re-runs skipped because the data layer proved the commits could not change the result. */
|
|
50
68
|
zoneSkips: number;
|
|
51
69
|
notificationsSuppressed: number;
|
|
70
|
+
/** Observer-only invalidations delivered without executing the statement inside the set. */
|
|
71
|
+
invalidations: number;
|
|
72
|
+
/** Work avoided because equal statements shared one query group or in-flight execution. */
|
|
73
|
+
sharedExecutions: number;
|
|
52
74
|
lastSweepMs: number;
|
|
53
75
|
}
|
|
54
76
|
/** A live query: plain SQL, parameterized SQL, or a compiled-plan envelope. */
|
|
@@ -61,30 +83,25 @@ export type LiveQueryInput = string | {
|
|
|
61
83
|
plan: CompiledQuery;
|
|
62
84
|
};
|
|
63
85
|
interface LiveQueryHost {
|
|
64
|
-
|
|
86
|
+
currentProbe(): Promise<CatalogProbe>;
|
|
65
87
|
manifestPage(afterVersion: number | null, limit: number): Promise<StoragePage<Manifest, number>>;
|
|
66
88
|
dependencyTableIds(query: LiveQueryInput): Promise<Set<string>>;
|
|
67
89
|
execute(query: LiveQueryInput): Promise<QueryResult>;
|
|
68
|
-
/**
|
|
69
|
-
* Optional data-layer selectivity: whether commits in (after, until] to the given
|
|
70
|
-
* dependency tables can possibly change this query's result. A host answers false only
|
|
71
|
-
* on proof — e.g. every introduced segment is a data-neutral compaction rewrite, or every
|
|
72
|
-
* new insert block's zone statistics reject the query's predicates — so a false skips the
|
|
73
|
-
* re-run entirely. Errors and unknown shapes must answer true.
|
|
74
|
-
*/
|
|
90
|
+
/** Returns false only on proof that the commit window cannot affect the statement. */
|
|
75
91
|
changeCanAffect?(query: LiveQueryInput, tableIds: readonly string[], after: number | null, until: number): Promise<boolean>;
|
|
76
92
|
}
|
|
77
93
|
export declare class LiveQuerySet {
|
|
78
94
|
#private;
|
|
79
95
|
constructor(host: LiveQueryHost, options?: LiveQuerySetOptions);
|
|
80
96
|
get stats(): LiveQueryStats;
|
|
81
|
-
/** Registers a query, delivers its
|
|
97
|
+
/** Registers a query, delivers its current result, and shares work with equal statements. */
|
|
82
98
|
subscribe(query: LiveQueryInput, options: LiveQuerySubscribeOptions): Promise<LiveQuerySubscription>;
|
|
99
|
+
/** Observes invalidation without executing the statement inside the set. */
|
|
100
|
+
observe(query: LiveQueryInput, options: LiveQueryObserveOptions): Promise<LiveQuerySubscription>;
|
|
83
101
|
/** Called by the owning database after each local write commit; also hints other tabs. */
|
|
84
102
|
notifyLocalCommit(): void;
|
|
85
|
-
/** Runs one authoritative
|
|
103
|
+
/** Runs one authoritative probe and selective re-execution sweep. */
|
|
86
104
|
refresh(): Promise<void>;
|
|
87
105
|
close(): void;
|
|
88
106
|
}
|
|
89
107
|
export {};
|
|
90
|
-
//# sourceMappingURL=live.d.ts.map
|