@lunora/replica 1.0.0-alpha.12 → 1.0.0-alpha.120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +55 -10
  2. package/dist/adapters/better-sqlite3.d.mts +2 -1
  3. package/dist/adapters/better-sqlite3.d.ts +2 -1
  4. package/dist/adapters/better-sqlite3.mjs +1 -1
  5. package/dist/adapters/sqlite-wasm.d.mts +3 -9
  6. package/dist/adapters/sqlite-wasm.d.ts +3 -9
  7. package/dist/adapters/sqlite-wasm.mjs +1 -1
  8. package/dist/adapters/sqljs.d.mts +4 -2
  9. package/dist/adapters/sqljs.d.ts +4 -2
  10. package/dist/adapters/sqljs.mjs +1 -1
  11. package/dist/index.d.mts +171 -67
  12. package/dist/index.d.ts +171 -67
  13. package/dist/index.mjs +1 -1
  14. package/dist/packem_shared/{EventEmitter-ovTsLeAj.mjs → EventEmitter-uo75adUL.mjs} +1 -1
  15. package/dist/packem_shared/EventLog-SuC_BKwj.mjs +1 -0
  16. package/dist/packem_shared/EventLogDO-BgUx2GGL.mjs +1 -0
  17. package/dist/packem_shared/EventLogDOClient-DWerZ3_n.mjs +1 -0
  18. package/dist/packem_shared/EventSource-Bg6zNmRn.mjs +1 -0
  19. package/dist/packem_shared/EventsSync-B3wzXm-b.mjs +1 -0
  20. package/dist/packem_shared/LocalMirror-Bn22hyEB.mjs +4 -0
  21. package/dist/packem_shared/MaterializerRuntime-DFXi-aqd.mjs +1 -0
  22. package/dist/packem_shared/SubscriptionManager-AhPw3lFc.mjs +1 -0
  23. package/dist/packem_shared/applyDiff-CZAqAC8Y.mjs +1 -0
  24. package/dist/packem_shared/applyDiffToDb-CK-Dcy17.mjs +1 -0
  25. package/dist/packem_shared/classifyChanges-BBc0-770.mjs +1 -0
  26. package/dist/packem_shared/defineEvents-DHo-VK7G.mjs +1 -0
  27. package/dist/packem_shared/fnv1a-BNN96GYb.mjs +1 -0
  28. package/dist/packem_shared/int64-CCVxepl4.mjs +1 -0
  29. package/dist/packem_shared/{isClientSeq-DSXBJskD.mjs → isClientSeq-D2Xm0_lj.mjs} +1 -1
  30. package/dist/packem_shared/{local-mirror.d-DTavX_y0.d.mts → local-mirror.d-DzREvGNM.d.mts} +54 -22
  31. package/dist/packem_shared/{local-mirror.d-CyGOpUES.d.ts → local-mirror.d-wbGXkXaE.d.ts} +54 -22
  32. package/dist/packem_shared/subscribeToMirror-DSn8n1IR.mjs +1 -0
  33. package/dist/packem_shared/{types.d-CkMkSwLJ.d.mts → types.d-BuLTPLaQ.d.mts} +0 -2
  34. package/dist/packem_shared/{types.d-CkMkSwLJ.d.ts → types.d-BuLTPLaQ.d.ts} +0 -2
  35. package/dist/packem_shared/wire-key-DfMHAtqH.mjs +1 -0
  36. package/dist/react.d.mts +39 -29
  37. package/dist/react.d.ts +39 -29
  38. package/dist/react.mjs +1 -1
  39. package/package.json +1 -1
  40. package/dist/packem_shared/EventLog-DmlRY_4Z.mjs +0 -1
  41. package/dist/packem_shared/EventLogDO-Cb-7iN9w.mjs +0 -1
  42. package/dist/packem_shared/EventLogDOClient-C6gA3b3u.mjs +0 -1
  43. package/dist/packem_shared/EventSource-B5UTlkl9.mjs +0 -1
  44. package/dist/packem_shared/EventsSync-pK_hg9KP.mjs +0 -1
  45. package/dist/packem_shared/LocalMirror-Q7cPH_rX.mjs +0 -4
  46. package/dist/packem_shared/MaterializerRuntime-B-I9jKDe.mjs +0 -1
  47. package/dist/packem_shared/SubscriptionManager-CbSjG_GA.mjs +0 -1
  48. package/dist/packem_shared/applyDiff-BUzddc6r.mjs +0 -1
  49. package/dist/packem_shared/applyDiffToDb-1M2I3BHi.mjs +0 -1
  50. package/dist/packem_shared/classifyChanges-BnOMcDGj.mjs +0 -1
  51. package/dist/packem_shared/defineEvents-CJZV8Bgi.mjs +0 -1
  52. package/dist/packem_shared/subscribeToMirror-Dru_AYBu.mjs +0 -1
