@lunora/replica 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (58) hide show
  1. package/LICENSE.md +214 -0
  2. package/README.md +2 -0
  3. package/dist/adapters/better-sqlite3.d.mts +14 -13
  4. package/dist/adapters/better-sqlite3.d.ts +14 -13
  5. package/dist/adapters/better-sqlite3.mjs +1 -29
  6. package/dist/adapters/sqlite-wasm.d.mts +29 -18
  7. package/dist/adapters/sqlite-wasm.d.ts +29 -18
  8. package/dist/adapters/sqlite-wasm.mjs +1 -56
  9. package/dist/adapters/sqljs.d.mts +10 -9
  10. package/dist/adapters/sqljs.d.ts +10 -9
  11. package/dist/adapters/sqljs.mjs +1 -55
  12. package/dist/index.d.mts +646 -531
  13. package/dist/index.d.ts +646 -531
  14. package/dist/index.mjs +1 -20
  15. package/dist/packem_shared/EventEmitter-ovTsLeAj.mjs +1 -0
  16. package/dist/packem_shared/EventLog-DmlRY_4Z.mjs +1 -0
  17. package/dist/packem_shared/EventLogDO-Cb-7iN9w.mjs +1 -0
  18. package/dist/packem_shared/EventLogDOClient-C6gA3b3u.mjs +1 -0
  19. package/dist/packem_shared/EventSource-B5UTlkl9.mjs +1 -0
  20. package/dist/packem_shared/EventsSync-DWyGGZQZ.mjs +1 -0
  21. package/dist/packem_shared/InMemorySnapshotStore-C4taIG5K.mjs +1 -0
  22. package/dist/packem_shared/LocalMirror-Q7cPH_rX.mjs +4 -0
  23. package/dist/packem_shared/MaterializerRuntime-CqGGjSxl.mjs +1 -0
  24. package/dist/packem_shared/SubscriptionManager-CbSjG_GA.mjs +1 -0
  25. package/dist/packem_shared/applyDiff-BUzddc6r.mjs +1 -0
  26. package/dist/packem_shared/applyDiffToDb-1M2I3BHi.mjs +1 -0
  27. package/dist/packem_shared/classifyChanges-IoOGDVVx.mjs +1 -0
  28. package/dist/packem_shared/defineEvents-CJZV8Bgi.mjs +1 -0
  29. package/dist/packem_shared/eventsContext-Dxow9Y7S.mjs +1 -0
  30. package/dist/packem_shared/isClientSeq-DSXBJskD.mjs +1 -0
  31. package/dist/packem_shared/local-mirror.d-CyGOpUES.d.ts +530 -0
  32. package/dist/packem_shared/local-mirror.d-DTavX_y0.d.mts +530 -0
  33. package/dist/packem_shared/subscribeToMirror-Dru_AYBu.mjs +1 -0
  34. package/dist/packem_shared/{types.d-VfJ76cK4.d.mts → types.d-CkMkSwLJ.d.mts} +10 -9
  35. package/dist/packem_shared/{types.d-VfJ76cK4.d.ts → types.d-CkMkSwLJ.d.ts} +10 -9
  36. package/dist/react.d.mts +59 -57
  37. package/dist/react.d.ts +59 -57
  38. package/dist/react.mjs +1 -15
  39. package/package.json +2 -2
  40. package/dist/packem_shared/EventEmitter-CMZfct03.mjs +0 -92
  41. package/dist/packem_shared/EventLog-zMy7AYP4.mjs +0 -162
  42. package/dist/packem_shared/EventLogDO-CZYUvvSr.mjs +0 -235
  43. package/dist/packem_shared/EventLogDOClient-DGiEdi96.mjs +0 -86
  44. package/dist/packem_shared/EventSource-DfV4VoRD.mjs +0 -195
  45. package/dist/packem_shared/EventsSync-DkVbU0WV.mjs +0 -91
  46. package/dist/packem_shared/InMemorySnapshotStore-BHVAD-Bp.mjs +0 -24
  47. package/dist/packem_shared/LocalMirror-GeJ26eNe.mjs +0 -188
  48. package/dist/packem_shared/MaterializerRuntime-HqNXqJxp.mjs +0 -204
  49. package/dist/packem_shared/SubscriptionManager-C5xbw0pg.mjs +0 -75
  50. package/dist/packem_shared/applyDiff-BtbIl1D3.mjs +0 -40
  51. package/dist/packem_shared/applyDiffToDb-DQ1xZp5J.mjs +0 -58
  52. package/dist/packem_shared/classifyChanges-aZmkxgVI.mjs +0 -38
  53. package/dist/packem_shared/defineEvents-DiBkPTh_.mjs +0 -28
  54. package/dist/packem_shared/eventsContext-Bk_p48hj.mjs +0 -6
  55. package/dist/packem_shared/isClientSeq-C46BkzqJ.mjs +0 -5
  56. package/dist/packem_shared/local-mirror.d-Bp19ueGy.d.mts +0 -412
  57. package/dist/packem_shared/local-mirror.d-GhuZAKgm.d.ts +0 -412
  58. package/dist/packem_shared/subscribeToMirror-CiaM-nQ7.mjs +0 -45
