event-sourced-collection 0.0.10 → 0.0.12
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/browser.d.mts +118 -2
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.mjs +4 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/{create-event-sourced-db-handle-D-fHKczr.mjs → create-event-sourced-db-handle-CD8WSjXW.mjs} +204 -51
- package/dist/create-event-sourced-db-handle-CD8WSjXW.mjs.map +1 -0
- package/dist/{create-event-sourced-db-handle-B_uCsLJC.d.mts → create-event-sourced-db-handle-Di5qN4QH.d.mts} +2 -2
- package/dist/{create-event-sourced-db-handle-B_uCsLJC.d.mts.map → create-event-sourced-db-handle-Di5qN4QH.d.mts.map} +1 -1
- package/dist/index.d.mts +21 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/node.d.mts +2 -3
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +4 -1
- package/dist/node.mjs.map +1 -1
- package/dist/react-native-event-sourced-db-CL1Rcfq_.d.mts +61 -0
- package/dist/react-native-event-sourced-db-CL1Rcfq_.d.mts.map +1 -0
- package/dist/react-native.d.mts +3 -58
- package/dist/react-native.d.mts.map +1 -1
- package/dist/react-native.mjs +4 -1
- package/dist/react-native.mjs.map +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/{types-DChF-gGL.d.mts → types-DrV5f9_t.d.mts} +31 -6
- package/dist/{types-DChF-gGL.d.mts.map → types-DrV5f9_t.d.mts.map} +1 -1
- package/package.json +1 -1
- package/dist/browser-event-sourced-db-YBmcmWDR.d.mts +0 -121
- package/dist/browser-event-sourced-db-YBmcmWDR.d.mts.map +0 -1
- package/dist/create-event-sourced-db-handle-D-fHKczr.mjs.map +0 -1
package/dist/browser.d.mts
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-
|
|
1
|
+
import { $ as InjectedCreateCollection, L as SyncLock, P as SQLiteDriver, et as InjectedModuleFn, p as EventSourcedSharedOptions } from "./types-DrV5f9_t.mjs";
|
|
2
|
+
import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-Di5qN4QH.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-B_uC
|
|
|
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
|
package/dist/browser.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.mts","names":[],"sources":["../src/platforms/browser.ts"],"mappings":";;;;;
|
|
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-
|
|
1
|
+
import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-CD8WSjXW.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) {
|
|
@@ -203,6 +203,9 @@ function createBrowserEventSourcedDB(config) {
|
|
|
203
203
|
upcastEvent: config.upcastEvent,
|
|
204
204
|
retry: config.retry,
|
|
205
205
|
pushBatchSize: config.pushBatchSize,
|
|
206
|
+
maxPushBytes: config.maxPushBytes,
|
|
207
|
+
syncPreset: config.syncPreset,
|
|
208
|
+
recordLocalEchoes: config.recordLocalEchoes,
|
|
206
209
|
backendMismatch: config.backendMismatch,
|
|
207
210
|
conflictDetection: config.conflictDetection,
|
|
208
211
|
hooks: config.hooks,
|
package/dist/browser.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.mjs","names":[],"sources":["../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser.ts","../src/platforms/browser-event-sourced-db.ts"],"sourcesContent":["import type { SQLiteDriver } from \"../core/types\";\n\nexport type BrowserWASQLiteDatabase = {\n execute: <TRow = unknown>(\n sql: string,\n params?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<TRow>>;\n close?: () => Promise<void> | void;\n};\n\nfunction assertTransactionCallbackHasDriverArg(\n fn: (transactionDriver: SQLiteDriver) => Promise<unknown>,\n): void {\n if (fn.length > 0) {\n return;\n }\n\n throw new Error(\"SQLiteDriver.transaction callback must accept the transaction driver argument\");\n}\n\nfunction assertDatabaseShape(\n database: BrowserWASQLiteDatabase,\n): asserts database is BrowserWASQLiteDatabase {\n if (typeof database.execute !== \"function\") {\n throw new Error(\"Browser wa-sqlite database handle must provide execute(sql, params?)\");\n }\n}\n\nexport class BrowserWASQLiteDriver implements SQLiteDriver {\n private readonly database: BrowserWASQLiteDatabase;\n private queue: Promise<void> = Promise.resolve();\n private nextSavepointId = 1;\n private closed = false;\n\n constructor(database: BrowserWASQLiteDatabase) {\n assertDatabaseShape(database);\n this.database = database;\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql);\n });\n }\n\n async query<T>(sql: string, params: ReadonlyArray<unknown> = []): Promise<ReadonlyArray<T>> {\n return this.enqueue(() => this.database.execute<T>(sql, params));\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql, params);\n });\n }\n\n async transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn);\n\n return this.enqueue(async () => {\n await this.database.execute(`BEGIN IMMEDIATE`);\n try {\n const result = await fn(this.createTransactionDriver());\n await this.database.execute(`COMMIT`);\n return result;\n } catch (error) {\n try {\n await this.database.execute(`ROLLBACK`);\n } catch {}\n throw error;\n }\n });\n }\n\n async transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T> {\n return this.transaction(fn);\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n\n if (typeof this.database.close === \"function\") {\n await Promise.resolve(this.database.close());\n }\n }\n\n private createTransactionDriver(): SQLiteDriver {\n return {\n exec: (sql) => this.database.execute(sql).then(() => undefined),\n query: <T>(sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute<T>(sql, params),\n run: (sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute(sql, params).then(() => undefined),\n transaction: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) =>\n this.runNestedTransaction(fn),\n transactionWithDriver: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) =>\n this.runNestedTransaction(fn),\n };\n }\n\n private async runNestedTransaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn);\n\n const savepointName = `tsdb_sp_${this.nextSavepointId}`;\n this.nextSavepointId++;\n await this.database.execute(`SAVEPOINT ${savepointName}`);\n try {\n const result = await fn(this.createTransactionDriver());\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);\n return result;\n } catch (error) {\n await this.database.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`);\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);\n throw error;\n }\n }\n\n private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {\n const queuedOperation = this.queue.then(operation, operation);\n this.queue = queuedOperation.then(\n () => undefined,\n () => undefined,\n );\n return queuedOperation;\n }\n}\n","import { BrowserWASQLiteDriver } from \"./browser-wa-sqlite-driver\";\nimport type {\n BrowserPlatformConfig,\n BrowserPlatformDeps,\n BrowserPlatformResult,\n} from \"./browser-types\";\n\nexport type {\n BrowserCoordinatorInstance,\n BrowserPlatformConfig,\n BrowserPlatformDeps,\n BrowserPlatformResult,\n} from \"./browser-types\";\n\nexport type { BrowserWASQLiteDatabase } from \"./browser-wa-sqlite-driver\";\n\n/**\n * Opens OPFS SQLite, a cross-tab coordinator, and TanStack persistence.\n * Prefer {@link createBrowserEventSourcedDB}; call this only when you need the\n * raw `persistence` / `close` pair (for example a custom handle).\n *\n * @example\n * ```ts\n * import { createBrowserPlatform } from \"event-sourced-collection/browser\"\n * import {\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * } from \"@tanstack/browser-db-sqlite-persistence\"\n *\n * const platform = await createBrowserPlatform(\n * {\n * openBrowserWASQLiteOPFSDatabase,\n * createBrowserWASQLitePersistence,\n * BrowserCollectionCoordinator,\n * },\n * { databaseName: \"app.sqlite\" },\n * )\n *\n * // pass platform.persistence into createEventSourcedDB\n * await platform.close()\n * ```\n */\nexport async function createBrowserPlatform(\n deps: BrowserPlatformDeps,\n config: BrowserPlatformConfig,\n): Promise<BrowserPlatformResult> {\n const database = await deps.openBrowserWASQLiteOPFSDatabase({\n databaseName: config.databaseName,\n } as never);\n\n const coordinator = new deps.BrowserCollectionCoordinator({\n dbName: config.coordinatorDbName ?? config.databaseName.replace(/\\.sqlite$/, \"\"),\n } as never);\n\n const persistence = deps.createBrowserWASQLitePersistence({\n database,\n coordinator,\n } as never);\n\n const driver = new BrowserWASQLiteDriver(database);\n\n return {\n driver,\n persistence,\n close: async () => {\n coordinator.dispose();\n await driver.close();\n },\n };\n}\n","import {\n createEventSourcedDBHandle,\n resolveModules,\n type ModulesInput,\n} from \"../core/create-event-sourced-db-handle\";\nimport type {\n CreateCollectionFn,\n InjectedCreateCollection,\n InjectedModuleFn,\n PersistedCollectionOptionsFn,\n} from \"../core/persisted-collection\";\nimport type { EventSourcedSharedOptions, SyncLock } from \"../core/types\";\nimport { createBrowserPlatform } from \"./browser\";\nimport type { BrowserPlatformDeps } from \"./browser-types\";\nimport { createWebLocksSyncLock } from \"./web-locks\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n};\n\nexport type BrowserEventSourcedModules = BrowserPlatformDeps & {\n createCollection: InjectedCreateCollection;\n persistedCollectionOptions: InjectedModuleFn;\n};\n\n/**\n * Browser entry-point config. Shared sync/lifecycle fields come from\n * {@link EventSourcedSharedOptions} (hover those properties for docs).\n * Deep guide: `docs/usage.md`.\n */\nexport type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> =\n Omit<EventSourcedSharedOptions, \"lock\" | \"lockName\"> & {\n /**\n * User collection registry. Object keys become `db.collections.<key>` and\n * the wire `collectionId`. Must not collide with reserved names.\n */\n collections: TDefs;\n /**\n * OPFS SQLite database file name (e.g. `\"my-app.sqlite\"`). Also used as the\n * default sync lock namespace so only one tab syncs for this DB at a time.\n */\n databaseName: string;\n /**\n * Name for the cross-tab collection coordinator database. Defaults to\n * `databaseName` with a trailing `.sqlite` stripped.\n */\n coordinatorDbName?: string;\n /**\n * Browser OPFS persistence modules from\n * `@tanstack/browser-db-sqlite-persistence` plus `createCollection`.\n * Pass the imported bindings directly, or a function to keep WASM off the\n * critical path.\n */\n modules: ModulesInput<BrowserEventSourcedModules>;\n /**\n * Defaults to a Web Locks–backed lock so only one tab syncs at a time.\n * Pass `null` to opt out and let every tab sync independently.\n */\n lock?: SyncLock | null;\n };\n\nexport type { EventSourcedDBHandle as BrowserEventSourcedDBHandle } from \"../core/create-event-sourced-db-handle\";\n\n/**\n * Browser-flavoured event-sourced DB: OPFS SQLite + optional Web Locks sync\n * election, returned as a lazy singleton.\n *\n * @param config - See {@link BrowserEventSourcedDBConfig} for every option.\n *\n * @example\n * ```ts\n * import { createBrowserEventSourcedDB } from \"event-sourced-collection/browser\"\n * import type { CollectionDef, EventSourcedDB } from \"event-sourced-collection\"\n *\n * type Todo = { id: string; title: string }\n * type Defs = { todos: CollectionDef<Todo, string> }\n *\n * const { ensureDb, db } = createBrowserEventSourcedDB<Defs>({\n * databaseName: \"app.sqlite\",\n * collections: { todos: { getKey: (todo) => todo.id } },\n * sync: { push: \"/api/sync/events\", pull: \"/api/sync/events\" },\n * modules: async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * const {\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * persistedCollectionOptions,\n * } = await import(\"@tanstack/browser-db-sqlite-persistence\")\n * return {\n * createCollection,\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * persistedCollectionOptions,\n * }\n * },\n * })\n *\n * export async function start() {\n * await ensureDb()\n * }\n * export { db }\n * ```\n */\nexport function createBrowserEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: BrowserEventSourcedDBConfig<TDefs>) {\n return createEventSourcedDBHandle<TDefs>({\n guard: assertBrowserEnvironment,\n setup: async () => {\n const modules = await resolveModules(config.modules);\n\n const platform = await createBrowserPlatform(\n {\n openBrowserWASQLiteOPFSDatabase: modules.openBrowserWASQLiteOPFSDatabase,\n createBrowserWASQLitePersistence: modules.createBrowserWASQLitePersistence,\n BrowserCollectionCoordinator: modules.BrowserCollectionCoordinator,\n },\n {\n databaseName: config.databaseName,\n coordinatorDbName: config.coordinatorDbName,\n },\n );\n\n return {\n persistence: platform.persistence,\n createCollection: modules.createCollection as CreateCollectionFn,\n persistedCollectionOptions:\n modules.persistedCollectionOptions as PersistedCollectionOptionsFn,\n collections: config.collections,\n sync: config.sync,\n syncEnabled: config.syncEnabled,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n clientId: config.clientId,\n unknownEventHandling: config.unknownEventHandling,\n pullOverlap: config.pullOverlap,\n eventSchemaVersion: config.eventSchemaVersion,\n upcastEvent: config.upcastEvent,\n retry: config.retry,\n pushBatchSize: config.pushBatchSize,\n backendMismatch: config.backendMismatch,\n conflictDetection: config.conflictDetection,\n hooks: config.hooks,\n lock: config.lock === null ? undefined : (config.lock ?? createWebLocksSyncLock()),\n lockName: config.databaseName,\n close: platform.close,\n };\n },\n });\n}\n\nfunction assertBrowserEnvironment(): void {\n if (typeof window === \"undefined\") {\n throw new Error(\"Event-sourced DB (browser) is only available in a browser environment.\");\n }\n}\n"],"mappings":";;;AAUA,SAAS,sCACP,IACM;CACN,IAAI,GAAG,SAAS,GACd;CAGF,MAAM,IAAI,MAAM,+EAA+E;AACjG;AAEA,SAAS,oBACP,UAC6C;CAC7C,IAAI,OAAO,SAAS,YAAY,YAC9B,MAAM,IAAI,MAAM,sEAAsE;AAE1F;AAEA,IAAa,wBAAb,MAA2D;CACzD;CACA,QAA+B,QAAQ,QAAQ;CAC/C,kBAA0B;CAC1B,SAAiB;CAEjB,YAAY,UAAmC;EAC7C,oBAAoB,QAAQ;EAC5B,KAAK,WAAW;CAClB;CAEA,MAAM,KAAK,KAA4B;EACrC,MAAM,KAAK,QAAQ,YAAY;GAC7B,MAAM,KAAK,SAAS,QAAQ,GAAG;EACjC,CAAC;CACH;CAEA,MAAM,MAAS,KAAa,SAAiC,CAAC,GAA8B;EAC1F,OAAO,KAAK,cAAc,KAAK,SAAS,QAAW,KAAK,MAAM,CAAC;CACjE;CAEA,MAAM,IAAI,KAAa,SAAiC,CAAC,GAAkB;EACzE,MAAM,KAAK,QAAQ,YAAY;GAC7B,MAAM,KAAK,SAAS,QAAQ,KAAK,MAAM;EACzC,CAAC;CACH;CAEA,MAAM,YAAe,IAAiE;EACpF,sCAAsC,EAAE;EAExC,OAAO,KAAK,QAAQ,YAAY;GAC9B,MAAM,KAAK,SAAS,QAAQ,iBAAiB;GAC7C,IAAI;IACF,MAAM,SAAS,MAAM,GAAG,KAAK,wBAAwB,CAAC;IACtD,MAAM,KAAK,SAAS,QAAQ,QAAQ;IACpC,OAAO;GACT,SAAS,OAAO;IACd,IAAI;KACF,MAAM,KAAK,SAAS,QAAQ,UAAU;IACxC,QAAQ,CAAC;IACT,MAAM;GACR;EACF,CAAC;CACH;CAEA,MAAM,sBAAyB,IAAiE;EAC9F,OAAO,KAAK,YAAY,EAAE;CAC5B;CAEA,MAAM,QAAuB;EAC3B,IAAI,KAAK,QACP;EAEF,KAAK,SAAS;EAEd,IAAI,OAAO,KAAK,SAAS,UAAU,YACjC,MAAM,QAAQ,QAAQ,KAAK,SAAS,MAAM,CAAC;CAE/C;CAEA,0BAAgD;EAC9C,OAAO;GACL,OAAO,QAAQ,KAAK,SAAS,QAAQ,GAAG,CAAC,CAAC,WAAW,KAAA,CAAS;GAC9D,QAAW,KAAa,SAAiC,CAAC,MACxD,KAAK,SAAS,QAAW,KAAK,MAAM;GACtC,MAAM,KAAa,SAAiC,CAAC,MACnD,KAAK,SAAS,QAAQ,KAAK,MAAM,CAAC,CAAC,WAAW,KAAA,CAAS;GACzD,cAAiB,OACf,KAAK,qBAAqB,EAAE;GAC9B,wBAA2B,OACzB,KAAK,qBAAqB,EAAE;EAChC;CACF;CAEA,MAAc,qBACZ,IACY;EACZ,sCAAsC,EAAE;EAExC,MAAM,gBAAgB,WAAW,KAAK;EACtC,KAAK;EACL,MAAM,KAAK,SAAS,QAAQ,aAAa,eAAe;EACxD,IAAI;GACF,MAAM,SAAS,MAAM,GAAG,KAAK,wBAAwB,CAAC;GACtD,MAAM,KAAK,SAAS,QAAQ,qBAAqB,eAAe;GAChE,OAAO;EACT,SAAS,OAAO;GACd,MAAM,KAAK,SAAS,QAAQ,yBAAyB,eAAe;GACpE,MAAM,KAAK,SAAS,QAAQ,qBAAqB,eAAe;GAChE,MAAM;EACR;CACF;CAEA,QAAmB,WAA6C;EAC9D,MAAM,kBAAkB,KAAK,MAAM,KAAK,WAAW,SAAS;EAC5D,KAAK,QAAQ,gBAAgB,WACrB,KAAA,SACA,KAAA,CACR;EACA,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtFA,eAAsB,sBACpB,MACA,QACgC;CAChC,MAAM,WAAW,MAAM,KAAK,gCAAgC,EAC1D,cAAc,OAAO,aACvB,CAAU;CAEV,MAAM,cAAc,IAAI,KAAK,6BAA6B,EACxD,QAAQ,OAAO,qBAAqB,OAAO,aAAa,QAAQ,aAAa,EAAE,EACjF,CAAU;CAEV,MAAM,cAAc,KAAK,iCAAiC;EACxD;EACA;CACF,CAAU;CAEV,MAAM,SAAS,IAAI,sBAAsB,QAAQ;CAEjD,OAAO;EACL;EACA;EACA,OAAO,YAAY;GACjB,YAAY,QAAQ;GACpB,MAAM,OAAO,MAAM;EACrB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACmCA,SAAgB,4BAEd,QAA4C;CAC5C,OAAO,2BAAkC;EACvC,OAAO;EACP,OAAO,YAAY;GACjB,MAAM,UAAU,MAAM,eAAe,OAAO,OAAO;GAEnD,MAAM,WAAW,MAAM,sBACrB;IACE,iCAAiC,QAAQ;IACzC,kCAAkC,QAAQ;IAC1C,8BAA8B,QAAQ;GACxC,GACA;IACE,cAAc,OAAO;IACrB,mBAAmB,OAAO;GAC5B,CACF;GAEA,OAAO;IACL,aAAa,SAAS;IACtB,kBAAkB,QAAQ;IAC1B,4BACE,QAAQ;IACV,aAAa,OAAO;IACpB,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,eAAe,OAAO;IACtB,OAAO,OAAO;IACd,UAAU,OAAO;IACjB,sBAAsB,OAAO;IAC7B,aAAa,OAAO;IACpB,oBAAoB,OAAO;IAC3B,aAAa,OAAO;IACpB,OAAO,OAAO;IACd,eAAe,OAAO;IACtB,iBAAiB,OAAO;IACxB,mBAAmB,OAAO;IAC1B,OAAO,OAAO;IACd,MAAM,OAAO,SAAS,OAAO,KAAA,IAAa,OAAO,QAAQ,uBAAuB;IAChF,UAAU,OAAO;IACjB,OAAO,SAAS;GAClB;EACF;CACF,CAAC;AACH;AAEA,SAAS,2BAAiC;CACxC,IAAI,OAAO,WAAW,aACpB,MAAM,IAAI,MAAM,wEAAwE;AAE5F"}
|
|
1
|
+
{"version":3,"file":"browser.mjs","names":[],"sources":["../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser.ts","../src/platforms/browser-event-sourced-db.ts"],"sourcesContent":["import type { SQLiteDriver } from \"../core/types\";\n\nexport type BrowserWASQLiteDatabase = {\n execute: <TRow = unknown>(\n sql: string,\n params?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<TRow>>;\n close?: () => Promise<void> | void;\n};\n\nfunction assertTransactionCallbackHasDriverArg(\n fn: (transactionDriver: SQLiteDriver) => Promise<unknown>,\n): void {\n if (fn.length > 0) {\n return;\n }\n\n throw new Error(\"SQLiteDriver.transaction callback must accept the transaction driver argument\");\n}\n\nfunction assertDatabaseShape(\n database: BrowserWASQLiteDatabase,\n): asserts database is BrowserWASQLiteDatabase {\n if (typeof database.execute !== \"function\") {\n throw new Error(\"Browser wa-sqlite database handle must provide execute(sql, params?)\");\n }\n}\n\nexport class BrowserWASQLiteDriver implements SQLiteDriver {\n private readonly database: BrowserWASQLiteDatabase;\n private queue: Promise<void> = Promise.resolve();\n private nextSavepointId = 1;\n private closed = false;\n\n constructor(database: BrowserWASQLiteDatabase) {\n assertDatabaseShape(database);\n this.database = database;\n }\n\n async exec(sql: string): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql);\n });\n }\n\n async query<T>(sql: string, params: ReadonlyArray<unknown> = []): Promise<ReadonlyArray<T>> {\n return this.enqueue(() => this.database.execute<T>(sql, params));\n }\n\n async run(sql: string, params: ReadonlyArray<unknown> = []): Promise<void> {\n await this.enqueue(async () => {\n await this.database.execute(sql, params);\n });\n }\n\n async transaction<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn);\n\n return this.enqueue(async () => {\n await this.database.execute(`BEGIN IMMEDIATE`);\n try {\n const result = await fn(this.createTransactionDriver());\n await this.database.execute(`COMMIT`);\n return result;\n } catch (error) {\n try {\n await this.database.execute(`ROLLBACK`);\n } catch {}\n throw error;\n }\n });\n }\n\n async transactionWithDriver<T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>): Promise<T> {\n return this.transaction(fn);\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n\n if (typeof this.database.close === \"function\") {\n await Promise.resolve(this.database.close());\n }\n }\n\n private createTransactionDriver(): SQLiteDriver {\n return {\n exec: (sql) => this.database.execute(sql).then(() => undefined),\n query: <T>(sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute<T>(sql, params),\n run: (sql: string, params: ReadonlyArray<unknown> = []) =>\n this.database.execute(sql, params).then(() => undefined),\n transaction: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) =>\n this.runNestedTransaction(fn),\n transactionWithDriver: <T>(fn: (transactionDriver: SQLiteDriver) => Promise<T>) =>\n this.runNestedTransaction(fn),\n };\n }\n\n private async runNestedTransaction<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): Promise<T> {\n assertTransactionCallbackHasDriverArg(fn);\n\n const savepointName = `tsdb_sp_${this.nextSavepointId}`;\n this.nextSavepointId++;\n await this.database.execute(`SAVEPOINT ${savepointName}`);\n try {\n const result = await fn(this.createTransactionDriver());\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);\n return result;\n } catch (error) {\n await this.database.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`);\n await this.database.execute(`RELEASE SAVEPOINT ${savepointName}`);\n throw error;\n }\n }\n\n private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {\n const queuedOperation = this.queue.then(operation, operation);\n this.queue = queuedOperation.then(\n () => undefined,\n () => undefined,\n );\n return queuedOperation;\n }\n}\n","import { BrowserWASQLiteDriver } from \"./browser-wa-sqlite-driver\";\nimport type {\n BrowserPlatformConfig,\n BrowserPlatformDeps,\n BrowserPlatformResult,\n} from \"./browser-types\";\n\nexport type {\n BrowserCoordinatorInstance,\n BrowserPlatformConfig,\n BrowserPlatformDeps,\n BrowserPlatformResult,\n} from \"./browser-types\";\n\nexport type { BrowserWASQLiteDatabase } from \"./browser-wa-sqlite-driver\";\n\n/**\n * Opens OPFS SQLite, a cross-tab coordinator, and TanStack persistence.\n * Prefer {@link createBrowserEventSourcedDB}; call this only when you need the\n * raw `persistence` / `close` pair (for example a custom handle).\n *\n * @example\n * ```ts\n * import { createBrowserPlatform } from \"event-sourced-collection/browser\"\n * import {\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * } from \"@tanstack/browser-db-sqlite-persistence\"\n *\n * const platform = await createBrowserPlatform(\n * {\n * openBrowserWASQLiteOPFSDatabase,\n * createBrowserWASQLitePersistence,\n * BrowserCollectionCoordinator,\n * },\n * { databaseName: \"app.sqlite\" },\n * )\n *\n * // pass platform.persistence into createEventSourcedDB\n * await platform.close()\n * ```\n */\nexport async function createBrowserPlatform(\n deps: BrowserPlatformDeps,\n config: BrowserPlatformConfig,\n): Promise<BrowserPlatformResult> {\n const database = await deps.openBrowserWASQLiteOPFSDatabase({\n databaseName: config.databaseName,\n } as never);\n\n const coordinator = new deps.BrowserCollectionCoordinator({\n dbName: config.coordinatorDbName ?? config.databaseName.replace(/\\.sqlite$/, \"\"),\n } as never);\n\n const persistence = deps.createBrowserWASQLitePersistence({\n database,\n coordinator,\n } as never);\n\n const driver = new BrowserWASQLiteDriver(database);\n\n return {\n driver,\n persistence,\n close: async () => {\n coordinator.dispose();\n await driver.close();\n },\n };\n}\n","import {\n createEventSourcedDBHandle,\n resolveModules,\n type ModulesInput,\n} from \"../core/create-event-sourced-db-handle\";\nimport type {\n CreateCollectionFn,\n InjectedCreateCollection,\n InjectedModuleFn,\n PersistedCollectionOptionsFn,\n} from \"../core/persisted-collection\";\nimport type { EventSourcedSharedOptions, SyncLock } from \"../core/types\";\nimport { createBrowserPlatform } from \"./browser\";\nimport type { BrowserPlatformDeps } from \"./browser-types\";\nimport { createWebLocksSyncLock } from \"./web-locks\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n};\n\nexport type BrowserEventSourcedModules = BrowserPlatformDeps & {\n createCollection: InjectedCreateCollection;\n persistedCollectionOptions: InjectedModuleFn;\n};\n\n/**\n * Browser entry-point config. Shared sync/lifecycle fields come from\n * {@link EventSourcedSharedOptions} (hover those properties for docs).\n * Deep guide: `docs/usage.md`.\n */\nexport type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> =\n Omit<EventSourcedSharedOptions, \"lock\" | \"lockName\"> & {\n /**\n * User collection registry. Object keys become `db.collections.<key>` and\n * the wire `collectionId`. Must not collide with reserved names.\n */\n collections: TDefs;\n /**\n * OPFS SQLite database file name (e.g. `\"my-app.sqlite\"`). Also used as the\n * default sync lock namespace so only one tab syncs for this DB at a time.\n */\n databaseName: string;\n /**\n * Name for the cross-tab collection coordinator database. Defaults to\n * `databaseName` with a trailing `.sqlite` stripped.\n */\n coordinatorDbName?: string;\n /**\n * Browser OPFS persistence modules from\n * `@tanstack/browser-db-sqlite-persistence` plus `createCollection`.\n * Pass the imported bindings directly, or a function to keep WASM off the\n * critical path.\n */\n modules: ModulesInput<BrowserEventSourcedModules>;\n /**\n * Defaults to a Web Locks–backed lock so only one tab syncs at a time.\n * Pass `null` to opt out and let every tab sync independently.\n */\n lock?: SyncLock | null;\n };\n\nexport type { EventSourcedDBHandle as BrowserEventSourcedDBHandle } from \"../core/create-event-sourced-db-handle\";\n\n/**\n * Browser-flavoured event-sourced DB: OPFS SQLite + optional Web Locks sync\n * election, returned as a lazy singleton.\n *\n * @param config - See {@link BrowserEventSourcedDBConfig} for every option.\n *\n * @example\n * ```ts\n * import { createBrowserEventSourcedDB } from \"event-sourced-collection/browser\"\n * import type { CollectionDef, EventSourcedDB } from \"event-sourced-collection\"\n *\n * type Todo = { id: string; title: string }\n * type Defs = { todos: CollectionDef<Todo, string> }\n *\n * const { ensureDb, db } = createBrowserEventSourcedDB<Defs>({\n * databaseName: \"app.sqlite\",\n * collections: { todos: { getKey: (todo) => todo.id } },\n * sync: { push: \"/api/sync/events\", pull: \"/api/sync/events\" },\n * modules: async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * const {\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * persistedCollectionOptions,\n * } = await import(\"@tanstack/browser-db-sqlite-persistence\")\n * return {\n * createCollection,\n * BrowserCollectionCoordinator,\n * createBrowserWASQLitePersistence,\n * openBrowserWASQLiteOPFSDatabase,\n * persistedCollectionOptions,\n * }\n * },\n * })\n *\n * export async function start() {\n * await ensureDb()\n * }\n * export { db }\n * ```\n */\nexport function createBrowserEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: BrowserEventSourcedDBConfig<TDefs>) {\n return createEventSourcedDBHandle<TDefs>({\n guard: assertBrowserEnvironment,\n setup: async () => {\n const modules = await resolveModules(config.modules);\n\n const platform = await createBrowserPlatform(\n {\n openBrowserWASQLiteOPFSDatabase: modules.openBrowserWASQLiteOPFSDatabase,\n createBrowserWASQLitePersistence: modules.createBrowserWASQLitePersistence,\n BrowserCollectionCoordinator: modules.BrowserCollectionCoordinator,\n },\n {\n databaseName: config.databaseName,\n coordinatorDbName: config.coordinatorDbName,\n },\n );\n\n return {\n persistence: platform.persistence,\n createCollection: modules.createCollection as CreateCollectionFn,\n persistedCollectionOptions:\n modules.persistedCollectionOptions as PersistedCollectionOptionsFn,\n collections: config.collections,\n sync: config.sync,\n syncEnabled: config.syncEnabled,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n clientId: config.clientId,\n unknownEventHandling: config.unknownEventHandling,\n pullOverlap: config.pullOverlap,\n eventSchemaVersion: config.eventSchemaVersion,\n upcastEvent: config.upcastEvent,\n retry: config.retry,\n pushBatchSize: config.pushBatchSize,\n maxPushBytes: config.maxPushBytes,\n syncPreset: config.syncPreset,\n recordLocalEchoes: config.recordLocalEchoes,\n backendMismatch: config.backendMismatch,\n conflictDetection: config.conflictDetection,\n hooks: config.hooks,\n lock: config.lock === null ? undefined : (config.lock ?? createWebLocksSyncLock()),\n lockName: config.databaseName,\n close: platform.close,\n };\n },\n });\n}\n\nfunction assertBrowserEnvironment(): void {\n if (typeof window === \"undefined\") {\n throw new Error(\"Event-sourced DB (browser) is only available in a browser environment.\");\n }\n}\n"],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ const RESERVED_IDS = /* @__PURE__ */ new Set([
|
|
|
14
14
|
SYNCMETA_ID,
|
|
15
15
|
ROWVERSIONS_ID
|
|
16
16
|
]);
|
|
17
|
+
const PAYLOAD_TOO_LARGE_ERROR_CODE = "PAYLOAD_TOO_LARGE";
|
|
17
18
|
const SYNC_LOCK_PREFIX = "event-sourced-sync";
|
|
18
19
|
//#endregion
|
|
19
20
|
//#region src/internal/hooks.ts
|
|
@@ -946,56 +947,157 @@ async function deadLetter(deadletter, outbox, entry, reason, message, code, now,
|
|
|
946
947
|
});
|
|
947
948
|
emit("onDeadLetter", record);
|
|
948
949
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const batches = [];
|
|
957
|
-
let current = [];
|
|
950
|
+
const utf8 = new TextEncoder();
|
|
951
|
+
function jsonBytes(entry) {
|
|
952
|
+
return utf8.encode(JSON.stringify(toOutboundEvent(entry))).length;
|
|
953
|
+
}
|
|
954
|
+
/** Events are ordered by localSeq, so one transaction's events are adjacent. */
|
|
955
|
+
function groupByTransaction(events, measure) {
|
|
956
|
+
const groups = [];
|
|
958
957
|
for (let index = 0; index < events.length;) {
|
|
959
958
|
const txId = events[index].txId;
|
|
960
|
-
const group =
|
|
959
|
+
const group = {
|
|
960
|
+
entries: [],
|
|
961
|
+
bytes: 0
|
|
962
|
+
};
|
|
961
963
|
while (index < events.length && events[index].txId === txId) {
|
|
962
|
-
|
|
964
|
+
const entry = events[index];
|
|
965
|
+
group.entries.push(entry);
|
|
966
|
+
if (measure) group.bytes += jsonBytes(entry) + 1;
|
|
963
967
|
index++;
|
|
964
968
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
969
|
+
groups.push(group);
|
|
970
|
+
}
|
|
971
|
+
return groups;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Packs transaction groups into batches of at most `size` events and
|
|
975
|
+
* `maxBytes` of JSON, never separating events that share a `txId`: the server
|
|
976
|
+
* commits each transaction atomically, which it cannot do if we deliver half
|
|
977
|
+
* of one now and half later. A transaction with more than `size` events is
|
|
978
|
+
* sent whole; one larger than `maxBytes` is returned in `oversized` instead.
|
|
979
|
+
*/
|
|
980
|
+
function packBatches(groups, size, maxBytes) {
|
|
981
|
+
const batches = [];
|
|
982
|
+
const oversized = [];
|
|
983
|
+
let current = {
|
|
984
|
+
groups: [],
|
|
985
|
+
count: 0,
|
|
986
|
+
bytes: 1
|
|
987
|
+
};
|
|
988
|
+
const close = () => {
|
|
989
|
+
if (current.count > 0) batches.push(current);
|
|
990
|
+
current = {
|
|
991
|
+
groups: [],
|
|
992
|
+
count: 0,
|
|
993
|
+
bytes: 1
|
|
994
|
+
};
|
|
995
|
+
};
|
|
996
|
+
for (const group of groups) {
|
|
997
|
+
if (maxBytes !== null && group.bytes + 1 > maxBytes) {
|
|
998
|
+
oversized.push(group);
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
const overCount = current.count + group.entries.length > size;
|
|
1002
|
+
const overBytes = maxBytes !== null && current.bytes + group.bytes > maxBytes;
|
|
1003
|
+
if (current.count > 0 && (overCount || overBytes)) close();
|
|
1004
|
+
current.groups.push(group);
|
|
1005
|
+
current.count += group.entries.length;
|
|
1006
|
+
current.bytes += group.bytes;
|
|
1007
|
+
if (current.count >= size) close();
|
|
1008
|
+
}
|
|
1009
|
+
close();
|
|
1010
|
+
return {
|
|
1011
|
+
batches,
|
|
1012
|
+
oversized
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function isPayloadTooLarge(err) {
|
|
1016
|
+
return typeof err === "object" && err !== null && "status" in err && err.status === 413;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Posts `groups` as one request. On HTTP 413 the request is halved along
|
|
1020
|
+
* transaction boundaries and each half posted in turn, down to a single
|
|
1021
|
+
* transaction, so one oversized event cannot hold the rest hostage.
|
|
1022
|
+
*/
|
|
1023
|
+
async function postGroups(push, groups, log) {
|
|
1024
|
+
const entries = groups.flatMap((group) => group.entries);
|
|
1025
|
+
try {
|
|
1026
|
+
const response = normalizePushResponse(await push(entries.map(toOutboundEvent)));
|
|
1027
|
+
return {
|
|
1028
|
+
confirmed: [...response.confirmed],
|
|
1029
|
+
failed: [...response.failed ?? []],
|
|
1030
|
+
tooLarge: [],
|
|
1031
|
+
unsent: []
|
|
1032
|
+
};
|
|
1033
|
+
} catch (err) {
|
|
1034
|
+
if (!isPayloadTooLarge(err)) return {
|
|
1035
|
+
confirmed: [],
|
|
1036
|
+
failed: [],
|
|
1037
|
+
tooLarge: [],
|
|
1038
|
+
unsent: entries,
|
|
1039
|
+
error: err
|
|
1040
|
+
};
|
|
1041
|
+
if (groups.length === 1) {
|
|
1042
|
+
log.warn("push transaction too large for the server", { events: entries.length });
|
|
1043
|
+
return {
|
|
1044
|
+
confirmed: [],
|
|
1045
|
+
failed: [],
|
|
1046
|
+
tooLarge: entries,
|
|
1047
|
+
unsent: []
|
|
1048
|
+
};
|
|
968
1049
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1050
|
+
const middle = Math.ceil(groups.length / 2);
|
|
1051
|
+
log.info("push batch hit HTTP 413, splitting", {
|
|
1052
|
+
groups: groups.length,
|
|
1053
|
+
events: entries.length
|
|
1054
|
+
});
|
|
1055
|
+
const first = await postGroups(push, groups.slice(0, middle), log);
|
|
1056
|
+
if (first.error !== void 0) {
|
|
1057
|
+
const rest = groups.slice(middle).flatMap((group) => group.entries);
|
|
1058
|
+
return {
|
|
1059
|
+
...first,
|
|
1060
|
+
unsent: [...first.unsent, ...rest]
|
|
1061
|
+
};
|
|
973
1062
|
}
|
|
1063
|
+
const second = await postGroups(push, groups.slice(middle), log);
|
|
1064
|
+
return {
|
|
1065
|
+
confirmed: [...first.confirmed, ...second.confirmed],
|
|
1066
|
+
failed: [...first.failed, ...second.failed],
|
|
1067
|
+
tooLarge: [...first.tooLarge, ...second.tooLarge],
|
|
1068
|
+
unsent: second.unsent,
|
|
1069
|
+
error: second.error
|
|
1070
|
+
};
|
|
974
1071
|
}
|
|
975
|
-
|
|
976
|
-
|
|
1072
|
+
}
|
|
1073
|
+
async function deadLetterTooLarge(args, entries, message) {
|
|
1074
|
+
for (const entry of entries) await deadLetter(args.deadletter, args.outbox, entry, "rejected", message, PAYLOAD_TOO_LARGE_ERROR_CODE, args.now, args.emit, args.log, args.rowversions);
|
|
1075
|
+
return entries.length;
|
|
977
1076
|
}
|
|
978
1077
|
/**
|
|
979
|
-
* Pushes due outbox events in batches
|
|
980
|
-
* the next is sent, so a failure
|
|
981
|
-
* made instead of restarting
|
|
1078
|
+
* Pushes due outbox events in batches limited by event count and JSON bytes.
|
|
1079
|
+
* Each batch's result is persisted before the next is sent, so a failure
|
|
1080
|
+
* part-way through keeps the progress already made instead of restarting.
|
|
982
1081
|
*/
|
|
983
1082
|
async function pushOutbox(args) {
|
|
984
|
-
const { outbox, push, batchSize, now, log } = args;
|
|
1083
|
+
const { outbox, push, batchSize, maxBytes, now, log } = args;
|
|
985
1084
|
const due = [...outbox.state.values()].filter((entry) => isDue(entry, now)).sort(byLocalSeq);
|
|
986
1085
|
log.debug("push outbox", {
|
|
987
1086
|
dueCount: due.length,
|
|
988
|
-
batchSize
|
|
1087
|
+
batchSize,
|
|
1088
|
+
maxBytes
|
|
989
1089
|
});
|
|
990
1090
|
if (due.length === 0) return {
|
|
991
1091
|
pushed: 0,
|
|
992
1092
|
deadLettered: 0
|
|
993
1093
|
};
|
|
1094
|
+
const { batches, oversized } = packBatches(groupByTransaction(due, maxBytes !== null), batchSize, maxBytes);
|
|
994
1095
|
let pushed = 0;
|
|
995
1096
|
let deadLettered = 0;
|
|
996
|
-
for (const
|
|
1097
|
+
for (const group of oversized) deadLettered += await deadLetterTooLarge(args, group.entries, `Transaction is ${group.bytes} bytes, over maxPushBytes (${maxBytes})`);
|
|
1098
|
+
for (const batch of batches) {
|
|
997
1099
|
const attempted = [];
|
|
998
|
-
for (const
|
|
1100
|
+
for (const group of batch.groups) for (const entry of group.entries) {
|
|
999
1101
|
const attemptCount = (entry.attemptCount ?? 0) + 1;
|
|
1000
1102
|
await outbox.update(entry.eventId, (draft) => {
|
|
1001
1103
|
draft.syncStatus = "pending";
|
|
@@ -1011,27 +1113,17 @@ async function pushOutbox(args) {
|
|
|
1011
1113
|
attemptCount
|
|
1012
1114
|
});
|
|
1013
1115
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1020
|
-
log.warn("push stopped after transport failure", {
|
|
1021
|
-
message: error.message,
|
|
1022
|
-
pushed,
|
|
1023
|
-
remaining: due.length - pushed
|
|
1024
|
-
});
|
|
1025
|
-
return {
|
|
1026
|
-
pushed,
|
|
1027
|
-
deadLettered,
|
|
1028
|
-
error
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1116
|
+
const byId = new Map(attempted.map((entry) => [entry.eventId, entry]));
|
|
1117
|
+
const response = await postGroups(push, batch.groups.map((group) => ({
|
|
1118
|
+
bytes: group.bytes,
|
|
1119
|
+
entries: group.entries.map((entry) => byId.get(entry.eventId))
|
|
1120
|
+
})), log);
|
|
1031
1121
|
log.info("push batch confirmed", {
|
|
1032
1122
|
sent: attempted.length,
|
|
1123
|
+
bytes: batch.bytes,
|
|
1033
1124
|
confirmed: response.confirmed.length,
|
|
1034
|
-
failed: response.failed
|
|
1125
|
+
failed: response.failed.length,
|
|
1126
|
+
tooLarge: response.tooLarge.length
|
|
1035
1127
|
});
|
|
1036
1128
|
for (const confirmation of response.confirmed) {
|
|
1037
1129
|
if (!outbox.has(confirmation.eventId)) continue;
|
|
@@ -1050,12 +1142,26 @@ async function pushOutbox(args) {
|
|
|
1050
1142
|
globalSeq: confirmation.globalSeq
|
|
1051
1143
|
});
|
|
1052
1144
|
}
|
|
1053
|
-
const
|
|
1054
|
-
for (const failure of response.failed ?? []) {
|
|
1145
|
+
for (const failure of response.failed) {
|
|
1055
1146
|
const entry = byId.get(failure.eventId);
|
|
1056
1147
|
if (!entry) continue;
|
|
1057
1148
|
deadLettered += await handleFailure(args, entry, failure);
|
|
1058
1149
|
}
|
|
1150
|
+
deadLettered += await deadLetterTooLarge(args, response.tooLarge, "Server rejected the transaction as too large (HTTP 413)");
|
|
1151
|
+
if (response.error !== void 0) {
|
|
1152
|
+
deadLettered += await backoffBatch(args, response.unsent, response.error);
|
|
1153
|
+
const error = response.error instanceof Error ? response.error : new Error(String(response.error));
|
|
1154
|
+
log.warn("push stopped after transport failure", {
|
|
1155
|
+
message: error.message,
|
|
1156
|
+
pushed,
|
|
1157
|
+
remaining: due.length - pushed
|
|
1158
|
+
});
|
|
1159
|
+
return {
|
|
1160
|
+
pushed,
|
|
1161
|
+
deadLettered,
|
|
1162
|
+
error
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1059
1165
|
}
|
|
1060
1166
|
return {
|
|
1061
1167
|
pushed,
|
|
@@ -1103,6 +1209,41 @@ async function backoffBatch(args, batch, err) {
|
|
|
1103
1209
|
return deadLettered;
|
|
1104
1210
|
}
|
|
1105
1211
|
//#endregion
|
|
1212
|
+
//#region src/core/sync-presets.ts
|
|
1213
|
+
/**
|
|
1214
|
+
* Push limits per hosting platform. Byte caps leave headroom under the
|
|
1215
|
+
* platform's request body limit (Vercel functions: 4.5 MB; AWS Lambda and
|
|
1216
|
+
* Netlify: 6 MB) for headers and JSON size estimation drift.
|
|
1217
|
+
*
|
|
1218
|
+
* @example
|
|
1219
|
+
* ```ts
|
|
1220
|
+
* createBrowserEventSourcedDB({ ..., syncPreset: "vercel" })
|
|
1221
|
+
* ```
|
|
1222
|
+
*/
|
|
1223
|
+
const SYNC_PUSH_PRESETS = {
|
|
1224
|
+
default: {
|
|
1225
|
+
pushBatchSize: 20,
|
|
1226
|
+
maxPushBytes: null
|
|
1227
|
+
},
|
|
1228
|
+
serverless: {
|
|
1229
|
+
pushBatchSize: 25,
|
|
1230
|
+
maxPushBytes: 4e6
|
|
1231
|
+
},
|
|
1232
|
+
vercel: {
|
|
1233
|
+
pushBatchSize: 10,
|
|
1234
|
+
maxPushBytes: 35e5
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
function resolvePushLimits(config) {
|
|
1238
|
+
const preset = SYNC_PUSH_PRESETS[config.syncPreset ?? "default"];
|
|
1239
|
+
const size = Math.floor(config.pushBatchSize ?? preset.pushBatchSize);
|
|
1240
|
+
const bytes = config.maxPushBytes ?? preset.maxPushBytes;
|
|
1241
|
+
return {
|
|
1242
|
+
pushBatchSize: Math.min(100, Math.max(1, size)),
|
|
1243
|
+
maxPushBytes: bytes === null ? null : Math.max(1, Math.floor(bytes))
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
//#endregion
|
|
1106
1247
|
//#region src/internal/serial-queue.ts
|
|
1107
1248
|
/**
|
|
1108
1249
|
* Serializes async work so overlapping callers run one after another instead of
|
|
@@ -1283,7 +1424,8 @@ async function createEventSourcedDB(config) {
|
|
|
1283
1424
|
const pullOverlap = Math.max(0, config.pullOverlap ?? 0);
|
|
1284
1425
|
const recordLocalEchoes = config.recordLocalEchoes ?? true;
|
|
1285
1426
|
const eventSchemaVersion = config.eventSchemaVersion ?? 1;
|
|
1286
|
-
|
|
1427
|
+
let pushLimits = resolvePushLimits(config);
|
|
1428
|
+
const { pushBatchSize, maxPushBytes } = pushLimits;
|
|
1287
1429
|
const backendMismatch = config.backendMismatch ?? "resetCursor";
|
|
1288
1430
|
const conflictDetection = config.conflictDetection ?? false;
|
|
1289
1431
|
const lockName = `${SYNC_LOCK_PREFIX}:${config.lockName ?? "default"}`;
|
|
@@ -1301,6 +1443,7 @@ async function createEventSourcedDB(config) {
|
|
|
1301
1443
|
pullOverlap,
|
|
1302
1444
|
recordLocalEchoes,
|
|
1303
1445
|
pushBatchSize,
|
|
1446
|
+
maxPushBytes,
|
|
1304
1447
|
backendMismatch,
|
|
1305
1448
|
conflictDetection,
|
|
1306
1449
|
eventSchemaVersion
|
|
@@ -1435,7 +1578,8 @@ async function createEventSourcedDB(config) {
|
|
|
1435
1578
|
deadletter,
|
|
1436
1579
|
rowversions,
|
|
1437
1580
|
push: transport.push,
|
|
1438
|
-
batchSize: pushBatchSize,
|
|
1581
|
+
batchSize: pushLimits.pushBatchSize,
|
|
1582
|
+
maxBytes: pushLimits.maxPushBytes,
|
|
1439
1583
|
retry,
|
|
1440
1584
|
now: Date.now(),
|
|
1441
1585
|
emit,
|
|
@@ -1734,6 +1878,15 @@ async function createEventSourcedDB(config) {
|
|
|
1734
1878
|
subscribeSyncStatus,
|
|
1735
1879
|
retryDeadLetter,
|
|
1736
1880
|
discardDeadLetter,
|
|
1881
|
+
getPushLimits: () => pushLimits,
|
|
1882
|
+
setPushLimits(next) {
|
|
1883
|
+
pushLimits = resolvePushLimits({
|
|
1884
|
+
pushBatchSize: next.pushBatchSize ?? pushLimits.pushBatchSize,
|
|
1885
|
+
maxPushBytes: (next.maxPushBytes === void 0 ? pushLimits.maxPushBytes : next.maxPushBytes) ?? void 0
|
|
1886
|
+
});
|
|
1887
|
+
log.info("push limits updated", pushLimits);
|
|
1888
|
+
return pushLimits;
|
|
1889
|
+
},
|
|
1737
1890
|
pruneSyncedEvents,
|
|
1738
1891
|
dispose
|
|
1739
1892
|
};
|
|
@@ -2028,6 +2181,6 @@ async function resolveModules(modules) {
|
|
|
2028
2181
|
return typeof modules === "function" ? await modules() : modules;
|
|
2029
2182
|
}
|
|
2030
2183
|
//#endregion
|
|
2031
|
-
export { generateEventId as a,
|
|
2184
|
+
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 };
|
|
2032
2185
|
|
|
2033
|
-
//# sourceMappingURL=create-event-sourced-db-handle-
|
|
2186
|
+
//# sourceMappingURL=create-event-sourced-db-handle-CD8WSjXW.mjs.map
|