@rindle/wasm 0.1.6 → 0.3.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.
- package/build.sh +10 -5
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -7
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/pkg/rindle.d.ts +20 -0
- package/pkg/rindle.js +27 -0
- package/pkg/rindle_bg.wasm +0 -0
- package/pkg/rindle_bg.wasm.d.ts +1 -0
- package/src/index.ts +44 -7
package/build.sh
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Build the rindle wasm engine into this package as a portable ESM artifact
|
|
3
|
-
# works in browsers/bundlers (await init()) and Node (await init(bytes)) — see
|
|
2
|
+
# Build the rindle wasm engine into this package as a portable ESM artifact by default
|
|
3
|
+
# (--target web): works in browsers/bundlers (await init()) and Node (await init(bytes)) — see
|
|
4
|
+
# src/index.ts.
|
|
4
5
|
#
|
|
5
6
|
# ./build.sh [profile] # wasm-release (default) | release | dev
|
|
6
7
|
#
|
|
7
8
|
# Produces packages/wasm/pkg/{rindle.js, rindle_bg.wasm, rindle.d.ts}.
|
|
9
|
+
# Advanced wrappers may override:
|
|
10
|
+
# RINDLE_WASM_OUT output directory
|
|
11
|
+
# RINDLE_WASM_BINDGEN_TARGET wasm-bindgen target (default: web)
|
|
8
12
|
set -euo pipefail
|
|
9
13
|
|
|
10
14
|
PROFILE="${1:-wasm-release}"
|
|
11
15
|
TARGET=wasm32-unknown-unknown
|
|
12
16
|
FEATURES=wasm
|
|
13
17
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
14
|
-
OUT="$ROOT/packages/wasm/pkg"
|
|
18
|
+
OUT="${RINDLE_WASM_OUT:-$ROOT/packages/wasm/pkg}"
|
|
19
|
+
BINDGEN_TARGET="${RINDLE_WASM_BINDGEN_TARGET:-web}"
|
|
15
20
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
16
21
|
|
|
17
22
|
cd "$ROOT"
|
|
@@ -29,7 +34,7 @@ case "$PROFILE" in
|
|
|
29
34
|
*) cargo rustc -p rindle --profile "$PROFILE" --target "$TARGET" --no-default-features --features "$FEATURES" --crate-type cdylib; WASM="$ROOT/target/$TARGET/$PROFILE/rindle.wasm" ;;
|
|
30
35
|
esac
|
|
31
36
|
|
|
32
|
-
wasm-bindgen --target
|
|
37
|
+
wasm-bindgen --target "$BINDGEN_TARGET" --out-dir "$OUT" --out-name rindle "$WASM"
|
|
33
38
|
|
|
34
39
|
if command -v wasm-opt >/dev/null 2>&1 && [ "$PROFILE" != "dev" ]; then
|
|
35
40
|
# Guard the binaryen version. Since Rust 1.82, wasm32 enables the `reference-types` feature by
|
|
@@ -50,4 +55,4 @@ if command -v wasm-opt >/dev/null 2>&1 && [ "$PROFILE" != "dev" ]; then
|
|
|
50
55
|
echo "wasm-opt -Oz applied"
|
|
51
56
|
fi
|
|
52
57
|
|
|
53
|
-
echo "built $OUT (target
|
|
58
|
+
echo "built $OUT (target=$BINDGEN_TARGET, profile=$PROFILE, $(wc -c < "$OUT/rindle_bg.wasm") bytes)"
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export interface WasmWriteTxn {
|
|
|
9
9
|
remove(table: string, row: unknown[]): void;
|
|
10
10
|
edit(table: string, oldRow: unknown[], newRow: unknown[]): void;
|
|
11
11
|
get(table: string, pk: unknown[]): unknown[] | undefined;
|
|
12
|
+
/** Run a one-shot query over the state this txn is mutating — the live base plus this
|
|
13
|
+
* txn's own staged writes-so-far (read-your-writes; 203-MUTATOR-READS-DESIGN.md §5.2).
|
|
14
|
+
* Synchronous (over a lazy read-cache fork of the staged buffer). Returns the query's rows
|
|
15
|
+
* as keyed objects with their materialized `related` children nested by name (identical in
|
|
16
|
+
* shape to a `view.data` row), in the query's order. */
|
|
17
|
+
query(ast: Ast): unknown[];
|
|
12
18
|
commit(): Array<{
|
|
13
19
|
queryId: number;
|
|
14
20
|
events: unknown[];
|
|
@@ -37,6 +43,7 @@ export declare function initWasm(moduleOrPath?: unknown): Promise<void>;
|
|
|
37
43
|
export declare class WasmBackend<S extends ColsMap> implements Backend {
|
|
38
44
|
private readonly db;
|
|
39
45
|
private handler;
|
|
46
|
+
private boundaryHandler;
|
|
40
47
|
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
41
48
|
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
42
49
|
private readonly localTables;
|
|
@@ -65,11 +72,21 @@ export declare class WasmBackend<S extends ColsMap> implements Backend {
|
|
|
65
72
|
unregisterQuery(qid: QueryId): void;
|
|
66
73
|
mutate(mutations: Mutation[]): Promise<void>;
|
|
67
74
|
onEvent(handler: (qid: QueryId, ev: ChangeEvent) => void): void;
|
|
75
|
+
/** Register the Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch`
|
|
76
|
+
* calls it around each commit's per-query batch delivery so the Store can fold every affected
|
|
77
|
+
* view before notifying any subscriber. */
|
|
78
|
+
onCommitBoundary(handler: (phase: "begin" | "end") => void): void;
|
|
68
79
|
/** Deliver one commit's per-query batches. Non-reentrant (#15): if a delivery is already in
|
|
69
80
|
* progress (a subscriber re-entered via write()), enqueue and let the active drain pick it
|
|
70
81
|
* up FIFO, so each query folds commits in order. Per-query isolated (#11): a view's fold or
|
|
71
82
|
* subscriber throwing does NOT drop sibling queries' batches — the first error is re-raised
|
|
72
|
-
* only after every query has been delivered.
|
|
83
|
+
* only after every query has been delivered.
|
|
84
|
+
*
|
|
85
|
+
* Cross-view-atomic notification: each commit is bracketed with `boundaryHandler("begin"/"end")`
|
|
86
|
+
* so the Store folds ALL of this commit's views before notifying ANY subscriber (a subscriber
|
|
87
|
+
* re-reading a sibling view then sees post-commit data). `begin`/`end` stay balanced even if a
|
|
88
|
+
* fold throws (the `finally`), and a re-entrant write enqueued during the `end` flush drains as
|
|
89
|
+
* its own bracketed commit in the next loop turn — preserving per-query commit order (#15). */
|
|
73
90
|
private dispatch;
|
|
74
91
|
/** Run `f` against a raw staged write txn (with the §4.1 `get` read path), commit, and
|
|
75
92
|
* dispatch the resulting batches on the ordinary event stream. Inside an open server
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,EAAa,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EACV,GAAG,EACH,OAAO,EACP,WAAW,EACX,OAAO,EACP,QAAQ,EACR,OAAO,EACP,MAAM,EACP,MAAM,gBAAgB,CAAC;AAExB,cAAc,gBAAgB,CAAC;AAa/B;;qFAEqF;AACrF,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,SAAS,CAAC;IACzD,MAAM,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;IACxD,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,mFAAmF;AACnF,MAAM,MAAM,aAAa,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,OAAO,EAAE,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,OAAO,EAAE,CAAA;CAAE,GACjD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,EAAE,CAAC;IAAC,GAAG,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAIpE;;sGAEsG;AACtG,wBAAgB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAe9D;AAMD,qBAAa,WAAW,CAAC,CAAC,SAAS,OAAO,CAAE,YAAW,OAAO;IAC5D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAqD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,11 @@ export function initWasm(moduleOrPath) {
|
|
|
33
33
|
export class WasmBackend {
|
|
34
34
|
db;
|
|
35
35
|
handler = () => { };
|
|
36
|
+
// The Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch` brackets
|
|
37
|
+
// each commit's per-query batch delivery with `begin`/`end` so the Store folds every affected
|
|
38
|
+
// view before notifying any subscriber (cross-view-atomic notification). Defaults to a no-op so
|
|
39
|
+
// a Store that never registers it (or none at all) just gets per-event notification.
|
|
40
|
+
boundaryHandler = () => { };
|
|
36
41
|
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
37
42
|
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
38
43
|
localTables;
|
|
@@ -110,11 +115,23 @@ export class WasmBackend {
|
|
|
110
115
|
onEvent(handler) {
|
|
111
116
|
this.handler = handler;
|
|
112
117
|
}
|
|
118
|
+
/** Register the Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch`
|
|
119
|
+
* calls it around each commit's per-query batch delivery so the Store can fold every affected
|
|
120
|
+
* view before notifying any subscriber. */
|
|
121
|
+
onCommitBoundary(handler) {
|
|
122
|
+
this.boundaryHandler = handler;
|
|
123
|
+
}
|
|
113
124
|
/** Deliver one commit's per-query batches. Non-reentrant (#15): if a delivery is already in
|
|
114
125
|
* progress (a subscriber re-entered via write()), enqueue and let the active drain pick it
|
|
115
126
|
* up FIFO, so each query folds commits in order. Per-query isolated (#11): a view's fold or
|
|
116
127
|
* subscriber throwing does NOT drop sibling queries' batches — the first error is re-raised
|
|
117
|
-
* only after every query has been delivered.
|
|
128
|
+
* only after every query has been delivered.
|
|
129
|
+
*
|
|
130
|
+
* Cross-view-atomic notification: each commit is bracketed with `boundaryHandler("begin"/"end")`
|
|
131
|
+
* so the Store folds ALL of this commit's views before notifying ANY subscriber (a subscriber
|
|
132
|
+
* re-reading a sibling view then sees post-commit data). `begin`/`end` stay balanced even if a
|
|
133
|
+
* fold throws (the `finally`), and a re-entrant write enqueued during the `end` flush drains as
|
|
134
|
+
* its own bracketed commit in the next loop turn — preserving per-query commit order (#15). */
|
|
118
135
|
dispatch(batches) {
|
|
119
136
|
this.dispatchQueue.push(batches);
|
|
120
137
|
if (this.draining)
|
|
@@ -122,18 +139,34 @@ export class WasmBackend {
|
|
|
122
139
|
this.draining = true;
|
|
123
140
|
let firstError;
|
|
124
141
|
let hasError = false;
|
|
142
|
+
const note = (err) => {
|
|
143
|
+
if (!hasError) {
|
|
144
|
+
hasError = true;
|
|
145
|
+
firstError = err;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
125
148
|
try {
|
|
126
149
|
while (this.dispatchQueue.length) {
|
|
127
150
|
const next = this.dispatchQueue.shift();
|
|
128
|
-
|
|
151
|
+
if (next.length === 0)
|
|
152
|
+
continue; // an empty commit (no query changed) — no barrier needed
|
|
153
|
+
this.boundaryHandler("begin");
|
|
154
|
+
try {
|
|
155
|
+
for (const b of next) {
|
|
156
|
+
try {
|
|
157
|
+
this.handler(b.queryId, { type: "batch", events: b.events });
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
note(err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
129
165
|
try {
|
|
130
|
-
this.
|
|
166
|
+
this.boundaryHandler("end"); // flush: notify every folded view's subscribers
|
|
131
167
|
}
|
|
132
168
|
catch (err) {
|
|
133
|
-
|
|
134
|
-
hasError = true;
|
|
135
|
-
firstError = err;
|
|
136
|
-
}
|
|
169
|
+
note(err);
|
|
137
170
|
}
|
|
138
171
|
}
|
|
139
172
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,kGAAkG;AAClG,6CAA6C;AAC7C,EAAE;AACF,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AAEzF,OAAO,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAWnE,cAAc,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rindle/wasm",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
"@rindle/source": "./src/index.ts",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"default": "./dist/index.js"
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json",
|
|
22
|
+
"./pkg/*": "./pkg/*"
|
|
21
23
|
},
|
|
22
24
|
"files": [
|
|
23
25
|
"dist",
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
"README.md"
|
|
28
30
|
],
|
|
29
31
|
"dependencies": {
|
|
30
|
-
"@rindle/client": "0.1
|
|
32
|
+
"@rindle/client": "0.3.1"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@types/node": "^22.10.0",
|
package/pkg/rindle.d.ts
CHANGED
|
@@ -191,6 +191,25 @@ export class WriteTxn {
|
|
|
191
191
|
* mutations re-invoked so far), which is exactly what re-invocation must read.
|
|
192
192
|
*/
|
|
193
193
|
get(table: string, pk: any): any;
|
|
194
|
+
/**
|
|
195
|
+
* Run a one-shot query (a `where`/`orderBy`/`limit`/join AST) over the state this
|
|
196
|
+
* transaction is mutating — the live base **plus** this txn's own staged writes-so-far
|
|
197
|
+
* (`203-MUTATOR-READS-DESIGN.md` §5.2; the same read-your-writes contract `get`
|
|
198
|
+
* provides, §4.1). Returns the query's rows as keyed JS objects with their materialized
|
|
199
|
+
* `related` children nested by name — presented identically to a `view.data` row of the
|
|
200
|
+
* same query ([`marshal::caught_node_to_js`]) — in the query's order.
|
|
201
|
+
*
|
|
202
|
+
* Mechanically this is [`Db::query`] minus the persisted `QueryReg`, pointed at the
|
|
203
|
+
* per-table read-cache forks (§4): for every table the AST reads it seeds a cache fork
|
|
204
|
+
* ([`ensure_fork`](Self::ensure_fork)) and registers a throwaway COW **fork-of-fork** of
|
|
205
|
+
* it as a *scratch* source node — `add_memory_source` takes the source by value, so the
|
|
206
|
+
* cache fork stays in `forks` for later reads/writes. It then builds a transient
|
|
207
|
+
* pipeline over those scratch nodes, hydrates the snapshot, and tears **everything**
|
|
208
|
+
* down — the pipeline (incl. its sink) and every scratch source — on **every** exit
|
|
209
|
+
* path, including a malformed-AST build failure (invariant 4). No durable sink is
|
|
210
|
+
* registered and nothing is delivered to any live query.
|
|
211
|
+
*/
|
|
212
|
+
query(ast_json: any): any;
|
|
194
213
|
/**
|
|
195
214
|
* Stage a remove of `row` from `table`.
|
|
196
215
|
*/
|
|
@@ -236,6 +255,7 @@ export interface InitOutput {
|
|
|
236
255
|
readonly writetxn_commit: (a: number) => [number, number, number];
|
|
237
256
|
readonly writetxn_edit: (a: number, b: number, c: number, d: any, e: any) => [number, number];
|
|
238
257
|
readonly writetxn_get: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
258
|
+
readonly writetxn_query: (a: number, b: any) => [number, number, number];
|
|
239
259
|
readonly writetxn_remove: (a: number, b: number, c: number, d: any) => [number, number];
|
|
240
260
|
readonly writetxn_rollback: (a: number) => void;
|
|
241
261
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
package/pkg/rindle.js
CHANGED
|
@@ -355,6 +355,33 @@ export class WriteTxn {
|
|
|
355
355
|
}
|
|
356
356
|
return takeFromExternrefTable0(ret[0]);
|
|
357
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Run a one-shot query (a `where`/`orderBy`/`limit`/join AST) over the state this
|
|
360
|
+
* transaction is mutating — the live base **plus** this txn's own staged writes-so-far
|
|
361
|
+
* (`203-MUTATOR-READS-DESIGN.md` §5.2; the same read-your-writes contract `get`
|
|
362
|
+
* provides, §4.1). Returns the query's rows as keyed JS objects with their materialized
|
|
363
|
+
* `related` children nested by name — presented identically to a `view.data` row of the
|
|
364
|
+
* same query ([`marshal::caught_node_to_js`]) — in the query's order.
|
|
365
|
+
*
|
|
366
|
+
* Mechanically this is [`Db::query`] minus the persisted `QueryReg`, pointed at the
|
|
367
|
+
* per-table read-cache forks (§4): for every table the AST reads it seeds a cache fork
|
|
368
|
+
* ([`ensure_fork`](Self::ensure_fork)) and registers a throwaway COW **fork-of-fork** of
|
|
369
|
+
* it as a *scratch* source node — `add_memory_source` takes the source by value, so the
|
|
370
|
+
* cache fork stays in `forks` for later reads/writes. It then builds a transient
|
|
371
|
+
* pipeline over those scratch nodes, hydrates the snapshot, and tears **everything**
|
|
372
|
+
* down — the pipeline (incl. its sink) and every scratch source — on **every** exit
|
|
373
|
+
* path, including a malformed-AST build failure (invariant 4). No durable sink is
|
|
374
|
+
* registered and nothing is delivered to any live query.
|
|
375
|
+
* @param {any} ast_json
|
|
376
|
+
* @returns {any}
|
|
377
|
+
*/
|
|
378
|
+
query(ast_json) {
|
|
379
|
+
const ret = wasm.writetxn_query(this.__wbg_ptr, ast_json);
|
|
380
|
+
if (ret[2]) {
|
|
381
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
382
|
+
}
|
|
383
|
+
return takeFromExternrefTable0(ret[0]);
|
|
384
|
+
}
|
|
358
385
|
/**
|
|
359
386
|
* Stage a remove of `row` from `table`.
|
|
360
387
|
* @param {string} table
|
package/pkg/rindle_bg.wasm
CHANGED
|
Binary file
|
package/pkg/rindle_bg.wasm.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const writetxn_add: (a: number, b: number, c: number, d: any) => [number,
|
|
|
24
24
|
export const writetxn_commit: (a: number) => [number, number, number];
|
|
25
25
|
export const writetxn_edit: (a: number, b: number, c: number, d: any, e: any) => [number, number];
|
|
26
26
|
export const writetxn_get: (a: number, b: number, c: number, d: any) => [number, number, number];
|
|
27
|
+
export const writetxn_query: (a: number, b: any) => [number, number, number];
|
|
27
28
|
export const writetxn_remove: (a: number, b: number, c: number, d: any) => [number, number];
|
|
28
29
|
export const writetxn_rollback: (a: number) => void;
|
|
29
30
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,12 @@ export interface WasmWriteTxn {
|
|
|
39
39
|
remove(table: string, row: unknown[]): void;
|
|
40
40
|
edit(table: string, oldRow: unknown[], newRow: unknown[]): void;
|
|
41
41
|
get(table: string, pk: unknown[]): unknown[] | undefined;
|
|
42
|
+
/** Run a one-shot query over the state this txn is mutating — the live base plus this
|
|
43
|
+
* txn's own staged writes-so-far (read-your-writes; 203-MUTATOR-READS-DESIGN.md §5.2).
|
|
44
|
+
* Synchronous (over a lazy read-cache fork of the staged buffer). Returns the query's rows
|
|
45
|
+
* as keyed objects with their materialized `related` children nested by name (identical in
|
|
46
|
+
* shape to a `view.data` row), in the query's order. */
|
|
47
|
+
query(ast: Ast): unknown[];
|
|
42
48
|
commit(): Array<{ queryId: number; events: unknown[] }>;
|
|
43
49
|
rollback(): void;
|
|
44
50
|
}
|
|
@@ -78,6 +84,11 @@ type BatchSet = Array<{ queryId: number; events: unknown[] }>;
|
|
|
78
84
|
export class WasmBackend<S extends ColsMap> implements Backend {
|
|
79
85
|
private readonly db: WasmDb;
|
|
80
86
|
private handler: (qid: QueryId, ev: ChangeEvent) => void = () => {};
|
|
87
|
+
// The Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch` brackets
|
|
88
|
+
// each commit's per-query batch delivery with `begin`/`end` so the Store folds every affected
|
|
89
|
+
// view before notifying any subscriber (cross-view-atomic notification). Defaults to a no-op so
|
|
90
|
+
// a Store that never registers it (or none at all) just gets per-event notification.
|
|
91
|
+
private boundaryHandler: (phase: "begin" | "end") => void = () => {};
|
|
81
92
|
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
82
93
|
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
83
94
|
private readonly localTables: Set<string>;
|
|
@@ -159,28 +170,54 @@ export class WasmBackend<S extends ColsMap> implements Backend {
|
|
|
159
170
|
this.handler = handler;
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
/** Register the Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch`
|
|
174
|
+
* calls it around each commit's per-query batch delivery so the Store can fold every affected
|
|
175
|
+
* view before notifying any subscriber. */
|
|
176
|
+
onCommitBoundary(handler: (phase: "begin" | "end") => void): void {
|
|
177
|
+
this.boundaryHandler = handler;
|
|
178
|
+
}
|
|
179
|
+
|
|
162
180
|
/** Deliver one commit's per-query batches. Non-reentrant (#15): if a delivery is already in
|
|
163
181
|
* progress (a subscriber re-entered via write()), enqueue and let the active drain pick it
|
|
164
182
|
* up FIFO, so each query folds commits in order. Per-query isolated (#11): a view's fold or
|
|
165
183
|
* subscriber throwing does NOT drop sibling queries' batches — the first error is re-raised
|
|
166
|
-
* only after every query has been delivered.
|
|
184
|
+
* only after every query has been delivered.
|
|
185
|
+
*
|
|
186
|
+
* Cross-view-atomic notification: each commit is bracketed with `boundaryHandler("begin"/"end")`
|
|
187
|
+
* so the Store folds ALL of this commit's views before notifying ANY subscriber (a subscriber
|
|
188
|
+
* re-reading a sibling view then sees post-commit data). `begin`/`end` stay balanced even if a
|
|
189
|
+
* fold throws (the `finally`), and a re-entrant write enqueued during the `end` flush drains as
|
|
190
|
+
* its own bracketed commit in the next loop turn — preserving per-query commit order (#15). */
|
|
167
191
|
private dispatch(batches: BatchSet): void {
|
|
168
192
|
this.dispatchQueue.push(batches);
|
|
169
193
|
if (this.draining) return; // an outer drain is running; it will deliver this set
|
|
170
194
|
this.draining = true;
|
|
171
195
|
let firstError: unknown;
|
|
172
196
|
let hasError = false;
|
|
197
|
+
const note = (err: unknown) => {
|
|
198
|
+
if (!hasError) {
|
|
199
|
+
hasError = true;
|
|
200
|
+
firstError = err;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
173
203
|
try {
|
|
174
204
|
while (this.dispatchQueue.length) {
|
|
175
205
|
const next = this.dispatchQueue.shift()!;
|
|
176
|
-
|
|
206
|
+
if (next.length === 0) continue; // an empty commit (no query changed) — no barrier needed
|
|
207
|
+
this.boundaryHandler("begin");
|
|
208
|
+
try {
|
|
209
|
+
for (const b of next) {
|
|
210
|
+
try {
|
|
211
|
+
this.handler(b.queryId, { type: "batch", events: b.events as never });
|
|
212
|
+
} catch (err) {
|
|
213
|
+
note(err);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} finally {
|
|
177
217
|
try {
|
|
178
|
-
this.
|
|
218
|
+
this.boundaryHandler("end"); // flush: notify every folded view's subscribers
|
|
179
219
|
} catch (err) {
|
|
180
|
-
|
|
181
|
-
hasError = true;
|
|
182
|
-
firstError = err;
|
|
183
|
-
}
|
|
220
|
+
note(err);
|
|
184
221
|
}
|
|
185
222
|
}
|
|
186
223
|
}
|