package/README.md CHANGED
@@ -112,9 +112,11 @@ const SQL = await initSqlJs();
112
112
  const adapter = createSqlJsAdapter(new SQL.Database());
113
113
  const mirror = new LocalMirror({ db: adapter });
114
114
 
115
+ // A diff is `{ table, timestamp, changes }` (plus an optional stable `id`).
116
+ // `createTableDiff("todos", changes)` fills the timestamp and id for you.
115
117
  mirror.applyDiff({
116
118
  table: "todos",
117
- schema: "1.0",
119
+ timestamp: Date.now(),
118
120
  changes: [{ type: "insert", data: { id: "1", title: "hello", done: false } }],
119
121
  });
120
122
 
@@ -140,7 +142,9 @@ machine, and pushes the resulting diffs to the mirror:
140
142
  import { EventsSync } from "@lunora/replica";
141
143
 
142
144
  const sync = new EventsSync({
143
- fetchEventsSince: (seq) => eventLogClient.getSince(seq),
145
+ // `getSince` answers ONE bounded page; EventsSync keeps calling with the
146
+ // advanced watermark until the log is exhausted.
147
+ fetchEventsSince: async (seq) => (await eventLogClient.getSince(seq)).entries,
144
148
  applyEvents: (events) => {
145
149
  /* feed events into your state machine */
146
150
  },
@@ -160,15 +164,23 @@ See the [EventsSync JSDoc](src/sync-events.ts) for full API details.
160
164
 
161
165
  ### useLocalQuery (React)
162
166
 
163
- Live-updating hook that re-queries the mirror whenever a diff is applied.
164
- Returns `undefined` when the query fails (e.g. table doesn't exist yet),
165
- and the result rows otherwise.
167
+ Live-updating hook that re-queries the mirror whenever a diff is applied. It
168
+ returns a discriminated union — `{ data }` on success, `{ error }` when the
169
+ query fails (malformed SQL, or the table doesn't exist yet because no matching
170
+ diff has been applied). A failure is never collapsed to `undefined`, so check
171
+ `error` explicitly rather than reading a missing `data` as "still loading".
166
172
 
167
173
  ```tsx
168
174
  import { useLocalQuery } from "@lunora/replica/react";
169
175
 
170
176
  function TodoList() {
171
- const todos = useLocalQuery<{ id: string; title: string; done: boolean }>(mirror, "SELECT id, title, done FROM todos WHERE done = ?", [false]);
177
+ const { data: todos, error } = useLocalQuery<{ id: string; title: string; done: boolean }>(mirror, "SELECT id, title, done FROM todos WHERE done = ?", [
178
+ false,
179
+ ]);
180
+
181
+ if (error) {
182
+ return <p>Query failed: {error.message}</p>;
183
+ }
172
184
 
173
185
  if (todos === undefined) {
174
186
  return <p>Waiting for data…</p>;
@@ -189,14 +201,47 @@ React 18+ concurrent features and Suspense-based frameworks (Next.js, Remix).
189
201
 
190
202
  ## EventLogDO
191
203
 
192
- A Durable Object that persists the event log to DO SQLite storage:
204
+ A Durable Object that persists the event log to DO SQLite storage.
205
+
206
+ Re-export the class from your worker entry so Wrangler can find it:
193
207
 
194
208
  ```ts
209
+ // src/worker.ts
195
210
  export { EventLogDO } from "@lunora/replica";
211
+ ```
212
+
213
+ Then declare the binding in `wrangler.jsonc`. The DO uses `state.storage.sql`,
214
+ so its migration **must** use `new_sqlite_classes` — `new_classes` gives the
215
+ instance a key-value store with no `.sql`, and every request fails at the first
216
+ statement:
217
+
218
+ ```jsonc
219
+ {
220
+ "durable_objects": {
221
+ "bindings": [{ "name": "EVENT_LOG_DO", "class_name": "EventLogDO" }],
222
+ },
223
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["EventLogDO"] }],
224
+ }
225
+ ```
226
+
227
+ `EventLogDOClient` wraps the DO's `fetch()` RPC surface. Its only option is
228
+ `fetch` — a function that dispatches a request to the instance you want, which
229
+ is where the namespace and instance id are chosen:
230
+
231
+ ```ts
232
+ const client = new EventLogDOClient({
233
+ fetch: (request) => env.EVENT_LOG_DO.get(env.EVENT_LOG_DO.idFromName("my-app")).fetch(request),
234
+ });
235
+
236
+ // Append takes an ARRAY of events and returns them with their assigned `seq`s.
237
+ const [entry] = await client.append([{ type: "order:placed", payload: { orderId: "123" } }]);
196
238
 
197
- const client = new EventLogDOClient({ namespace: "my-app" });
198
- await client.append({ type: "order:placed", payload: { orderId: "123" } });
199
- const events = await client.query({ type: "order:placed", limit: 10 });
239
+ // Read back by sequence number — the log is append-only and ordered, so
240
+ // there is no filter-by-type query. Every read is ONE bounded page (500
241
+ // entries by default, 1000 max): walk `cursor` while `truncated` is true.
242
+ const { entries, truncated, cursor } = await client.getSince(entry.seq);
243
+ const page = await client.getSince(0, 50);
244
+ const size = await client.getSize();
200
245
  ```
201
246
 
202
247
  ## Custom adapters
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.mjs";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.mjs";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by [better-sqlite3](https://github.com/WiseLibs/better-sqlite3)
4
4
  * (a synchronous SQLite3 binding for Node.js).
@@ -22,6 +22,7 @@ declare const createBetterSqlite3Adapter: (database: {
22
22
  run: (params?: unknown[]) => {
23
23
  lastInsertRowid: number | bigint;
24
24
  };
25
+ safeIntegers: (toggle?: boolean) => unknown;
25
26
  };
26
27
  transaction: (function_: () => void) => () => void;
27
28
  }) => SqliteAdapter;
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.js";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.js";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by [better-sqlite3](https://github.com/WiseLibs/better-sqlite3)
4
4
  * (a synchronous SQLite3 binding for Node.js).
@@ -22,6 +22,7 @@ declare const createBetterSqlite3Adapter: (database: {
22
22
  run: (params?: unknown[]) => {
23
23
  lastInsertRowid: number | bigint;
24
24
  };
25
+ safeIntegers: (toggle?: boolean) => unknown;
25
26
  };
26
27
  transaction: (function_: () => void) => () => void;
27
28
  }) => SqliteAdapter;
@@ -1 +1 @@
1
- const a=r=>({exec(e,t){t&&t.length>0?r.prepare(e).run([...t]):r.exec(e)},query(e,t){const n=r.prepare(e);return t&&t.length>0?n.all([...t]):n.all()},transaction(e){r.transaction(e)()},lastInsertRowId(){const e=r.prepare("SELECT last_insert_rowid() AS id").get();return Number(e?.id??-1)},close(){r.close()}});export{a as createBetterSqlite3Adapter};
1
+ import{n as c}from"../packem_shared/int64-CCVxepl4.mjs";const p=t=>({exec(r,e){e&&e.length>0?t.prepare(r).run([...e]):t.exec(r)},query(r,e){const n=t.prepare(r);n.safeIntegers(!0);const o=e&&e.length>0?n.all([...e]):n.all();return c(o)},transaction(r){t.transaction(r)()},close(){t.close()}});export{p as createBetterSqlite3Adapter};
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.mjs";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.mjs";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by the [official SQLite Wasm](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm)
4
4
  * (a WebAssembly build of SQLite that runs in browsers and Node.js).
@@ -10,18 +10,13 @@ import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.mjs";
10
10
  * IMPORTANT (REPLICA-01): the real `oo1.DB.exec()` does NOT return sql.js's
11
11
  * `{ columns, values }[]` result shape — with `rowMode: "object"` and
12
12
  * `returnValue: "resultRows"` it returns the rows directly, as
13
- * `Record&lt;string, unknown>[]`. This adapter is written against that real
14
- * shape; `lastInsertRowId` uses the driver's `selectValue()` convenience
15
- * method (a single-scalar query helper) rather than parsing a result-row
16
- * array.
13
+ * `Record<string, unknown>[]`. This adapter is written against that real shape.
17
14
  * @param database An already-initialised `sqlite3.oo1.DB` instance.
18
15
  * @param database.close Tear down the database connection.
19
16
  * @param database.exec Execute SQL with optional bind params. With
20
17
  * `{ returnValue: "resultRows", rowMode: "object" }` it returns the matched
21
- * rows directly (`Record&lt;string, unknown>[]`); otherwise (DDL/DML/BEGIN/
18
+ * rows directly (`Record<string, unknown>[]`); otherwise (DDL/DML/BEGIN/
22
19
  * COMMIT/ROLLBACK) its return value is unused here.
23
- * @param database.selectValue Run a query and return the first column of the
24
- * first row as a single scalar — used for `SELECT last_insert_rowid()`.
25
20
  * @experimental
26
21
  */
27
22
  declare const createSqliteWasmAdapter: (database: {
@@ -31,6 +26,5 @@ declare const createSqliteWasmAdapter: (database: {
31
26
  returnValue?: "resultRows";
32
27
  rowMode?: "object";
33
28
  }) => Record<string, unknown>[] | undefined;
34
- selectValue: (sql: string, bind?: unknown[]) => unknown;
35
29
  }) => SqliteAdapter;
36
30
  export { createSqliteWasmAdapter };
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.js";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.js";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by the [official SQLite Wasm](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm)
4
4
  * (a WebAssembly build of SQLite that runs in browsers and Node.js).
@@ -10,18 +10,13 @@ import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.js";
10
10
  * IMPORTANT (REPLICA-01): the real `oo1.DB.exec()` does NOT return sql.js's
11
11
  * `{ columns, values }[]` result shape — with `rowMode: "object"` and
12
12
  * `returnValue: "resultRows"` it returns the rows directly, as
13
- * `Record&lt;string, unknown>[]`. This adapter is written against that real
14
- * shape; `lastInsertRowId` uses the driver's `selectValue()` convenience
15
- * method (a single-scalar query helper) rather than parsing a result-row
16
- * array.
13
+ * `Record<string, unknown>[]`. This adapter is written against that real shape.
17
14
  * @param database An already-initialised `sqlite3.oo1.DB` instance.
18
15
  * @param database.close Tear down the database connection.
19
16
  * @param database.exec Execute SQL with optional bind params. With
20
17
  * `{ returnValue: "resultRows", rowMode: "object" }` it returns the matched
21
- * rows directly (`Record&lt;string, unknown>[]`); otherwise (DDL/DML/BEGIN/
18
+ * rows directly (`Record<string, unknown>[]`); otherwise (DDL/DML/BEGIN/
22
19
  * COMMIT/ROLLBACK) its return value is unused here.
23
- * @param database.selectValue Run a query and return the first column of the
24
- * first row as a single scalar — used for `SELECT last_insert_rowid()`.
25
20
  * @experimental
26
21
  */
27
22
  declare const createSqliteWasmAdapter: (database: {
@@ -31,6 +26,5 @@ declare const createSqliteWasmAdapter: (database: {
31
26
  returnValue?: "resultRows";
32
27
  rowMode?: "object";
33
28
  }) => Record<string, unknown>[] | undefined;
34
- selectValue: (sql: string, bind?: unknown[]) => unknown;
35
29
  }) => SqliteAdapter;
36
30
  export { createSqliteWasmAdapter };
@@ -1 +1 @@
1
- const o=c=>({exec(e,t){t&&t.length>0?c.exec(e,{bind:[...t]}):c.exec(e)},query(e,t){return c.exec(e,{bind:t&&t.length>0?[...t]:void 0,returnValue:"resultRows",rowMode:"object"})??[]},transaction(e){c.exec("BEGIN");try{e(),c.exec("COMMIT")}catch(t){throw c.exec("ROLLBACK"),t}},lastInsertRowId(){const e=c.selectValue("SELECT last_insert_rowid()");return typeof e=="number"?e:typeof e=="bigint"?Number(e):-1},close(){c.close()}});export{o as createSqliteWasmAdapter};
1
+ import{n}from"../packem_shared/int64-CCVxepl4.mjs";const x=r=>({exec(o,e){e&&e.length>0?r.exec(o,{bind:[...e]}):r.exec(o)},query(o,e){const c=r.exec(o,{bind:e&&e.length>0?[...e]:void 0,returnValue:"resultRows",rowMode:"object"});return n(c??[])},transaction(o){r.exec("BEGIN");try{o(),r.exec("COMMIT")}catch(e){throw r.exec("ROLLBACK"),e}},close(){r.close()}});export{x as createSqliteWasmAdapter};
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.mjs";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.mjs";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by [sql.js](https://sql.js.org)
4
4
  * (a WebAssembly build of SQLite that runs in browsers, Node, and
@@ -11,7 +11,9 @@ import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.mjs";
11
11
  */
12
12
  declare const createSqlJsAdapter: (database: {
13
13
  close: () => void;
14
- exec: (sql: string, params?: unknown[]) => {
14
+ exec: (sql: string, params?: unknown[], config?: {
15
+ useBigInt?: boolean;
16
+ }) => {
15
17
  columns: string[];
16
18
  values: unknown[][];
17
19
  }[];
@@ -1,4 +1,4 @@
1
- import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.js";
1
+ import { S as SqliteAdapter } from "../packem_shared/types.d-BuLTPLaQ.js";
2
2
  /**
3
3
  * Create a {@link SqliteAdapter} backed by [sql.js](https://sql.js.org)
4
4
  * (a WebAssembly build of SQLite that runs in browsers, Node, and
@@ -11,7 +11,9 @@ import { S as SqliteAdapter } from "../packem_shared/types.d-CkMkSwLJ.js";
11
11
  */
12
12
  declare const createSqlJsAdapter: (database: {
13
13
  close: () => void;
14
- exec: (sql: string, params?: unknown[]) => {
14
+ exec: (sql: string, params?: unknown[], config?: {
15
+ useBigInt?: boolean;
16
+ }) => {
15
17
  columns: string[];
16
18
  values: unknown[][];
17
19
  }[];
@@ -1 +1 @@
1
- const h=n=>({exec(e,t){t&&t.length>0?n.run(e,[...t]):n.exec(e)},query(e,t){const r=(t&&t.length>0?n.exec(e,[...t]):n.exec(e))[0];if(!r)return[];const o=r.columns,s=[];for(const u of r.values){const c={};for(const[l,a]of o.entries())c[a]=u[l];s.push(c)}return s},transaction(e){n.run("BEGIN");try{e(),n.run("COMMIT")}catch(t){throw n.run("ROLLBACK"),t}},lastInsertRowId(){const e=n.exec("SELECT last_insert_rowid() AS id"),t=e[0];if(e.length===0||!t||t.values.length===0)return-1;const r=t.values[0];return!r||r.length===0?-1:Number(r[0])},close(){n.close()}});export{h as createSqlJsAdapter};
1
+ import{n as i}from"../packem_shared/int64-CCVxepl4.mjs";const w=o=>({exec(e,r){r&&r.length>0?o.run(e,[...r]):o.exec(e)},query(e,r){const n=o.exec(e,r&&r.length>0?[...r]:void 0,{useBigInt:!0})[0];if(!n)return[];const u=n.columns,t=[];for(const s of n.values){const c={};for(const[l,f]of u.entries())c[f]=s[l];t.push(c)}return i(t)},transaction(e){o.run("BEGIN");try{e(),o.run("COMMIT")}catch(r){throw o.run("ROLLBACK"),r}},close(){o.close()}});export{w as createSqlJsAdapter};