@absolutejs/sync 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/tanstack-db/index.js +45 -5
- package/dist/adapters/tanstack-db/index.js.map +5 -4
- package/dist/angular/index.js +45 -5
- package/dist/angular/index.js.map +5 -4
- package/dist/client/index.js +75 -17
- package/dist/client/index.js.map +8 -7
- package/dist/client/presence.d.ts +6 -0
- package/dist/client/syncClient.d.ts +8 -0
- package/dist/client/syncCollection.d.ts +6 -0
- package/dist/client/syncStore.d.ts +6 -0
- package/dist/engine/connection.d.ts +46 -3
- package/dist/engine/index.js +103 -21
- package/dist/engine/index.js.map +6 -5
- package/dist/engine/socket.d.ts +61 -7
- package/dist/engine/syncEngine.d.ts +30 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +165 -33
- package/dist/index.js.map +7 -6
- package/dist/react/index.js +45 -5
- package/dist/react/index.js.map +5 -4
- package/dist/serializer.d.ts +36 -0
- package/dist/svelte/index.js +45 -5
- package/dist/svelte/index.js.map +5 -4
- package/dist/testing.js +47 -11
- package/dist/testing.js.map +3 -3
- package/dist/vue/index.js +45 -5
- package/dist/vue/index.js.map +5 -4
- package/package.json +1 -1
|
@@ -68,6 +68,36 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
|
68
68
|
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
// src/serializer.ts
|
|
72
|
+
var jsonSerializer = {
|
|
73
|
+
decode: (raw) => {
|
|
74
|
+
if (typeof raw === "string") {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(raw);
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (raw instanceof Uint8Array) {
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(new TextDecoder().decode(raw));
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (raw instanceof ArrayBuffer) {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(raw)));
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return raw;
|
|
96
|
+
},
|
|
97
|
+
encodeClient: (frame) => JSON.stringify(frame),
|
|
98
|
+
encodeServer: (frame) => JSON.stringify(frame)
|
|
99
|
+
};
|
|
100
|
+
|
|
71
101
|
// src/client/syncCollection.ts
|
|
72
102
|
var localStorageMutationStorage = (key) => ({
|
|
73
103
|
load: () => {
|
|
@@ -134,6 +164,7 @@ var createSyncCollection = (options) => {
|
|
|
134
164
|
const key = options.key ?? ((row) => row.id);
|
|
135
165
|
const reconnectMs = options.reconnectMs ?? 500;
|
|
136
166
|
const maxReconnectMs = options.maxReconnectMs ?? 1e4;
|
|
167
|
+
const serializer = options.serializer ?? jsonSerializer;
|
|
137
168
|
const Impl = options.webSocketImpl ?? globalThis.WebSocket;
|
|
138
169
|
if (!Impl) {
|
|
139
170
|
throw new Error("createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.");
|
|
@@ -243,9 +274,12 @@ var createSyncCollection = (options) => {
|
|
|
243
274
|
}
|
|
244
275
|
}
|
|
245
276
|
};
|
|
277
|
+
const wsSend = (payload) => {
|
|
278
|
+
socket?.send(payload);
|
|
279
|
+
};
|
|
246
280
|
const sendMutate = (mutation) => {
|
|
247
281
|
if (connected) {
|
|
248
|
-
|
|
282
|
+
wsSend(serializer.encodeClient({
|
|
249
283
|
type: "mutate",
|
|
250
284
|
mutationId: mutation.mutationId,
|
|
251
285
|
name: mutation.name,
|
|
@@ -263,7 +297,7 @@ var createSyncCollection = (options) => {
|
|
|
263
297
|
ws.onopen = () => {
|
|
264
298
|
attempt = 0;
|
|
265
299
|
connected = true;
|
|
266
|
-
ws.send(
|
|
300
|
+
ws.send(serializer.encodeClient({
|
|
267
301
|
type: "subscribe",
|
|
268
302
|
id: SUBSCRIPTION_ID,
|
|
269
303
|
collection: options.collection,
|
|
@@ -276,7 +310,10 @@ var createSyncCollection = (options) => {
|
|
|
276
310
|
};
|
|
277
311
|
ws.onmessage = (event) => {
|
|
278
312
|
try {
|
|
279
|
-
|
|
313
|
+
const decoded = serializer.decode(event.data);
|
|
314
|
+
if (decoded !== null && typeof decoded === "object") {
|
|
315
|
+
applyFrame(decoded);
|
|
316
|
+
}
|
|
280
317
|
} catch {}
|
|
281
318
|
};
|
|
282
319
|
ws.onclose = () => {
|
|
@@ -382,7 +419,10 @@ var createSyncCollection = (options) => {
|
|
|
382
419
|
clearTimeout(reconnectTimer);
|
|
383
420
|
}
|
|
384
421
|
try {
|
|
385
|
-
|
|
422
|
+
wsSend(serializer.encodeClient({
|
|
423
|
+
type: "unsubscribe",
|
|
424
|
+
id: SUBSCRIPTION_ID
|
|
425
|
+
}));
|
|
386
426
|
socket?.close();
|
|
387
427
|
} catch {}
|
|
388
428
|
for (const mutation of pending.splice(0)) {
|
|
@@ -519,5 +559,5 @@ export {
|
|
|
519
559
|
createSyncTanStackCollectionOptions
|
|
520
560
|
};
|
|
521
561
|
|
|
522
|
-
//# debugId=
|
|
562
|
+
//# debugId=DFC28799B4F74CBA64756E2164756E21
|
|
523
563
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/client/syncCollection.ts", "../src/adapters/tanstack-db/index.ts"],
|
|
3
|
+
"sources": ["../src/serializer.ts", "../src/client/syncCollection.ts", "../src/adapters/tanstack-db/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ServerFrame } from '../engine/connection';\nimport type { RowKey } from '../engine/types';\n\nexport type { ServerFrame } from '../engine/connection';\n\nexport type SyncCollectionStatus = 'connecting' | 'ready' | 'closed';\n\nexport type SyncCollectionState<T> = {\n\t/** Visible rows: the server state with pending optimistic mutations applied. */\n\tdata: T[];\n\t/** Connection/sync status. */\n\tstatus: SyncCollectionStatus;\n\t/** Last error message from the server, or `undefined`. */\n\terror: unknown;\n};\n\n/** A working set a mutation's optimistic effect edits in place. */\nexport type OptimisticDraft<T> = {\n\t/** Insert or replace a row by key. */\n\tset: (row: T) => void;\n\t/** Remove a row by key. */\n\tdelete: (key: RowKey) => void;\n};\n\nexport type MutateOptions<T> = {\n\t/** Registered server mutation name. */\n\tname: string;\n\t/** Arguments forwarded to the mutation handler. */\n\targs?: unknown;\n\t/**\n\t * Apply this mutation's effect to the local set immediately for instant UI.\n\t * Reverted automatically if the server rejects it. Omit for a non-optimistic\n\t * mutation (UI updates only once the authoritative diff arrives).\n\t */\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n};\n\n/** A pending mutation persisted for replay across reloads. */\nexport type PendingMutationRecord = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n};\n\n/**\n * Durable storage for the pending-mutation queue, so unconfirmed mutations\n * survive a page reload (offline). The queue is replayed when the socket\n * connects; records are dropped as they're acked.\n */\nexport type MutationStorage = {\n\tload: () => PendingMutationRecord[] | Promise<PendingMutationRecord[]>;\n\tsave: (records: PendingMutationRecord[]) => void | Promise<void>;\n};\n\n/**\n * A {@link MutationStorage} backed by `localStorage` under `key`. No-ops where\n * `localStorage` is unavailable (e.g. SSR).\n */\nexport const localStorageMutationStorage = (key: string): MutationStorage => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw ? (JSON.parse(raw) as PendingMutationRecord[]) : [];\n\t},\n\tsave: (records) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(records));\n\t}\n});\n\n/**\n * A persisted snapshot of a collection's server-authoritative rows plus the\n * change-feed `version` they were current as of — the cursor used to resume on\n * the next connect (catch-up diff if the server's changelog still covers it, a\n * fresh snapshot otherwise).\n */\nexport type CollectionCacheSnapshot<T> = {\n\trows: T[];\n\tversion: number;\n};\n\n/**\n * Durable local cache of a collection's confirmed rows, so reads are instant on\n * reload and available offline (local-first). Distinct from {@link\n * MutationStorage}, which persists *unconfirmed writes*: the cache is the\n * read side, the queue is the write side. On startup the cache hydrates the\n * collection before the socket connects; the engine then resumes from the\n * cached `version`.\n */\nexport type CollectionCache<T> = {\n\tload: () =>\n\t\t| CollectionCacheSnapshot<T>\n\t\t| undefined\n\t\t| Promise<CollectionCacheSnapshot<T> | undefined>;\n\tsave: (snapshot: CollectionCacheSnapshot<T>) => void | Promise<void>;\n\t/** Drop the cached snapshot (optional). */\n\tclear?: () => void | Promise<void>;\n};\n\n/**\n * A {@link CollectionCache} backed by `localStorage` under `key`. Synchronous\n * and capped (~5MB); fine for small collections. No-ops where `localStorage`\n * is unavailable (e.g. SSR). For larger sets use {@link indexedDbCollectionCache}.\n */\nexport const localStorageCollectionCache = <T>(\n\tkey: string\n): CollectionCache<T> => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw\n\t\t\t? (JSON.parse(raw) as CollectionCacheSnapshot<T>)\n\t\t\t: undefined;\n\t},\n\tsave: (snapshot) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(snapshot));\n\t},\n\tclear: () => {\n\t\tglobalThis.localStorage?.removeItem(key);\n\t}\n});\n\nconst openIndexedDb = (\n\tdatabaseName: string,\n\tstoreName: string\n): Promise<IDBDatabase> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst request = globalThis.indexedDB.open(databaseName, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\trequest.result.createObjectStore(storeName);\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error);\n\t});\n\n/**\n * A {@link CollectionCache} backed by IndexedDB — the durable, large-capacity\n * local-first store. Asynchronous; one row per collection `key` in a shared\n * object store. No-ops (resolving to `undefined`) where `indexedDB` is\n * unavailable (e.g. SSR), so the collection falls back to the server snapshot.\n */\nexport const indexedDbCollectionCache = <T>({\n\tkey,\n\tdatabaseName = 'absolutejs-sync',\n\tstoreName = 'collections'\n}: {\n\t/** Distinct entry name within the store (e.g. the collection + params). */\n\tkey: string;\n\t/** IndexedDB database name. Defaults to `absolutejs-sync`. */\n\tdatabaseName?: string;\n\t/** Object-store name. Defaults to `collections`. */\n\tstoreName?: string;\n}): CollectionCache<T> => {\n\tlet handle: Promise<IDBDatabase> | undefined;\n\tconst database = () => {\n\t\thandle ??= openIndexedDb(databaseName, storeName);\n\t\treturn handle;\n\t};\n\tconst withStore = async <R>(\n\t\tmode: IDBTransactionMode,\n\t\trun: (store: IDBObjectStore) => IDBRequest\n\t): Promise<R | undefined> => {\n\t\tif (globalThis.indexedDB === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst db = await database();\n\t\treturn new Promise<R>((resolve, reject) => {\n\t\t\tconst request = run(\n\t\t\t\tdb.transaction(storeName, mode).objectStore(storeName)\n\t\t\t);\n\t\t\trequest.onsuccess = () => resolve(request.result as R);\n\t\t\trequest.onerror = () => reject(request.error);\n\t\t});\n\t};\n\n\treturn {\n\t\tload: () =>\n\t\t\twithStore<CollectionCacheSnapshot<T>>('readonly', (store) =>\n\t\t\t\tstore.get(key)\n\t\t\t),\n\t\tsave: async (snapshot) => {\n\t\t\tawait withStore('readwrite', (store) => store.put(snapshot, key));\n\t\t},\n\t\tclear: async () => {\n\t\t\tawait withStore('readwrite', (store) => store.delete(key));\n\t\t}\n\t};\n};\n\nexport type SyncCollectionOptions<T> = {\n\t/** WebSocket URL of the {@link syncSocket} endpoint (e.g. `ws://host/sync/ws`). */\n\turl: string;\n\t/** Registered collection name to subscribe to. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection's hydrate/match/authorize. */\n\tparams?: unknown;\n\t/** Row identity, used to apply diffs and optimistic edits. Defaults to `row.id`. */\n\tkey?: (row: T) => RowKey;\n\t/** WebSocket implementation; defaults to the global one (pass for tests/SSR). */\n\twebSocketImpl?: typeof WebSocket;\n\t/**\n\t * Base reconnect delay (ms), doubled each attempt up to `maxReconnectMs`.\n\t * Set 0 to disable auto-reconnect. Defaults to 500.\n\t */\n\treconnectMs?: number;\n\t/** Maximum reconnect backoff (ms). Defaults to 10000. */\n\tmaxReconnectMs?: number;\n\t/**\n\t * Persist the pending-mutation queue so it survives a reload (offline) and\n\t * replays on connect. See {@link localStorageMutationStorage}.\n\t */\n\tstorage?: MutationStorage;\n\t/**\n\t * Persist confirmed rows locally for instant reads on reload and offline\n\t * (local-first). Hydrated before the socket connects; the engine then\n\t * resumes from the cached version (catch-up diff, or a fresh snapshot if the\n\t * server's changelog no longer covers it). See {@link\n\t * localStorageCollectionCache} / {@link indexedDbCollectionCache}.\n\t */\n\tcache?: CollectionCache<T>;\n\t/** Called with each server error message. */\n\tonError?: (error: unknown) => void;\n};\n\nexport type SyncCollection<T> = {\n\t/** Current state snapshot (stable reference until the next change). */\n\tget: () => SyncCollectionState<T>;\n\t/** Subscribe to state changes; returns an unsubscribe. */\n\tsubscribe: (\n\t\tlistener: (state: SyncCollectionState<T>) => void\n\t) => () => void;\n\t/**\n\t * Run a server mutation, optionally applying it optimistically. Resolves with\n\t * the server's result on ack, rejects (and rolls back) on reject. Pending\n\t * mutations are replayed when the socket reconnects, so they survive a drop.\n\t */\n\tmutate: <R = unknown>(options: MutateOptions<T>) => Promise<R>;\n\t/**\n\t * Force-close the underlying WebSocket without tearing down state. The\n\t * auto-reconnect loop fires after `reconnectMs`; the collection's\n\t * `appliedVersion` is preserved so the resumed subscribe carries `since`\n\t * and the engine replies with a catch-up diff (or a fresh snapshot if\n\t * the change log no longer covers the gap).\n\t *\n\t * Useful for simulating an offline blip in tests and benches that need\n\t * to measure resume cost specifically (vs cold-hydration on a fresh\n\t * collection). No-op if the collection has been `close()`d.\n\t */\n\tdisconnect: () => void;\n\t/** Unsubscribe on the server, close the socket, and stop reconnecting. */\n\tclose: () => void;\n};\n\n// One store subscribes to exactly one collection, so a fixed frame id suffices.\nconst SUBSCRIPTION_ID = 's';\n\ntype PendingMutation<T> = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n\tresolve: (result: unknown) => void;\n\treject: (error: unknown) => void;\n};\n\n/**\n * A live collection backed by the WebSocket sync engine. Reads: connect,\n * subscribe, apply the server's snapshot then row-level diffs, re-sync on\n * reconnect. Writes: {@link SyncCollection.mutate} applies an optimistic overlay\n * immediately, sends the mutation, and reconciles on ack (drop the overlay — the\n * authoritative diff already arrived) or reject (roll back). Framework-agnostic\n * (`get` + `subscribe`).\n *\n * Mutations are replayed on reconnect, so make server mutations idempotent —\n * delivery is at-least-once if an ack is lost across a drop.\n */\nexport const createSyncCollection = <T>(\n\toptions: SyncCollectionOptions<T>\n): SyncCollection<T> => {\n\tconst key = options.key ?? ((row: T) => (row as { id: RowKey }).id);\n\tconst reconnectMs = options.reconnectMs ?? 500;\n\tconst maxReconnectMs = options.maxReconnectMs ?? 10_000;\n\tconst Impl = options.webSocketImpl ?? globalThis.WebSocket;\n\tif (!Impl) {\n\t\tthrow new Error(\n\t\t\t'createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.'\n\t\t);\n\t}\n\n\t// Server-authoritative rows; `pending` is the optimistic overlay on top.\n\tconst confirmed = new Map<RowKey, T>();\n\tconst pending: PendingMutation<T>[] = [];\n\tlet mutationSeq = 0;\n\n\tlet state: SyncCollectionState<T> = {\n\t\tdata: [],\n\t\tstatus: 'connecting',\n\t\terror: undefined\n\t};\n\tconst listeners = new Set<(state: SyncCollectionState<T>) => void>();\n\tconst setState = (patch: Partial<SyncCollectionState<T>>) => {\n\t\tstate = { ...state, ...patch };\n\t\tfor (const listener of listeners) {\n\t\t\tlistener(state);\n\t\t}\n\t};\n\n\t/** Recompute visible rows = confirmed + pending optimistic effects. */\n\tconst recompute = (patch: Partial<SyncCollectionState<T>> = {}) => {\n\t\tconst working = new Map(confirmed);\n\t\tconst draft: OptimisticDraft<T> = {\n\t\t\tset: (row) => working.set(key(row), row),\n\t\t\tdelete: (rowKey) => working.delete(rowKey)\n\t\t};\n\t\tfor (const mutation of pending) {\n\t\t\tmutation.optimistic?.(draft);\n\t\t}\n\t\tsetState({ ...patch, data: [...working.values()] });\n\t};\n\n\tlet socket: WebSocket | undefined;\n\tlet connected = false;\n\tlet closed = false;\n\tlet attempt = 0;\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\t// Highest change-feed version applied; sent as `since` to resume on reconnect.\n\tlet appliedVersion = 0;\n\n\tconst persist = () => {\n\t\tvoid options.storage?.save(\n\t\t\tpending.map((mutation) => ({\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}))\n\t\t);\n\t};\n\n\t// Coalesce a burst of confirmed changes (a frame of diffs) into one cache\n\t// write per tick. Persists only the server-authoritative set — never the\n\t// optimistic overlay (those live in the mutation queue instead).\n\tlet cacheScheduled = false;\n\tconst persistCache = () => {\n\t\tif (options.cache === undefined || cacheScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tcacheScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tcacheScheduled = false;\n\t\t\tvoid options.cache?.save({\n\t\t\t\trows: [...confirmed.values()],\n\t\t\t\tversion: appliedVersion\n\t\t\t});\n\t\t});\n\t};\n\n\tconst settlePending = (mutationId: number) => {\n\t\tconst index = pending.findIndex(\n\t\t\t(mutation) => mutation.mutationId === mutationId\n\t\t);\n\t\tif (index === -1) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst [mutation] = pending.splice(index, 1);\n\t\tpersist();\n\t\treturn mutation;\n\t};\n\n\tconst applyFrame = (frame: ServerFrame<T>) => {\n\t\tif (frame.type === 'snapshot') {\n\t\t\tconfirmed.clear();\n\t\t\tfor (const row of frame.rows) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = frame.version;\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'diff') {\n\t\t\tfor (const row of frame.removed) {\n\t\t\t\tconfirmed.delete(key(row));\n\t\t\t}\n\t\t\tfor (const row of frame.added) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tfor (const row of frame.changed) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = Math.max(appliedVersion, frame.version);\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\t// A diff only arrives once subscribed — including the catch-up diff a\n\t\t\t// resume replies with — so receiving one means we're live.\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'error') {\n\t\t\tsetState({ error: frame.message });\n\t\t\toptions.onError?.(frame.message);\n\t\t} else if (frame.type === 'ack') {\n\t\t\t// The authoritative diff already arrived (ordered before the ack), so\n\t\t\t// dropping the overlay leaves the confirmed row in place — no flicker.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.resolve(frame.result);\n\t\t\t}\n\t\t} else if (frame.type === 'reject') {\n\t\t\t// roll the optimistic overlay back.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.reject(new Error(String(frame.message)));\n\t\t\t}\n\t\t}\n\t\t// A `frame` (multi-collection batch) never reaches a single-collection\n\t\t// store — that's the multiplexed createSyncClient's job — so ignore it.\n\t};\n\n\tconst sendMutate = (mutation: PendingMutation<T>) => {\n\t\tif (connected) {\n\t\t\tsocket?.send(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: 'mutate',\n\t\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\t\tname: mutation.name,\n\t\t\t\t\targs: mutation.args\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\t};\n\n\tconst connect = () => {\n\t\tif (closed) {\n\t\t\treturn;\n\t\t}\n\t\tsetState({ status: 'connecting' });\n\t\tconst ws = new Impl(options.url);\n\t\tsocket = ws;\n\t\tws.onopen = () => {\n\t\t\tattempt = 0;\n\t\t\tconnected = true;\n\t\t\tws.send(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: 'subscribe',\n\t\t\t\t\tid: SUBSCRIPTION_ID,\n\t\t\t\t\tcollection: options.collection,\n\t\t\t\t\tparams: options.params,\n\t\t\t\t\t// Resume from what we've applied (catch-up instead of snapshot).\n\t\t\t\t\tsince: appliedVersion > 0 ? appliedVersion : undefined\n\t\t\t\t})\n\t\t\t);\n\t\t\t// Replay anything still pending across the (re)connect.\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t};\n\t\tws.onmessage = (event) => {\n\t\t\ttry {\n\t\t\t\tapplyFrame(JSON.parse(event.data as string) as ServerFrame<T>);\n\t\t\t} catch {\n\t\t\t\t// ignore non-JSON frames\n\t\t\t}\n\t\t};\n\t\tws.onclose = () => {\n\t\t\tconnected = false;\n\t\t\tif (closed || reconnectMs <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst delay = Math.min(reconnectMs * 2 ** attempt, maxReconnectMs);\n\t\t\tattempt += 1;\n\t\t\treconnectTimer = setTimeout(connect, delay);\n\t\t};\n\t};\n\n\t// Reload recovery: re-queue persisted unconfirmed mutations so they replay on\n\t// connect. They carry no optimistic effect or promise (the resumed/snapshot\n\t// state is authoritative); resending produces the diffs that bring them in.\n\tconst hydratePersisted = async () => {\n\t\tif (options.storage === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst records = await options.storage.load();\n\t\tfor (const record of records) {\n\t\t\tif (pending.some((m) => m.mutationId === record.mutationId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpending.push({\n\t\t\t\tmutationId: record.mutationId,\n\t\t\t\tname: record.name,\n\t\t\t\targs: record.args,\n\t\t\t\tresolve: () => {},\n\t\t\t\treject: () => {}\n\t\t\t});\n\t\t\tmutationSeq = Math.max(mutationSeq, record.mutationId);\n\t\t}\n\t\tif (connected) {\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t}\n\t};\n\n\t// Local-first: load cached rows + version before connecting, so reads are\n\t// instant on reload and available offline. The subscribe then resumes from\n\t// the cached version — a catch-up diff if the server's changelog still\n\t// covers it, else a fresh snapshot that replaces the stale cache.\n\tconst hydrateCache = async () => {\n\t\tif (options.cache === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tlet snapshot: CollectionCacheSnapshot<T> | undefined;\n\t\ttry {\n\t\t\tsnapshot = await options.cache.load();\n\t\t} catch {\n\t\t\treturn; // corrupt/unavailable cache: fall back to the server snapshot\n\t\t}\n\t\t// Don't clobber server data if a frame somehow already landed.\n\t\tif (snapshot === undefined || appliedVersion > 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const row of snapshot.rows) {\n\t\t\tconfirmed.set(key(row), row);\n\t\t}\n\t\tappliedVersion = snapshot.version;\n\t\trecompute(); // show cached rows immediately (status stays 'connecting')\n\t};\n\n\tif (options.cache === undefined) {\n\t\t// No cache: preserve the original connect-then-hydrate ordering/timing.\n\t\tconnect();\n\t\tvoid hydratePersisted();\n\t} else {\n\t\t// Cache: hydrate reads + queued writes first, then connect so the\n\t\t// subscribe carries the cached resume version.\n\t\tvoid (async () => {\n\t\t\tawait hydrateCache();\n\t\t\tawait hydratePersisted();\n\t\t\tconnect();\n\t\t})();\n\t}\n\n\treturn {\n\t\tget: () => state,\n\t\tsubscribe: (listener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tmutate: <R = unknown>(mutateOptions: MutateOptions<T>) =>\n\t\t\tnew Promise<R>((resolve, reject) => {\n\t\t\t\tconst mutation: PendingMutation<T> = {\n\t\t\t\t\tmutationId: (mutationSeq += 1),\n\t\t\t\t\tname: mutateOptions.name,\n\t\t\t\t\targs: mutateOptions.args,\n\t\t\t\t\toptimistic: mutateOptions.optimistic,\n\t\t\t\t\tresolve: (result) => resolve(result as R),\n\t\t\t\t\treject\n\t\t\t\t};\n\t\t\t\tpending.push(mutation);\n\t\t\t\tpersist();\n\t\t\t\trecompute(); // apply the optimistic overlay immediately\n\t\t\t\tsendMutate(mutation);\n\t\t\t}),\n\t\tdisconnect: () => {\n\t\t\t// Force-close the WS without tearing down state. The existing\n\t\t\t// `ws.onclose` handler schedules a reconnect via the auto-\n\t\t\t// reconnect loop (unless the collection has been `close()`d).\n\t\t\t// `appliedVersion` is preserved, so the resumed subscribe carries\n\t\t\t// `since` and the engine sends a catch-up diff (or snapshot if\n\t\t\t// the change log can't cover the gap).\n\t\t\tif (closed || socket === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket.close();\n\t\t\t} catch {\n\t\t\t\t// already closing/closed\n\t\t\t}\n\t\t},\n\t\tclose: () => {\n\t\t\tif (closed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclosed = true;\n\t\t\tconnected = false;\n\t\t\tif (reconnectTimer !== undefined) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket?.send(\n\t\t\t\t\tJSON.stringify({ type: 'unsubscribe', id: SUBSCRIPTION_ID })\n\t\t\t\t);\n\t\t\t\tsocket?.close();\n\t\t\t} catch {\n\t\t\t\t// socket already closing/closed\n\t\t\t}\n\t\t\t// Fail any still-pending mutations so their promises don't hang.\n\t\t\tfor (const mutation of pending.splice(0)) {\n\t\t\t\tmutation.reject(new Error('sync collection closed'));\n\t\t\t}\n\t\t\tpersist();\n\t\t\tsetState({ status: 'closed' });\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n};\n",
|
|
5
|
+
"import type { ClientFrame, ServerFrame } from './engine/connection';\n\n/**\n * Wire-format adapter (1.16.0). The engine and client default to JSON, but\n * for high-throughput deployments — a customer pushing 1 MB initial\n * snapshots, a tenant fan-out hitting 100k frames/sec — a binary serializer\n * (msgpack, cbor, or a custom tagged layout) cuts both bandwidth and\n * parse-side CPU.\n *\n * Both ends of the connection MUST use the same serializer. The default\n * `jsonSerializer` keeps every existing client + server pair working\n * unchanged; opt in to a different one on BOTH `syncSocket` and the client\n * lib to gain the win.\n *\n * The serializer only handles the wire format. Frame-shape validation\n * stays in the engine (`parseFrame` in connection.ts) — it runs on the\n * decoded object, so the same validation works for JSON, msgpack, etc.\n */\nexport type FrameSerializer = {\n\t/** Server→client: encode an outgoing `ServerFrame` for transport. */\n\tencodeServer: (frame: ServerFrame) => string | ArrayBufferLike | Uint8Array;\n\t/** Client→server: encode an outgoing `ClientFrame` for transport. */\n\tencodeClient: (frame: ClientFrame) => string | ArrayBufferLike | Uint8Array;\n\t/**\n\t * Deserialize a wire payload into a raw object. Return `null` for\n\t * unparseable input — the engine's validation step turns that into\n\t * a protocol error.\n\t */\n\tdecode: (raw: unknown) => unknown;\n};\n\n/**\n * Default JSON serializer — what `@absolutejs/sync` has always shipped.\n * Strings go through `JSON.parse`; already-parsed objects pass through\n * (some WS adapters auto-decode). `Uint8Array` / `ArrayBuffer` get\n * UTF-8 decoded first (binary WS frames carrying JSON text).\n */\nexport const jsonSerializer: FrameSerializer = {\n\tdecode: (raw: unknown): unknown => {\n\t\tif (typeof raw === 'string') {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(raw);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof Uint8Array) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(new TextDecoder().decode(raw));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tif (raw instanceof ArrayBuffer) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(new TextDecoder().decode(new Uint8Array(raw)));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn raw;\n\t},\n\tencodeClient: (frame: ClientFrame): string => JSON.stringify(frame),\n\tencodeServer: (frame: ServerFrame): string => JSON.stringify(frame)\n};\n",
|
|
6
|
+
"import type { ServerFrame } from '../engine/connection';\nimport type { RowKey } from '../engine/types';\nimport { jsonSerializer, type FrameSerializer } from '../serializer';\n\nexport type { ServerFrame } from '../engine/connection';\n\nexport type SyncCollectionStatus = 'connecting' | 'ready' | 'closed';\n\nexport type SyncCollectionState<T> = {\n\t/** Visible rows: the server state with pending optimistic mutations applied. */\n\tdata: T[];\n\t/** Connection/sync status. */\n\tstatus: SyncCollectionStatus;\n\t/** Last error message from the server, or `undefined`. */\n\terror: unknown;\n};\n\n/** A working set a mutation's optimistic effect edits in place. */\nexport type OptimisticDraft<T> = {\n\t/** Insert or replace a row by key. */\n\tset: (row: T) => void;\n\t/** Remove a row by key. */\n\tdelete: (key: RowKey) => void;\n};\n\nexport type MutateOptions<T> = {\n\t/** Registered server mutation name. */\n\tname: string;\n\t/** Arguments forwarded to the mutation handler. */\n\targs?: unknown;\n\t/**\n\t * Apply this mutation's effect to the local set immediately for instant UI.\n\t * Reverted automatically if the server rejects it. Omit for a non-optimistic\n\t * mutation (UI updates only once the authoritative diff arrives).\n\t */\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n};\n\n/** A pending mutation persisted for replay across reloads. */\nexport type PendingMutationRecord = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n};\n\n/**\n * Durable storage for the pending-mutation queue, so unconfirmed mutations\n * survive a page reload (offline). The queue is replayed when the socket\n * connects; records are dropped as they're acked.\n */\nexport type MutationStorage = {\n\tload: () => PendingMutationRecord[] | Promise<PendingMutationRecord[]>;\n\tsave: (records: PendingMutationRecord[]) => void | Promise<void>;\n};\n\n/**\n * A {@link MutationStorage} backed by `localStorage` under `key`. No-ops where\n * `localStorage` is unavailable (e.g. SSR).\n */\nexport const localStorageMutationStorage = (key: string): MutationStorage => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw ? (JSON.parse(raw) as PendingMutationRecord[]) : [];\n\t},\n\tsave: (records) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(records));\n\t}\n});\n\n/**\n * A persisted snapshot of a collection's server-authoritative rows plus the\n * change-feed `version` they were current as of — the cursor used to resume on\n * the next connect (catch-up diff if the server's changelog still covers it, a\n * fresh snapshot otherwise).\n */\nexport type CollectionCacheSnapshot<T> = {\n\trows: T[];\n\tversion: number;\n};\n\n/**\n * Durable local cache of a collection's confirmed rows, so reads are instant on\n * reload and available offline (local-first). Distinct from {@link\n * MutationStorage}, which persists *unconfirmed writes*: the cache is the\n * read side, the queue is the write side. On startup the cache hydrates the\n * collection before the socket connects; the engine then resumes from the\n * cached `version`.\n */\nexport type CollectionCache<T> = {\n\tload: () =>\n\t\t| CollectionCacheSnapshot<T>\n\t\t| undefined\n\t\t| Promise<CollectionCacheSnapshot<T> | undefined>;\n\tsave: (snapshot: CollectionCacheSnapshot<T>) => void | Promise<void>;\n\t/** Drop the cached snapshot (optional). */\n\tclear?: () => void | Promise<void>;\n};\n\n/**\n * A {@link CollectionCache} backed by `localStorage` under `key`. Synchronous\n * and capped (~5MB); fine for small collections. No-ops where `localStorage`\n * is unavailable (e.g. SSR). For larger sets use {@link indexedDbCollectionCache}.\n */\nexport const localStorageCollectionCache = <T>(\n\tkey: string\n): CollectionCache<T> => ({\n\tload: () => {\n\t\tconst raw = globalThis.localStorage?.getItem(key);\n\t\treturn raw\n\t\t\t? (JSON.parse(raw) as CollectionCacheSnapshot<T>)\n\t\t\t: undefined;\n\t},\n\tsave: (snapshot) => {\n\t\tglobalThis.localStorage?.setItem(key, JSON.stringify(snapshot));\n\t},\n\tclear: () => {\n\t\tglobalThis.localStorage?.removeItem(key);\n\t}\n});\n\nconst openIndexedDb = (\n\tdatabaseName: string,\n\tstoreName: string\n): Promise<IDBDatabase> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst request = globalThis.indexedDB.open(databaseName, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\trequest.result.createObjectStore(storeName);\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error);\n\t});\n\n/**\n * A {@link CollectionCache} backed by IndexedDB — the durable, large-capacity\n * local-first store. Asynchronous; one row per collection `key` in a shared\n * object store. No-ops (resolving to `undefined`) where `indexedDB` is\n * unavailable (e.g. SSR), so the collection falls back to the server snapshot.\n */\nexport const indexedDbCollectionCache = <T>({\n\tkey,\n\tdatabaseName = 'absolutejs-sync',\n\tstoreName = 'collections'\n}: {\n\t/** Distinct entry name within the store (e.g. the collection + params). */\n\tkey: string;\n\t/** IndexedDB database name. Defaults to `absolutejs-sync`. */\n\tdatabaseName?: string;\n\t/** Object-store name. Defaults to `collections`. */\n\tstoreName?: string;\n}): CollectionCache<T> => {\n\tlet handle: Promise<IDBDatabase> | undefined;\n\tconst database = () => {\n\t\thandle ??= openIndexedDb(databaseName, storeName);\n\t\treturn handle;\n\t};\n\tconst withStore = async <R>(\n\t\tmode: IDBTransactionMode,\n\t\trun: (store: IDBObjectStore) => IDBRequest\n\t): Promise<R | undefined> => {\n\t\tif (globalThis.indexedDB === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst db = await database();\n\t\treturn new Promise<R>((resolve, reject) => {\n\t\t\tconst request = run(\n\t\t\t\tdb.transaction(storeName, mode).objectStore(storeName)\n\t\t\t);\n\t\t\trequest.onsuccess = () => resolve(request.result as R);\n\t\t\trequest.onerror = () => reject(request.error);\n\t\t});\n\t};\n\n\treturn {\n\t\tload: () =>\n\t\t\twithStore<CollectionCacheSnapshot<T>>('readonly', (store) =>\n\t\t\t\tstore.get(key)\n\t\t\t),\n\t\tsave: async (snapshot) => {\n\t\t\tawait withStore('readwrite', (store) => store.put(snapshot, key));\n\t\t},\n\t\tclear: async () => {\n\t\t\tawait withStore('readwrite', (store) => store.delete(key));\n\t\t}\n\t};\n};\n\nexport type SyncCollectionOptions<T> = {\n\t/** WebSocket URL of the {@link syncSocket} endpoint (e.g. `ws://host/sync/ws`). */\n\turl: string;\n\t/** Registered collection name to subscribe to. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection's hydrate/match/authorize. */\n\tparams?: unknown;\n\t/** Row identity, used to apply diffs and optimistic edits. Defaults to `row.id`. */\n\tkey?: (row: T) => RowKey;\n\t/** WebSocket implementation; defaults to the global one (pass for tests/SSR). */\n\twebSocketImpl?: typeof WebSocket;\n\t/**\n\t * Base reconnect delay (ms), doubled each attempt up to `maxReconnectMs`.\n\t * Set 0 to disable auto-reconnect. Defaults to 500.\n\t */\n\treconnectMs?: number;\n\t/** Maximum reconnect backoff (ms). Defaults to 10000. */\n\tmaxReconnectMs?: number;\n\t/**\n\t * Persist the pending-mutation queue so it survives a reload (offline) and\n\t * replays on connect. See {@link localStorageMutationStorage}.\n\t */\n\tstorage?: MutationStorage;\n\t/**\n\t * Persist confirmed rows locally for instant reads on reload and offline\n\t * (local-first). Hydrated before the socket connects; the engine then\n\t * resumes from the cached version (catch-up diff, or a fresh snapshot if the\n\t * server's changelog no longer covers it). See {@link\n\t * localStorageCollectionCache} / {@link indexedDbCollectionCache}.\n\t */\n\tcache?: CollectionCache<T>;\n\t/** Called with each server error message. */\n\tonError?: (error: unknown) => void;\n\t/**\n\t * Wire-format serializer (1.16.0). Defaults to `jsonSerializer`. MUST\n\t * match the server's `syncSocket` serializer.\n\t */\n\tserializer?: FrameSerializer;\n};\n\nexport type SyncCollection<T> = {\n\t/** Current state snapshot (stable reference until the next change). */\n\tget: () => SyncCollectionState<T>;\n\t/** Subscribe to state changes; returns an unsubscribe. */\n\tsubscribe: (\n\t\tlistener: (state: SyncCollectionState<T>) => void\n\t) => () => void;\n\t/**\n\t * Run a server mutation, optionally applying it optimistically. Resolves with\n\t * the server's result on ack, rejects (and rolls back) on reject. Pending\n\t * mutations are replayed when the socket reconnects, so they survive a drop.\n\t */\n\tmutate: <R = unknown>(options: MutateOptions<T>) => Promise<R>;\n\t/**\n\t * Force-close the underlying WebSocket without tearing down state. The\n\t * auto-reconnect loop fires after `reconnectMs`; the collection's\n\t * `appliedVersion` is preserved so the resumed subscribe carries `since`\n\t * and the engine replies with a catch-up diff (or a fresh snapshot if\n\t * the change log no longer covers the gap).\n\t *\n\t * Useful for simulating an offline blip in tests and benches that need\n\t * to measure resume cost specifically (vs cold-hydration on a fresh\n\t * collection). No-op if the collection has been `close()`d.\n\t */\n\tdisconnect: () => void;\n\t/** Unsubscribe on the server, close the socket, and stop reconnecting. */\n\tclose: () => void;\n};\n\n// One store subscribes to exactly one collection, so a fixed frame id suffices.\nconst SUBSCRIPTION_ID = 's';\n\ntype PendingMutation<T> = {\n\tmutationId: number;\n\tname: string;\n\targs: unknown;\n\toptimistic?: (draft: OptimisticDraft<T>) => void;\n\tresolve: (result: unknown) => void;\n\treject: (error: unknown) => void;\n};\n\n/**\n * A live collection backed by the WebSocket sync engine. Reads: connect,\n * subscribe, apply the server's snapshot then row-level diffs, re-sync on\n * reconnect. Writes: {@link SyncCollection.mutate} applies an optimistic overlay\n * immediately, sends the mutation, and reconciles on ack (drop the overlay — the\n * authoritative diff already arrived) or reject (roll back). Framework-agnostic\n * (`get` + `subscribe`).\n *\n * Mutations are replayed on reconnect, so make server mutations idempotent —\n * delivery is at-least-once if an ack is lost across a drop.\n */\nexport const createSyncCollection = <T>(\n\toptions: SyncCollectionOptions<T>\n): SyncCollection<T> => {\n\tconst key = options.key ?? ((row: T) => (row as { id: RowKey }).id);\n\tconst reconnectMs = options.reconnectMs ?? 500;\n\tconst maxReconnectMs = options.maxReconnectMs ?? 10_000;\n\tconst serializer: FrameSerializer = options.serializer ?? jsonSerializer;\n\tconst Impl = options.webSocketImpl ?? globalThis.WebSocket;\n\tif (!Impl) {\n\t\tthrow new Error(\n\t\t\t'createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.'\n\t\t);\n\t}\n\n\t// Server-authoritative rows; `pending` is the optimistic overlay on top.\n\tconst confirmed = new Map<RowKey, T>();\n\tconst pending: PendingMutation<T>[] = [];\n\tlet mutationSeq = 0;\n\n\tlet state: SyncCollectionState<T> = {\n\t\tdata: [],\n\t\tstatus: 'connecting',\n\t\terror: undefined\n\t};\n\tconst listeners = new Set<(state: SyncCollectionState<T>) => void>();\n\tconst setState = (patch: Partial<SyncCollectionState<T>>) => {\n\t\tstate = { ...state, ...patch };\n\t\tfor (const listener of listeners) {\n\t\t\tlistener(state);\n\t\t}\n\t};\n\n\t/** Recompute visible rows = confirmed + pending optimistic effects. */\n\tconst recompute = (patch: Partial<SyncCollectionState<T>> = {}) => {\n\t\tconst working = new Map(confirmed);\n\t\tconst draft: OptimisticDraft<T> = {\n\t\t\tset: (row) => working.set(key(row), row),\n\t\t\tdelete: (rowKey) => working.delete(rowKey)\n\t\t};\n\t\tfor (const mutation of pending) {\n\t\t\tmutation.optimistic?.(draft);\n\t\t}\n\t\tsetState({ ...patch, data: [...working.values()] });\n\t};\n\n\tlet socket: WebSocket | undefined;\n\tlet connected = false;\n\tlet closed = false;\n\tlet attempt = 0;\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\t// Highest change-feed version applied; sent as `since` to resume on reconnect.\n\tlet appliedVersion = 0;\n\n\tconst persist = () => {\n\t\tvoid options.storage?.save(\n\t\t\tpending.map((mutation) => ({\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}))\n\t\t);\n\t};\n\n\t// Coalesce a burst of confirmed changes (a frame of diffs) into one cache\n\t// write per tick. Persists only the server-authoritative set — never the\n\t// optimistic overlay (those live in the mutation queue instead).\n\tlet cacheScheduled = false;\n\tconst persistCache = () => {\n\t\tif (options.cache === undefined || cacheScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tcacheScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tcacheScheduled = false;\n\t\t\tvoid options.cache?.save({\n\t\t\t\trows: [...confirmed.values()],\n\t\t\t\tversion: appliedVersion\n\t\t\t});\n\t\t});\n\t};\n\n\tconst settlePending = (mutationId: number) => {\n\t\tconst index = pending.findIndex(\n\t\t\t(mutation) => mutation.mutationId === mutationId\n\t\t);\n\t\tif (index === -1) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst [mutation] = pending.splice(index, 1);\n\t\tpersist();\n\t\treturn mutation;\n\t};\n\n\tconst applyFrame = (frame: ServerFrame<T>) => {\n\t\tif (frame.type === 'snapshot') {\n\t\t\tconfirmed.clear();\n\t\t\tfor (const row of frame.rows) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = frame.version;\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'diff') {\n\t\t\tfor (const row of frame.removed) {\n\t\t\t\tconfirmed.delete(key(row));\n\t\t\t}\n\t\t\tfor (const row of frame.added) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tfor (const row of frame.changed) {\n\t\t\t\tconfirmed.set(key(row), row);\n\t\t\t}\n\t\t\tif (frame.version !== undefined) {\n\t\t\t\tappliedVersion = Math.max(appliedVersion, frame.version);\n\t\t\t}\n\t\t\tpersistCache();\n\t\t\t// A diff only arrives once subscribed — including the catch-up diff a\n\t\t\t// resume replies with — so receiving one means we're live.\n\t\t\trecompute({ status: 'ready', error: undefined });\n\t\t} else if (frame.type === 'error') {\n\t\t\tsetState({ error: frame.message });\n\t\t\toptions.onError?.(frame.message);\n\t\t} else if (frame.type === 'ack') {\n\t\t\t// The authoritative diff already arrived (ordered before the ack), so\n\t\t\t// dropping the overlay leaves the confirmed row in place — no flicker.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.resolve(frame.result);\n\t\t\t}\n\t\t} else if (frame.type === 'reject') {\n\t\t\t// roll the optimistic overlay back.\n\t\t\tconst mutation = settlePending(frame.mutationId);\n\t\t\tif (mutation !== undefined) {\n\t\t\t\trecompute();\n\t\t\t\tmutation.reject(new Error(String(frame.message)));\n\t\t\t}\n\t\t}\n\t\t// A `frame` (multi-collection batch) never reaches a single-collection\n\t\t// store — that's the multiplexed createSyncClient's job — so ignore it.\n\t};\n\n\tconst wsSend = (payload: string | ArrayBufferLike | Uint8Array) => {\n\t\tsocket?.send(payload as string);\n\t};\n\n\tconst sendMutate = (mutation: PendingMutation<T>) => {\n\t\tif (connected) {\n\t\t\twsSend(serializer.encodeClient({\n\t\t\t\ttype: 'mutate',\n\t\t\t\tmutationId: mutation.mutationId,\n\t\t\t\tname: mutation.name,\n\t\t\t\targs: mutation.args\n\t\t\t}));\n\t\t}\n\t};\n\n\tconst connect = () => {\n\t\tif (closed) {\n\t\t\treturn;\n\t\t}\n\t\tsetState({ status: 'connecting' });\n\t\tconst ws = new Impl(options.url);\n\t\tsocket = ws;\n\t\tws.onopen = () => {\n\t\t\tattempt = 0;\n\t\t\tconnected = true;\n\t\t\tws.send(serializer.encodeClient({\n\t\t\t\ttype: 'subscribe',\n\t\t\t\tid: SUBSCRIPTION_ID,\n\t\t\t\tcollection: options.collection,\n\t\t\t\tparams: options.params,\n\t\t\t\t// Resume from what we've applied (catch-up instead of snapshot).\n\t\t\t\tsince: appliedVersion > 0 ? appliedVersion : undefined\n\t\t\t}) as string);\n\t\t\t// Replay anything still pending across the (re)connect.\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t};\n\t\tws.onmessage = (event) => {\n\t\t\ttry {\n\t\t\t\tconst decoded = serializer.decode(event.data);\n\t\t\t\tif (decoded !== null && typeof decoded === 'object') {\n\t\t\t\t\tapplyFrame(decoded as ServerFrame<T>);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore non-JSON frames\n\t\t\t}\n\t\t};\n\t\tws.onclose = () => {\n\t\t\tconnected = false;\n\t\t\tif (closed || reconnectMs <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst delay = Math.min(reconnectMs * 2 ** attempt, maxReconnectMs);\n\t\t\tattempt += 1;\n\t\t\treconnectTimer = setTimeout(connect, delay);\n\t\t};\n\t};\n\n\t// Reload recovery: re-queue persisted unconfirmed mutations so they replay on\n\t// connect. They carry no optimistic effect or promise (the resumed/snapshot\n\t// state is authoritative); resending produces the diffs that bring them in.\n\tconst hydratePersisted = async () => {\n\t\tif (options.storage === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst records = await options.storage.load();\n\t\tfor (const record of records) {\n\t\t\tif (pending.some((m) => m.mutationId === record.mutationId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpending.push({\n\t\t\t\tmutationId: record.mutationId,\n\t\t\t\tname: record.name,\n\t\t\t\targs: record.args,\n\t\t\t\tresolve: () => {},\n\t\t\t\treject: () => {}\n\t\t\t});\n\t\t\tmutationSeq = Math.max(mutationSeq, record.mutationId);\n\t\t}\n\t\tif (connected) {\n\t\t\tfor (const mutation of pending) {\n\t\t\t\tsendMutate(mutation);\n\t\t\t}\n\t\t}\n\t};\n\n\t// Local-first: load cached rows + version before connecting, so reads are\n\t// instant on reload and available offline. The subscribe then resumes from\n\t// the cached version — a catch-up diff if the server's changelog still\n\t// covers it, else a fresh snapshot that replaces the stale cache.\n\tconst hydrateCache = async () => {\n\t\tif (options.cache === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tlet snapshot: CollectionCacheSnapshot<T> | undefined;\n\t\ttry {\n\t\t\tsnapshot = await options.cache.load();\n\t\t} catch {\n\t\t\treturn; // corrupt/unavailable cache: fall back to the server snapshot\n\t\t}\n\t\t// Don't clobber server data if a frame somehow already landed.\n\t\tif (snapshot === undefined || appliedVersion > 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const row of snapshot.rows) {\n\t\t\tconfirmed.set(key(row), row);\n\t\t}\n\t\tappliedVersion = snapshot.version;\n\t\trecompute(); // show cached rows immediately (status stays 'connecting')\n\t};\n\n\tif (options.cache === undefined) {\n\t\t// No cache: preserve the original connect-then-hydrate ordering/timing.\n\t\tconnect();\n\t\tvoid hydratePersisted();\n\t} else {\n\t\t// Cache: hydrate reads + queued writes first, then connect so the\n\t\t// subscribe carries the cached resume version.\n\t\tvoid (async () => {\n\t\t\tawait hydrateCache();\n\t\t\tawait hydratePersisted();\n\t\t\tconnect();\n\t\t})();\n\t}\n\n\treturn {\n\t\tget: () => state,\n\t\tsubscribe: (listener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t};\n\t\t},\n\t\tmutate: <R = unknown>(mutateOptions: MutateOptions<T>) =>\n\t\t\tnew Promise<R>((resolve, reject) => {\n\t\t\t\tconst mutation: PendingMutation<T> = {\n\t\t\t\t\tmutationId: (mutationSeq += 1),\n\t\t\t\t\tname: mutateOptions.name,\n\t\t\t\t\targs: mutateOptions.args,\n\t\t\t\t\toptimistic: mutateOptions.optimistic,\n\t\t\t\t\tresolve: (result) => resolve(result as R),\n\t\t\t\t\treject\n\t\t\t\t};\n\t\t\t\tpending.push(mutation);\n\t\t\t\tpersist();\n\t\t\t\trecompute(); // apply the optimistic overlay immediately\n\t\t\t\tsendMutate(mutation);\n\t\t\t}),\n\t\tdisconnect: () => {\n\t\t\t// Force-close the WS without tearing down state. The existing\n\t\t\t// `ws.onclose` handler schedules a reconnect via the auto-\n\t\t\t// reconnect loop (unless the collection has been `close()`d).\n\t\t\t// `appliedVersion` is preserved, so the resumed subscribe carries\n\t\t\t// `since` and the engine sends a catch-up diff (or snapshot if\n\t\t\t// the change log can't cover the gap).\n\t\t\tif (closed || socket === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket.close();\n\t\t\t} catch {\n\t\t\t\t// already closing/closed\n\t\t\t}\n\t\t},\n\t\tclose: () => {\n\t\t\tif (closed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclosed = true;\n\t\t\tconnected = false;\n\t\t\tif (reconnectTimer !== undefined) {\n\t\t\t\tclearTimeout(reconnectTimer);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twsSend(serializer.encodeClient({\n\t\t\t\t\ttype: 'unsubscribe',\n\t\t\t\t\tid: SUBSCRIPTION_ID\n\t\t\t\t}));\n\t\t\t\tsocket?.close();\n\t\t\t} catch {\n\t\t\t\t// socket already closing/closed\n\t\t\t}\n\t\t\t// Fail any still-pending mutations so their promises don't hang.\n\t\t\tfor (const mutation of pending.splice(0)) {\n\t\t\t\tmutation.reject(new Error('sync collection closed'));\n\t\t\t}\n\t\t\tpersist();\n\t\t\tsetState({ status: 'closed' });\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n};\n",
|
|
6
7
|
"import type {\n\tCollectionConfig,\n\tDeleteMutationFn,\n\tInsertMutationFn,\n\tPendingMutation,\n\tUpdateMutationFn\n} from '@tanstack/db';\nimport {\n\tcreateSyncCollection,\n\ttype CollectionCache,\n\ttype MutationStorage,\n\ttype SyncCollection,\n\ttype SyncCollectionOptions\n} from '../../client/syncCollection';\nimport type { RowKey } from '../../engine/types';\n\nexport type TanStackRowKey = Extract<RowKey, string | number>;\n\nexport type TanStackMutationCall = {\n\tname: string;\n\targs?: unknown;\n};\n\nexport type TanStackMutationMapper<\n\tT extends object,\n\tTOperation extends 'insert' | 'update' | 'delete'\n> =\n\t| string\n\t| ((\n\t\t\tmutation: PendingMutation<T, TOperation>\n\t ) => TanStackMutationCall | undefined);\n\nexport type SyncTanStackMutations<T extends object> = {\n\tinsert?: TanStackMutationMapper<T, 'insert'>;\n\tupdate?: TanStackMutationMapper<T, 'update'>;\n\tdelete?: TanStackMutationMapper<T, 'delete'>;\n};\n\nexport type SyncTanStackCollectionOptions<\n\tT extends object,\n\tTKey extends TanStackRowKey = TanStackRowKey\n> = Omit<\n\tCollectionConfig<T, TKey>,\n\t'sync' | 'getKey' | 'onInsert' | 'onUpdate' | 'onDelete'\n> & {\n\t/** WebSocket URL of the Absolute Sync endpoint. */\n\turl: string;\n\t/** Registered Absolute Sync collection name. */\n\tcollection: string;\n\t/** Query params forwarded to the server collection hydrate/match/authorize hooks. */\n\tparams?: unknown;\n\t/** Row identity shared by TanStack DB and Absolute Sync. */\n\tgetKey: (row: T) => TKey;\n\twebSocketImpl?: typeof WebSocket;\n\treconnectMs?: number;\n\tmaxReconnectMs?: number;\n\tstorage?: MutationStorage;\n\tcache?: CollectionCache<T>;\n\tonError?: (error: unknown) => void;\n\t/**\n\t * Optional mapping from TanStack mutations to registered Absolute Sync\n\t * mutation names. TanStack already applies optimistic writes, so forwarded\n\t * sync mutations intentionally do not add another optimistic overlay.\n\t */\n\tmutations?: SyncTanStackMutations<T>;\n\t/** Optional prebuilt Absolute Sync collection, useful when sharing lifecycle externally. */\n\tsyncCollection?: SyncCollection<T>;\n};\n\nconst toMutationCall = <\n\tT extends object,\n\tTOperation extends 'insert' | 'update' | 'delete'\n>(\n\tmapper: TanStackMutationMapper<T, TOperation>,\n\tmutation: PendingMutation<T, TOperation>\n): TanStackMutationCall | undefined => {\n\tif (typeof mapper === 'function') {\n\t\treturn mapper(mutation);\n\t}\n\tif (mutation.type === 'insert') {\n\t\treturn {\n\t\t\tname: mapper,\n\t\t\targs: { row: mutation.modified, metadata: mutation.metadata }\n\t\t};\n\t}\n\tif (mutation.type === 'update') {\n\t\treturn {\n\t\t\tname: mapper,\n\t\t\targs: {\n\t\t\t\tkey: mutation.key,\n\t\t\t\trow: mutation.modified,\n\t\t\t\tchanges: mutation.changes,\n\t\t\t\tmetadata: mutation.metadata\n\t\t\t}\n\t\t};\n\t}\n\treturn {\n\t\tname: mapper,\n\t\targs: {\n\t\t\tkey: mutation.key,\n\t\t\trow: mutation.original,\n\t\t\tmetadata: mutation.metadata\n\t\t}\n\t};\n};\n\nconst createMutationHandler =\n\t<T extends object, TOperation extends 'insert' | 'update' | 'delete'>(\n\t\tsync: SyncCollection<T>,\n\t\tmapper: TanStackMutationMapper<T, TOperation> | undefined\n\t) =>\n\tasync ({\n\t\ttransaction\n\t}: {\n\t\ttransaction: {\n\t\t\tmutations: [\n\t\t\t\tPendingMutation<T, TOperation>,\n\t\t\t\t...PendingMutation<T, TOperation>[]\n\t\t\t];\n\t\t};\n\t}) => {\n\t\tif (mapper === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tawait Promise.all(\n\t\t\ttransaction.mutations.map((mutation) => {\n\t\t\t\tconst call = toMutationCall(mapper, mutation);\n\t\t\t\treturn call === undefined\n\t\t\t\t\t? Promise.resolve()\n\t\t\t\t\t: sync.mutate({ name: call.name, args: call.args });\n\t\t\t})\n\t\t);\n\t};\n\nconst createSyncConfig = <T extends object, TKey extends TanStackRowKey>(\n\tsync: SyncCollection<T>,\n\tgetKey: (row: T) => TKey\n): CollectionConfig<T, TKey>['sync'] => ({\n\trowUpdateMode: 'full',\n\tsync: ({ begin, write, commit, markReady }) => {\n\t\tlet previous = new Map<TKey, T>();\n\t\tlet markedReady = false;\n\n\t\tconst flush = () => {\n\t\t\tconst state = sync.get();\n\t\t\tconst next = new Map<TKey, T>();\n\t\t\tfor (const row of state.data) {\n\t\t\t\tnext.set(getKey(row), row);\n\t\t\t}\n\n\t\t\tbegin();\n\t\t\tfor (const [key, row] of next) {\n\t\t\t\tconst old = previous.get(key);\n\t\t\t\tif (old === undefined) {\n\t\t\t\t\twrite({ type: 'insert', value: row });\n\t\t\t\t} else if (!Object.is(old, row)) {\n\t\t\t\t\twrite({ type: 'update', value: row, previousValue: old });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of previous.keys()) {\n\t\t\t\tif (!next.has(key)) {\n\t\t\t\t\twrite({ type: 'delete', key });\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevious = next;\n\t\t\tcommit();\n\n\t\t\tif (state.status === 'ready' && !markedReady) {\n\t\t\t\tmarkedReady = true;\n\t\t\t\tmarkReady();\n\t\t\t}\n\t\t};\n\n\t\tflush();\n\t\tconst unsubscribe = sync.subscribe(flush);\n\t\treturn () => {\n\t\t\tunsubscribe();\n\t\t\tsync.close();\n\t\t};\n\t}\n});\n\nexport const createSyncTanStackCollectionOptions = <\n\tT extends object,\n\tTKey extends TanStackRowKey = TanStackRowKey\n>(\n\toptions: SyncTanStackCollectionOptions<T, TKey>\n): CollectionConfig<T, TKey> => {\n\tconst {\n\t\turl,\n\t\tcollection,\n\t\tparams,\n\t\tgetKey,\n\t\twebSocketImpl,\n\t\treconnectMs,\n\t\tmaxReconnectMs,\n\t\tstorage,\n\t\tcache,\n\t\tonError,\n\t\tmutations,\n\t\tsyncCollection,\n\t\t...collectionOptions\n\t} = options;\n\n\tconst sync =\n\t\tsyncCollection ??\n\t\tcreateSyncCollection<T>({\n\t\t\turl,\n\t\t\tcollection,\n\t\t\tparams,\n\t\t\tkey: getKey as SyncCollectionOptions<T>['key'],\n\t\t\twebSocketImpl,\n\t\t\treconnectMs,\n\t\t\tmaxReconnectMs,\n\t\t\tstorage,\n\t\t\tcache,\n\t\t\tonError\n\t\t});\n\n\treturn {\n\t\t...collectionOptions,\n\t\tgetKey,\n\t\tsync: createSyncConfig(sync, getKey),\n\t\tonInsert: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.insert\n\t\t) as InsertMutationFn<T, TKey>,\n\t\tonUpdate: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.update\n\t\t) as UpdateMutationFn<T, TKey>,\n\t\tonDelete: createMutationHandler(\n\t\t\tsync,\n\t\t\tmutations?.delete\n\t\t) as DeleteMutationFn<T, TKey>\n\t};\n};\n\nexport { createSyncTanStackCollectionOptions as syncTanStackCollectionOptions };\n"
|
|
7
8
|
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "DFC28799B4F74CBA64756E2164756E21",
|
|
10
11
|
"names": []
|
|
11
12
|
}
|
package/dist/angular/index.js
CHANGED
|
@@ -500,6 +500,36 @@ var rgaText = {
|
|
|
500
500
|
// src/angular/sync-collection.service.ts
|
|
501
501
|
import { computed, Injectable, signal } from "@angular/core";
|
|
502
502
|
|
|
503
|
+
// src/serializer.ts
|
|
504
|
+
var jsonSerializer = {
|
|
505
|
+
decode: (raw) => {
|
|
506
|
+
if (typeof raw === "string") {
|
|
507
|
+
try {
|
|
508
|
+
return JSON.parse(raw);
|
|
509
|
+
} catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (raw instanceof Uint8Array) {
|
|
514
|
+
try {
|
|
515
|
+
return JSON.parse(new TextDecoder().decode(raw));
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (raw instanceof ArrayBuffer) {
|
|
521
|
+
try {
|
|
522
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(raw)));
|
|
523
|
+
} catch {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return raw;
|
|
528
|
+
},
|
|
529
|
+
encodeClient: (frame) => JSON.stringify(frame),
|
|
530
|
+
encodeServer: (frame) => JSON.stringify(frame)
|
|
531
|
+
};
|
|
532
|
+
|
|
503
533
|
// src/client/syncCollection.ts
|
|
504
534
|
var localStorageMutationStorage = (key) => ({
|
|
505
535
|
load: () => {
|
|
@@ -566,6 +596,7 @@ var createSyncCollection = (options) => {
|
|
|
566
596
|
const key = options.key ?? ((row) => row.id);
|
|
567
597
|
const reconnectMs = options.reconnectMs ?? 500;
|
|
568
598
|
const maxReconnectMs = options.maxReconnectMs ?? 1e4;
|
|
599
|
+
const serializer = options.serializer ?? jsonSerializer;
|
|
569
600
|
const Impl = options.webSocketImpl ?? globalThis.WebSocket;
|
|
570
601
|
if (!Impl) {
|
|
571
602
|
throw new Error("createSyncCollection requires WebSocket. Run in a browser or pass webSocketImpl.");
|
|
@@ -675,9 +706,12 @@ var createSyncCollection = (options) => {
|
|
|
675
706
|
}
|
|
676
707
|
}
|
|
677
708
|
};
|
|
709
|
+
const wsSend = (payload) => {
|
|
710
|
+
socket?.send(payload);
|
|
711
|
+
};
|
|
678
712
|
const sendMutate = (mutation) => {
|
|
679
713
|
if (connected) {
|
|
680
|
-
|
|
714
|
+
wsSend(serializer.encodeClient({
|
|
681
715
|
type: "mutate",
|
|
682
716
|
mutationId: mutation.mutationId,
|
|
683
717
|
name: mutation.name,
|
|
@@ -695,7 +729,7 @@ var createSyncCollection = (options) => {
|
|
|
695
729
|
ws.onopen = () => {
|
|
696
730
|
attempt = 0;
|
|
697
731
|
connected = true;
|
|
698
|
-
ws.send(
|
|
732
|
+
ws.send(serializer.encodeClient({
|
|
699
733
|
type: "subscribe",
|
|
700
734
|
id: SUBSCRIPTION_ID,
|
|
701
735
|
collection: options.collection,
|
|
@@ -708,7 +742,10 @@ var createSyncCollection = (options) => {
|
|
|
708
742
|
};
|
|
709
743
|
ws.onmessage = (event) => {
|
|
710
744
|
try {
|
|
711
|
-
|
|
745
|
+
const decoded = serializer.decode(event.data);
|
|
746
|
+
if (decoded !== null && typeof decoded === "object") {
|
|
747
|
+
applyFrame(decoded);
|
|
748
|
+
}
|
|
712
749
|
} catch {}
|
|
713
750
|
};
|
|
714
751
|
ws.onclose = () => {
|
|
@@ -814,7 +851,10 @@ var createSyncCollection = (options) => {
|
|
|
814
851
|
clearTimeout(reconnectTimer);
|
|
815
852
|
}
|
|
816
853
|
try {
|
|
817
|
-
|
|
854
|
+
wsSend(serializer.encodeClient({
|
|
855
|
+
type: "unsubscribe",
|
|
856
|
+
id: SUBSCRIPTION_ID
|
|
857
|
+
}));
|
|
818
858
|
socket?.close();
|
|
819
859
|
} catch {}
|
|
820
860
|
for (const mutation of pending.splice(0)) {
|
|
@@ -963,5 +1003,5 @@ export {
|
|
|
963
1003
|
SyncCollectionService
|
|
964
1004
|
};
|
|
965
1005
|
|
|
966
|
-
//# debugId=
|
|
1006
|
+
//# debugId=B7FB2C3EE968F18164756E2164756E21
|
|
967
1007
|
//# sourceMappingURL=index.js.map
|