@rindle/wasm 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/pkg/rindle.d.ts +13 -3
- package/pkg/rindle.js +14 -3
- package/pkg/rindle_bg.wasm +0 -0
- package/pkg/rindle_bg.wasm.d.ts +1 -1
- package/src/index.ts +29 -5
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ export declare function initWasm(moduleOrPath?: unknown): Promise<void>;
|
|
|
37
37
|
export declare class WasmBackend<S extends ColsMap> implements Backend {
|
|
38
38
|
private readonly db;
|
|
39
39
|
private handler;
|
|
40
|
+
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
41
|
+
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
42
|
+
private readonly localTables;
|
|
40
43
|
private readonly dispatchQueue;
|
|
41
44
|
private draining;
|
|
42
45
|
constructor(schema: Schema<S>);
|
|
@@ -44,11 +47,15 @@ export declare class WasmBackend<S extends ColsMap> implements Backend {
|
|
|
44
47
|
* (`AGGREGATE-SYNC-DESIGN.md` §3.3) that is not in the typed schema. The
|
|
45
48
|
* `NormalizedBackend` registers each such `__agg_*` table once, before a query that reads
|
|
46
49
|
* it, so the local engine can join to it (the relationship `count` it backs is shipped by
|
|
47
|
-
* the server, never recomputed). */
|
|
50
|
+
* the server, never recomputed). Always synced (tracked) — synthetic tables are never local. */
|
|
48
51
|
registerTable(name: string, spec: {
|
|
49
52
|
columns: string[];
|
|
50
53
|
primaryKey: number[];
|
|
51
54
|
}): void;
|
|
55
|
+
/** Direct-commit a batch of LOCAL-only writes (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6): push them
|
|
56
|
+
* straight through the engine on the ordinary delivery path, OUTSIDE any optimistic cycle (a
|
|
57
|
+
* local table is untracked, so it never rebases). Rejects a synced/tracked table (M2). */
|
|
58
|
+
writeLocal(mutations: Mutation[]): void;
|
|
52
59
|
/** Remove a synthetic aggregate table registered by {@link registerTable}, once the last
|
|
53
60
|
* query reading it has been unregistered (`AGGREGATE-SYNC-DESIGN.md` §4): frees the engine
|
|
54
61
|
* source + its optimistic baseline so aggregate state is reclaimed, not permanent. Throws
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// needs an explicit, one-time init — `await initWasm()` — before constructing a backend.
|
|
7
7
|
// In Node the bytes are read from the package; in a browser/bundler the wasm is fetched.
|
|
8
8
|
import init, { Db } from "../pkg/rindle.js";
|
|
9
|
-
import { Store, tableSpec } from "@rindle/client";
|
|
9
|
+
import { localTableNames, Store, tableSpec } from "@rindle/client";
|
|
10
10
|
export * from "@rindle/client";
|
|
11
11
|
let initialized = null;
|
|
12
12
|
/** Initialize the wasm module (idempotent). Call once at startup before `new WasmBackend`
|
|
@@ -33,6 +33,9 @@ export function initWasm(moduleOrPath) {
|
|
|
33
33
|
export class WasmBackend {
|
|
34
34
|
db;
|
|
35
35
|
handler = () => { };
|
|
36
|
+
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
37
|
+
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
38
|
+
localTables;
|
|
36
39
|
// Non-reentrant delivery (#15): commits dispatch through a FIFO queue. A subscriber that
|
|
37
40
|
// synchronously triggers another write enqueues that commit's batches; they drain only after
|
|
38
41
|
// the in-flight commit's batches finish, preserving per-query commit order regardless of what
|
|
@@ -41,17 +44,40 @@ export class WasmBackend {
|
|
|
41
44
|
draining = false;
|
|
42
45
|
constructor(schema) {
|
|
43
46
|
this.db = new Db();
|
|
47
|
+
this.localTables = localTableNames(schema);
|
|
44
48
|
for (const name of Object.keys(schema.tables)) {
|
|
45
|
-
|
|
49
|
+
// A local table registers as a source but skips optimistic tracking (C1) — the `local` bit
|
|
50
|
+
// crosses to the engine here.
|
|
51
|
+
this.db.registerTable(name, tableSpec(schema.tables[name]), this.localTables.has(name));
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
/** Register an additional base table after construction — for a SYNTHETIC aggregate table
|
|
49
55
|
* (`AGGREGATE-SYNC-DESIGN.md` §3.3) that is not in the typed schema. The
|
|
50
56
|
* `NormalizedBackend` registers each such `__agg_*` table once, before a query that reads
|
|
51
57
|
* it, so the local engine can join to it (the relationship `count` it backs is shipped by
|
|
52
|
-
* the server, never recomputed). */
|
|
58
|
+
* the server, never recomputed). Always synced (tracked) — synthetic tables are never local. */
|
|
53
59
|
registerTable(name, spec) {
|
|
54
|
-
this.db.registerTable(name, spec);
|
|
60
|
+
this.db.registerTable(name, spec, false);
|
|
61
|
+
}
|
|
62
|
+
/** Direct-commit a batch of LOCAL-only writes (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6): push them
|
|
63
|
+
* straight through the engine on the ordinary delivery path, OUTSIDE any optimistic cycle (a
|
|
64
|
+
* local table is untracked, so it never rebases). Rejects a synced/tracked table (M2). */
|
|
65
|
+
writeLocal(mutations) {
|
|
66
|
+
for (const m of mutations) {
|
|
67
|
+
if (!this.localTables.has(m.table)) {
|
|
68
|
+
throw new Error(`writeLocal: table "${m.table}" is not local-only — a direct commit to a synced table is reverted on the next rewind (M2).`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.writeWith((tx) => {
|
|
72
|
+
for (const m of mutations) {
|
|
73
|
+
if (m.op === "add")
|
|
74
|
+
tx.add(m.table, m.row);
|
|
75
|
+
else if (m.op === "remove")
|
|
76
|
+
tx.remove(m.table, m.row);
|
|
77
|
+
else
|
|
78
|
+
tx.edit(m.table, m.old, m.new);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
55
81
|
}
|
|
56
82
|
/** Remove a synthetic aggregate table registered by {@link registerTable}, once the last
|
|
57
83
|
* query reading it has been unregistered (`AGGREGATE-SYNC-DESIGN.md` §4): frees the engine
|
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,KAAK,EAAE,SAAS,EAAE,MAAM,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.1.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"README.md"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@rindle/client": "0.1.
|
|
30
|
+
"@rindle/client": "0.1.3"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.10.0",
|
package/pkg/rindle.d.ts
CHANGED
|
@@ -25,9 +25,19 @@ export class Db {
|
|
|
25
25
|
/**
|
|
26
26
|
* Register a base table from a `SchemaSpec` (`{ columns, primaryKey, sort? }` —
|
|
27
27
|
* [`marshal::schema_from_js`]; any `relationships` in the spec are ignored — nesting
|
|
28
|
-
* is query-local via `sub`). Seeds an empty shared source.
|
|
28
|
+
* is query-local via `sub`). Seeds an empty shared source.
|
|
29
|
+
*
|
|
30
|
+
* `local` marks a **local-only** table (`201-LOCAL-ONLY-TABLES-DESIGN.md` §3.1): it is
|
|
31
|
+
* registered as a source but **NOT optimistically tracked** — so the §1.3 `rewind` never
|
|
32
|
+
* iterates it (it has no `sync` baseline to revert to), and a direct write to it persists
|
|
33
|
+
* across every server cycle (C1). A synced table tracks as before.
|
|
34
|
+
*
|
|
35
|
+
* Idempotent ONLY when the re-registration is truly identical — same schema **and** same
|
|
36
|
+
* locality (N1a). A genuine collision (one name claiming two different shapes/localities)
|
|
37
|
+
* errors loudly rather than silently no-op'ing, so the `__agg_*`/`_rindle_*` prefix ban
|
|
38
|
+
* (`createSchema`, N1) has a loud backstop instead of a silent one.
|
|
29
39
|
*/
|
|
30
|
-
registerTable(table: string, schema: any): void;
|
|
40
|
+
registerTable(table: string, schema: any, local: boolean): void;
|
|
31
41
|
/**
|
|
32
42
|
* Open a §1.3 reconcile cycle against the coherent server delta `deltas` —
|
|
33
43
|
* an array of base-table row ops `{ table, type: "add"|"remove"|"edit", row,
|
|
@@ -210,7 +220,7 @@ export interface InitOutput {
|
|
|
210
220
|
readonly db_destroyQuery: (a: number, b: number) => void;
|
|
211
221
|
readonly db_new: () => number;
|
|
212
222
|
readonly db_query: (a: number, b: number, c: any) => [number, number, number];
|
|
213
|
-
readonly db_registerTable: (a: number, b: number, c: number, d: any) => [number, number];
|
|
223
|
+
readonly db_registerTable: (a: number, b: number, c: number, d: any, e: number) => [number, number];
|
|
214
224
|
readonly db_serverBatchBegin: (a: number, b: any) => [number, number];
|
|
215
225
|
readonly db_serverBatchEnd: (a: number) => [number, number, number];
|
|
216
226
|
readonly db_unregisterTable: (a: number, b: number, c: number) => [number, number];
|
package/pkg/rindle.js
CHANGED
|
@@ -49,14 +49,25 @@ export class Db {
|
|
|
49
49
|
/**
|
|
50
50
|
* Register a base table from a `SchemaSpec` (`{ columns, primaryKey, sort? }` —
|
|
51
51
|
* [`marshal::schema_from_js`]; any `relationships` in the spec are ignored — nesting
|
|
52
|
-
* is query-local via `sub`). Seeds an empty shared source.
|
|
52
|
+
* is query-local via `sub`). Seeds an empty shared source.
|
|
53
|
+
*
|
|
54
|
+
* `local` marks a **local-only** table (`201-LOCAL-ONLY-TABLES-DESIGN.md` §3.1): it is
|
|
55
|
+
* registered as a source but **NOT optimistically tracked** — so the §1.3 `rewind` never
|
|
56
|
+
* iterates it (it has no `sync` baseline to revert to), and a direct write to it persists
|
|
57
|
+
* across every server cycle (C1). A synced table tracks as before.
|
|
58
|
+
*
|
|
59
|
+
* Idempotent ONLY when the re-registration is truly identical — same schema **and** same
|
|
60
|
+
* locality (N1a). A genuine collision (one name claiming two different shapes/localities)
|
|
61
|
+
* errors loudly rather than silently no-op'ing, so the `__agg_*`/`_rindle_*` prefix ban
|
|
62
|
+
* (`createSchema`, N1) has a loud backstop instead of a silent one.
|
|
53
63
|
* @param {string} table
|
|
54
64
|
* @param {any} schema
|
|
65
|
+
* @param {boolean} local
|
|
55
66
|
*/
|
|
56
|
-
registerTable(table, schema) {
|
|
67
|
+
registerTable(table, schema, local) {
|
|
57
68
|
const ptr0 = passStringToWasm0(table, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
58
69
|
const len0 = WASM_VECTOR_LEN;
|
|
59
|
-
const ret = wasm.db_registerTable(this.__wbg_ptr, ptr0, len0, schema);
|
|
70
|
+
const ret = wasm.db_registerTable(this.__wbg_ptr, ptr0, len0, schema, local);
|
|
60
71
|
if (ret[1]) {
|
|
61
72
|
throw takeFromExternrefTable0(ret[0]);
|
|
62
73
|
}
|
package/pkg/rindle_bg.wasm
CHANGED
|
Binary file
|
package/pkg/rindle_bg.wasm.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const __wbg_writetxn_free: (a: number, b: number) => void;
|
|
|
8
8
|
export const db_destroyQuery: (a: number, b: number) => void;
|
|
9
9
|
export const db_new: () => number;
|
|
10
10
|
export const db_query: (a: number, b: number, c: any) => [number, number, number];
|
|
11
|
-
export const db_registerTable: (a: number, b: number, c: number, d: any) => [number, number];
|
|
11
|
+
export const db_registerTable: (a: number, b: number, c: number, d: any, e: number) => [number, number];
|
|
12
12
|
export const db_serverBatchBegin: (a: number, b: any) => [number, number];
|
|
13
13
|
export const db_serverBatchEnd: (a: number) => [number, number, number];
|
|
14
14
|
export const db_unregisterTable: (a: number, b: number, c: number) => [number, number];
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// In Node the bytes are read from the package; in a browser/bundler the wasm is fetched.
|
|
8
8
|
|
|
9
9
|
import init, { Db } from "../pkg/rindle.js";
|
|
10
|
-
import { Store, tableSpec } from "@rindle/client";
|
|
10
|
+
import { localTableNames, Store, tableSpec } from "@rindle/client";
|
|
11
11
|
import type {
|
|
12
12
|
Ast,
|
|
13
13
|
Backend,
|
|
@@ -22,7 +22,7 @@ export * from "@rindle/client";
|
|
|
22
22
|
|
|
23
23
|
// The wasm Db surface this backend uses (kept local so it doesn't depend on the generated .d.ts).
|
|
24
24
|
interface WasmDb {
|
|
25
|
-
registerTable(table: string, spec: { columns: string[]; primaryKey: number[] }): void;
|
|
25
|
+
registerTable(table: string, spec: { columns: string[]; primaryKey: number[] }, local: boolean): void;
|
|
26
26
|
unregisterTable(table: string): void;
|
|
27
27
|
query(queryId: number, ast: Ast): { comparatorVersion: number; schema: unknown; snapshot: unknown[] };
|
|
28
28
|
destroyQuery(queryId: number): void;
|
|
@@ -78,6 +78,9 @@ type BatchSet = Array<{ queryId: number; events: unknown[] }>;
|
|
|
78
78
|
export class WasmBackend<S extends ColsMap> implements Backend {
|
|
79
79
|
private readonly db: WasmDb;
|
|
80
80
|
private handler: (qid: QueryId, ev: ChangeEvent) => void = () => {};
|
|
81
|
+
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
|
|
82
|
+
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
|
|
83
|
+
private readonly localTables: Set<string>;
|
|
81
84
|
|
|
82
85
|
// Non-reentrant delivery (#15): commits dispatch through a FIFO queue. A subscriber that
|
|
83
86
|
// synchronously triggers another write enqueues that commit's batches; they drain only after
|
|
@@ -88,8 +91,11 @@ export class WasmBackend<S extends ColsMap> implements Backend {
|
|
|
88
91
|
|
|
89
92
|
constructor(schema: Schema<S>) {
|
|
90
93
|
this.db = new Db() as unknown as WasmDb;
|
|
94
|
+
this.localTables = localTableNames(schema);
|
|
91
95
|
for (const name of Object.keys(schema.tables)) {
|
|
92
|
-
|
|
96
|
+
// A local table registers as a source but skips optimistic tracking (C1) — the `local` bit
|
|
97
|
+
// crosses to the engine here.
|
|
98
|
+
this.db.registerTable(name, tableSpec(schema.tables[name]), this.localTables.has(name));
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -97,9 +103,27 @@ export class WasmBackend<S extends ColsMap> implements Backend {
|
|
|
97
103
|
* (`AGGREGATE-SYNC-DESIGN.md` §3.3) that is not in the typed schema. The
|
|
98
104
|
* `NormalizedBackend` registers each such `__agg_*` table once, before a query that reads
|
|
99
105
|
* it, so the local engine can join to it (the relationship `count` it backs is shipped by
|
|
100
|
-
* the server, never recomputed). */
|
|
106
|
+
* the server, never recomputed). Always synced (tracked) — synthetic tables are never local. */
|
|
101
107
|
registerTable(name: string, spec: { columns: string[]; primaryKey: number[] }): void {
|
|
102
|
-
this.db.registerTable(name, spec);
|
|
108
|
+
this.db.registerTable(name, spec, false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Direct-commit a batch of LOCAL-only writes (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6): push them
|
|
112
|
+
* straight through the engine on the ordinary delivery path, OUTSIDE any optimistic cycle (a
|
|
113
|
+
* local table is untracked, so it never rebases). Rejects a synced/tracked table (M2). */
|
|
114
|
+
writeLocal(mutations: Mutation[]): void {
|
|
115
|
+
for (const m of mutations) {
|
|
116
|
+
if (!this.localTables.has(m.table)) {
|
|
117
|
+
throw new Error(`writeLocal: table "${m.table}" is not local-only — a direct commit to a synced table is reverted on the next rewind (M2).`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.writeWith((tx) => {
|
|
121
|
+
for (const m of mutations) {
|
|
122
|
+
if (m.op === "add") tx.add(m.table, m.row);
|
|
123
|
+
else if (m.op === "remove") tx.remove(m.table, m.row);
|
|
124
|
+
else tx.edit(m.table, m.old, m.new);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
103
127
|
}
|
|
104
128
|
|
|
105
129
|
/** Remove a synthetic aggregate table registered by {@link registerTable}, once the last
|