@@ -1,188 +0,0 @@
1
- import { createSqlJsAdapter } from '../adapters/sqljs.mjs';
2
- import { applyDiffToDb as applyDiffToDatabase, escapeIdentifier } from './applyDiffToDb-DQ1xZp5J.mjs';
3
- import { EventLog } from './EventLog-zMy7AYP4.mjs';
4
-
5
- const MIRROR_META_TABLE = "__lunora_mirror_meta";
6
- const ensureMetaTable = (database) => {
7
- database.exec(
8
- `CREATE TABLE IF NOT EXISTS ${MIRROR_META_TABLE} (
9
- key TEXT PRIMARY KEY NOT NULL,
10
- value TEXT NOT NULL
11
- )`
12
- );
13
- };
14
- class LocalMirror {
15
- #db;
16
- #tables;
17
- #eventLog = new EventLog();
18
- #changeListeners = /* @__PURE__ */ new Set();
19
- /**
20
- * Convenience factory that creates a {@link LocalMirror} backed by a
21
- * {@link createSqlJsAdapter sql.js adapter} without needing to import
22
- * and wire sql.js manually.
23
- *
24
- * The caller provides an initialised sql.js database — this method wraps
25
- * it in an adapter and constructs the mirror.
26
- * @example
27
- * ```ts
28
- * import initSqlJs from "sql.js";
29
- *
30
- * const SQL = await initSqlJs();
31
- * const mirror = LocalMirror.create(new SQL.Database(), {
32
- * tables: { todos: { primaryKey: "id" } },
33
- * });
34
- * ```
35
- */
36
- static create(sqlJsDatabase, options) {
37
- const adapter = createSqlJsAdapter(sqlJsDatabase);
38
- return new LocalMirror({ db: adapter, tables: options?.tables });
39
- }
40
- constructor(options) {
41
- this.#db = options.db;
42
- this.#tables = { ...options.tables };
43
- ensureMetaTable(this.#db);
44
- }
45
- /**
46
- * Subscribe to data-change notifications. Fires after every {@link applyDiff}.
47
- * Returns an unsubscribe function.
48
- */
49
- onChange(callback) {
50
- this.#changeListeners.add(callback);
51
- return () => {
52
- this.#changeListeners.delete(callback);
53
- };
54
- }
55
- // ── Public API ─────────────────────────────────────────────────────
56
- /**
57
- * The in-memory event log tracking every diff applied to this mirror.
58
- * Use {@link EventLog.getSince} for catch-up replication across tabs
59
- * or service-worker instances.
60
- */
61
- get eventLog() {
62
- return this.#eventLog;
63
- }
64
- /**
65
- * The raw SQLite adapter. Advanced consumers (e.g. the React hook)
66
- * can use it for ad-hoc queries or bulk operations.
67
- */
68
- get db() {
69
- return this.#db;
70
- }
71
- /**
72
- * Apply a server-side diff to the local SQLite mirror.
73
- *
74
- * The diff is applied in a transaction and recorded in the event log
75
- * so other tabs or the SW can catch up.
76
- */
77
- applyDiff(diff) {
78
- if (diff.changes.length === 0) {
79
- return;
80
- }
81
- const pkColumn = this.#tables[diff.table]?.primaryKey ?? "id";
82
- this.#ensureTableSchema(diff);
83
- applyDiffToDatabase(this.#db, diff, pkColumn);
84
- this.#eventLog.append("table-diff", diff, [diff]);
85
- for (const listener of this.#changeListeners) {
86
- try {
87
- listener();
88
- } catch {
89
- }
90
- }
91
- }
92
- /**
93
- * Run an arbitrary SQL query against the local mirror and return
94
- * typed results.
95
- * @example
96
- * ```ts
97
- * const users = mirror.query<{ id: string; name: string }>(
98
- * "SELECT id, name FROM users WHERE active = ?",
99
- * [true],
100
- * );
101
- * ```
102
- */
103
- query(sql, params) {
104
- return this.#db.query(sql, params);
105
- }
106
- /**
107
- * Delete every row from all known tables (preserves the event log
108
- * and schema). Useful when re-syncing from scratch.
109
- */
110
- clearData() {
111
- const tables = this.#db.query(
112
- `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE '__lunora_%' AND name NOT LIKE 'sqlite_%'`
113
- );
114
- this.#db.transaction(() => {
115
- for (const { name } of tables) {
116
- this.#db.exec(`DELETE FROM ${escapeIdentifier(name)}`);
117
- }
118
- });
119
- }
120
- /**
121
- * Dispose the mirror and close the database connection.
122
- */
123
- close() {
124
- this.#db.close();
125
- this.#eventLog.clear();
126
- }
127
- // ── Schema helpers ─────────────────────────────────────────────────
128
- /**
129
- * Register a table schema so the mirror can create the table on
130
- * first use.
131
- */
132
- registerTable(name, definition) {
133
- this.#tables[name] = definition;
134
- }
135
- /**
136
- * Return the list of mirrored table names.
137
- */
138
- get mirroredTables() {
139
- return Object.keys(this.#tables);
140
- }
141
- // ── Internal ───────────────────────────────────────────────────────
142
- /**
143
- * Derive the UNION of non-PK column names across every non-delete change.
144
- * @param diff The table diff whose changes are scanned.
145
- * @param pk The primary-key column to exclude from the result.
146
- */
147
- static #collectDiffColumns(diff, pk) {
148
- const requiredColumns = /* @__PURE__ */ new Set();
149
- for (const change of diff.changes) {
150
- if (change.type === "delete") {
151
- continue;
152
- }
153
- for (const key of Object.keys(change.data)) {
154
- if (key !== pk) {
155
- requiredColumns.add(key);
156
- }
157
- }
158
- }
159
- return requiredColumns;
160
- }
161
- /**
162
- * Ensure the target table exists with all columns needed by the diff.
163
- *
164
- * - If the table doesn't exist yet, CREATE it with columns derived from the diff data (PK + every non-delete column).
165
- * - If the table already exists, ALTER TABLE ADD COLUMN for any keys in the diff that don't have a corresponding column yet (schema evolution).
166
- */
167
- #ensureTableSchema(diff) {
168
- const pk = this.#tables[diff.table]?.primaryKey ?? "id";
169
- const requiredColumns = LocalMirror.#collectDiffColumns(diff, pk);
170
- const existing = this.#db.query(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, [diff.table]);
171
- if (existing.length === 0) {
172
- let columnDefs = `${escapeIdentifier(pk)} TEXT PRIMARY KEY NOT NULL`;
173
- for (const key of requiredColumns) {
174
- columnDefs += `, ${escapeIdentifier(key)} TEXT`;
175
- }
176
- this.#db.exec(`CREATE TABLE IF NOT EXISTS ${escapeIdentifier(diff.table)} (${columnDefs})`);
177
- } else if (requiredColumns.size > 0) {
178
- const existingColumns = new Set(this.#db.query(`PRAGMA table_info(${escapeIdentifier(diff.table)})`).map((row) => row.name));
179
- for (const key of requiredColumns) {
180
- if (!existingColumns.has(key)) {
181
- this.#db.exec(`ALTER TABLE ${escapeIdentifier(diff.table)} ADD COLUMN ${escapeIdentifier(key)} TEXT`);
182
- }
183
- }
184
- }
185
- }
186
- }
187
-
188
- export { LocalMirror };
@@ -1,204 +0,0 @@
1
- const defineMaterializer = (definition) => {
2
- let state = definition.initial();
3
- return {
4
- def: definition,
5
- get state() {
6
- return Object.freeze(state);
7
- },
8
- setState(newState) {
9
- state = newState;
10
- },
11
- apply(entry) {
12
- state = definition.handle(state, entry);
13
- },
14
- reset() {
15
- state = definition.initial();
16
- }
17
- };
18
- };
19
- class MaterializerRuntime {
20
- #materializers;
21
- #snapshotStore;
22
- #doClient;
23
- #unknownEventHandling;
24
- /**
25
- * The highest event seq that has been applied to all materializers.
26
- * Starts at `0` and advances monotonically.
27
- */
28
- #appliedSeq = 0;
29
- constructor(materializers, options = {}) {
30
- this.#materializers = [...materializers];
31
- this.#snapshotStore = options.snapshotStore;
32
- this.#doClient = options.doClient;
33
- this.#unknownEventHandling = options.unknownEventHandling ?? "warn";
34
- }
35
- // ── Public API ──────────────────────────────────────────────────────
36
- /**
37
- * The sequence number of the last event applied to all materializers.
38
- */
39
- get appliedSeq() {
40
- return this.#appliedSeq;
41
- }
42
- /**
43
- * Replay a batch of entries through all materializers.
44
- *
45
- * Entries with `seq < this.appliedSeq` are silently skipped (idempotent).
46
- * @returns The number of entries actually applied.
47
- */
48
- applyEntries(entries) {
49
- let count = 0;
50
- for (const entry of entries) {
51
- if (entry.seq < this.#appliedSeq) {
52
- continue;
53
- }
54
- const statesBefore = this.#materializers.map((m) => m.state);
55
- let stateChanged = false;
56
- for (const m of this.#materializers) {
57
- m.apply(entry);
58
- }
59
- for (let i = 0; i < this.#materializers.length; i += 1) {
60
- if (this.#materializers[i]?.state !== statesBefore[i]) {
61
- stateChanged = true;
62
- break;
63
- }
64
- }
65
- if (!stateChanged) {
66
- this.#handleUnknownEvent(entry);
67
- }
68
- this.#appliedSeq = entry.seq + 1;
69
- count += 1;
70
- }
71
- return count;
72
- }
73
- /**
74
- * Apply the configured {@link UnknownEventHandling} strategy for an event
75
- * that no materializer handled.
76
- */
77
- #handleUnknownEvent(entry) {
78
- const strategy = this.#unknownEventHandling;
79
- if (typeof strategy === "function") {
80
- strategy(entry);
81
- return;
82
- }
83
- switch (strategy) {
84
- case "ignore": {
85
- return;
86
- }
87
- case "fail": {
88
- throw new Error(
89
- `MaterializerRuntime: unhandled event type "${entry.type}" (seq ${String(entry.seq)}). Configure \`unknownEventHandling\` to handle this event or change the strategy.`
90
- );
91
- }
92
- default: {
93
- console.warn(
94
- `[MaterializerRuntime] unhandled event type "${entry.type}" (seq ${String(entry.seq)}). The event was skipped. Configure \`unknownEventHandling\` if this is expected.`
95
- );
96
- }
97
- }
98
- }
99
- /**
100
- * Attempt to recover materialized state from a snapshot store.
101
- *
102
- * When a snapshot is found for a materializer, its state is restored
103
- * and the snapshot's watermark (`appliedSeq`) is returned so the caller
104
- * can skip replaying entries up to that point.
105
- * @returns The highest `appliedSeq` across all recovered snapshots, or `0`.
106
- */
107
- async recoverFromSnapshots() {
108
- if (!this.#snapshotStore) {
109
- return 0;
110
- }
111
- let maxSeq = 0;
112
- for (const m of this.#materializers) {
113
- const raw = await this.#snapshotStore.load(m.def.name);
114
- if (raw !== null && typeof raw === "object") {
115
- const snapshot = raw;
116
- if (snapshot.state !== void 0) {
117
- m.setState(snapshot.state);
118
- }
119
- if (snapshot.appliedSeq > maxSeq) {
120
- maxSeq = snapshot.appliedSeq;
121
- }
122
- }
123
- }
124
- if (maxSeq > this.#appliedSeq) {
125
- this.#appliedSeq = maxSeq;
126
- }
127
- return maxSeq;
128
- }
129
- /**
130
- * Persist the current state of all materializers as snapshots.
131
- */
132
- async persistSnapshots() {
133
- if (!this.#snapshotStore) {
134
- return;
135
- }
136
- for (const m of this.#materializers) {
137
- await this.#snapshotStore.save(m.def.name, {
138
- appliedSeq: this.#appliedSeq,
139
- state: m.state
140
- });
141
- }
142
- }
143
- // ── DO-backed lifecycle (when a doClient is provided) ──────────────
144
- /**
145
- * Bootstrap the runtime from the EventLogDO.
146
- *
147
- * 1. Recover materialized state from snapshots (if a snapshotStore is
148
- * configured).
149
- * 2. Fetch all entries since the recovered watermark from the DO.
150
- * 3. Apply them through the materializers.
151
- *
152
- * Call this once on startup / after the DO binding is available.
153
- * @returns The number of entries applied during catch-up.
154
- */
155
- async initialize() {
156
- if (!this.#doClient) {
157
- return 0;
158
- }
159
- const snapshotSeq = await this.recoverFromSnapshots();
160
- const entries = await this.#doClient.getSince(snapshotSeq);
161
- if (entries.length === 0) {
162
- return 0;
163
- }
164
- return this.applyEntries(entries);
165
- }
166
- /**
167
- * Append an event to the EventLogDO and apply it through all
168
- * materializers.
169
- *
170
- * This is a convenience over calling `doClient.append(...)` +
171
- * `runtime.applyEntries(...)` yourself — it persists the event
172
- * **then** applies the returned entry (with its assigned seq).
173
- * @returns The persisted entry with its DO-assigned `seq`.
174
- */
175
- async appendEvent(input) {
176
- if (!this.#doClient) {
177
- throw new Error("MaterializerRuntime.appendEvent requires a doClient — pass one in the constructor options.");
178
- }
179
- const persisted = await this.#doClient.append([input]);
180
- const entry = persisted[0];
181
- if (!entry) {
182
- throw new Error("MaterializerRuntime.appendEvent: DO returned empty result");
183
- }
184
- this.applyEntries([entry]);
185
- return entry;
186
- }
187
- /**
188
- * Reset all materializers to their initial state and clear snapshots.
189
- */
190
- reset() {
191
- this.#appliedSeq = 0;
192
- for (const m of this.#materializers) {
193
- m.reset();
194
- }
195
- }
196
- /**
197
- * The list of registered materializers.
198
- */
199
- get materializers() {
200
- return this.#materializers;
201
- }
202
- }
203
-
204
- export { MaterializerRuntime, defineMaterializer };
@@ -1,75 +0,0 @@
1
- class SubscriptionManager {
2
- #subscriptions = /* @__PURE__ */ new Map();
3
- #nextId = 0;
4
- // ── Registration ──────────────────────────────────────────────────
5
- /**
6
- * Subscribe to every state change emitted by the event source.
7
- * @returns Unsubscribe function.
8
- */
9
- onStateChange(callback) {
10
- const id = String(this.#nextId);
11
- this.#nextId += 1;
12
- const sub = { kind: "state", id, callback };
13
- this.#subscriptions.set(id, sub);
14
- return () => {
15
- this.#subscriptions.delete(id);
16
- };
17
- }
18
- /**
19
- * Subscribe to a specific event type.
20
- * @param eventType The event type to listen for (matches `entry.type`).
21
- * @param callback Invoked with each matching entry.
22
- * @returns Unsubscribe function.
23
- */
24
- onEvent(eventType, callback) {
25
- const id = String(this.#nextId);
26
- this.#nextId += 1;
27
- const sub = { kind: "event", id, eventType, callback };
28
- this.#subscriptions.set(id, sub);
29
- return () => {
30
- this.#subscriptions.delete(id);
31
- };
32
- }
33
- // ── Notification ──────────────────────────────────────────────────
34
- /**
35
- * Notify all state-change subscribers with the current state.
36
- */
37
- notifyState(state) {
38
- for (const sub of this.#subscriptions.values()) {
39
- if (sub.kind === "state") {
40
- try {
41
- sub.callback(state);
42
- } catch {
43
- }
44
- }
45
- }
46
- }
47
- /**
48
- * Notify event-type subscribers whose `eventType` matches.
49
- */
50
- notifyEvent(entry) {
51
- for (const sub of this.#subscriptions.values()) {
52
- if (sub.kind === "event" && sub.eventType === entry.type) {
53
- try {
54
- sub.callback(entry);
55
- } catch {
56
- }
57
- }
58
- }
59
- }
60
- // ── Introspection ─────────────────────────────────────────────────
61
- /**
62
- * Return the total number of active subscriptions.
63
- */
64
- get size() {
65
- return this.#subscriptions.size;
66
- }
67
- /**
68
- * Remove all subscriptions.
69
- */
70
- clear() {
71
- this.#subscriptions.clear();
72
- }
73
- }
74
-
75
- export { SubscriptionManager };
@@ -1,40 +0,0 @@
1
- const applyDiff = (current, diff) => {
2
- const next = new Map(current);
3
- for (const change of diff.changes) {
4
- switch (change.type) {
5
- case "delete": {
6
- next.delete(change.id);
7
- break;
8
- }
9
- case "insert": {
10
- const rawId = change.data.id;
11
- const id = typeof rawId === "string" || typeof rawId === "number" ? String(rawId) : crypto.randomUUID();
12
- next.set(id, { ...change.data, id });
13
- break;
14
- }
15
- case "update": {
16
- const existing = next.get(change.id);
17
- if (existing) {
18
- next.set(change.id, { ...existing, ...change.data });
19
- }
20
- break;
21
- }
22
- }
23
- }
24
- return next;
25
- };
26
- const applyDiffs = (current, diffs) => {
27
- let result = new Map(current);
28
- for (const diff of diffs) {
29
- result = applyDiff(result, diff);
30
- }
31
- return result;
32
- };
33
- const applyDiffToSnapshot = (snapshot, diff) => {
34
- const next = new Map(snapshot);
35
- const tableMap = next.get(diff.table) ?? /* @__PURE__ */ new Map();
36
- next.set(diff.table, applyDiff(tableMap, diff));
37
- return next;
38
- };
39
-
40
- export { applyDiff, applyDiffToSnapshot, applyDiffs };
@@ -1,58 +0,0 @@
1
- const escapeIdentifier = (id) => `\`${id.replaceAll("`", "``")}\``;
2
- const setClause = (keys) => keys.map((key) => `${escapeIdentifier(key)} = ?`).join(", ");
3
- const colList = (keys) => `(${keys.map((key) => escapeIdentifier(key)).join(", ")})`;
4
- const valueList = (count) => `(${Array.from({ length: count }).fill("?").join(", ")})`;
5
- const applySingleDiff = (database, diff, pkColumn) => {
6
- if (diff.changes.length === 0) {
7
- return;
8
- }
9
- const table = escapeIdentifier(diff.table);
10
- const pk = escapeIdentifier(pkColumn);
11
- for (const change of diff.changes) {
12
- switch (change.type) {
13
- case "delete": {
14
- database.exec(`DELETE FROM ${table} WHERE ${pk} = ?`, [change.id]);
15
- break;
16
- }
17
- case "insert": {
18
- const { data } = change;
19
- const keys = Object.keys(data);
20
- if (keys.length === 0) {
21
- continue;
22
- }
23
- const sql = `INSERT OR REPLACE INTO ${table} ${colList(keys)} VALUES ${valueList(keys.length)}`;
24
- const values = keys.map((k) => data[k]);
25
- database.exec(sql, values);
26
- break;
27
- }
28
- case "update": {
29
- const { data } = change;
30
- const keys = Object.keys(data);
31
- if (keys.length === 0) {
32
- continue;
33
- }
34
- const sql = `UPDATE ${table} SET ${setClause(keys)} WHERE ${pk} = ?`;
35
- const values = [...keys.map((k) => data[k]), change.id];
36
- database.exec(sql, values);
37
- break;
38
- }
39
- }
40
- }
41
- };
42
- const applyDiffToDatabase = (database, diff, pkColumn) => {
43
- database.transaction(() => {
44
- applySingleDiff(database, diff, pkColumn ?? "id");
45
- });
46
- };
47
- const applyDiffsToDatabase = (database, diffs) => {
48
- if (diffs.length === 0) {
49
- return;
50
- }
51
- database.transaction(() => {
52
- for (const diff of diffs) {
53
- applySingleDiff(database, diff, "id");
54
- }
55
- });
56
- };
57
-
58
- export { applyDiffToDatabase as applyDiffToDb, applyDiffsToDatabase as applyDiffsToDb, escapeIdentifier };
@@ -1,38 +0,0 @@
1
- const createTableDiff = (table, changes, timestamp) => {
2
- return {
3
- table,
4
- changes,
5
- timestamp: timestamp ?? Date.now()
6
- };
7
- };
8
- const isDiffEmpty = (diff) => diff.changes.length === 0;
9
- const diffSize = (diff) => diff.changes.length;
10
- const classifyChanges = (diff) => {
11
- const inserts = [];
12
- const updates = [];
13
- const deletes = [];
14
- for (const change of diff.changes) {
15
- if (change.type === "insert") {
16
- inserts.push(change);
17
- } else if (change.type === "update") {
18
- updates.push(change);
19
- } else {
20
- deletes.push(change);
21
- }
22
- }
23
- return { inserts, updates, deletes };
24
- };
25
- const mergeDiffs = (diffs) => {
26
- if (diffs.length === 0) {
27
- return null;
28
- }
29
- const first = diffs[0];
30
- const last = diffs[diffs.length - 1];
31
- return createTableDiff(
32
- first.table,
33
- diffs.flatMap((d) => d.changes),
34
- last.timestamp
35
- );
36
- };
37
-
38
- export { classifyChanges, createTableDiff, diffSize, isDiffEmpty, mergeDiffs };
@@ -1,28 +0,0 @@
1
- const defineEvents = (definition, options) => {
2
- const result = {};
3
- const typeMap = {};
4
- const prefix = options?.version ? `${options.version}.` : "";
5
- for (const [namespace, events] of Object.entries(definition)) {
6
- const nsObject = {};
7
- for (const [name] of Object.entries(events)) {
8
- const qualifiedType = `${prefix}${namespace}.${name}`;
9
- typeMap[qualifiedType] = void 0;
10
- const factory = Object.assign(
11
- (payload) => {
12
- return {
13
- type: qualifiedType,
14
- payload,
15
- timestamp: Date.now()
16
- };
17
- },
18
- { type: qualifiedType }
19
- );
20
- nsObject[name] = factory;
21
- }
22
- result[namespace] = nsObject;
23
- }
24
- result._types = typeMap;
25
- return result;
26
- };
27
-
28
- export { defineEvents };
@@ -1,6 +0,0 @@
1
- const eventsContext = (client) => {
2
- const facade = client;
3
- return async ({ ctx: _context, next }) => next({ ctx: { events: facade } });
4
- };
5
-
6
- export { eventsContext };
@@ -1,5 +0,0 @@
1
- const isGlobalSeq = (seq) => typeof seq === "number";
2
- const isClientSeq = (seq) => typeof seq !== "number" && "rebaseGeneration" in seq;
3
- const isInputEvent = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && "payload" in value && "timestamp" in value;
4
-
5
- export { isClientSeq, isGlobalSeq, isInputEvent };