event-sourced-collection 0.0.13 → 0.0.14

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.
@@ -1,6 +1,36 @@
1
- import { a as BrowserPlatformConfig, c as BrowserWASQLiteDatabase, i as BrowserCoordinatorInstance, n as BrowserEventSourcedModules, o as BrowserPlatformDeps, r as createBrowserEventSourcedDB, s as BrowserPlatformResult, t as BrowserEventSourcedDBConfig } from "./browser-event-sourced-db-D3vSW3QV.mjs";
2
- import { t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-DmHdplVr.mjs";
1
+ import { I as SQLiteDriver, nt as InjectedModuleFn, p as EventSourcedSharedOptions, tt as InjectedCreateCollection, z as SyncLock } from "./types-Dc-_92xv.mjs";
2
+ import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-DlUfot8-.mjs";
3
+ import { PersistedCollectionPersistence } from "@tanstack/db-sqlite-persistence-core";
3
4
 
5
+ //#region src/platforms/browser-wa-sqlite-driver.d.ts
6
+ type BrowserWASQLiteDatabase = {
7
+ execute: <TRow = unknown>(sql: string, params?: ReadonlyArray<unknown>) => Promise<ReadonlyArray<TRow>>;
8
+ close?: () => Promise<void> | void;
9
+ };
10
+ //#endregion
11
+ //#region src/platforms/browser-types.d.ts
12
+ type BrowserCoordinatorInstance = {
13
+ dispose: () => void;
14
+ };
15
+ /**
16
+ * Injected TanStack browser persistence bindings. Parameters are `never` so
17
+ * version-skewed `@tanstack/browser-db-sqlite-persistence` exports stay assignable.
18
+ */
19
+ type BrowserPlatformDeps = {
20
+ openBrowserWASQLiteOPFSDatabase: (options: never) => Promise<BrowserWASQLiteDatabase>;
21
+ createBrowserWASQLitePersistence: (options: never) => PersistedCollectionPersistence;
22
+ BrowserCollectionCoordinator: new (options: never) => BrowserCoordinatorInstance;
23
+ };
24
+ type BrowserPlatformConfig = {
25
+ databaseName: string;
26
+ coordinatorDbName?: string;
27
+ };
28
+ type BrowserPlatformResult = {
29
+ driver: SQLiteDriver;
30
+ persistence: PersistedCollectionPersistence;
31
+ close: () => Promise<void>;
32
+ };
33
+ //#endregion
4
34
  //#region src/platforms/browser.d.ts
5
35
  /**
6
36
  * Opens OPFS SQLite, a cross-tab coordinator, and TanStack persistence.
@@ -31,5 +61,91 @@ import { t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-DmHd
31
61
  */
32
62
  declare function createBrowserPlatform(deps: BrowserPlatformDeps, config: BrowserPlatformConfig): Promise<BrowserPlatformResult>;
33
63
  //#endregion
64
+ //#region src/platforms/browser-event-sourced-db.d.ts
65
+ type CollectionDefConstraint = {
66
+ getKey: (state: never) => string | number;
67
+ };
68
+ type BrowserEventSourcedModules = BrowserPlatformDeps & {
69
+ createCollection: InjectedCreateCollection;
70
+ persistedCollectionOptions: InjectedModuleFn;
71
+ };
72
+ /**
73
+ * Browser entry-point config. Shared sync/lifecycle fields come from
74
+ * {@link EventSourcedSharedOptions} (hover those properties for docs).
75
+ * Deep guide: `docs/usage.md`.
76
+ */
77
+ type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> = Omit<EventSourcedSharedOptions, "lock" | "lockName"> & {
78
+ /**
79
+ * User collection registry. Object keys become `db.collections.<key>` and
80
+ * the wire `collectionId`. Must not collide with reserved names.
81
+ */
82
+ collections: TDefs;
83
+ /**
84
+ * OPFS SQLite database file name (e.g. `"my-app.sqlite"`). Also used as the
85
+ * default sync lock namespace so only one tab syncs for this DB at a time.
86
+ */
87
+ databaseName: string;
88
+ /**
89
+ * Name for the cross-tab collection coordinator database. Defaults to
90
+ * `databaseName` with a trailing `.sqlite` stripped.
91
+ */
92
+ coordinatorDbName?: string;
93
+ /**
94
+ * Browser OPFS persistence modules from
95
+ * `@tanstack/browser-db-sqlite-persistence` plus `createCollection`.
96
+ * Pass the imported bindings directly, or a function to keep WASM off the
97
+ * critical path.
98
+ */
99
+ modules: ModulesInput<BrowserEventSourcedModules>;
100
+ /**
101
+ * Defaults to a Web Locks–backed lock so only one tab syncs at a time.
102
+ * Pass `null` to opt out and let every tab sync independently.
103
+ */
104
+ lock?: SyncLock | null;
105
+ };
106
+ /**
107
+ * Browser-flavoured event-sourced DB: OPFS SQLite + optional Web Locks sync
108
+ * election, returned as a lazy singleton.
109
+ *
110
+ * @param config - See {@link BrowserEventSourcedDBConfig} for every option.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * import { createBrowserEventSourcedDB } from "event-sourced-collection/browser"
115
+ * import type { CollectionDef, EventSourcedDB } from "event-sourced-collection"
116
+ *
117
+ * type Todo = { id: string; title: string }
118
+ * type Defs = { todos: CollectionDef<Todo, string> }
119
+ *
120
+ * const { ensureDb, db } = createBrowserEventSourcedDB<Defs>({
121
+ * databaseName: "app.sqlite",
122
+ * collections: { todos: { getKey: (todo) => todo.id } },
123
+ * sync: { push: "/api/sync/events", pull: "/api/sync/events" },
124
+ * modules: async () => {
125
+ * const { createCollection } = await import("@tanstack/db")
126
+ * const {
127
+ * BrowserCollectionCoordinator,
128
+ * createBrowserWASQLitePersistence,
129
+ * openBrowserWASQLiteOPFSDatabase,
130
+ * persistedCollectionOptions,
131
+ * } = await import("@tanstack/browser-db-sqlite-persistence")
132
+ * return {
133
+ * createCollection,
134
+ * BrowserCollectionCoordinator,
135
+ * createBrowserWASQLitePersistence,
136
+ * openBrowserWASQLiteOPFSDatabase,
137
+ * persistedCollectionOptions,
138
+ * }
139
+ * },
140
+ * })
141
+ *
142
+ * export async function start() {
143
+ * await ensureDb()
144
+ * }
145
+ * export { db }
146
+ * ```
147
+ */
148
+ declare function createBrowserEventSourcedDB<const TDefs extends Record<string, CollectionDefConstraint>>(config: BrowserEventSourcedDBConfig<TDefs>): EventSourcedDBHandle<TDefs>;
149
+ //#endregion
34
150
  export { type BrowserCoordinatorInstance, type BrowserEventSourcedDBConfig, type EventSourcedDBHandle as BrowserEventSourcedDBHandle, type BrowserEventSourcedModules, type BrowserPlatformConfig, type BrowserPlatformDeps, type BrowserPlatformResult, type BrowserWASQLiteDatabase, createBrowserEventSourcedDB, createBrowserPlatform };
35
151
  //# sourceMappingURL=browser.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.mts","names":[],"sources":["../src/platforms/browser.ts"],"mappings":";;;;;AA2CA;;;;;;;;;;;;;;;;AAGgC;;;;;;;;;;iBAHV,qBAAA,CACpB,IAAA,EAAM,mBAAA,EACN,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA"}
1
+ {"version":3,"file":"browser.d.mts","names":[],"sources":["../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser-types.ts","../src/platforms/browser.ts","../src/platforms/browser-event-sourced-db.ts"],"mappings":";;;;;KAEY,uBAAA;EACV,OAAA,mBACE,GAAA,UACA,MAAA,GAAS,aAAA,cACN,OAAA,CAAQ,aAAA,CAAc,IAAA;EAC3B,KAAA,SAAc,OAAA;AAAA;;;KCJJ,0BAAA;EACV,OAAO;AAAA;;ADFT;;;KCSY,mBAAA;EACV,+BAAA,GAAkC,OAAA,YAAmB,OAAA,CAAQ,uBAAA;EAC7D,gCAAA,GAAmC,OAAA,YAAmB,8BAAA;EACtD,4BAAA,OAAmC,OAAA,YAAmB,0BAAA;AAAA;AAAA,KAG5C,qBAAA;EACV,YAAA;EACA,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EACV,MAAA,EAD+B,YAAA;EAE/B,WAAA,EAAa,8BAAA;EACb,KAAA,QAAa,OAAA;AAAA;;;;;;ADvBf;;;;;;;;;;;;;;;;;;;;;AAKuB;;;iBEoCD,qBAAA,CACpB,IAAA,EAAM,mBAAA,EACN,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;;;KC9BN,uBAAA;EACH,MAAA,GAAS,KAAY;AAAA;AAAA,KAGX,0BAAA,GAA6B,mBAAA;EACvC,gBAAA,EAAkB,wBAAA;EAClB,0BAAA,EAA4B,gBAAA;AAAA;;;;;;KAQlB,2BAAA,eAA0C,MAAA,SAAe,uBAAA,KACnE,IAAA,CAAK,yBAAA;EH5BK;;;;EGiCR,WAAA,EAAa,KAAA;EH9BF;;;;EGmCX,YAAA;EHlCmB;;;;EGuCnB,iBAAA;EF3CkC;;;AAC7B;AAOT;;EE0CI,OAAA,EAAS,YAAA,CAAa,0BAAA;EFzCqC;;;;EE8C3D,IAAA,GAAO,QAAA;AAAA;;;;;;;;;;;AF5CuE;AAGlF;;;;AAEmB;AAGnB;;;;;;;;;;;;;;;AAGsB;;;;ACkBtB;;;;;;;iBC8DgB,2BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,2BAAA,CAA4B,KAAA,IAAM,oBAAA,CAAA,KAAA"}
package/dist/browser.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-BqV3vLjS.mjs";
1
+ import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-BRSDLeq9.mjs";
2
2
  import { t as createWebLocksSyncLock } from "./web-locks-Bi2f732X.mjs";
3
3
  //#region src/platforms/browser-wa-sqlite-driver.ts
4
4
  function assertTransactionCallbackHasDriverArg(fn) {
@@ -1380,6 +1380,18 @@ function generateEventId() {
1380
1380
  }
1381
1381
  //#endregion
1382
1382
  //#region src/core/create-event-sourced-db.ts
1383
+ /**
1384
+ * Thrown by `resetLocalReplica()` while the outbox still holds changes the
1385
+ * server has not accepted. Sync first, or pass `discardPending: true`.
1386
+ */
1387
+ var UnsyncedChangesError = class extends Error {
1388
+ pendingCount;
1389
+ constructor(pendingCount) {
1390
+ super(`${pendingCount} local change(s) have not been pushed yet`);
1391
+ this.pendingCount = pendingCount;
1392
+ this.name = "UnsyncedChangesError";
1393
+ }
1394
+ };
1383
1395
  function emptyResult(overrides = {}) {
1384
1396
  return {
1385
1397
  pushed: 0,
@@ -1475,6 +1487,7 @@ async function createEventSourcedDB(config) {
1475
1487
  };
1476
1488
  const { outbox, inbox, deadletter, syncmeta, rowversions } = meta;
1477
1489
  const userCollections = {};
1490
+ const syncedCollections = [];
1478
1491
  const localOnlyIds = new Set(Object.keys(config.collections).filter((id) => config.collections[id].localOnly === true));
1479
1492
  for (const collectionId of Object.keys(config.collections)) {
1480
1493
  const def = config.collections[collectionId];
@@ -1506,6 +1519,10 @@ async function createEventSourcedDB(config) {
1506
1519
  });
1507
1520
  const collection = config.createCollection(options);
1508
1521
  applyCollectionIndexes(collection, collectionId, def.indexes, log);
1522
+ if (!def.localOnly) syncedCollections.push({
1523
+ collectionId,
1524
+ collection
1525
+ });
1509
1526
  log.debug("registered collection", {
1510
1527
  collectionId,
1511
1528
  hasAcceptMutations: Boolean(collection.utils?.acceptMutations)
@@ -1870,6 +1887,59 @@ async function createEventSourcedDB(config) {
1870
1887
  };
1871
1888
  });
1872
1889
  }
1890
+ function resetLocalReplica(options = {}) {
1891
+ const deferred = {
1892
+ deferred: true,
1893
+ removedRows: 0,
1894
+ discardedPending: 0,
1895
+ clientId: clientId.value
1896
+ };
1897
+ return runExclusive(() => withLock(async () => {
1898
+ const pending = [...outbox.state.values()].filter((entry) => !entry.sync);
1899
+ if (pending.length > 0 && options.discardPending !== true) throw new UnsyncedChangesError(pending.length);
1900
+ let removedRows = 0;
1901
+ for (const { collectionId, collection } of syncedCollections) {
1902
+ const target = replayTargets[collectionId];
1903
+ const rows = [...collection.state.entries()];
1904
+ if (rows.length === 0 || !target?.utils.acceptMutations) continue;
1905
+ await target.utils.acceptMutations({ mutations: rows.map(([key, row]) => ({
1906
+ mutationId: generateEventId(),
1907
+ type: "delete",
1908
+ key,
1909
+ modified: row,
1910
+ original: row,
1911
+ changes: row,
1912
+ collection: target
1913
+ })) });
1914
+ removedRows += rows.length;
1915
+ }
1916
+ for (const meta of [
1917
+ outbox,
1918
+ inbox,
1919
+ deadletter
1920
+ ]) for (const eventId of [...meta.state.keys()]) await meta.delete(eventId).isPersisted.promise;
1921
+ for (const id of [...rowversions.state.keys()]) await rowversions.delete(id).isPersisted.promise;
1922
+ clientId.value = generateEventId();
1923
+ await ensureSyncMeta(syncmeta);
1924
+ await syncmeta.update(SYNCMETA_KEY, (draft) => {
1925
+ draft.clientId = clientId.value;
1926
+ draft.pullCursor = 0;
1927
+ draft.lastError = null;
1928
+ }).isPersisted.promise;
1929
+ log.warn("local replica reset", {
1930
+ removedRows,
1931
+ discardedPending: pending.length,
1932
+ clientId: clientId.value
1933
+ });
1934
+ notifyStatus();
1935
+ return {
1936
+ deferred: false,
1937
+ removedRows,
1938
+ discardedPending: pending.length,
1939
+ clientId: clientId.value
1940
+ };
1941
+ }, deferred));
1942
+ }
1873
1943
  function getSyncEnabled() {
1874
1944
  return syncEnabled;
1875
1945
  }
@@ -1902,6 +1972,7 @@ async function createEventSourcedDB(config) {
1902
1972
  return pushLimits;
1903
1973
  },
1904
1974
  pruneSyncedEvents,
1975
+ resetLocalReplica,
1905
1976
  dispose
1906
1977
  };
1907
1978
  }
@@ -2199,6 +2270,6 @@ async function resolveModules(modules) {
2199
2270
  return typeof modules === "function" ? await modules() : modules;
2200
2271
  }
2201
2272
  //#endregion
2202
- export { generateEventId as a, resolvePushLimits as c, createHttpTransport as d, BackendMismatchError as f, createEventSourcedDB as i, SyncPullError as l, resolveModules as n, createEventSourcedLogger as o, createLazySingleton as r, SYNC_PUSH_PRESETS as s, createEventSourcedDBHandle as t, SyncPushError as u };
2273
+ export { createEventSourcedDB as a, SYNC_PUSH_PRESETS as c, SyncPushError as d, createHttpTransport as f, UnsyncedChangesError as i, resolvePushLimits as l, resolveModules as n, generateEventId as o, BackendMismatchError as p, createLazySingleton as r, createEventSourcedLogger as s, createEventSourcedDBHandle as t, SyncPullError as u };
2203
2274
 
2204
- //# sourceMappingURL=create-event-sourced-db-handle-BqV3vLjS.mjs.map
2275
+ //# sourceMappingURL=create-event-sourced-db-handle-BRSDLeq9.mjs.map