event-sourced-collection 0.0.2 → 0.0.5
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/README.md +304 -57
- package/dist/browser.d.mts +7 -1
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.mjs +2 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +8 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{lazy-singleton-DFrAsYTV.mjs → lazy-singleton-BG02GHLj.mjs} +39 -4
- package/dist/lazy-singleton-BG02GHLj.mjs.map +1 -0
- package/dist/react-native.d.mts +7 -1
- package/dist/react-native.d.mts.map +1 -1
- package/dist/react-native.mjs +2 -1
- package/dist/react-native.mjs.map +1 -1
- package/dist/{types-CAT14Tbj.d.mts → types-BX8dtGOu.d.mts} +17 -3
- package/dist/{types-CAT14Tbj.d.mts.map → types-BX8dtGOu.d.mts.map} +1 -1
- package/package.json +1 -1
- package/dist/lazy-singleton-DFrAsYTV.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -22,36 +22,49 @@ npm install @tanstack/react-native-db-sqlite-persistence
|
|
|
22
22
|
|
|
23
23
|
## Project structure
|
|
24
24
|
|
|
25
|
-
A minimal browser app
|
|
25
|
+
A minimal browser app splits setup by concern. **`collections.ts`** is the single source of truth for types, sync transport, collection registry, indexes, and the `createBrowserEventSourcedDB` call.
|
|
26
26
|
|
|
27
27
|
```
|
|
28
|
-
src/
|
|
29
|
-
├──
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
src/data-access-layer/
|
|
29
|
+
├── collections.ts # types, push/pull, DB init → exports `ensureDb` + `db`
|
|
30
|
+
├── app-settings.ts # seeds settings row, persists syncEnabled, calls setSyncEnabled()
|
|
31
|
+
└── sync-events.ts # thin wrappers around db.sync() / db.manualSync()
|
|
32
|
+
|
|
33
|
+
src/hooks/common/
|
|
34
|
+
├── use-event-sourced-sync.ts # periodic background sync (optional)
|
|
35
|
+
└── use-sync-enabled.ts # reads settings.syncEnabled for hooks/UI
|
|
36
|
+
|
|
37
|
+
src/routes/…/settings/ # UI toggle for background sync (optional)
|
|
38
|
+
src/routes/…/layout.tsx # call ensureAppSettings() once before data UI
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
| File | Role |
|
|
39
42
|
| ---- | ---- |
|
|
40
|
-
| `collections.ts` |
|
|
43
|
+
| `collections.ts` | Row types, collection registry, indexes, `syncEnabled` default, push/pull handlers, lazy singleton (`ensureDb` + `db` proxy) |
|
|
44
|
+
| `app-settings.ts` | Seeds the `"app"` settings row, mirrors `syncEnabled` to `db.setSyncEnabled()` on startup and when the user toggles |
|
|
41
45
|
| `sync-events.ts` | Keeps sync calls out of React; import from buttons, jobs, or hooks |
|
|
42
|
-
| `use-event-sourced-sync.ts` | Polls `manualSync()` on an interval when
|
|
43
|
-
|
|
|
46
|
+
| `use-event-sourced-sync.ts` | Polls `manualSync()` on an interval when DB is ready **and** sync is enabled |
|
|
47
|
+
| `use-sync-enabled.ts` | Live-query helper for `settings.syncEnabled` (disable background sync in UI) |
|
|
48
|
+
| Settings page | Optional UI for `setSyncEnabled()` — lets users opt out of push/pull |
|
|
49
|
+
| App shell | `await ensureAppSettings()` before routes that read/write collections |
|
|
50
|
+
|
|
51
|
+
**Imports in components:** use `db` for reads/writes after startup. Call `ensureDb()` (or `ensureAppSettings()`) once at app mount.
|
|
52
|
+
|
|
53
|
+
**Built-in collections** (always present — do not register these ids):
|
|
54
|
+
|
|
55
|
+
- `db.collections.outbox` — local mutations waiting to upload
|
|
56
|
+
- `db.collections.inbox` — server events pulled to this device
|
|
44
57
|
|
|
45
58
|
Flow:
|
|
46
59
|
|
|
47
60
|
```
|
|
48
|
-
app mount →
|
|
61
|
+
app mount → ensureAppSettings() → ensureDb() + seed settings + setSyncEnabled
|
|
49
62
|
↓
|
|
50
63
|
db.collections.* available
|
|
51
64
|
↓
|
|
52
65
|
useLiveQuery / insert / update / delete
|
|
53
66
|
↓
|
|
54
|
-
|
|
67
|
+
useEventSourcedSync(dbReady && syncEnabled) or manual Sync now
|
|
55
68
|
```
|
|
56
69
|
|
|
57
70
|
## Quick start
|
|
@@ -83,6 +96,7 @@ type AppSettings = {
|
|
|
83
96
|
id: string;
|
|
84
97
|
theme: "light" | "dark";
|
|
85
98
|
language: string;
|
|
99
|
+
syncEnabled: boolean; // persisted preference; mirrored to db.setSyncEnabled()
|
|
86
100
|
};
|
|
87
101
|
```
|
|
88
102
|
|
|
@@ -91,12 +105,12 @@ type AppSettings = {
|
|
|
91
105
|
Keys here must match the `collections` option you pass in step 4. This gives you `db.collections.users`, etc. with full inference.
|
|
92
106
|
|
|
93
107
|
```typescript
|
|
94
|
-
import type { EventSourcedDB } from "event-sourced-collection";
|
|
108
|
+
import type { CollectionDef, EventSourcedDB } from "event-sourced-collection";
|
|
95
109
|
|
|
96
110
|
type AppCollectionDefs = {
|
|
97
|
-
users:
|
|
98
|
-
todos:
|
|
99
|
-
settings:
|
|
111
|
+
users: CollectionDef<User, string>;
|
|
112
|
+
todos: CollectionDef<Todo, string>;
|
|
113
|
+
settings: CollectionDef<AppSettings, string>;
|
|
100
114
|
};
|
|
101
115
|
|
|
102
116
|
export type AppDb = EventSourcedDB<AppCollectionDefs>;
|
|
@@ -136,74 +150,250 @@ async function pullEvents({ since }: { since: number }): Promise<PullResponse> {
|
|
|
136
150
|
}
|
|
137
151
|
```
|
|
138
152
|
|
|
139
|
-
### 4. Create the database (
|
|
153
|
+
### 4. Create the database (`collections.ts`)
|
|
154
|
+
|
|
155
|
+
`collections.ts` is the single source of truth for row types, sync transport, collection registry, indexes, and DB init. Import `db` in components for reads/writes after startup. Call `ensureDb()` (or `ensureAppSettings()`) once at app mount before touching collections.
|
|
140
156
|
|
|
141
|
-
`createBrowserEventSourcedDB` (from `event-sourced-collection/browser`) wires the SQLite platform, registers collections, and
|
|
157
|
+
`createBrowserEventSourcedDB` (from `event-sourced-collection/browser`) wires the SQLite platform, registers collections, and returns a lazy singleton (`ensureDb`) plus a `db` proxy you can import anywhere. **Collections are created here** — there is no separate `createUsersCollection()` call.
|
|
142
158
|
|
|
143
|
-
The `load` callback is where you import
|
|
159
|
+
The `load` callback is where you import platform packages. Keeping those imports in *your* app (not inside the library) means you choose `@tanstack/react-db` vs `@tanstack/db`, and SSR bundles stay clean because `load` runs on first `ensureDb()`.
|
|
160
|
+
|
|
161
|
+
Full reference (matches the example app):
|
|
144
162
|
|
|
145
163
|
```typescript
|
|
164
|
+
import { BasicIndex } from "@tanstack/db";
|
|
146
165
|
import { createBrowserEventSourcedDB } from "event-sourced-collection/browser";
|
|
166
|
+
import type {
|
|
167
|
+
CollectionDef,
|
|
168
|
+
EventSourcedDB,
|
|
169
|
+
OutboundEvent,
|
|
170
|
+
PullResponse,
|
|
171
|
+
PushResponse,
|
|
172
|
+
} from "event-sourced-collection";
|
|
173
|
+
|
|
174
|
+
// --- Row types (one per collection) ---
|
|
175
|
+
|
|
176
|
+
export type User = { id: string; name: string; email: string; createdAt: number };
|
|
177
|
+
export type Todo = {
|
|
178
|
+
id: string;
|
|
179
|
+
userId: string;
|
|
180
|
+
title: string;
|
|
181
|
+
status: "pending" | "complete";
|
|
182
|
+
createdAt: number;
|
|
183
|
+
updatedAt: number;
|
|
184
|
+
};
|
|
185
|
+
// Singleton preferences row — use id "app" (see app-settings.ts)
|
|
186
|
+
export type AppSettings = {
|
|
187
|
+
id: string;
|
|
188
|
+
theme: "light" | "dark";
|
|
189
|
+
language: string;
|
|
190
|
+
syncEnabled: boolean;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
type AppCollectionDefs = {
|
|
194
|
+
users: CollectionDef<User, string>;
|
|
195
|
+
todos: CollectionDef<Todo, string>;
|
|
196
|
+
settings: CollectionDef<AppSettings, string>;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export type AppDb = EventSourcedDB<AppCollectionDefs>;
|
|
200
|
+
|
|
201
|
+
// --- Sync transport (runs when sync is enabled) ---
|
|
202
|
+
|
|
203
|
+
const getAccessToken = () => localStorage.getItem("accessToken") ?? "";
|
|
147
204
|
|
|
148
|
-
|
|
205
|
+
async function pushEvents(events: ReadonlyArray<OutboundEvent>): Promise<PushResponse> {
|
|
206
|
+
const response = await fetch("/api/sync/events", {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAccessToken()}` },
|
|
209
|
+
body: JSON.stringify(events),
|
|
210
|
+
});
|
|
211
|
+
if (!response.ok) throw new Error(`Push failed: ${response.status}`);
|
|
212
|
+
return response.json();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function pullEvents({ since }: { since: number }): Promise<PullResponse> {
|
|
216
|
+
const response = await fetch(`/api/sync/events?since=${since}`, {
|
|
217
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${getAccessToken()}` },
|
|
218
|
+
});
|
|
219
|
+
if (!response.ok) throw new Error(`Pull failed: ${response.status}`);
|
|
220
|
+
return response.json();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// --- DB init: lazy singleton; db proxy forwards after ensureDb() ---
|
|
224
|
+
|
|
225
|
+
const { ensureDb, db } = createBrowserEventSourcedDB<AppCollectionDefs>({
|
|
149
226
|
databaseName: "my-app.sqlite",
|
|
150
227
|
debug: import.meta.env.DEV,
|
|
228
|
+
|
|
151
229
|
collections: {
|
|
152
|
-
users: {
|
|
153
|
-
|
|
230
|
+
users: {
|
|
231
|
+
getKey: (user: User) => user.id,
|
|
232
|
+
indexes: [{ select: (user: User) => user.id, indexType: BasicIndex, name: "by-id" }],
|
|
233
|
+
},
|
|
234
|
+
todos: {
|
|
235
|
+
getKey: (todo: Todo) => todo.id,
|
|
236
|
+
indexes: [
|
|
237
|
+
{ select: (todo: Todo) => todo.id, indexType: BasicIndex, name: "by-id" },
|
|
238
|
+
{ select: (todo: Todo) => todo.userId, indexType: BasicIndex, name: "by-user" },
|
|
239
|
+
{ select: (todo: Todo) => todo.status, indexType: BasicIndex, name: "by-status" },
|
|
240
|
+
{ select: (todo: Todo) => todo.title, indexType: BasicIndex, name: "by-title" },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
154
243
|
settings: { getKey: (settings: AppSettings) => settings.id },
|
|
155
244
|
},
|
|
245
|
+
|
|
246
|
+
syncEnabled: true, // initial default; users can toggle at runtime (see app-settings.ts)
|
|
156
247
|
sync: { pushEvents, pullEvents },
|
|
248
|
+
|
|
157
249
|
load: async () => {
|
|
158
250
|
const { createCollection } = await import("@tanstack/react-db");
|
|
159
|
-
const
|
|
160
|
-
|
|
251
|
+
const {
|
|
252
|
+
BrowserCollectionCoordinator,
|
|
253
|
+
createBrowserWASQLitePersistence,
|
|
254
|
+
openBrowserWASQLiteOPFSDatabase,
|
|
255
|
+
persistedCollectionOptions,
|
|
256
|
+
} = await import("@tanstack/browser-db-sqlite-persistence");
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
openBrowserWASQLiteOPFSDatabase,
|
|
260
|
+
createBrowserWASQLitePersistence,
|
|
261
|
+
BrowserCollectionCoordinator,
|
|
262
|
+
createCollection,
|
|
263
|
+
persistedCollectionOptions,
|
|
264
|
+
};
|
|
161
265
|
},
|
|
162
266
|
});
|
|
163
267
|
|
|
164
|
-
export { db, ensureDb
|
|
268
|
+
export { db, ensureDb };
|
|
165
269
|
```
|
|
166
270
|
|
|
167
271
|
The handle is `{ ensureDb, db, close }`:
|
|
168
272
|
|
|
169
|
-
- `ensureDb()` — runs `load` + setup once (deduped
|
|
170
|
-
- `db` —
|
|
171
|
-
- `close()` — disposes the engine and
|
|
273
|
+
- `ensureDb()` — runs `load` + setup once (deduped). Throws outside a browser environment.
|
|
274
|
+
- `db` — proxy that forwards after `ensureDb()`; throws if used before init.
|
|
275
|
+
- `close()` — disposes the engine and SQLite platform; next `ensureDb()` re-initializes.
|
|
172
276
|
|
|
173
|
-
After setup you get
|
|
277
|
+
After setup you get `db.collections.users`, `db.collections.todos`, `db.collections.settings`, plus built-in `outbox` and `inbox` (see [Project structure](#project-structure)).
|
|
174
278
|
|
|
175
|
-
|
|
176
|
-
- `db.collections.todos`
|
|
177
|
-
- `db.collections.settings`
|
|
279
|
+
#### Collection indexes
|
|
178
280
|
|
|
179
|
-
|
|
281
|
+
Add one or more indexes per collection via the optional `indexes` array on each collection definition. Each entry becomes a `collection.createIndex(select, { name?, indexType? })` call during DB init — no post-`ensureDb` setup required.
|
|
180
282
|
|
|
181
|
-
|
|
182
|
-
|
|
283
|
+
`indexType` comes from `@tanstack/db` (e.g. `BasicIndex`). Use `name` when a collection has multiple indexes so they are easy to tell apart.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { BasicIndex } from "@tanstack/db";
|
|
287
|
+
|
|
288
|
+
// Multiple indexes: add more objects to the array
|
|
289
|
+
todos: {
|
|
290
|
+
getKey: (todo: Todo) => todo.id,
|
|
291
|
+
indexes: [
|
|
292
|
+
{ select: (todo: Todo) => todo.id, indexType: BasicIndex, name: "by-id" },
|
|
293
|
+
{ select: (todo: Todo) => todo.userId, indexType: BasicIndex, name: "by-user" },
|
|
294
|
+
{ select: (todo: Todo) => todo.status, indexType: BasicIndex, name: "by-status" },
|
|
295
|
+
{ select: (todo: Todo) => todo.title, indexType: BasicIndex, name: "by-title" },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
// Single index: a one-element array works the same way
|
|
300
|
+
favorites: {
|
|
301
|
+
getKey: (item) => item.movieId,
|
|
302
|
+
indexes: [{ select: (item) => item.movieId, indexType: BasicIndex }],
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// No indexes: omit the field entirely
|
|
306
|
+
settings: { getKey: (settings: AppSettings) => settings.id },
|
|
307
|
+
```
|
|
183
308
|
|
|
184
|
-
|
|
309
|
+
Indexes speed up `useLiveQuery` filters, joins, and `orderBy` on those fields. Match indexes to how you query — e.g. `by-user` for `where(({ todo }) => eq(todo.userId, userId))`, `by-title` for title search.
|
|
185
310
|
|
|
186
|
-
### 5.
|
|
311
|
+
### 5. App settings (`app-settings.ts`)
|
|
187
312
|
|
|
188
|
-
|
|
313
|
+
Seed a singleton settings row and keep `syncEnabled` in sync with `db.setSyncEnabled()`:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import type { AppSettings } from "./collections";
|
|
317
|
+
import { db, ensureDb } from "./collections";
|
|
318
|
+
|
|
319
|
+
export const APP_SETTINGS_ID = "app";
|
|
320
|
+
|
|
321
|
+
const DEFAULT_APP_SETTINGS: AppSettings = {
|
|
322
|
+
id: APP_SETTINGS_ID,
|
|
323
|
+
theme: "dark",
|
|
324
|
+
language: "en",
|
|
325
|
+
syncEnabled: true,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
export async function ensureAppSettings(): Promise<AppSettings> {
|
|
329
|
+
const database = await ensureDb();
|
|
330
|
+
const existing = database.collections.settings.get(APP_SETTINGS_ID);
|
|
331
|
+
|
|
332
|
+
if (!existing) {
|
|
333
|
+
await database.collections.settings.insert(DEFAULT_APP_SETTINGS).isPersisted.promise;
|
|
334
|
+
database.setSyncEnabled(DEFAULT_APP_SETTINGS.syncEnabled);
|
|
335
|
+
return DEFAULT_APP_SETTINGS;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const syncEnabled = existing.syncEnabled ?? true;
|
|
339
|
+
|
|
340
|
+
if (existing.syncEnabled === undefined) {
|
|
341
|
+
await database.collections.settings
|
|
342
|
+
.update(APP_SETTINGS_ID, (draft) => {
|
|
343
|
+
draft.syncEnabled = true;
|
|
344
|
+
})
|
|
345
|
+
.isPersisted.promise;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
database.setSyncEnabled(syncEnabled);
|
|
349
|
+
return { ...existing, syncEnabled };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export async function setSyncEnabled(enabled: boolean): Promise<void> {
|
|
353
|
+
const database = await ensureDb();
|
|
354
|
+
database.setSyncEnabled(enabled);
|
|
355
|
+
|
|
356
|
+
const existing = database.collections.settings.get(APP_SETTINGS_ID);
|
|
357
|
+
if (!existing) {
|
|
358
|
+
await database.collections.settings.insert({ ...DEFAULT_APP_SETTINGS, syncEnabled: enabled })
|
|
359
|
+
.isPersisted.promise;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
await database.collections.settings
|
|
364
|
+
.update(APP_SETTINGS_ID, (draft) => {
|
|
365
|
+
draft.syncEnabled = enabled;
|
|
366
|
+
})
|
|
367
|
+
.isPersisted.promise;
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 6. Wire the app shell
|
|
372
|
+
|
|
373
|
+
Call `ensureAppSettings()` once when the app mounts (it calls `ensureDb()` internally):
|
|
189
374
|
|
|
190
375
|
```typescript
|
|
191
376
|
import { useEffect, useState } from "react";
|
|
192
|
-
import {
|
|
377
|
+
import { ensureAppSettings } from "./data-access-layer/app-settings";
|
|
378
|
+
import { useEventSourcedSync } from "./hooks/common/use-event-sourced-sync";
|
|
379
|
+
import { useSyncEnabled } from "./hooks/common/use-sync-enabled";
|
|
193
380
|
|
|
194
381
|
function AppShell() {
|
|
195
|
-
const [
|
|
382
|
+
const [dbReady, setDbReady] = useState(false);
|
|
196
383
|
|
|
197
384
|
useEffect(() => {
|
|
198
|
-
void
|
|
385
|
+
void ensureAppSettings().then(() => setDbReady(true));
|
|
199
386
|
}, []);
|
|
200
387
|
|
|
201
|
-
|
|
388
|
+
const syncEnabled = useSyncEnabled(dbReady);
|
|
389
|
+
useEventSourcedSync(dbReady && syncEnabled);
|
|
390
|
+
|
|
391
|
+
if (!dbReady) return <Loading />;
|
|
202
392
|
return <YourRoutes />;
|
|
203
393
|
}
|
|
204
394
|
```
|
|
205
395
|
|
|
206
|
-
Optional
|
|
396
|
+
Optional sync helpers:
|
|
207
397
|
|
|
208
398
|
```typescript
|
|
209
399
|
// sync-events.ts
|
|
@@ -214,10 +404,7 @@ export async function manualSyncEvents() {
|
|
|
214
404
|
return database.manualSync();
|
|
215
405
|
}
|
|
216
406
|
|
|
217
|
-
// use-event-sourced-sync.ts
|
|
218
|
-
import { useQuery } from "@tanstack/react-query";
|
|
219
|
-
import { manualSyncEvents } from "@/data-access-layer/sync-events";
|
|
220
|
-
|
|
407
|
+
// use-event-sourced-sync.ts — polls manualSync when enabled
|
|
221
408
|
export function useEventSourcedSync(enabled: boolean) {
|
|
222
409
|
return useQuery({
|
|
223
410
|
queryKey: ["sync"],
|
|
@@ -226,9 +413,42 @@ export function useEventSourcedSync(enabled: boolean) {
|
|
|
226
413
|
refetchInterval: 60_000,
|
|
227
414
|
});
|
|
228
415
|
}
|
|
416
|
+
|
|
417
|
+
// use-sync-enabled.ts — live-query settings.syncEnabled for UI
|
|
418
|
+
export function useSyncEnabled(dbReady: boolean) {
|
|
419
|
+
const { data = [] } = useLiveQuery(
|
|
420
|
+
(query) =>
|
|
421
|
+
query
|
|
422
|
+
.from({ setting: db.collections.settings })
|
|
423
|
+
.where(({ setting }) => eq(setting.id, APP_SETTINGS_ID)),
|
|
424
|
+
[dbReady],
|
|
425
|
+
);
|
|
426
|
+
if (!dbReady) return true;
|
|
427
|
+
return data[0]?.syncEnabled ?? true;
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Settings UI toggle (calls `setSyncEnabled` from `app-settings.ts`):
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { setSyncEnabled } from "@/data-access-layer/app-settings";
|
|
435
|
+
import { useSyncEnabled } from "@/hooks/common/use-sync-enabled";
|
|
436
|
+
|
|
437
|
+
function SettingsPage() {
|
|
438
|
+
const syncEnabled = useSyncEnabled(true);
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<Switch
|
|
442
|
+
checked={syncEnabled}
|
|
443
|
+
onCheckedChange={(checked) => void setSyncEnabled(checked)}
|
|
444
|
+
/>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
229
447
|
```
|
|
230
448
|
|
|
231
|
-
|
|
449
|
+
Disable manual **Sync now** when sync is off: `const syncEnabled = useSyncEnabled(true)` and pass it to your button's `disabled` prop.
|
|
450
|
+
|
|
451
|
+
### 7. Write data
|
|
232
452
|
|
|
233
453
|
Use collections like normal TanStack DB. Mutations are logged to the event store automatically.
|
|
234
454
|
|
|
@@ -257,6 +477,7 @@ db.collections.settings.insert({
|
|
|
257
477
|
id: "app",
|
|
258
478
|
theme: "dark",
|
|
259
479
|
language: "en",
|
|
480
|
+
syncEnabled: true,
|
|
260
481
|
});
|
|
261
482
|
|
|
262
483
|
db.collections.todos.update("todo-id", (draft) => {
|
|
@@ -267,7 +488,7 @@ db.collections.todos.update("todo-id", (draft) => {
|
|
|
267
488
|
db.collections.todos.delete("todo-id");
|
|
268
489
|
```
|
|
269
490
|
|
|
270
|
-
For `settings`, use a fixed id (e.g. `"app"`) as a singleton row:
|
|
491
|
+
For `settings`, use a fixed id (e.g. `"app"`) as a singleton row. Prefer `ensureAppSettings()` at startup instead of inserting manually:
|
|
271
492
|
|
|
272
493
|
```typescript
|
|
273
494
|
db.collections.settings.update("app", (draft) => {
|
|
@@ -275,7 +496,7 @@ db.collections.settings.update("app", (draft) => {
|
|
|
275
496
|
});
|
|
276
497
|
```
|
|
277
498
|
|
|
278
|
-
###
|
|
499
|
+
### 8. Read data
|
|
279
500
|
|
|
280
501
|
```typescript
|
|
281
502
|
import { useLiveQuery } from "@tanstack/react-db";
|
|
@@ -306,7 +527,7 @@ function TodoList() {
|
|
|
306
527
|
}
|
|
307
528
|
```
|
|
308
529
|
|
|
309
|
-
###
|
|
530
|
+
### 9. Sync
|
|
310
531
|
|
|
311
532
|
```typescript
|
|
312
533
|
import { ensureDb } from "./data-access-layer/collections";
|
|
@@ -317,7 +538,7 @@ await db.sync();
|
|
|
317
538
|
window.addEventListener("online", () => void db.sync());
|
|
318
539
|
```
|
|
319
540
|
|
|
320
|
-
Or use a periodic hook (see step
|
|
541
|
+
Or use a periodic hook (see step 6). Respect `syncEnabled` — background sync should stay off when the user opts out.
|
|
321
542
|
|
|
322
543
|
## Inspecting sync state
|
|
323
544
|
|
|
@@ -493,6 +714,28 @@ const transport: SyncTransport = {
|
|
|
493
714
|
const db = await createEventSourcedDB({ sync: transport, /* ... */ });
|
|
494
715
|
```
|
|
495
716
|
|
|
717
|
+
## Opting out of sync
|
|
718
|
+
|
|
719
|
+
`syncEnabled` defaults to `true` on `createBrowserEventSourcedDB`. When disabled:
|
|
720
|
+
|
|
721
|
+
- `sync()` skips push/pull (returns `{ pushed: 0, pulled: 0, errors: [] }`)
|
|
722
|
+
- `manualSync()` still replays pending inbox events locally but does not hit the network
|
|
723
|
+
- Background sync hooks should pass `dbReady && syncEnabled` as their `enabled` flag
|
|
724
|
+
|
|
725
|
+
Toggle at runtime:
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
const database = await ensureDb();
|
|
729
|
+
database.setSyncEnabled(false);
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Recommended pattern:** persist the preference in your `settings` collection and mirror it on startup — see [App settings](#5-app-settings-app-settingsts) (`ensureAppSettings` + `setSyncEnabled`). That keeps the Settings UI, background sync hook, and engine in sync.
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
await setSyncEnabled(false);
|
|
736
|
+
// updates settings row + calls db.setSyncEnabled(false)
|
|
737
|
+
```
|
|
738
|
+
|
|
496
739
|
## React Native
|
|
497
740
|
|
|
498
741
|
Same helper shape via `createReactNativeEventSourcedDB` (from `event-sourced-collection/react-native`). React Native can't auto-open the database, so `load` returns the `database` driver alongside the platform functions.
|
|
@@ -550,11 +793,12 @@ The recommended entry points. Import from `event-sourced-collection/browser` or
|
|
|
550
793
|
|
|
551
794
|
| Option | Required | Description |
|
|
552
795
|
| ------ | -------- | ----------- |
|
|
553
|
-
| `collections` | Yes | Collection definitions (`getKey`, optional `schemaVersion`) — must not use the reserved ids `outbox`/`inbox` |
|
|
796
|
+
| `collections` | Yes | Collection definitions (`getKey`, optional `schemaVersion`, optional `indexes[]`) — must not use the reserved ids `outbox`/`inbox` |
|
|
554
797
|
| `load` | Yes | Async callback returning the platform modules + `createCollection` + `persistedCollectionOptions`. Keeps platform/framework imports in your app |
|
|
555
798
|
| `databaseName` | Browser only | SQLite database file name |
|
|
556
799
|
| `coordinatorDbName` | No (browser) | Multi-tab coordinator name; defaults to `databaseName` without the `.sqlite` suffix |
|
|
557
800
|
| `sync` | No | Offline by default. Same shapes as `createEventSourcedDB` |
|
|
801
|
+
| `syncEnabled` | No | Default `true`. When `false`, push/pull are skipped until re-enabled |
|
|
558
802
|
| `schemaVersion` | No | Default `1` |
|
|
559
803
|
| `debug` | No | `boolean` or a custom logger |
|
|
560
804
|
|
|
@@ -577,11 +821,14 @@ Low-level core for full control or non-standard platforms. The helpers above cal
|
|
|
577
821
|
| `persistence` | Yes | TanStack DB persistence config |
|
|
578
822
|
| `createCollection` | Yes | TanStack DB `createCollection` |
|
|
579
823
|
| `persistedCollectionOptions` | Yes | From your platform package |
|
|
580
|
-
| `collections` | Yes | Collection definitions (`getKey`, optional `schemaVersion`) — must not use the reserved ids `outbox`/`inbox` |
|
|
824
|
+
| `collections` | Yes | Collection definitions (`getKey`, optional `schemaVersion`, optional `indexes[]`) — must not use the reserved ids `outbox`/`inbox` |
|
|
581
825
|
| `sync` | No | Offline by default. Accepts typed `pushEvents`/`pullEvents`, URL `pushUrl`/`pullUrl`, legacy URL `push`/`pull`, or legacy `SyncTransport` |
|
|
826
|
+
| `syncEnabled` | No | Default `true`. When `false`, push/pull are skipped until re-enabled |
|
|
582
827
|
| `schemaVersion` | No | Default `1` |
|
|
583
828
|
|
|
584
|
-
Returns `{ collections, sync, manualSync, dispose }`, where `collections` includes your registered collections plus the built-in `outbox` and `inbox`.
|
|
829
|
+
Returns `{ collections, sync, manualSync, getSyncEnabled, setSyncEnabled, dispose }`, where `collections` includes your registered collections plus the built-in `outbox` and `inbox`.
|
|
830
|
+
|
|
831
|
+
- `getSyncEnabled()` / `setSyncEnabled(enabled)` — runtime toggle for push/pull (default `true` at init via `syncEnabled` config option).
|
|
585
832
|
|
|
586
833
|
### `createBrowserPlatform(deps, config)`
|
|
587
834
|
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as CreateCollectionFn, D as SyncUrlConfig, E as SyncTransport, O as EventSourcedLogger, S as SQLiteDriver, i as EventSourcedDB, j as PersistedCollectionOptionsFn, w as SyncHandlersConfig } from "./types-BX8dtGOu.mjs";
|
|
2
2
|
import { PersistedCollectionCoordinator, PersistedCollectionPersistence } from "@tanstack/db-sqlite-persistence-core";
|
|
3
3
|
import { BrowserCollectionCoordinatorOptions, BrowserWASQLiteDatabase as BrowserWASQLiteDatabase$1, BrowserWASQLitePersistenceOptions, OpenBrowserWASQLiteOPFSDatabaseOptions } from "@tanstack/browser-db-sqlite-persistence";
|
|
4
4
|
|
|
@@ -34,6 +34,11 @@ declare function createBrowserPlatform(deps: BrowserPlatformDeps, config: Browse
|
|
|
34
34
|
type CollectionDefConstraint = {
|
|
35
35
|
getKey: (state: never) => string | number;
|
|
36
36
|
schemaVersion?: number;
|
|
37
|
+
indexes?: ReadonlyArray<{
|
|
38
|
+
select: (row: never) => unknown;
|
|
39
|
+
name?: string;
|
|
40
|
+
indexType?: import("@tanstack/db").IndexConstructor<string | number>;
|
|
41
|
+
}>;
|
|
37
42
|
};
|
|
38
43
|
type BrowserEventSourcedModules = BrowserPlatformDeps & {
|
|
39
44
|
createCollection: CreateCollectionFn;
|
|
@@ -45,6 +50,7 @@ type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConst
|
|
|
45
50
|
collections: TDefs;
|
|
46
51
|
load: () => Promise<BrowserEventSourcedModules>;
|
|
47
52
|
sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;
|
|
53
|
+
syncEnabled?: boolean;
|
|
48
54
|
schemaVersion?: number;
|
|
49
55
|
debug?: boolean | EventSourcedLogger;
|
|
50
56
|
};
|
package/dist/browser.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.mts","names":[],"sources":["../src/platforms/browser-types.ts","../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser.ts","../src/platforms/browser-event-sourced-db.ts"],"mappings":";;;;;KAQY,0BAAA,GAA6B,8BAA8B;EACrE,OAAO;AAAA;AAAA,KAGG,mBAAA;EACV,+BAAA,GACE,OAAA,EAAS,sCAAA,KACN,OAAA,CAAQ,yBAAA;EACb,gCAAA,GACE,OAAA,EAAS,iCAAA,KACN,8BAAA;EACL,4BAAA,OACE,OAAA,EAAS,mCAAA,KACN,0BAAA;AAAA;AAAA,KAGK,qBAAA;EACV,YAAA;EACA,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EACV,MAAA,EAD+B,YAAA;EAE/B,WAAA,EAAa,8BAAA;EACb,KAAA,QAAa,OAAA;AAAA;;;KC9BH,uBAAA;EACV,OAAA,mBACE,GAAA,UACA,MAAA,GAAS,aAAA,cACN,OAAA,CAAQ,aAAA,CAAc,IAAA;EAC3B,KAAA,SAAc,OAAA;AAAA;;;iBCUM,qBAAA,CACpB,IAAA,EAAM,mBAAA,EACN,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;;;KCPN,uBAAA;EACH,MAAA,GAAS,KAAA;EACT,aAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"browser.d.mts","names":[],"sources":["../src/platforms/browser-types.ts","../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser.ts","../src/platforms/browser-event-sourced-db.ts"],"mappings":";;;;;KAQY,0BAAA,GAA6B,8BAA8B;EACrE,OAAO;AAAA;AAAA,KAGG,mBAAA;EACV,+BAAA,GACE,OAAA,EAAS,sCAAA,KACN,OAAA,CAAQ,yBAAA;EACb,gCAAA,GACE,OAAA,EAAS,iCAAA,KACN,8BAAA;EACL,4BAAA,OACE,OAAA,EAAS,mCAAA,KACN,0BAAA;AAAA;AAAA,KAGK,qBAAA;EACV,YAAA;EACA,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EACV,MAAA,EAD+B,YAAA;EAE/B,WAAA,EAAa,8BAAA;EACb,KAAA,QAAa,OAAA;AAAA;;;KC9BH,uBAAA;EACV,OAAA,mBACE,GAAA,UACA,MAAA,GAAS,aAAA,cACN,OAAA,CAAQ,aAAA,CAAc,IAAA;EAC3B,KAAA,SAAc,OAAA;AAAA;;;iBCUM,qBAAA,CACpB,IAAA,EAAM,mBAAA,EACN,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;;;KCPN,uBAAA;EACH,MAAA,GAAS,KAAA;EACT,aAAA;EACA,OAAA,GAAU,aAAa;IACrB,MAAA,GAAS,GAAA;IACT,IAAA;IACA,SAAA,0BAAmC,gBAAA;EAAA;AAAA;AAAA,KAI3B,0BAAA,GAA6B,mBAAA;EACvC,gBAAA,EAAkB,kBAAA;EAClB,0BAAA,EAA4B,4BAAA;AAAA;AAAA,KAGlB,2BAAA,eAA0C,MAAA,SAAe,uBAAA;EACnE,YAAA;EACA,iBAAA;EACA,WAAA,EAAa,KAAA;EACb,IAAA,QAAY,OAAA,CAAQ,0BAAA;EACpB,IAAA,GAAO,kBAAA,GAAqB,aAAA,GAAgB,aAAA;EAC5C,WAAA;EACA,aAAA;EACA,KAAA,aAAkB,kBAAA;AAAA;AAAA,KAGR,2BAAA,eAA0C,MAAA,SAAe,uBAAA;EACnE,QAAA,QAAgB,OAAA,CAAQ,cAAA,CAAe,KAAA;EACvC,EAAA,EAAI,cAAA,CAAe,KAAA;EACnB,KAAA,QAAa,OAAA;AAAA;AAAA,iBAGC,2BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,2BAAA,CAA4B,KAAA,IAAS,2BAAA,CAA4B,KAAA"}
|
package/dist/browser.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createEventSourcedDB, t as createLazySingleton } from "./lazy-singleton-
|
|
1
|
+
import { n as createEventSourcedDB, t as createLazySingleton } from "./lazy-singleton-BG02GHLj.mjs";
|
|
2
2
|
//#region src/platforms/browser-wa-sqlite-driver.ts
|
|
3
3
|
function assertTransactionCallbackHasDriverArg(fn) {
|
|
4
4
|
if (fn.length > 0) return;
|
|
@@ -124,6 +124,7 @@ function createBrowserEventSourcedDB(config) {
|
|
|
124
124
|
persistedCollectionOptions: modules.persistedCollectionOptions,
|
|
125
125
|
collections: config.collections,
|
|
126
126
|
sync: config.sync,
|
|
127
|
+
syncEnabled: config.syncEnabled,
|
|
127
128
|
schemaVersion: config.schemaVersion,
|
|
128
129
|
debug: config.debug
|
|
129
130
|
});
|
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 \"../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(\n \"SQLiteDriver.transaction callback must accept the transaction driver argument\",\n );\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 }\n throw error;\n }\n });\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): 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 type { PersistedCollectionPersistence } from \"../types\";\nimport { 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\nexport async function createBrowserPlatform(\n deps: BrowserPlatformDeps,\n config: BrowserPlatformConfig,\n): Promise<BrowserPlatformResult> {\n const database = await deps.openBrowserWASQLiteOPFSDatabase({\n databaseName: config.databaseName,\n });\n\n const coordinator = new deps.BrowserCollectionCoordinator({\n dbName: config.coordinatorDbName ?? config.databaseName.replace(/\\.sqlite$/, \"\"),\n });\n\n const persistence = deps.createBrowserWASQLitePersistence({\n database,\n coordinator,\n });\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 { createEventSourcedDB } from \"../create-event-sourced-db\";\nimport { createLazySingleton } from \"../lazy-singleton\";\nimport type { CreateCollectionFn, PersistedCollectionOptionsFn } from \"../persisted-collection\";\nimport type {\n EventSourcedDB,\n EventSourcedLogger,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"../types\";\nimport { createBrowserPlatform } from \"./browser\";\nimport type { BrowserPlatformDeps } from \"./browser-types\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n};\n\nexport type BrowserEventSourcedModules = BrowserPlatformDeps & {\n createCollection: CreateCollectionFn;\n persistedCollectionOptions: PersistedCollectionOptionsFn;\n};\n\nexport type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> = {\n databaseName: string;\n coordinatorDbName?: string;\n collections: TDefs;\n load: () => Promise<BrowserEventSourcedModules>;\n sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;\n schemaVersion?: number;\n debug?: boolean | EventSourcedLogger;\n};\n\nexport type BrowserEventSourcedDBHandle<TDefs extends Record<string, CollectionDefConstraint>> = {\n ensureDb: () => Promise<EventSourcedDB<TDefs>>;\n db: EventSourcedDB<TDefs>;\n close: () => Promise<void>;\n};\n\nexport function createBrowserEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: BrowserEventSourcedDBConfig<TDefs>): BrowserEventSourcedDBHandle<TDefs> {\n let activeDb: EventSourcedDB<TDefs> | null = null;\n let closePlatform: (() => Promise<void>) | null = null;\n\n const factory = async (): Promise<EventSourcedDB<TDefs>> => {\n const modules = await config.load();\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 closePlatform = platform.close;\n\n const database = await createEventSourcedDB<TDefs>({\n persistence: platform.persistence,\n createCollection: modules.createCollection,\n persistedCollectionOptions: modules.persistedCollectionOptions,\n collections: config.collections,\n sync: config.sync,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n });\n\n activeDb = database;\n return database;\n };\n\n const singleton = createLazySingleton<EventSourcedDB<TDefs>>(factory, {\n guard: assertBrowserEnvironment,\n notInitializedMessage:\n \"Event-sourced DB is not initialized. Call ensureDb() before using db.\",\n });\n\n const close = async (): Promise<void> => {\n if (activeDb) {\n activeDb.dispose();\n activeDb = null;\n }\n if (closePlatform) {\n await closePlatform();\n closePlatform = null;\n }\n singleton.reset();\n };\n\n return {\n ensureDb: singleton.ensure,\n db: singleton.proxy,\n close,\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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"}
|
|
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 \"../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(\n \"SQLiteDriver.transaction callback must accept the transaction driver argument\",\n );\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 }\n throw error;\n }\n });\n }\n\n async transactionWithDriver<T>(\n fn: (transactionDriver: SQLiteDriver) => Promise<T>,\n ): 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 type { PersistedCollectionPersistence } from \"../types\";\nimport { 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\nexport async function createBrowserPlatform(\n deps: BrowserPlatformDeps,\n config: BrowserPlatformConfig,\n): Promise<BrowserPlatformResult> {\n const database = await deps.openBrowserWASQLiteOPFSDatabase({\n databaseName: config.databaseName,\n });\n\n const coordinator = new deps.BrowserCollectionCoordinator({\n dbName: config.coordinatorDbName ?? config.databaseName.replace(/\\.sqlite$/, \"\"),\n });\n\n const persistence = deps.createBrowserWASQLitePersistence({\n database,\n coordinator,\n });\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 { createEventSourcedDB } from \"../create-event-sourced-db\";\nimport { createLazySingleton } from \"../lazy-singleton\";\nimport type { CreateCollectionFn, PersistedCollectionOptionsFn } from \"../persisted-collection\";\nimport type {\n EventSourcedDB,\n EventSourcedLogger,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"../types\";\nimport { createBrowserPlatform } from \"./browser\";\nimport type { BrowserPlatformDeps } from \"./browser-types\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n indexes?: ReadonlyArray<{\n select: (row: never) => unknown;\n name?: string;\n indexType?: import(\"@tanstack/db\").IndexConstructor<string | number>;\n }>;\n};\n\nexport type BrowserEventSourcedModules = BrowserPlatformDeps & {\n createCollection: CreateCollectionFn;\n persistedCollectionOptions: PersistedCollectionOptionsFn;\n};\n\nexport type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> = {\n databaseName: string;\n coordinatorDbName?: string;\n collections: TDefs;\n load: () => Promise<BrowserEventSourcedModules>;\n sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;\n syncEnabled?: boolean;\n schemaVersion?: number;\n debug?: boolean | EventSourcedLogger;\n};\n\nexport type BrowserEventSourcedDBHandle<TDefs extends Record<string, CollectionDefConstraint>> = {\n ensureDb: () => Promise<EventSourcedDB<TDefs>>;\n db: EventSourcedDB<TDefs>;\n close: () => Promise<void>;\n};\n\nexport function createBrowserEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: BrowserEventSourcedDBConfig<TDefs>): BrowserEventSourcedDBHandle<TDefs> {\n let activeDb: EventSourcedDB<TDefs> | null = null;\n let closePlatform: (() => Promise<void>) | null = null;\n\n const factory = async (): Promise<EventSourcedDB<TDefs>> => {\n const modules = await config.load();\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 closePlatform = platform.close;\n\n const database = await createEventSourcedDB<TDefs>({\n persistence: platform.persistence,\n createCollection: modules.createCollection,\n persistedCollectionOptions: modules.persistedCollectionOptions,\n collections: config.collections,\n sync: config.sync,\n syncEnabled: config.syncEnabled,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n });\n\n activeDb = database;\n return database;\n };\n\n const singleton = createLazySingleton<EventSourcedDB<TDefs>>(factory, {\n guard: assertBrowserEnvironment,\n notInitializedMessage:\n \"Event-sourced DB is not initialized. Call ensureDb() before using db.\",\n });\n\n const close = async (): Promise<void> => {\n if (activeDb) {\n activeDb.dispose();\n activeDb = null;\n }\n if (closePlatform) {\n await closePlatform();\n closePlatform = null;\n }\n singleton.reset();\n };\n\n return {\n ensureDb: singleton.ensure,\n db: singleton.proxy,\n close,\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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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as CreateCollectionFn, C as ServerEvent, D as SyncUrlConfig, E as SyncTransport, O as EventSourcedLogger, S as SQLiteDriver, T as SyncResult, _ as PushConfirmation, a as EventSourcedDBConfig, b as PushResponse, c as InferState, d as OutboundEvent, f as OutboxEntry, g as PullResponse, h as PullEventsFn, i as EventSourcedDB, j as PersistedCollectionOptionsFn, k as createEventSourcedLogger, l as ManualSyncResult, m as PersistedCollectionPersistence, n as CollectionIndexDef, o as InboxEntry, p as OutboxSyncStatus, r as CollectionMap, s as InferKey, t as CollectionDef, u as MutationType, v as PushEventsFn, w as SyncHandlersConfig, x as ReservedCollections, y as PushFailure } from "./types-BX8dtGOu.mjs";
|
|
2
2
|
import { uuidv7 as generateEventId } from "uuidv7";
|
|
3
|
+
import { IndexConstructor } from "@tanstack/db";
|
|
3
4
|
|
|
4
5
|
//#region src/create-event-sourced-db.d.ts
|
|
5
6
|
type CollectionDefConstraint = {
|
|
6
7
|
getKey: (state: never) => string | number;
|
|
7
8
|
schemaVersion?: number;
|
|
9
|
+
indexes?: ReadonlyArray<{
|
|
10
|
+
select: (row: never) => unknown;
|
|
11
|
+
name?: string;
|
|
12
|
+
indexType?: IndexConstructor<string | number>;
|
|
13
|
+
}>;
|
|
8
14
|
};
|
|
9
15
|
declare function createEventSourcedDB<const TDefs extends Record<string, CollectionDefConstraint>>(config: EventSourcedDBConfig<TDefs>): Promise<EventSourcedDB<TDefs>>;
|
|
10
16
|
//#endregion
|
|
@@ -33,5 +39,5 @@ declare class SyncPullError extends Error {
|
|
|
33
39
|
constructor(status: number, body: string);
|
|
34
40
|
}
|
|
35
41
|
//#endregion
|
|
36
|
-
export { type CollectionDef, type CollectionMap, type CreateCollectionFn, type EventSourcedDB, type EventSourcedDBConfig, type EventSourcedLogger, type InboxEntry, type InferKey, type InferState, type LazySingleton, type LazySingletonOptions, type ManualSyncResult, type MutationType, type OutboundEvent, type OutboxEntry, type OutboxSyncStatus, type PersistedCollectionOptionsFn, type PersistedCollectionPersistence, type PullEventsFn, type PullResponse, type PushConfirmation, type PushEventsFn, type PushFailure, type PushResponse, type ReservedCollections, type SQLiteDriver, type ServerEvent, type SyncHandlersConfig, SyncPullError, SyncPushError, type SyncResult, type SyncTransport, type SyncUrlConfig, createEventSourcedDB, createEventSourcedLogger, createHttpTransport, createLazySingleton, generateEventId };
|
|
42
|
+
export { type CollectionDef, type CollectionIndexDef, type CollectionMap, type CreateCollectionFn, type EventSourcedDB, type EventSourcedDBConfig, type EventSourcedLogger, type InboxEntry, type InferKey, type InferState, type LazySingleton, type LazySingletonOptions, type ManualSyncResult, type MutationType, type OutboundEvent, type OutboxEntry, type OutboxSyncStatus, type PersistedCollectionOptionsFn, type PersistedCollectionPersistence, type PullEventsFn, type PullResponse, type PushConfirmation, type PushEventsFn, type PushFailure, type PushResponse, type ReservedCollections, type SQLiteDriver, type ServerEvent, type SyncHandlersConfig, SyncPullError, SyncPushError, type SyncResult, type SyncTransport, type SyncUrlConfig, createEventSourcedDB, createEventSourcedLogger, createHttpTransport, createLazySingleton, generateEventId };
|
|
37
43
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-event-sourced-db.ts","../src/lazy-singleton.ts","../src/sync.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-event-sourced-db.ts","../src/lazy-singleton.ts","../src/sync.ts"],"mappings":";;;;;KAuBK,uBAAA;EACH,MAAA,GAAS,KAAA;EACT,aAAA;EACA,OAAA,GAAU,aAAa;IACrB,MAAA,GAAS,GAAA;IACT,IAAA;IACA,SAAA,GAAY,gBAAA;EAAA;AAAA;AAAA,iBAuCM,oBAAA,qBACA,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,oBAAA,CAAqB,KAAA,IAAS,OAAA,CAAQ,cAAA,CAAe,KAAA;;;KCtEnD,oBAAA;EACV,KAAA;EACA,qBAAqB;AAAA;AAAA,KAGX,aAAA;EACV,MAAA,QAAc,OAAA,CAAQ,CAAA;EACtB,KAAA,EAAO,CAAA;EACP,KAAA;AAAA;AAAA,iBAGc,mBAAA,mBACd,OAAA,QAAe,OAAA,CAAQ,CAAA,GACvB,OAAA,GAAS,oBAAA,GACR,aAAA,CAAc,CAAA;;;iBCKD,mBAAA,CAAoB,MAAA,EAAQ,aAAA,GAAgB,aAAa;AAAA,cA8I5D,aAAA,SAAsB,KAAK;EAAA,SAEpB,MAAA;EAAA,SACA,IAAA;cADA,MAAA,UACA,IAAA;AAAA;AAAA,cAOP,aAAA,SAAsB,KAAK;EAAA,SAEpB,MAAA;EAAA,SACA,IAAA;cADA,MAAA,UACA,IAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SyncPullError, i as createEventSourcedLogger, n as createEventSourcedDB, o as SyncPushError, r as generateEventId, s as createHttpTransport, t as createLazySingleton } from "./lazy-singleton-
|
|
1
|
+
import { a as SyncPullError, i as createEventSourcedLogger, n as createEventSourcedDB, o as SyncPushError, r as generateEventId, s as createHttpTransport, t as createLazySingleton } from "./lazy-singleton-BG02GHLj.mjs";
|
|
2
2
|
export { SyncPullError, SyncPushError, createEventSourcedDB, createEventSourcedLogger, createHttpTransport, createLazySingleton, generateEventId };
|
|
@@ -155,9 +155,11 @@ async function createEventSourcedDB(config) {
|
|
|
155
155
|
assertReservedNamesAvailable(config.collections);
|
|
156
156
|
const log = createEventSourcedLogger(config.debug);
|
|
157
157
|
const transport = createSyncTransport(config.sync);
|
|
158
|
+
let syncEnabled = config.syncEnabled ?? true;
|
|
158
159
|
log.info("creating event-sourced db", {
|
|
159
160
|
collectionIds: Object.keys(config.collections),
|
|
160
|
-
hasTransport: transport !== null
|
|
161
|
+
hasTransport: transport !== null,
|
|
162
|
+
syncEnabled
|
|
161
163
|
});
|
|
162
164
|
const defaultSchemaVersion = config.schemaVersion ?? 1;
|
|
163
165
|
const seq = { value: 0 };
|
|
@@ -177,6 +179,7 @@ async function createEventSourcedDB(config) {
|
|
|
177
179
|
onDelete: createMutationHook(outbox, collectionId, "delete", seq, log)
|
|
178
180
|
});
|
|
179
181
|
const collection = config.createCollection(options);
|
|
182
|
+
applyCollectionIndexes(collection, collectionId, def.indexes, log);
|
|
180
183
|
const hasAcceptMutations = Boolean(collection.utils?.acceptMutations);
|
|
181
184
|
log.debug("registered collection", {
|
|
182
185
|
collectionId,
|
|
@@ -201,6 +204,14 @@ async function createEventSourcedDB(config) {
|
|
|
201
204
|
});
|
|
202
205
|
await replayInbox(inbox, replayTargets, log);
|
|
203
206
|
async function sync() {
|
|
207
|
+
if (!syncEnabled) {
|
|
208
|
+
log.debug("sync skipped: sync disabled");
|
|
209
|
+
return {
|
|
210
|
+
pushed: 0,
|
|
211
|
+
pulled: 0,
|
|
212
|
+
errors: []
|
|
213
|
+
};
|
|
214
|
+
}
|
|
204
215
|
if (!transport) {
|
|
205
216
|
log.warn("sync skipped: no transport configured");
|
|
206
217
|
return {
|
|
@@ -250,7 +261,7 @@ async function createEventSourcedDB(config) {
|
|
|
250
261
|
let pushed = 0;
|
|
251
262
|
let pulled = 0;
|
|
252
263
|
let replayed = 0;
|
|
253
|
-
if (transport) {
|
|
264
|
+
if (syncEnabled && transport) {
|
|
254
265
|
try {
|
|
255
266
|
if (transport.push) pushed = await pushOutbox(outbox, transport.push, log);
|
|
256
267
|
else log.debug("manual sync push skipped: no push transport configured");
|
|
@@ -267,7 +278,8 @@ async function createEventSourcedDB(config) {
|
|
|
267
278
|
log.error("manual sync pull failed", { message: error.message });
|
|
268
279
|
errors.push(error);
|
|
269
280
|
}
|
|
270
|
-
} else log.
|
|
281
|
+
} else if (!syncEnabled) log.debug("manual sync push/pull skipped: sync disabled");
|
|
282
|
+
else log.warn("manual sync: no transport configured, skipping push/pull");
|
|
271
283
|
try {
|
|
272
284
|
replayed = await replayInbox(inbox, replayTargets, log);
|
|
273
285
|
} catch (err) {
|
|
@@ -288,6 +300,13 @@ async function createEventSourcedDB(config) {
|
|
|
288
300
|
errors
|
|
289
301
|
};
|
|
290
302
|
}
|
|
303
|
+
function getSyncEnabled() {
|
|
304
|
+
return syncEnabled;
|
|
305
|
+
}
|
|
306
|
+
function setSyncEnabled(enabled) {
|
|
307
|
+
syncEnabled = enabled;
|
|
308
|
+
log.debug("sync enabled updated", { syncEnabled: enabled });
|
|
309
|
+
}
|
|
291
310
|
function dispose() {
|
|
292
311
|
log.debug("disposing event-sourced db");
|
|
293
312
|
for (const subscription of subscriptions) subscription.unsubscribe();
|
|
@@ -296,6 +315,8 @@ async function createEventSourcedDB(config) {
|
|
|
296
315
|
collections,
|
|
297
316
|
sync,
|
|
298
317
|
manualSync,
|
|
318
|
+
getSyncEnabled,
|
|
319
|
+
setSyncEnabled,
|
|
299
320
|
dispose
|
|
300
321
|
};
|
|
301
322
|
}
|
|
@@ -308,6 +329,20 @@ function createMetaCollection(config, id, getKey, schemaVersion) {
|
|
|
308
329
|
});
|
|
309
330
|
return config.createCollection(options);
|
|
310
331
|
}
|
|
332
|
+
function applyCollectionIndexes(collection, collectionId, indexes, log) {
|
|
333
|
+
if (!indexes?.length) return;
|
|
334
|
+
const indexable = collection;
|
|
335
|
+
for (const indexDef of indexes) {
|
|
336
|
+
indexable.createIndex(indexDef.select, {
|
|
337
|
+
name: indexDef.name,
|
|
338
|
+
indexType: indexDef.indexType
|
|
339
|
+
});
|
|
340
|
+
log.debug("registered collection index", {
|
|
341
|
+
collectionId,
|
|
342
|
+
name: indexDef.name
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
311
346
|
function createMutationHook(outbox, collectionId, type, seq, log) {
|
|
312
347
|
return async (params) => {
|
|
313
348
|
for (const mutation of params.transaction.mutations) {
|
|
@@ -626,4 +661,4 @@ function createLazySingleton(factory, options = {}) {
|
|
|
626
661
|
//#endregion
|
|
627
662
|
export { SyncPullError as a, createEventSourcedLogger as i, createEventSourcedDB as n, SyncPushError as o, generateEventId as r, createHttpTransport as s, createLazySingleton as t };
|
|
628
663
|
|
|
629
|
-
//# sourceMappingURL=lazy-singleton-
|
|
664
|
+
//# sourceMappingURL=lazy-singleton-BG02GHLj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-singleton-BG02GHLj.mjs","names":[],"sources":["../src/sync.ts","../src/utils/logger.ts","../src/create-event-sourced-db.ts","../src/lazy-singleton.ts"],"sourcesContent":["import type {\n OutboundEvent,\n PullEventsFn,\n PullResponse,\n PushConfirmation,\n PushEventsFn,\n PushResponse,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"./types\";\n\nexport type NormalizedSyncTransport = {\n push?: PushEventsFn;\n pull?: (since: number) => Promise<PullResponse>;\n};\n\ntype HeaderConfig = SyncHandlersConfig[\"headers\"];\n\nexport function createHttpTransport(config: SyncUrlConfig): SyncTransport {\n return {\n push: createHttpPushEvents(config.push, config.headers),\n pull: createHttpPullEvents(config.pull, config.headers),\n };\n}\n\nexport function createSyncTransport(\n config?: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): NormalizedSyncTransport | null {\n if (!config) {\n return null;\n }\n\n if (isTransport(config)) {\n return {\n push: config.push,\n pull: config.pull,\n };\n }\n\n const pushUrl = getPushUrl(config);\n const pullUrl = getPullUrl(config);\n\n const push = \"pushEvents\" in config && config.pushEvents\n ? config.pushEvents\n : pushUrl\n ? createHttpPushEvents(pushUrl, config.headers)\n : undefined;\n\n const pullEvents = \"pullEvents\" in config ? config.pullEvents : undefined;\n const pull = pullEvents\n ? createPullFromHandler(pullEvents)\n : pullUrl\n ? createHttpPullEvents(pullUrl, config.headers)\n : undefined;\n\n if (!push && !pull) {\n return null;\n }\n\n return { push, pull };\n}\n\nexport function normalizePushResponse(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): PushResponse {\n if (isConfirmationArray(response)) {\n return { confirmed: response };\n }\n\n return {\n confirmed: response.confirmed,\n failed: response.failed,\n };\n}\n\nfunction isConfirmationArray(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): response is ReadonlyArray<PushConfirmation> {\n return Array.isArray(response);\n}\n\nfunction createHttpPushEvents(url: string, headers: HeaderConfig): PushEventsFn {\n return async (events: ReadonlyArray<OutboundEvent>): Promise<PushResponse> => {\n if (events.length === 0) return { confirmed: [] };\n\n const resolvedHeaders = await resolveHeaders(headers);\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...resolvedHeaders },\n body: JSON.stringify(events),\n });\n\n if (!response.ok) {\n throw new SyncPushError(response.status, await response.text());\n }\n\n return response.json() as Promise<PushResponse>;\n };\n}\n\nfunction createPullFromHandler(pullEvents: PullEventsFn): (since: number) => Promise<PullResponse> {\n return (since: number) => pullEvents({ since });\n}\n\nfunction createHttpPullEvents(\n url: string,\n headers: HeaderConfig,\n): (since: number) => Promise<PullResponse> {\n return async (since: number): Promise<PullResponse> => {\n const resolvedHeaders = await resolveHeaders(headers);\n const pullUrl = appendSince(url, since);\n\n const response = await fetch(pullUrl, {\n headers: { Accept: \"application/json\", ...resolvedHeaders },\n });\n\n if (!response.ok) {\n throw new SyncPullError(response.status, await response.text());\n }\n\n return response.json() as Promise<PullResponse>;\n };\n}\n\nasync function resolveHeaders(headers: HeaderConfig): Promise<Record<string, string>> {\n if (!headers) return {};\n if (typeof headers === \"function\") return headers();\n return headers;\n}\n\nfunction appendSince(url: string, since: number): string {\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}since=${encodeURIComponent(String(since))}`;\n}\n\nfunction getPushUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pushUrl\" in config && config.pushUrl) {\n return config.pushUrl;\n }\n\n if (\"push\" in config && typeof config.push === \"string\") {\n return config.push;\n }\n\n return undefined;\n}\n\nfunction getPullUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pullUrl\" in config && config.pullUrl) {\n return config.pullUrl;\n }\n\n if (\"pull\" in config && typeof config.pull === \"string\") {\n return config.pull;\n }\n\n return undefined;\n}\n\nexport class SyncPushError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event push failed: HTTP ${status}`);\n this.name = \"SyncPushError\";\n }\n}\n\nexport class SyncPullError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event pull failed: HTTP ${status}`);\n this.name = \"SyncPullError\";\n }\n}\n\nexport function isTransport(\n value: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): value is SyncTransport {\n return \"push\" in value && typeof value.push === \"function\";\n}\n","export type EventSourcedLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport type EventSourcedLogger = {\n debug: (message: string, data?: Record<string, unknown>) => void;\n info: (message: string, data?: Record<string, unknown>) => void;\n warn: (message: string, data?: Record<string, unknown>) => void;\n error: (message: string, data?: Record<string, unknown>) => void;\n};\n\nconst noopLogger: EventSourcedLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nconst LOG_PREFIX = \"[event-sourced]\";\n\nexport function createEventSourcedLogger(\n debug?: boolean | EventSourcedLogger,\n): EventSourcedLogger {\n if (debug === undefined || debug === false) {\n return noopLogger;\n }\n\n if (typeof debug === \"object\") {\n return debug;\n }\n\n return {\n debug: (message, data) => {\n if (data === undefined) {\n console.debug(LOG_PREFIX, message);\n return;\n }\n console.debug(LOG_PREFIX, message, data);\n },\n info: (message, data) => {\n if (data === undefined) {\n console.info(LOG_PREFIX, message);\n return;\n }\n console.info(LOG_PREFIX, message, data);\n },\n warn: (message, data) => {\n if (data === undefined) {\n console.warn(LOG_PREFIX, message);\n return;\n }\n console.warn(LOG_PREFIX, message, data);\n },\n error: (message, data) => {\n if (data === undefined) {\n console.error(LOG_PREFIX, message);\n return;\n }\n console.error(LOG_PREFIX, message, data);\n },\n };\n}\n","import type { Collection, IndexConstructor } from \"@tanstack/db\";\nimport { createSyncTransport, normalizePushResponse } from \"./sync\";\nimport type { NormalizedSyncTransport } from \"./sync\";\nimport type { EventSourcedLogger } from \"./utils/logger\";\nimport { createEventSourcedLogger } from \"./utils/logger\";\nimport { generateEventId } from \"./utils/uuid\";\nimport type {\n CollectionMap,\n EventSourcedDB,\n EventSourcedDBConfig,\n InboxEntry,\n MutationType,\n OutboundEvent,\n OutboxEntry,\n ServerEvent,\n SyncResult,\n ManualSyncResult,\n} from \"./types\";\n\nconst OUTBOX_ID = \"outbox\";\nconst INBOX_ID = \"inbox\";\nconst RESERVED_IDS = new Set<string>([OUTBOX_ID, INBOX_ID]);\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n indexes?: ReadonlyArray<{\n select: (row: never) => unknown;\n name?: string;\n indexType?: IndexConstructor<string | number>;\n }>;\n};\n\ntype ReplayMutation = {\n mutationId: string;\n type: MutationType;\n key: string | number;\n modified: Record<string, unknown>;\n original: Record<string, unknown>;\n changes: Record<string, unknown>;\n collection: AcceptMutationsCollection;\n};\n\ntype AcceptMutationsCollection = {\n id?: string;\n utils: {\n acceptMutations?: (transaction: { mutations: Array<ReplayMutation> }) => Promise<void> | void;\n };\n};\n\ntype MutationHookParams = {\n transaction: {\n mutations: ReadonlyArray<{\n mutationId: string;\n key: string | number;\n modified: Record<string, unknown>;\n original: Record<string, unknown>;\n }>;\n };\n};\n\ntype SeqCounter = { value: number };\n\ntype MetaCollectionFactory = Pick<\n EventSourcedDBConfig<Record<string, CollectionDefConstraint>>,\n \"createCollection\" | \"persistedCollectionOptions\" | \"persistence\"\n>;\n\nexport async function createEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: EventSourcedDBConfig<TDefs>): Promise<EventSourcedDB<TDefs>> {\n assertReservedNamesAvailable(config.collections);\n\n const log = createEventSourcedLogger(config.debug);\n\n const transport = createSyncTransport(config.sync);\n let syncEnabled = config.syncEnabled ?? true;\n\n log.info(\"creating event-sourced db\", {\n collectionIds: Object.keys(config.collections),\n hasTransport: transport !== null,\n syncEnabled,\n });\n\n const defaultSchemaVersion = config.schemaVersion ?? 1;\n const seq: SeqCounter = { value: 0 };\n\n const outbox = createMetaCollection<OutboxEntry>(\n config,\n OUTBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n );\n\n const inbox = createMetaCollection<InboxEntry>(\n config,\n INBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n );\n\n const userCollections = {} as CollectionMap<TDefs>;\n\n for (const collectionId of Object.keys(config.collections)) {\n const def = config.collections[collectionId]!;\n const getKey = def.getKey as (item: Record<string, unknown>) => string | number;\n\n const options = config.persistedCollectionOptions<Record<string, unknown>, string | number>({\n id: collectionId,\n getKey,\n persistence: config.persistence,\n schemaVersion: def.schemaVersion ?? defaultSchemaVersion,\n onInsert: createMutationHook(outbox, collectionId, \"insert\", seq, log),\n onUpdate: createMutationHook(outbox, collectionId, \"update\", seq, log),\n onDelete: createMutationHook(outbox, collectionId, \"delete\", seq, log),\n });\n\n const collection = config.createCollection(options);\n applyCollectionIndexes(collection, collectionId, def.indexes, log);\n const hasAcceptMutations = Boolean(\n (collection as AcceptMutationsCollection).utils?.acceptMutations,\n );\n\n log.debug(\"registered collection\", {\n collectionId,\n hasAcceptMutations,\n });\n\n (userCollections as Record<string, unknown>)[collectionId] = collection;\n }\n\n const collections = {\n ...(userCollections as CollectionMap<TDefs>),\n outbox,\n inbox,\n } as EventSourcedDB<TDefs>[\"collections\"];\n\n const replayTargets = collections as unknown as Record<string, AcceptMutationsCollection>;\n\n const subscriptions = [\n outbox.subscribeChanges(() => {}),\n inbox.subscribeChanges(() => {}),\n ];\n\n await outbox.preload();\n await inbox.preload();\n\n seq.value = nextLocalSeq(outbox);\n\n log.info(\"preloaded meta collections\", {\n outboxCount: outbox.state.size,\n inboxCount: inbox.state.size,\n nextLocalSeq: seq.value,\n });\n\n await replayInbox(inbox, replayTargets, log);\n\n async function sync(): Promise<SyncResult> {\n if (!syncEnabled) {\n log.debug(\"sync skipped: sync disabled\");\n return { pushed: 0, pulled: 0, errors: [] };\n }\n\n if (!transport) {\n log.warn(\"sync skipped: no transport configured\");\n return { pushed: 0, pulled: 0, errors: [] };\n }\n\n log.info(\"sync started\");\n\n await outbox.preload();\n await inbox.preload();\n\n const errors: Error[] = [];\n let pushed = 0;\n let pulled = 0;\n\n try {\n if (transport.push) {\n pushed = await pushOutbox(outbox, transport.push, log);\n } else {\n log.debug(\"push skipped: no push transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"push outbox failed\", { message: error.message });\n errors.push(error);\n }\n\n try {\n if (transport.pull) {\n pulled = await pullInbox(outbox, inbox, transport.pull, replayTargets, log);\n } else {\n log.debug(\"pull skipped: no pull transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"pull inbox failed\", { message: error.message });\n errors.push(error);\n }\n\n log.info(\"sync finished\", { pushed, pulled, errorCount: errors.length });\n\n return { pushed, pulled, errors };\n }\n\n async function manualSync(): Promise<ManualSyncResult> {\n log.info(\"manual sync started\");\n\n await outbox.preload();\n await inbox.preload();\n\n const errors: Error[] = [];\n let pushed = 0;\n let pulled = 0;\n let replayed = 0;\n\n if (syncEnabled && transport) {\n try {\n if (transport.push) {\n pushed = await pushOutbox(outbox, transport.push, log);\n } else {\n log.debug(\"manual sync push skipped: no push transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync push failed\", { message: error.message });\n errors.push(error);\n }\n\n try {\n if (transport.pull) {\n pulled = await pullInbox(outbox, inbox, transport.pull, replayTargets, log);\n } else {\n log.debug(\"manual sync pull skipped: no pull transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync pull failed\", { message: error.message });\n errors.push(error);\n }\n } else if (!syncEnabled) {\n log.debug(\"manual sync push/pull skipped: sync disabled\");\n } else {\n log.warn(\"manual sync: no transport configured, skipping push/pull\");\n }\n\n try {\n replayed = await replayInbox(inbox, replayTargets, log);\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync replay failed\", { message: error.message });\n errors.push(error);\n }\n\n log.info(\"manual sync finished\", { pushed, pulled, replayed, errorCount: errors.length });\n\n return { pushed, pulled, replayed, errors };\n }\n\n function getSyncEnabled(): boolean {\n return syncEnabled;\n }\n\n function setSyncEnabled(enabled: boolean): void {\n syncEnabled = enabled;\n log.debug(\"sync enabled updated\", { syncEnabled: enabled });\n }\n\n function dispose(): void {\n log.debug(\"disposing event-sourced db\");\n for (const subscription of subscriptions) {\n subscription.unsubscribe();\n }\n }\n\n return { collections, sync, manualSync, getSyncEnabled, setSyncEnabled, dispose };\n}\n\nfunction createMetaCollection<TEntry extends object>(\n config: MetaCollectionFactory,\n id: string,\n getKey: (entry: TEntry) => string,\n schemaVersion: number,\n): Collection<TEntry, string> {\n const options = config.persistedCollectionOptions<TEntry, string>({\n id,\n getKey,\n persistence: config.persistence,\n schemaVersion,\n });\n\n return config.createCollection(options);\n}\n\ntype IndexableCollection = {\n createIndex: (\n indexCallback: (row: Record<string, unknown>) => unknown,\n config?: {\n name?: string;\n indexType?: IndexConstructor<string | number>;\n },\n ) => unknown;\n};\n\nfunction applyCollectionIndexes(\n collection: Collection<Record<string, unknown>, string | number>,\n collectionId: string,\n indexes: CollectionDefConstraint[\"indexes\"],\n log: EventSourcedLogger,\n): void {\n if (!indexes?.length) {\n return;\n }\n\n const indexable = collection as IndexableCollection;\n\n for (const indexDef of indexes) {\n indexable.createIndex(indexDef.select as (row: Record<string, unknown>) => unknown, {\n name: indexDef.name,\n indexType: indexDef.indexType,\n });\n\n log.debug(\"registered collection index\", {\n collectionId,\n name: indexDef.name,\n });\n }\n}\n\nfunction createMutationHook(\n outbox: Collection<OutboxEntry, string>,\n collectionId: string,\n type: MutationType,\n seq: SeqCounter,\n log: EventSourcedLogger,\n) {\n return async (params: MutationHookParams): Promise<Record<string, unknown>> => {\n for (const mutation of params.transaction.mutations) {\n const payload = type === \"delete\" ? mutation.original : mutation.modified;\n\n const entry: OutboxEntry = {\n eventId: generateEventId(),\n collectionId,\n type,\n key: mutation.key,\n payload,\n timestamp: Date.now(),\n localSeq: seq.value++,\n globalSeq: null,\n sync: false,\n syncStatus: \"pending\",\n attemptCount: 0,\n lastAttemptAt: null,\n lastError: null,\n lastErrorCode: null,\n retryable: null,\n };\n\n await outbox.insert(entry).isPersisted.promise;\n\n log.debug(\"outbox entry created\", {\n eventId: entry.eventId,\n collectionId,\n type,\n key: entry.key,\n localSeq: entry.localSeq,\n });\n }\n\n return {};\n };\n}\n\nasync function pushOutbox(\n outbox: Collection<OutboxEntry, string>,\n push: NonNullable<NormalizedSyncTransport[\"push\"]>,\n log: EventSourcedLogger,\n): Promise<number> {\n const pending = [...outbox.state.values()]\n .filter((entry) => !entry.sync && entry.syncStatus !== \"failed\")\n .sort((a, b) => a.localSeq - b.localSeq);\n\n log.debug(\"push outbox\", { pendingCount: pending.length });\n\n if (pending.length === 0) return 0;\n\n const attemptAt = Date.now();\n\n for (const entry of pending) {\n await outbox\n .update(entry.eventId, (draft) => {\n draft.syncStatus = \"pending\";\n draft.attemptCount = (draft.attemptCount ?? 0) + 1;\n draft.lastAttemptAt = attemptAt;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n })\n .isPersisted.promise;\n }\n\n const outbound: OutboundEvent[] = pending.map((entry) => ({\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n timestamp: entry.timestamp,\n }));\n\n const response = normalizePushResponse(await push(outbound));\n\n log.info(\"push outbox confirmed\", {\n sent: outbound.length,\n confirmed: response.confirmed.length,\n failed: response.failed?.length ?? 0,\n });\n\n for (const confirmation of response.confirmed) {\n await outbox\n .update(confirmation.eventId, (draft) => {\n draft.sync = true;\n draft.syncStatus = \"synced\";\n draft.globalSeq = confirmation.globalSeq;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n })\n .isPersisted.promise;\n\n log.debug(\"outbox entry marked pushed\", {\n eventId: confirmation.eventId,\n globalSeq: confirmation.globalSeq,\n });\n }\n\n for (const failure of response.failed ?? []) {\n await outbox\n .update(failure.eventId, (draft) => {\n draft.sync = false;\n draft.syncStatus = \"failed\";\n draft.lastError = failure.message;\n draft.lastErrorCode = failure.code ?? null;\n draft.retryable = failure.retryable ?? null;\n })\n .isPersisted.promise;\n\n log.warn(\"outbox entry marked failed\", {\n eventId: failure.eventId,\n message: failure.message,\n code: failure.code,\n retryable: failure.retryable,\n });\n }\n\n return response.confirmed.length;\n}\n\nasync function pullInbox(\n outbox: Collection<OutboxEntry, string>,\n inbox: Collection<InboxEntry, string>,\n pull: NonNullable<NormalizedSyncTransport[\"pull\"]>,\n targets: Record<string, AcceptMutationsCollection>,\n log: EventSourcedLogger,\n): Promise<number> {\n let pulled = 0;\n let hasMore = true;\n\n while (hasMore) {\n const since = currentSince(inbox);\n log.debug(\"pull inbox page\", { since });\n\n const response = await pull(since);\n\n log.debug(\"pull inbox response\", {\n since,\n eventCount: response.events.length,\n hasMore: response.hasMore,\n cursor: response.cursor,\n });\n\n if (response.events.length === 0) break;\n\n const sorted = [...response.events].sort((a, b) => a.globalSeq - b.globalSeq);\n\n for (const event of sorted) {\n if (outbox.has(event.eventId)) {\n await markInboxEventSynced(inbox, event);\n\n log.debug(\"pull skipped: event originated locally\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n });\n continue;\n }\n\n const existing = inbox.get(event.eventId);\n if (existing?.sync) {\n log.debug(\"pull skipped: inbox already applied\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n });\n continue;\n }\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, false)).isPersisted.promise;\n log.debug(\"inbox entry inserted\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n });\n }\n\n const applied = await replayEvent(\n targets,\n event.collectionId,\n event.eventId,\n event.type,\n event.key,\n event.payload,\n log,\n );\n\n if (!applied) {\n return pulled;\n }\n\n await inbox\n .update(event.eventId, (draft) => {\n draft.sync = true;\n })\n .isPersisted.promise;\n\n log.info(\"pull replay applied\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n });\n\n pulled++;\n }\n\n hasMore = response.hasMore;\n }\n\n log.info(\"pull inbox finished\", { pulled });\n\n return pulled;\n}\n\nasync function replayInbox(\n inbox: Collection<InboxEntry, string>,\n targets: Record<string, AcceptMutationsCollection>,\n log: EventSourcedLogger,\n): Promise<number> {\n const pending = [...inbox.state.values()]\n .filter((entry) => !entry.sync)\n .sort((a, b) => a.globalSeq - b.globalSeq);\n\n log.info(\"pending inbox replay started\", { pendingCount: pending.length });\n\n let replayed = 0;\n\n for (const entry of pending) {\n const applied = await replayEvent(\n targets,\n entry.collectionId,\n entry.eventId,\n entry.type,\n entry.key,\n entry.payload,\n log,\n );\n\n if (!applied) {\n break;\n }\n\n await inbox\n .update(entry.eventId, (draft) => {\n draft.sync = true;\n })\n .isPersisted.promise;\n\n replayed++;\n\n log.info(\"pending inbox replay applied\", {\n eventId: entry.eventId,\n globalSeq: entry.globalSeq,\n collectionId: entry.collectionId,\n });\n }\n\n log.info(\"pending inbox replay finished\", { replayed });\n\n return replayed;\n}\n\nasync function replayEvent(\n targets: Record<string, AcceptMutationsCollection>,\n collectionId: string,\n eventId: string,\n type: MutationType,\n key: string | number,\n payload: Record<string, unknown>,\n log: EventSourcedLogger,\n): Promise<boolean> {\n if (RESERVED_IDS.has(collectionId)) {\n log.warn(\"replay skipped: reserved collection\", { eventId, collectionId, type, key });\n return false;\n }\n\n const target = targets[collectionId];\n if (!target) {\n log.warn(\"replay skipped: unknown collection\", {\n eventId,\n collectionId,\n type,\n key,\n knownCollections: Object.keys(targets).filter((id) => !RESERVED_IDS.has(id)),\n });\n return false;\n }\n\n if (!target.utils.acceptMutations) {\n log.warn(\"replay skipped: collection missing acceptMutations\", {\n eventId,\n collectionId,\n type,\n key,\n targetId: target.id,\n });\n return false;\n }\n\n log.debug(\"replay applying mutation\", { eventId, collectionId, type, key });\n\n try {\n await target.utils.acceptMutations({\n mutations: [\n {\n mutationId: eventId,\n type,\n key,\n modified: payload,\n original: payload,\n changes: payload,\n collection: target,\n },\n ],\n });\n\n log.info(\"replay mutation accepted\", { eventId, collectionId, type, key });\n } catch (err) {\n const error = toError(err);\n log.error(\"replay mutation failed\", {\n eventId,\n collectionId,\n type,\n key,\n message: error.message,\n });\n throw error;\n }\n\n return true;\n}\n\nfunction currentSince(inbox: Collection<InboxEntry, string>): number {\n let max = 0;\n\n for (const entry of inbox.state.values()) {\n if (entry.sync && entry.globalSeq > max) max = entry.globalSeq;\n }\n\n return max;\n}\n\nfunction nextLocalSeq(outbox: Collection<OutboxEntry, string>): number {\n let max = -1;\n\n for (const entry of outbox.state.values()) {\n if (entry.localSeq > max) max = entry.localSeq;\n }\n\n return max + 1;\n}\n\nasync function markInboxEventSynced(\n inbox: Collection<InboxEntry, string>,\n event: ServerEvent,\n): Promise<void> {\n const existing = inbox.get(event.eventId);\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, true)).isPersisted.promise;\n return;\n }\n\n if (!existing.sync || existing.globalSeq !== event.globalSeq) {\n await inbox\n .update(event.eventId, (draft) => {\n draft.globalSeq = event.globalSeq;\n draft.sync = true;\n })\n .isPersisted.promise;\n }\n}\n\nfunction toInboxEntry(event: ServerEvent, sync: boolean): InboxEntry {\n return {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n payload: event.payload,\n timestamp: event.timestamp,\n sync,\n };\n}\n\nfunction assertReservedNamesAvailable(collections: Record<string, unknown>): void {\n for (const id of Object.keys(collections)) {\n if (RESERVED_IDS.has(id)) {\n throw new Error(\n `Collection id \"${id}\" is reserved. \"outbox\" and \"inbox\" are built-in collections.`,\n );\n }\n }\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n","export type LazySingletonOptions = {\n guard?: () => void;\n notInitializedMessage?: string;\n};\n\nexport type LazySingleton<T extends object> = {\n ensure: () => Promise<T>;\n proxy: T;\n reset: () => void;\n};\n\nexport function createLazySingleton<T extends object>(\n factory: () => Promise<T>,\n options: LazySingletonOptions = {},\n): LazySingleton<T> {\n let instance: T | null = null;\n let initPromise: Promise<T> | null = null;\n\n const ensure = async (): Promise<T> => {\n options.guard?.();\n\n if (instance) {\n return instance;\n }\n\n if (!initPromise) {\n initPromise = factory()\n .then((resolved) => {\n instance = resolved;\n return resolved;\n })\n .catch((error: unknown) => {\n initPromise = null;\n throw error;\n });\n }\n\n return initPromise;\n };\n\n const message =\n options.notInitializedMessage ?? \"Instance is not initialized. Call ensure() first.\";\n\n const proxy = new Proxy({} as T, {\n get(_target, prop, receiver) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.get(instance, prop, receiver);\n },\n has(_target, prop) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.has(instance, prop);\n },\n getPrototypeOf() {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.getPrototypeOf(instance);\n },\n });\n\n const reset = (): void => {\n instance = null;\n initPromise = null;\n };\n\n return { ensure, proxy, reset };\n}\n"],"mappings":"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"}
|
package/dist/react-native.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as CreateCollectionFn, D as SyncUrlConfig, E as SyncTransport, O as EventSourcedLogger, S as SQLiteDriver, i as EventSourcedDB, j as PersistedCollectionOptionsFn, m as PersistedCollectionPersistence, w as SyncHandlersConfig } from "./types-BX8dtGOu.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/platforms/react-native.d.ts
|
|
4
4
|
type ReactNativePlatformDeps = {
|
|
@@ -19,6 +19,11 @@ declare function createReactNativePlatform(deps: ReactNativePlatformDeps, config
|
|
|
19
19
|
type CollectionDefConstraint = {
|
|
20
20
|
getKey: (state: never) => string | number;
|
|
21
21
|
schemaVersion?: number;
|
|
22
|
+
indexes?: ReadonlyArray<{
|
|
23
|
+
select: (row: never) => unknown;
|
|
24
|
+
name?: string;
|
|
25
|
+
indexType?: import("@tanstack/db").IndexConstructor<string | number>;
|
|
26
|
+
}>;
|
|
22
27
|
};
|
|
23
28
|
type ReactNativeEventSourcedModules = ReactNativePlatformDeps & {
|
|
24
29
|
database: SQLiteDriver;
|
|
@@ -29,6 +34,7 @@ type ReactNativeEventSourcedDBConfig<TDefs extends Record<string, CollectionDefC
|
|
|
29
34
|
collections: TDefs;
|
|
30
35
|
load: () => Promise<ReactNativeEventSourcedModules>;
|
|
31
36
|
sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;
|
|
37
|
+
syncEnabled?: boolean;
|
|
32
38
|
schemaVersion?: number;
|
|
33
39
|
debug?: boolean | EventSourcedLogger;
|
|
34
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-native.d.mts","names":[],"sources":["../src/platforms/react-native.ts","../src/platforms/react-native-event-sourced-db.ts"],"mappings":";;;KAEY,uBAAA;EACV,kCAAA,GAAqC,OAAA;IACnC,QAAA,EAAU,YAAA;EAAA,MACN,8BAA8B;AAAA;AAAA,KAG1B,yBAAA;EACV,QAAA,EAAU,YAAY;AAAA;AAAA,KAGZ,yBAAA;EACV,MAAA,EAAQ,YAAA;EACR,WAAA,EAAa,8BAA8B;AAAA;AAAA,iBAG7B,yBAAA,CACd,IAAA,EAAM,uBAAA,EACN,MAAA,EAAQ,yBAAA,GACP,yBAAA;;;KCNE,uBAAA;EACH,MAAA,GAAS,KAAA;EACT,aAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"react-native.d.mts","names":[],"sources":["../src/platforms/react-native.ts","../src/platforms/react-native-event-sourced-db.ts"],"mappings":";;;KAEY,uBAAA;EACV,kCAAA,GAAqC,OAAA;IACnC,QAAA,EAAU,YAAA;EAAA,MACN,8BAA8B;AAAA;AAAA,KAG1B,yBAAA;EACV,QAAA,EAAU,YAAY;AAAA;AAAA,KAGZ,yBAAA;EACV,MAAA,EAAQ,YAAA;EACR,WAAA,EAAa,8BAA8B;AAAA;AAAA,iBAG7B,yBAAA,CACd,IAAA,EAAM,uBAAA,EACN,MAAA,EAAQ,yBAAA,GACP,yBAAA;;;KCNE,uBAAA;EACH,MAAA,GAAS,KAAA;EACT,aAAA;EACA,OAAA,GAAU,aAAa;IACrB,MAAA,GAAS,GAAA;IACT,IAAA;IACA,SAAA,0BAAmC,gBAAA;EAAA;AAAA;AAAA,KAI3B,8BAAA,GAAiC,uBAAA;EAC3C,QAAA,EAAU,YAAA;EACV,gBAAA,EAAkB,kBAAA;EAClB,0BAAA,EAA4B,4BAAA;AAAA;AAAA,KAGlB,+BAAA,eAA8C,MAAA,SAAe,uBAAA;EAErE,WAAA,EAAa,KAAA;EACb,IAAA,QAAY,OAAA,CAAQ,8BAAA;EACpB,IAAA,GAAO,kBAAA,GAAqB,aAAA,GAAgB,aAAA;EAC5C,WAAA;EACA,aAAA;EACA,KAAA,aAAkB,kBAAA;AAAA;AAAA,KAGV,+BAAA,eACI,MAAA,SAAe,uBAAA;EAE7B,QAAA,QAAgB,OAAA,CAAQ,cAAA,CAAe,KAAA;EACvC,EAAA,EAAI,cAAA,CAAe,KAAA;EACnB,KAAA,QAAa,OAAA;AAAA;AAAA,iBAGC,+BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,+BAAA,CAAgC,KAAA,IAAS,+BAAA,CAAgC,KAAA"}
|
package/dist/react-native.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createEventSourcedDB, t as createLazySingleton } from "./lazy-singleton-
|
|
1
|
+
import { n as createEventSourcedDB, t as createLazySingleton } from "./lazy-singleton-BG02GHLj.mjs";
|
|
2
2
|
//#region src/platforms/react-native.ts
|
|
3
3
|
function createReactNativePlatform(deps, config) {
|
|
4
4
|
const persistence = deps.createReactNativeSQLitePersistence({ database: config.database });
|
|
@@ -19,6 +19,7 @@ function createReactNativeEventSourcedDB(config) {
|
|
|
19
19
|
persistedCollectionOptions: modules.persistedCollectionOptions,
|
|
20
20
|
collections: config.collections,
|
|
21
21
|
sync: config.sync,
|
|
22
|
+
syncEnabled: config.syncEnabled,
|
|
22
23
|
schemaVersion: config.schemaVersion,
|
|
23
24
|
debug: config.debug
|
|
24
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-native.mjs","names":[],"sources":["../src/platforms/react-native.ts","../src/platforms/react-native-event-sourced-db.ts"],"sourcesContent":["import type { PersistedCollectionPersistence, SQLiteDriver } from \"../types\";\n\nexport type ReactNativePlatformDeps = {\n createReactNativeSQLitePersistence: (options: {\n database: SQLiteDriver;\n }) => PersistedCollectionPersistence;\n};\n\nexport type ReactNativePlatformConfig = {\n database: SQLiteDriver;\n};\n\nexport type ReactNativePlatformResult = {\n driver: SQLiteDriver;\n persistence: PersistedCollectionPersistence;\n};\n\nexport function createReactNativePlatform(\n deps: ReactNativePlatformDeps,\n config: ReactNativePlatformConfig,\n): ReactNativePlatformResult {\n const persistence = deps.createReactNativeSQLitePersistence({\n database: config.database,\n });\n\n return {\n driver: config.database,\n persistence,\n };\n}\n","import { createEventSourcedDB } from \"../create-event-sourced-db\";\nimport { createLazySingleton } from \"../lazy-singleton\";\nimport type { CreateCollectionFn, PersistedCollectionOptionsFn } from \"../persisted-collection\";\nimport type {\n EventSourcedDB,\n EventSourcedLogger,\n SQLiteDriver,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"../types\";\nimport { createReactNativePlatform } from \"./react-native\";\nimport type { ReactNativePlatformDeps } from \"./react-native\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n};\n\nexport type ReactNativeEventSourcedModules = ReactNativePlatformDeps & {\n database: SQLiteDriver;\n createCollection: CreateCollectionFn;\n persistedCollectionOptions: PersistedCollectionOptionsFn;\n};\n\nexport type ReactNativeEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> =\n {\n collections: TDefs;\n load: () => Promise<ReactNativeEventSourcedModules>;\n sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;\n schemaVersion?: number;\n debug?: boolean | EventSourcedLogger;\n };\n\nexport type ReactNativeEventSourcedDBHandle<\n TDefs extends Record<string, CollectionDefConstraint>,\n> = {\n ensureDb: () => Promise<EventSourcedDB<TDefs>>;\n db: EventSourcedDB<TDefs>;\n close: () => Promise<void>;\n};\n\nexport function createReactNativeEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: ReactNativeEventSourcedDBConfig<TDefs>): ReactNativeEventSourcedDBHandle<TDefs> {\n let activeDb: EventSourcedDB<TDefs> | null = null;\n\n const factory = async (): Promise<EventSourcedDB<TDefs>> => {\n const modules = await config.load();\n\n const platform = createReactNativePlatform(\n { createReactNativeSQLitePersistence: modules.createReactNativeSQLitePersistence },\n { database: modules.database },\n );\n\n const database = await createEventSourcedDB<TDefs>({\n persistence: platform.persistence,\n createCollection: modules.createCollection,\n persistedCollectionOptions: modules.persistedCollectionOptions,\n collections: config.collections,\n sync: config.sync,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n });\n\n activeDb = database;\n return database;\n };\n\n const singleton = createLazySingleton<EventSourcedDB<TDefs>>(factory, {\n notInitializedMessage:\n \"Event-sourced DB is not initialized. Call ensureDb() before using db.\",\n });\n\n const close = async (): Promise<void> => {\n if (activeDb) {\n activeDb.dispose();\n activeDb = null;\n }\n singleton.reset();\n };\n\n return {\n ensureDb: singleton.ensure,\n db: singleton.proxy,\n close,\n };\n}\n"],"mappings":";;AAiBA,SAAgB,0BACd,MACA,QAC2B;CAC3B,MAAM,cAAc,KAAK,mCAAmC,EAC1D,UAAU,OAAO,SACnB,CAAC;CAED,OAAO;EACL,QAAQ,OAAO;EACf;CACF;AACF;;;
|
|
1
|
+
{"version":3,"file":"react-native.mjs","names":[],"sources":["../src/platforms/react-native.ts","../src/platforms/react-native-event-sourced-db.ts"],"sourcesContent":["import type { PersistedCollectionPersistence, SQLiteDriver } from \"../types\";\n\nexport type ReactNativePlatformDeps = {\n createReactNativeSQLitePersistence: (options: {\n database: SQLiteDriver;\n }) => PersistedCollectionPersistence;\n};\n\nexport type ReactNativePlatformConfig = {\n database: SQLiteDriver;\n};\n\nexport type ReactNativePlatformResult = {\n driver: SQLiteDriver;\n persistence: PersistedCollectionPersistence;\n};\n\nexport function createReactNativePlatform(\n deps: ReactNativePlatformDeps,\n config: ReactNativePlatformConfig,\n): ReactNativePlatformResult {\n const persistence = deps.createReactNativeSQLitePersistence({\n database: config.database,\n });\n\n return {\n driver: config.database,\n persistence,\n };\n}\n","import { createEventSourcedDB } from \"../create-event-sourced-db\";\nimport { createLazySingleton } from \"../lazy-singleton\";\nimport type { CreateCollectionFn, PersistedCollectionOptionsFn } from \"../persisted-collection\";\nimport type {\n EventSourcedDB,\n EventSourcedLogger,\n SQLiteDriver,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"../types\";\nimport { createReactNativePlatform } from \"./react-native\";\nimport type { ReactNativePlatformDeps } from \"./react-native\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n indexes?: ReadonlyArray<{\n select: (row: never) => unknown;\n name?: string;\n indexType?: import(\"@tanstack/db\").IndexConstructor<string | number>;\n }>;\n};\n\nexport type ReactNativeEventSourcedModules = ReactNativePlatformDeps & {\n database: SQLiteDriver;\n createCollection: CreateCollectionFn;\n persistedCollectionOptions: PersistedCollectionOptionsFn;\n};\n\nexport type ReactNativeEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>> =\n {\n collections: TDefs;\n load: () => Promise<ReactNativeEventSourcedModules>;\n sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;\n syncEnabled?: boolean;\n schemaVersion?: number;\n debug?: boolean | EventSourcedLogger;\n };\n\nexport type ReactNativeEventSourcedDBHandle<\n TDefs extends Record<string, CollectionDefConstraint>,\n> = {\n ensureDb: () => Promise<EventSourcedDB<TDefs>>;\n db: EventSourcedDB<TDefs>;\n close: () => Promise<void>;\n};\n\nexport function createReactNativeEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: ReactNativeEventSourcedDBConfig<TDefs>): ReactNativeEventSourcedDBHandle<TDefs> {\n let activeDb: EventSourcedDB<TDefs> | null = null;\n\n const factory = async (): Promise<EventSourcedDB<TDefs>> => {\n const modules = await config.load();\n\n const platform = createReactNativePlatform(\n { createReactNativeSQLitePersistence: modules.createReactNativeSQLitePersistence },\n { database: modules.database },\n );\n\n const database = await createEventSourcedDB<TDefs>({\n persistence: platform.persistence,\n createCollection: modules.createCollection,\n persistedCollectionOptions: modules.persistedCollectionOptions,\n collections: config.collections,\n sync: config.sync,\n syncEnabled: config.syncEnabled,\n schemaVersion: config.schemaVersion,\n debug: config.debug,\n });\n\n activeDb = database;\n return database;\n };\n\n const singleton = createLazySingleton<EventSourcedDB<TDefs>>(factory, {\n notInitializedMessage:\n \"Event-sourced DB is not initialized. Call ensureDb() before using db.\",\n });\n\n const close = async (): Promise<void> => {\n if (activeDb) {\n activeDb.dispose();\n activeDb = null;\n }\n singleton.reset();\n };\n\n return {\n ensureDb: singleton.ensure,\n db: singleton.proxy,\n close,\n };\n}\n"],"mappings":";;AAiBA,SAAgB,0BACd,MACA,QAC2B;CAC3B,MAAM,cAAc,KAAK,mCAAmC,EAC1D,UAAU,OAAO,SACnB,CAAC;CAED,OAAO;EACL,QAAQ,OAAO;EACf;CACF;AACF;;;ACmBA,SAAgB,gCAEd,QAAwF;CACxF,IAAI,WAAyC;CAE7C,MAAM,UAAU,YAA4C;EAC1D,MAAM,UAAU,MAAM,OAAO,KAAK;EAOlC,MAAM,WAAW,MAAM,qBAA4B;GACjD,aANe,0BACf,EAAE,oCAAoC,QAAQ,mCAAmC,GACjF,EAAE,UAAU,QAAQ,SAAS,CAIT,CAAC,CAAC;GACtB,kBAAkB,QAAQ;GAC1B,4BAA4B,QAAQ;GACpC,aAAa,OAAO;GACpB,MAAM,OAAO;GACb,aAAa,OAAO;GACpB,eAAe,OAAO;GACtB,OAAO,OAAO;EAChB,CAAC;EAED,WAAW;EACX,OAAO;CACT;CAEA,MAAM,YAAY,oBAA2C,SAAS,EACpE,uBACE,wEACJ,CAAC;CAED,MAAM,QAAQ,YAA2B;EACvC,IAAI,UAAU;GACZ,SAAS,QAAQ;GACjB,WAAW;EACb;EACA,UAAU,MAAM;CAClB;CAEA,OAAO;EACL,UAAU,UAAU;EACpB,IAAI,UAAU;EACd;CACF;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Collection, CollectionConfig, UtilsRecord } from "@tanstack/db";
|
|
1
|
+
import { Collection, CollectionConfig, IndexConstructor, UtilsRecord } from "@tanstack/db";
|
|
2
2
|
import { PersistedCollectionPersistence as PersistedCollectionPersistence$1, PersistedCollectionUtils, SQLiteDriver, persistedCollectionOptions } from "@tanstack/db-sqlite-persistence-core";
|
|
3
3
|
|
|
4
4
|
//#region src/persisted-collection.d.ts
|
|
@@ -101,9 +101,15 @@ type SyncHandlersConfig = {
|
|
|
101
101
|
pullUrl?: string;
|
|
102
102
|
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
103
103
|
};
|
|
104
|
+
type CollectionIndexDef<TState = object, TKey extends string | number = string> = {
|
|
105
|
+
select: (row: TState) => unknown;
|
|
106
|
+
name?: string;
|
|
107
|
+
indexType?: IndexConstructor<TKey>;
|
|
108
|
+
};
|
|
104
109
|
type CollectionDef<TState = object, TKey extends string | number = string> = {
|
|
105
110
|
getKey: (state: TState) => TKey;
|
|
106
111
|
schemaVersion?: number;
|
|
112
|
+
indexes?: ReadonlyArray<CollectionIndexDef<TState, TKey>>;
|
|
107
113
|
};
|
|
108
114
|
type InferState<T> = T extends {
|
|
109
115
|
getKey: (state: infer S) => string | number;
|
|
@@ -114,6 +120,11 @@ type InferKey<T> = T extends {
|
|
|
114
120
|
type CollectionDefConstraint = {
|
|
115
121
|
getKey: (state: never) => string | number;
|
|
116
122
|
schemaVersion?: number;
|
|
123
|
+
indexes?: ReadonlyArray<{
|
|
124
|
+
select: (row: never) => unknown;
|
|
125
|
+
name?: string;
|
|
126
|
+
indexType?: IndexConstructor<string | number>;
|
|
127
|
+
}>;
|
|
117
128
|
};
|
|
118
129
|
type CollectionMap<TDefs extends Record<string, CollectionDefConstraint>> = { [K in keyof TDefs]: Collection<InferState<TDefs[K]>, InferKey<TDefs[K]>> };
|
|
119
130
|
type ReservedCollections = {
|
|
@@ -124,6 +135,7 @@ type EventSourcedDBConfig<TDefs extends Record<string, CollectionDefConstraint>>
|
|
|
124
135
|
persistence: PersistedCollectionPersistence$1;
|
|
125
136
|
collections: TDefs;
|
|
126
137
|
sync?: SyncHandlersConfig | SyncUrlConfig | SyncTransport;
|
|
138
|
+
syncEnabled?: boolean;
|
|
127
139
|
schemaVersion?: number;
|
|
128
140
|
debug?: boolean | EventSourcedLogger;
|
|
129
141
|
createCollection: CreateCollectionFn;
|
|
@@ -133,6 +145,8 @@ type EventSourcedDB<TDefs extends Record<string, CollectionDefConstraint>> = {
|
|
|
133
145
|
collections: CollectionMap<TDefs> & ReservedCollections;
|
|
134
146
|
sync: () => Promise<SyncResult>;
|
|
135
147
|
manualSync: () => Promise<ManualSyncResult>;
|
|
148
|
+
getSyncEnabled: () => boolean;
|
|
149
|
+
setSyncEnabled: (enabled: boolean) => void;
|
|
136
150
|
dispose: () => void;
|
|
137
151
|
};
|
|
138
152
|
type SyncResult = {
|
|
@@ -144,5 +158,5 @@ type ManualSyncResult = SyncResult & {
|
|
|
144
158
|
replayed: number;
|
|
145
159
|
};
|
|
146
160
|
//#endregion
|
|
147
|
-
export {
|
|
148
|
-
//# sourceMappingURL=types-
|
|
161
|
+
export { CreateCollectionFn as A, ServerEvent as C, SyncUrlConfig as D, SyncTransport as E, EventSourcedLogger as O, SQLiteDriver as S, SyncResult as T, PushConfirmation as _, EventSourcedDBConfig as a, PushResponse as b, InferState as c, OutboundEvent as d, OutboxEntry as f, PullResponse as g, PullEventsFn as h, EventSourcedDB as i, PersistedCollectionOptionsFn as j, createEventSourcedLogger as k, ManualSyncResult as l, PersistedCollectionPersistence$1 as m, CollectionIndexDef as n, InboxEntry as o, OutboxSyncStatus as p, CollectionMap as r, InferKey as s, CollectionDef as t, MutationType as u, PushEventsFn as v, SyncHandlersConfig as w, ReservedCollections as x, PushFailure as y };
|
|
162
|
+
//# sourceMappingURL=types-BX8dtGOu.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-
|
|
1
|
+
{"version":3,"file":"types-BX8dtGOu.d.mts","names":[],"sources":["../src/persisted-collection.ts","../src/utils/logger.ts","../src/types.ts"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-singleton-DFrAsYTV.mjs","names":[],"sources":["../src/sync.ts","../src/utils/logger.ts","../src/create-event-sourced-db.ts","../src/lazy-singleton.ts"],"sourcesContent":["import type {\n OutboundEvent,\n PullEventsFn,\n PullResponse,\n PushConfirmation,\n PushEventsFn,\n PushResponse,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"./types\";\n\nexport type NormalizedSyncTransport = {\n push?: PushEventsFn;\n pull?: (since: number) => Promise<PullResponse>;\n};\n\ntype HeaderConfig = SyncHandlersConfig[\"headers\"];\n\nexport function createHttpTransport(config: SyncUrlConfig): SyncTransport {\n return {\n push: createHttpPushEvents(config.push, config.headers),\n pull: createHttpPullEvents(config.pull, config.headers),\n };\n}\n\nexport function createSyncTransport(\n config?: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): NormalizedSyncTransport | null {\n if (!config) {\n return null;\n }\n\n if (isTransport(config)) {\n return {\n push: config.push,\n pull: config.pull,\n };\n }\n\n const pushUrl = getPushUrl(config);\n const pullUrl = getPullUrl(config);\n\n const push = \"pushEvents\" in config && config.pushEvents\n ? config.pushEvents\n : pushUrl\n ? createHttpPushEvents(pushUrl, config.headers)\n : undefined;\n\n const pullEvents = \"pullEvents\" in config ? config.pullEvents : undefined;\n const pull = pullEvents\n ? createPullFromHandler(pullEvents)\n : pullUrl\n ? createHttpPullEvents(pullUrl, config.headers)\n : undefined;\n\n if (!push && !pull) {\n return null;\n }\n\n return { push, pull };\n}\n\nexport function normalizePushResponse(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): PushResponse {\n if (isConfirmationArray(response)) {\n return { confirmed: response };\n }\n\n return {\n confirmed: response.confirmed,\n failed: response.failed,\n };\n}\n\nfunction isConfirmationArray(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): response is ReadonlyArray<PushConfirmation> {\n return Array.isArray(response);\n}\n\nfunction createHttpPushEvents(url: string, headers: HeaderConfig): PushEventsFn {\n return async (events: ReadonlyArray<OutboundEvent>): Promise<PushResponse> => {\n if (events.length === 0) return { confirmed: [] };\n\n const resolvedHeaders = await resolveHeaders(headers);\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...resolvedHeaders },\n body: JSON.stringify(events),\n });\n\n if (!response.ok) {\n throw new SyncPushError(response.status, await response.text());\n }\n\n return response.json() as Promise<PushResponse>;\n };\n}\n\nfunction createPullFromHandler(pullEvents: PullEventsFn): (since: number) => Promise<PullResponse> {\n return (since: number) => pullEvents({ since });\n}\n\nfunction createHttpPullEvents(\n url: string,\n headers: HeaderConfig,\n): (since: number) => Promise<PullResponse> {\n return async (since: number): Promise<PullResponse> => {\n const resolvedHeaders = await resolveHeaders(headers);\n const pullUrl = appendSince(url, since);\n\n const response = await fetch(pullUrl, {\n headers: { Accept: \"application/json\", ...resolvedHeaders },\n });\n\n if (!response.ok) {\n throw new SyncPullError(response.status, await response.text());\n }\n\n return response.json() as Promise<PullResponse>;\n };\n}\n\nasync function resolveHeaders(headers: HeaderConfig): Promise<Record<string, string>> {\n if (!headers) return {};\n if (typeof headers === \"function\") return headers();\n return headers;\n}\n\nfunction appendSince(url: string, since: number): string {\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}since=${encodeURIComponent(String(since))}`;\n}\n\nfunction getPushUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pushUrl\" in config && config.pushUrl) {\n return config.pushUrl;\n }\n\n if (\"push\" in config && typeof config.push === \"string\") {\n return config.push;\n }\n\n return undefined;\n}\n\nfunction getPullUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pullUrl\" in config && config.pullUrl) {\n return config.pullUrl;\n }\n\n if (\"pull\" in config && typeof config.pull === \"string\") {\n return config.pull;\n }\n\n return undefined;\n}\n\nexport class SyncPushError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event push failed: HTTP ${status}`);\n this.name = \"SyncPushError\";\n }\n}\n\nexport class SyncPullError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event pull failed: HTTP ${status}`);\n this.name = \"SyncPullError\";\n }\n}\n\nexport function isTransport(\n value: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): value is SyncTransport {\n return \"push\" in value && typeof value.push === \"function\";\n}\n","export type EventSourcedLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport type EventSourcedLogger = {\n debug: (message: string, data?: Record<string, unknown>) => void;\n info: (message: string, data?: Record<string, unknown>) => void;\n warn: (message: string, data?: Record<string, unknown>) => void;\n error: (message: string, data?: Record<string, unknown>) => void;\n};\n\nconst noopLogger: EventSourcedLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nconst LOG_PREFIX = \"[event-sourced]\";\n\nexport function createEventSourcedLogger(\n debug?: boolean | EventSourcedLogger,\n): EventSourcedLogger {\n if (debug === undefined || debug === false) {\n return noopLogger;\n }\n\n if (typeof debug === \"object\") {\n return debug;\n }\n\n return {\n debug: (message, data) => {\n if (data === undefined) {\n console.debug(LOG_PREFIX, message);\n return;\n }\n console.debug(LOG_PREFIX, message, data);\n },\n info: (message, data) => {\n if (data === undefined) {\n console.info(LOG_PREFIX, message);\n return;\n }\n console.info(LOG_PREFIX, message, data);\n },\n warn: (message, data) => {\n if (data === undefined) {\n console.warn(LOG_PREFIX, message);\n return;\n }\n console.warn(LOG_PREFIX, message, data);\n },\n error: (message, data) => {\n if (data === undefined) {\n console.error(LOG_PREFIX, message);\n return;\n }\n console.error(LOG_PREFIX, message, data);\n },\n };\n}\n","import type { Collection } from \"@tanstack/db\";\nimport { createSyncTransport, normalizePushResponse } from \"./sync\";\nimport type { NormalizedSyncTransport } from \"./sync\";\nimport type { EventSourcedLogger } from \"./utils/logger\";\nimport { createEventSourcedLogger } from \"./utils/logger\";\nimport { generateEventId } from \"./utils/uuid\";\nimport type {\n CollectionMap,\n EventSourcedDB,\n EventSourcedDBConfig,\n InboxEntry,\n MutationType,\n OutboundEvent,\n OutboxEntry,\n ServerEvent,\n SyncResult,\n ManualSyncResult,\n} from \"./types\";\n\nconst OUTBOX_ID = \"outbox\";\nconst INBOX_ID = \"inbox\";\nconst RESERVED_IDS = new Set<string>([OUTBOX_ID, INBOX_ID]);\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n};\n\ntype ReplayMutation = {\n mutationId: string;\n type: MutationType;\n key: string | number;\n modified: Record<string, unknown>;\n original: Record<string, unknown>;\n changes: Record<string, unknown>;\n collection: AcceptMutationsCollection;\n};\n\ntype AcceptMutationsCollection = {\n id?: string;\n utils: {\n acceptMutations?: (transaction: { mutations: Array<ReplayMutation> }) => Promise<void> | void;\n };\n};\n\ntype MutationHookParams = {\n transaction: {\n mutations: ReadonlyArray<{\n mutationId: string;\n key: string | number;\n modified: Record<string, unknown>;\n original: Record<string, unknown>;\n }>;\n };\n};\n\ntype SeqCounter = { value: number };\n\ntype MetaCollectionFactory = Pick<\n EventSourcedDBConfig<Record<string, CollectionDefConstraint>>,\n \"createCollection\" | \"persistedCollectionOptions\" | \"persistence\"\n>;\n\nexport async function createEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: EventSourcedDBConfig<TDefs>): Promise<EventSourcedDB<TDefs>> {\n assertReservedNamesAvailable(config.collections);\n\n const log = createEventSourcedLogger(config.debug);\n\n const transport = createSyncTransport(config.sync);\n\n log.info(\"creating event-sourced db\", {\n collectionIds: Object.keys(config.collections),\n hasTransport: transport !== null,\n });\n\n const defaultSchemaVersion = config.schemaVersion ?? 1;\n const seq: SeqCounter = { value: 0 };\n\n const outbox = createMetaCollection<OutboxEntry>(\n config,\n OUTBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n );\n\n const inbox = createMetaCollection<InboxEntry>(\n config,\n INBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n );\n\n const userCollections = {} as CollectionMap<TDefs>;\n\n for (const collectionId of Object.keys(config.collections)) {\n const def = config.collections[collectionId]!;\n const getKey = def.getKey as (item: Record<string, unknown>) => string | number;\n\n const options = config.persistedCollectionOptions<Record<string, unknown>, string | number>({\n id: collectionId,\n getKey,\n persistence: config.persistence,\n schemaVersion: def.schemaVersion ?? defaultSchemaVersion,\n onInsert: createMutationHook(outbox, collectionId, \"insert\", seq, log),\n onUpdate: createMutationHook(outbox, collectionId, \"update\", seq, log),\n onDelete: createMutationHook(outbox, collectionId, \"delete\", seq, log),\n });\n\n const collection = config.createCollection(options);\n const hasAcceptMutations = Boolean(\n (collection as AcceptMutationsCollection).utils?.acceptMutations,\n );\n\n log.debug(\"registered collection\", {\n collectionId,\n hasAcceptMutations,\n });\n\n (userCollections as Record<string, unknown>)[collectionId] = collection;\n }\n\n const collections = {\n ...(userCollections as CollectionMap<TDefs>),\n outbox,\n inbox,\n } as EventSourcedDB<TDefs>[\"collections\"];\n\n const replayTargets = collections as unknown as Record<string, AcceptMutationsCollection>;\n\n const subscriptions = [\n outbox.subscribeChanges(() => {}),\n inbox.subscribeChanges(() => {}),\n ];\n\n await outbox.preload();\n await inbox.preload();\n\n seq.value = nextLocalSeq(outbox);\n\n log.info(\"preloaded meta collections\", {\n outboxCount: outbox.state.size,\n inboxCount: inbox.state.size,\n nextLocalSeq: seq.value,\n });\n\n await replayInbox(inbox, replayTargets, log);\n\n async function sync(): Promise<SyncResult> {\n if (!transport) {\n log.warn(\"sync skipped: no transport configured\");\n return { pushed: 0, pulled: 0, errors: [] };\n }\n\n log.info(\"sync started\");\n\n await outbox.preload();\n await inbox.preload();\n\n const errors: Error[] = [];\n let pushed = 0;\n let pulled = 0;\n\n try {\n if (transport.push) {\n pushed = await pushOutbox(outbox, transport.push, log);\n } else {\n log.debug(\"push skipped: no push transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"push outbox failed\", { message: error.message });\n errors.push(error);\n }\n\n try {\n if (transport.pull) {\n pulled = await pullInbox(outbox, inbox, transport.pull, replayTargets, log);\n } else {\n log.debug(\"pull skipped: no pull transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"pull inbox failed\", { message: error.message });\n errors.push(error);\n }\n\n log.info(\"sync finished\", { pushed, pulled, errorCount: errors.length });\n\n return { pushed, pulled, errors };\n }\n\n async function manualSync(): Promise<ManualSyncResult> {\n log.info(\"manual sync started\");\n\n await outbox.preload();\n await inbox.preload();\n\n const errors: Error[] = [];\n let pushed = 0;\n let pulled = 0;\n let replayed = 0;\n\n if (transport) {\n try {\n if (transport.push) {\n pushed = await pushOutbox(outbox, transport.push, log);\n } else {\n log.debug(\"manual sync push skipped: no push transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync push failed\", { message: error.message });\n errors.push(error);\n }\n\n try {\n if (transport.pull) {\n pulled = await pullInbox(outbox, inbox, transport.pull, replayTargets, log);\n } else {\n log.debug(\"manual sync pull skipped: no pull transport configured\");\n }\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync pull failed\", { message: error.message });\n errors.push(error);\n }\n } else {\n log.warn(\"manual sync: no transport configured, skipping push/pull\");\n }\n\n try {\n replayed = await replayInbox(inbox, replayTargets, log);\n } catch (err) {\n const error = toError(err);\n log.error(\"manual sync replay failed\", { message: error.message });\n errors.push(error);\n }\n\n log.info(\"manual sync finished\", { pushed, pulled, replayed, errorCount: errors.length });\n\n return { pushed, pulled, replayed, errors };\n }\n\n function dispose(): void {\n log.debug(\"disposing event-sourced db\");\n for (const subscription of subscriptions) {\n subscription.unsubscribe();\n }\n }\n\n return { collections, sync, manualSync, dispose };\n}\n\nfunction createMetaCollection<TEntry extends object>(\n config: MetaCollectionFactory,\n id: string,\n getKey: (entry: TEntry) => string,\n schemaVersion: number,\n): Collection<TEntry, string> {\n const options = config.persistedCollectionOptions<TEntry, string>({\n id,\n getKey,\n persistence: config.persistence,\n schemaVersion,\n });\n\n return config.createCollection(options);\n}\n\nfunction createMutationHook(\n outbox: Collection<OutboxEntry, string>,\n collectionId: string,\n type: MutationType,\n seq: SeqCounter,\n log: EventSourcedLogger,\n) {\n return async (params: MutationHookParams): Promise<Record<string, unknown>> => {\n for (const mutation of params.transaction.mutations) {\n const payload = type === \"delete\" ? mutation.original : mutation.modified;\n\n const entry: OutboxEntry = {\n eventId: generateEventId(),\n collectionId,\n type,\n key: mutation.key,\n payload,\n timestamp: Date.now(),\n localSeq: seq.value++,\n globalSeq: null,\n sync: false,\n syncStatus: \"pending\",\n attemptCount: 0,\n lastAttemptAt: null,\n lastError: null,\n lastErrorCode: null,\n retryable: null,\n };\n\n await outbox.insert(entry).isPersisted.promise;\n\n log.debug(\"outbox entry created\", {\n eventId: entry.eventId,\n collectionId,\n type,\n key: entry.key,\n localSeq: entry.localSeq,\n });\n }\n\n return {};\n };\n}\n\nasync function pushOutbox(\n outbox: Collection<OutboxEntry, string>,\n push: NonNullable<NormalizedSyncTransport[\"push\"]>,\n log: EventSourcedLogger,\n): Promise<number> {\n const pending = [...outbox.state.values()]\n .filter((entry) => !entry.sync && entry.syncStatus !== \"failed\")\n .sort((a, b) => a.localSeq - b.localSeq);\n\n log.debug(\"push outbox\", { pendingCount: pending.length });\n\n if (pending.length === 0) return 0;\n\n const attemptAt = Date.now();\n\n for (const entry of pending) {\n await outbox\n .update(entry.eventId, (draft) => {\n draft.syncStatus = \"pending\";\n draft.attemptCount = (draft.attemptCount ?? 0) + 1;\n draft.lastAttemptAt = attemptAt;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n })\n .isPersisted.promise;\n }\n\n const outbound: OutboundEvent[] = pending.map((entry) => ({\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n timestamp: entry.timestamp,\n }));\n\n const response = normalizePushResponse(await push(outbound));\n\n log.info(\"push outbox confirmed\", {\n sent: outbound.length,\n confirmed: response.confirmed.length,\n failed: response.failed?.length ?? 0,\n });\n\n for (const confirmation of response.confirmed) {\n await outbox\n .update(confirmation.eventId, (draft) => {\n draft.sync = true;\n draft.syncStatus = \"synced\";\n draft.globalSeq = confirmation.globalSeq;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n })\n .isPersisted.promise;\n\n log.debug(\"outbox entry marked pushed\", {\n eventId: confirmation.eventId,\n globalSeq: confirmation.globalSeq,\n });\n }\n\n for (const failure of response.failed ?? []) {\n await outbox\n .update(failure.eventId, (draft) => {\n draft.sync = false;\n draft.syncStatus = \"failed\";\n draft.lastError = failure.message;\n draft.lastErrorCode = failure.code ?? null;\n draft.retryable = failure.retryable ?? null;\n })\n .isPersisted.promise;\n\n log.warn(\"outbox entry marked failed\", {\n eventId: failure.eventId,\n message: failure.message,\n code: failure.code,\n retryable: failure.retryable,\n });\n }\n\n return response.confirmed.length;\n}\n\nasync function pullInbox(\n outbox: Collection<OutboxEntry, string>,\n inbox: Collection<InboxEntry, string>,\n pull: NonNullable<NormalizedSyncTransport[\"pull\"]>,\n targets: Record<string, AcceptMutationsCollection>,\n log: EventSourcedLogger,\n): Promise<number> {\n let pulled = 0;\n let hasMore = true;\n\n while (hasMore) {\n const since = currentSince(inbox);\n log.debug(\"pull inbox page\", { since });\n\n const response = await pull(since);\n\n log.debug(\"pull inbox response\", {\n since,\n eventCount: response.events.length,\n hasMore: response.hasMore,\n cursor: response.cursor,\n });\n\n if (response.events.length === 0) break;\n\n const sorted = [...response.events].sort((a, b) => a.globalSeq - b.globalSeq);\n\n for (const event of sorted) {\n if (outbox.has(event.eventId)) {\n await markInboxEventSynced(inbox, event);\n\n log.debug(\"pull skipped: event originated locally\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n });\n continue;\n }\n\n const existing = inbox.get(event.eventId);\n if (existing?.sync) {\n log.debug(\"pull skipped: inbox already applied\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n });\n continue;\n }\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, false)).isPersisted.promise;\n log.debug(\"inbox entry inserted\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n });\n }\n\n const applied = await replayEvent(\n targets,\n event.collectionId,\n event.eventId,\n event.type,\n event.key,\n event.payload,\n log,\n );\n\n if (!applied) {\n return pulled;\n }\n\n await inbox\n .update(event.eventId, (draft) => {\n draft.sync = true;\n })\n .isPersisted.promise;\n\n log.info(\"pull replay applied\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n });\n\n pulled++;\n }\n\n hasMore = response.hasMore;\n }\n\n log.info(\"pull inbox finished\", { pulled });\n\n return pulled;\n}\n\nasync function replayInbox(\n inbox: Collection<InboxEntry, string>,\n targets: Record<string, AcceptMutationsCollection>,\n log: EventSourcedLogger,\n): Promise<number> {\n const pending = [...inbox.state.values()]\n .filter((entry) => !entry.sync)\n .sort((a, b) => a.globalSeq - b.globalSeq);\n\n log.info(\"pending inbox replay started\", { pendingCount: pending.length });\n\n let replayed = 0;\n\n for (const entry of pending) {\n const applied = await replayEvent(\n targets,\n entry.collectionId,\n entry.eventId,\n entry.type,\n entry.key,\n entry.payload,\n log,\n );\n\n if (!applied) {\n break;\n }\n\n await inbox\n .update(entry.eventId, (draft) => {\n draft.sync = true;\n })\n .isPersisted.promise;\n\n replayed++;\n\n log.info(\"pending inbox replay applied\", {\n eventId: entry.eventId,\n globalSeq: entry.globalSeq,\n collectionId: entry.collectionId,\n });\n }\n\n log.info(\"pending inbox replay finished\", { replayed });\n\n return replayed;\n}\n\nasync function replayEvent(\n targets: Record<string, AcceptMutationsCollection>,\n collectionId: string,\n eventId: string,\n type: MutationType,\n key: string | number,\n payload: Record<string, unknown>,\n log: EventSourcedLogger,\n): Promise<boolean> {\n if (RESERVED_IDS.has(collectionId)) {\n log.warn(\"replay skipped: reserved collection\", { eventId, collectionId, type, key });\n return false;\n }\n\n const target = targets[collectionId];\n if (!target) {\n log.warn(\"replay skipped: unknown collection\", {\n eventId,\n collectionId,\n type,\n key,\n knownCollections: Object.keys(targets).filter((id) => !RESERVED_IDS.has(id)),\n });\n return false;\n }\n\n if (!target.utils.acceptMutations) {\n log.warn(\"replay skipped: collection missing acceptMutations\", {\n eventId,\n collectionId,\n type,\n key,\n targetId: target.id,\n });\n return false;\n }\n\n log.debug(\"replay applying mutation\", { eventId, collectionId, type, key });\n\n try {\n await target.utils.acceptMutations({\n mutations: [\n {\n mutationId: eventId,\n type,\n key,\n modified: payload,\n original: payload,\n changes: payload,\n collection: target,\n },\n ],\n });\n\n log.info(\"replay mutation accepted\", { eventId, collectionId, type, key });\n } catch (err) {\n const error = toError(err);\n log.error(\"replay mutation failed\", {\n eventId,\n collectionId,\n type,\n key,\n message: error.message,\n });\n throw error;\n }\n\n return true;\n}\n\nfunction currentSince(inbox: Collection<InboxEntry, string>): number {\n let max = 0;\n\n for (const entry of inbox.state.values()) {\n if (entry.sync && entry.globalSeq > max) max = entry.globalSeq;\n }\n\n return max;\n}\n\nfunction nextLocalSeq(outbox: Collection<OutboxEntry, string>): number {\n let max = -1;\n\n for (const entry of outbox.state.values()) {\n if (entry.localSeq > max) max = entry.localSeq;\n }\n\n return max + 1;\n}\n\nasync function markInboxEventSynced(\n inbox: Collection<InboxEntry, string>,\n event: ServerEvent,\n): Promise<void> {\n const existing = inbox.get(event.eventId);\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, true)).isPersisted.promise;\n return;\n }\n\n if (!existing.sync || existing.globalSeq !== event.globalSeq) {\n await inbox\n .update(event.eventId, (draft) => {\n draft.globalSeq = event.globalSeq;\n draft.sync = true;\n })\n .isPersisted.promise;\n }\n}\n\nfunction toInboxEntry(event: ServerEvent, sync: boolean): InboxEntry {\n return {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n payload: event.payload,\n timestamp: event.timestamp,\n sync,\n };\n}\n\nfunction assertReservedNamesAvailable(collections: Record<string, unknown>): void {\n for (const id of Object.keys(collections)) {\n if (RESERVED_IDS.has(id)) {\n throw new Error(\n `Collection id \"${id}\" is reserved. \"outbox\" and \"inbox\" are built-in collections.`,\n );\n }\n }\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n","export type LazySingletonOptions = {\n guard?: () => void;\n notInitializedMessage?: string;\n};\n\nexport type LazySingleton<T extends object> = {\n ensure: () => Promise<T>;\n proxy: T;\n reset: () => void;\n};\n\nexport function createLazySingleton<T extends object>(\n factory: () => Promise<T>,\n options: LazySingletonOptions = {},\n): LazySingleton<T> {\n let instance: T | null = null;\n let initPromise: Promise<T> | null = null;\n\n const ensure = async (): Promise<T> => {\n options.guard?.();\n\n if (instance) {\n return instance;\n }\n\n if (!initPromise) {\n initPromise = factory()\n .then((resolved) => {\n instance = resolved;\n return resolved;\n })\n .catch((error: unknown) => {\n initPromise = null;\n throw error;\n });\n }\n\n return initPromise;\n };\n\n const message =\n options.notInitializedMessage ?? \"Instance is not initialized. Call ensure() first.\";\n\n const proxy = new Proxy({} as T, {\n get(_target, prop, receiver) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.get(instance, prop, receiver);\n },\n has(_target, prop) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.has(instance, prop);\n },\n getPrototypeOf() {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.getPrototypeOf(instance);\n },\n });\n\n const reset = (): void => {\n instance = null;\n initPromise = null;\n };\n\n return { ensure, proxy, reset };\n}\n"],"mappings":"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"}
|