@sync-subscribe/client 0.3.2

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.
@@ -0,0 +1,282 @@
1
+ import { resolveConflict, matchesFilter, encodeSyncToken, EMPTY_SYNC_TOKEN, } from "@sync-subscribe/core";
2
+ /**
3
+ * IndexedDB-backed local store. Records survive page reloads.
4
+ *
5
+ * Pass a unique `dbName` per logical collection (or per user if you need
6
+ * data isolation on the same origin).
7
+ *
8
+ * Usage:
9
+ * const store = new IdbLocalStore<NoteRecord>("notes-db");
10
+ * const client = new SyncClient(transport, store);
11
+ */
12
+ export class IdbLocalStore {
13
+ dbName;
14
+ storeName;
15
+ db = null;
16
+ constructor(dbName, storeName = "records") {
17
+ this.dbName = dbName;
18
+ this.storeName = storeName;
19
+ }
20
+ getDb() {
21
+ if (this.db)
22
+ return Promise.resolve(this.db);
23
+ return new Promise((resolve, reject) => {
24
+ const req = indexedDB.open(this.dbName, 2);
25
+ req.onupgradeneeded = () => {
26
+ const db = req.result;
27
+ if (!db.objectStoreNames.contains(this.storeName)) {
28
+ db.createObjectStore(this.storeName, { keyPath: "recordId" });
29
+ }
30
+ if (!db.objectStoreNames.contains("meta")) {
31
+ db.createObjectStore("meta");
32
+ }
33
+ };
34
+ req.onsuccess = () => {
35
+ this.db = req.result;
36
+ resolve(req.result);
37
+ };
38
+ req.onerror = () => reject(req.error);
39
+ });
40
+ }
41
+ /**
42
+ * Apply a batch of patches inside a single readwrite transaction.
43
+ * Copies `record.updatedAt` into `record.serverUpdatedAt` on upsert (server clock is authoritative).
44
+ * Conflict resolution mirrors InMemoryStore: server patch wins only when its
45
+ * revisionCount is higher (or equal with an older updatedAt).
46
+ */
47
+ async applyPatches(patches) {
48
+ if (patches.length === 0)
49
+ return [];
50
+ const db = await this.getDb();
51
+ return new Promise((resolve, reject) => {
52
+ const tx = db.transaction(this.storeName, "readwrite");
53
+ const store = tx.objectStore(this.storeName);
54
+ const applied = [];
55
+ let pending = patches.length;
56
+ function onPatchDone() {
57
+ if (--pending === 0)
58
+ tx.commit?.();
59
+ }
60
+ for (const patch of patches) {
61
+ if (patch.op === "delete") {
62
+ const req = store.delete(patch.recordId);
63
+ req.onsuccess = () => {
64
+ applied.push(patch);
65
+ onPatchDone();
66
+ };
67
+ req.onerror = () => onPatchDone();
68
+ }
69
+ else {
70
+ const getReq = store.get(patch.record.recordId);
71
+ getReq.onsuccess = () => {
72
+ const existing = getReq.result;
73
+ if (!existing || resolveConflict(patch.record, existing) === "a") {
74
+ // Stamp serverUpdatedAt from the server's updatedAt
75
+ const withServerTs = {
76
+ ...patch.record,
77
+ serverUpdatedAt: patch.record.updatedAt,
78
+ };
79
+ store.put(withServerTs);
80
+ applied.push({ op: "upsert", record: withServerTs });
81
+ }
82
+ onPatchDone();
83
+ };
84
+ getReq.onerror = () => onPatchDone();
85
+ }
86
+ }
87
+ tx.oncomplete = () => resolve(applied);
88
+ tx.onerror = () => reject(tx.error);
89
+ tx.onabort = () => reject(tx.error);
90
+ });
91
+ }
92
+ async write(record) {
93
+ const db = await this.getDb();
94
+ return new Promise((resolve, reject) => {
95
+ const tx = db.transaction(this.storeName, "readwrite");
96
+ tx.objectStore(this.storeName).put(record);
97
+ tx.oncomplete = () => resolve();
98
+ tx.onerror = () => reject(tx.error);
99
+ });
100
+ }
101
+ async getAll() {
102
+ const db = await this.getDb();
103
+ return new Promise((resolve, reject) => {
104
+ const tx = db.transaction(this.storeName, "readonly");
105
+ const req = tx.objectStore(this.storeName).getAll();
106
+ req.onsuccess = () => resolve(req.result);
107
+ req.onerror = () => reject(req.error);
108
+ });
109
+ }
110
+ async query(filter) {
111
+ const all = await this.getAll();
112
+ return all.filter((r) => matchesFilter(r, filter));
113
+ }
114
+ async count(filter) {
115
+ return (await this.query(filter)).length;
116
+ }
117
+ async getById(recordId) {
118
+ const db = await this.getDb();
119
+ return new Promise((resolve, reject) => {
120
+ const tx = db.transaction(this.storeName, "readonly");
121
+ const req = tx.objectStore(this.storeName).get(recordId);
122
+ req.onsuccess = () => resolve(req.result);
123
+ req.onerror = () => reject(req.error);
124
+ });
125
+ }
126
+ async clear() {
127
+ const db = await this.getDb();
128
+ return new Promise((resolve, reject) => {
129
+ const tx = db.transaction(this.storeName, "readwrite");
130
+ tx.objectStore(this.storeName).clear();
131
+ tx.oncomplete = () => resolve();
132
+ tx.onerror = () => reject(tx.error);
133
+ });
134
+ }
135
+ async delete(filter) {
136
+ const db = await this.getDb();
137
+ return new Promise((resolve, reject) => {
138
+ const tx = db.transaction(this.storeName, "readwrite");
139
+ const store = tx.objectStore(this.storeName);
140
+ const req = store.openCursor();
141
+ let deleted = 0;
142
+ req.onsuccess = () => {
143
+ const cursor = req.result;
144
+ if (!cursor) {
145
+ if (deleted > 0)
146
+ console.log(`[IdbLocalStore] evicted ${deleted} records`);
147
+ return;
148
+ }
149
+ if (matchesFilter(cursor.value, filter)) {
150
+ cursor.delete();
151
+ deleted++;
152
+ }
153
+ cursor.continue();
154
+ };
155
+ tx.oncomplete = () => resolve();
156
+ tx.onerror = () => reject(tx.error);
157
+ tx.onabort = () => reject(tx.error);
158
+ });
159
+ }
160
+ async evict(evictFilter) {
161
+ return this.delete(evictFilter);
162
+ }
163
+ async reconstructSyncToken(filter) {
164
+ const all = await this.getAll();
165
+ let best;
166
+ for (const record of all) {
167
+ if (record.serverUpdatedAt === undefined)
168
+ continue;
169
+ if (!matchesFilter(record, filter))
170
+ continue;
171
+ if (!best ||
172
+ record.serverUpdatedAt > best.serverUpdatedAt ||
173
+ (record.serverUpdatedAt === best.serverUpdatedAt &&
174
+ record.revisionCount > best.revisionCount) ||
175
+ (record.serverUpdatedAt === best.serverUpdatedAt &&
176
+ record.revisionCount === best.revisionCount &&
177
+ record.recordId > best.recordId)) {
178
+ best = record;
179
+ }
180
+ }
181
+ if (!best || best.serverUpdatedAt === undefined)
182
+ return EMPTY_SYNC_TOKEN;
183
+ return encodeSyncToken({
184
+ updatedAt: best.serverUpdatedAt,
185
+ revisionCount: best.revisionCount,
186
+ recordId: best.recordId,
187
+ });
188
+ }
189
+ async setServerUpdatedAt(recordId, serverUpdatedAt) {
190
+ const record = await this.getById(recordId);
191
+ if (record) {
192
+ await this.write({ ...record, serverUpdatedAt });
193
+ }
194
+ }
195
+ async setSyncToken(subscriptionId, token) {
196
+ const db = await this.getDb();
197
+ return new Promise((resolve, reject) => {
198
+ const tx = db.transaction("meta", "readwrite");
199
+ tx.objectStore("meta").put(token, `syncToken:${subscriptionId}`);
200
+ tx.oncomplete = () => resolve();
201
+ tx.onerror = () => reject(tx.error);
202
+ });
203
+ }
204
+ async getSyncToken(subscriptionId) {
205
+ const db = await this.getDb();
206
+ return new Promise((resolve, reject) => {
207
+ const tx = db.transaction("meta", "readonly");
208
+ const req = tx.objectStore("meta").get(`syncToken:${subscriptionId}`);
209
+ req.onsuccess = () => resolve(req.result);
210
+ req.onerror = () => reject(req.error);
211
+ });
212
+ }
213
+ async setSubscription(name, sub) {
214
+ if (sub.filter === undefined) {
215
+ throw new Error("Missing filter");
216
+ }
217
+ if (sub.syncToken === undefined) {
218
+ throw new Error("Missing syncToken");
219
+ }
220
+ const db = await this.getDb();
221
+ return new Promise((resolve, reject) => {
222
+ const tx = db.transaction("meta", "readwrite");
223
+ tx.objectStore("meta").put(sub, `subscription:${name}`);
224
+ tx.oncomplete = () => resolve();
225
+ tx.onerror = () => reject(tx.error);
226
+ });
227
+ }
228
+ async getSubscription(name) {
229
+ const db = await this.getDb();
230
+ return new Promise((resolve, reject) => {
231
+ const tx = db.transaction("meta", "readonly");
232
+ const req = tx.objectStore("meta").get(`subscription:${name}`);
233
+ req.onsuccess = () => resolve(req.result);
234
+ req.onerror = () => reject(req.error);
235
+ });
236
+ }
237
+ async getSubscriptionById(id) {
238
+ const subs = await this.listSubscriptions();
239
+ return subs.find((s) => s.subscriptionId === id);
240
+ }
241
+ async listSubscriptions() {
242
+ const db = await this.getDb();
243
+ return new Promise((resolve, reject) => {
244
+ const tx = db.transaction("meta", "readonly");
245
+ const store = tx.objectStore("meta");
246
+ const subs = [];
247
+ const req = store.openCursor();
248
+ req.onsuccess = () => {
249
+ const cursor = req.result;
250
+ if (cursor) {
251
+ if (String(cursor.key).startsWith("subscription:")) {
252
+ subs.push(cursor.value);
253
+ }
254
+ cursor.continue();
255
+ }
256
+ else {
257
+ resolve(subs);
258
+ }
259
+ };
260
+ req.onerror = () => reject(req.error);
261
+ });
262
+ }
263
+ async removeSubscription(name) {
264
+ const db = await this.getDb();
265
+ return new Promise((resolve, reject) => {
266
+ const tx = db.transaction("meta", "readwrite");
267
+ tx.objectStore("meta").delete(`subscription:${name}`);
268
+ tx.oncomplete = () => resolve();
269
+ tx.onerror = () => reject(tx.error);
270
+ });
271
+ }
272
+ async clearSubscriptions() {
273
+ const db = await this.getDb();
274
+ return new Promise((resolve, reject) => {
275
+ const tx = db.transaction("meta", "readwrite");
276
+ tx.objectStore("meta").clear();
277
+ tx.oncomplete = () => resolve();
278
+ tx.onerror = () => reject(tx.error);
279
+ });
280
+ }
281
+ }
282
+ //# sourceMappingURL=idbLocalStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idbLocalStore.js","sourceRoot":"","sources":["../src/idbLocalStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { SyncRecord, SyncPatch, SyncToken, SubscriptionFilter } from "@sync-subscribe/core";
2
+ import type { ILocalStore, PersistedSubscription } from "./types.js";
3
+ /**
4
+ * Minimal in-memory local store. State is lost on page reload.
5
+ * Use IdbLocalStore for persistent storage across sessions.
6
+ */
7
+ export declare class InMemoryStore<T extends SyncRecord> implements ILocalStore<T> {
8
+ private records;
9
+ private syncTokens;
10
+ private subscriptions;
11
+ /**
12
+ * Apply a batch of patches from the server.
13
+ * Copies `record.updatedAt` into `record.serverUpdatedAt` on upsert (server clock is authoritative).
14
+ * Returns the patches that were actually applied.
15
+ */
16
+ applyPatches(patches: SyncPatch<T>[]): Promise<SyncPatch<T>[]>;
17
+ write(record: T): Promise<void>;
18
+ getAll(): Promise<T[]>;
19
+ query(filter: SubscriptionFilter): Promise<T[]>;
20
+ count(filter: SubscriptionFilter): Promise<number>;
21
+ getById(recordId: string): Promise<T | undefined>;
22
+ clear(): Promise<void>;
23
+ delete(filter: SubscriptionFilter): Promise<void>;
24
+ evict(evictFilter: SubscriptionFilter): Promise<void>;
25
+ reconstructSyncToken(filter: SubscriptionFilter<T>): Promise<SyncToken>;
26
+ setServerUpdatedAt(recordId: string, serverUpdatedAt: number): Promise<void>;
27
+ setSyncToken(subscriptionId: string, token: SyncToken): Promise<void>;
28
+ getSyncToken(subscriptionId: string): Promise<SyncToken | undefined>;
29
+ setSubscription(name: string, sub: PersistedSubscription): Promise<void>;
30
+ getSubscription(name: string): Promise<PersistedSubscription | undefined>;
31
+ getSubscriptionById(id: string): Promise<PersistedSubscription | undefined>;
32
+ listSubscriptions(): Promise<PersistedSubscription[]>;
33
+ removeSubscription(name: string): Promise<void>;
34
+ clearSubscriptions(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=inMemoryStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inMemoryStore.d.ts","sourceRoot":"","sources":["../src/inMemoryStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAO9B,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAErE;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC,SAAS,UAAU,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,aAAa,CAA4C;IAEjE;;;;OAIG;IACG,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAwB9D,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,MAAM,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAItB,KAAK,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAM/C,KAAK,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAIjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,KAAK,CAAC,WAAW,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,oBAAoB,CACxB,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC5B,OAAO,CAAC,SAAS,CAAC;IA6Bf,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC;IAOV,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIpE,eAAe,CACnB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAIvC,mBAAmB,CACvB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAOvC,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAIrD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;CAI1C"}
@@ -0,0 +1,127 @@
1
+ import { resolveConflict, matchesFilter, encodeSyncToken, EMPTY_SYNC_TOKEN, } from "@sync-subscribe/core";
2
+ /**
3
+ * Minimal in-memory local store. State is lost on page reload.
4
+ * Use IdbLocalStore for persistent storage across sessions.
5
+ */
6
+ export class InMemoryStore {
7
+ records = new Map();
8
+ syncTokens = new Map();
9
+ subscriptions = new Map();
10
+ /**
11
+ * Apply a batch of patches from the server.
12
+ * Copies `record.updatedAt` into `record.serverUpdatedAt` on upsert (server clock is authoritative).
13
+ * Returns the patches that were actually applied.
14
+ */
15
+ async applyPatches(patches) {
16
+ const applied = [];
17
+ for (const patch of patches) {
18
+ if (patch.op === "delete") {
19
+ this.records.delete(patch.recordId);
20
+ applied.push(patch);
21
+ }
22
+ else {
23
+ const existing = this.records.get(patch.record.recordId);
24
+ if (!existing || resolveConflict(patch.record, existing) === "a") {
25
+ // Stamp serverUpdatedAt from the server's updatedAt
26
+ const withServerTs = {
27
+ ...patch.record,
28
+ serverUpdatedAt: patch.record.updatedAt,
29
+ };
30
+ this.records.set(withServerTs.recordId, withServerTs);
31
+ applied.push({ op: "upsert", record: withServerTs });
32
+ }
33
+ }
34
+ }
35
+ return applied;
36
+ }
37
+ async write(record) {
38
+ this.records.set(record.recordId, record);
39
+ }
40
+ async getAll() {
41
+ return [...this.records.values()];
42
+ }
43
+ async query(filter) {
44
+ return [...this.records.values()].filter((r) => matchesFilter(r, filter));
45
+ }
46
+ async count(filter) {
47
+ return (await this.query(filter)).length;
48
+ }
49
+ async getById(recordId) {
50
+ return this.records.get(recordId);
51
+ }
52
+ async clear() {
53
+ this.records.clear();
54
+ this.syncTokens.clear();
55
+ }
56
+ async delete(filter) {
57
+ for (const [id, record] of this.records) {
58
+ if (matchesFilter(record, filter)) {
59
+ this.records.delete(id);
60
+ }
61
+ }
62
+ }
63
+ async evict(evictFilter) {
64
+ return this.delete(evictFilter);
65
+ }
66
+ async reconstructSyncToken(filter) {
67
+ let best;
68
+ for (const record of this.records.values()) {
69
+ if (record.serverUpdatedAt === undefined)
70
+ continue;
71
+ if (!matchesFilter(record, filter))
72
+ continue;
73
+ if (!best ||
74
+ record.serverUpdatedAt > best.serverUpdatedAt ||
75
+ (record.serverUpdatedAt === best.serverUpdatedAt &&
76
+ record.revisionCount > best.revisionCount) ||
77
+ (record.serverUpdatedAt === best.serverUpdatedAt &&
78
+ record.revisionCount === best.revisionCount &&
79
+ record.recordId > best.recordId)) {
80
+ best = record;
81
+ }
82
+ }
83
+ if (!best || best.serverUpdatedAt === undefined)
84
+ return EMPTY_SYNC_TOKEN;
85
+ return encodeSyncToken({
86
+ updatedAt: best.serverUpdatedAt,
87
+ revisionCount: best.revisionCount,
88
+ recordId: best.recordId,
89
+ });
90
+ }
91
+ async setServerUpdatedAt(recordId, serverUpdatedAt) {
92
+ const record = this.records.get(recordId);
93
+ if (record) {
94
+ this.records.set(recordId, { ...record, serverUpdatedAt });
95
+ }
96
+ }
97
+ async setSyncToken(subscriptionId, token) {
98
+ this.syncTokens.set(subscriptionId, token);
99
+ }
100
+ async getSyncToken(subscriptionId) {
101
+ return this.syncTokens.get(subscriptionId);
102
+ }
103
+ async setSubscription(name, sub) {
104
+ this.subscriptions.set(name, sub);
105
+ }
106
+ async getSubscription(name) {
107
+ return this.subscriptions.get(name);
108
+ }
109
+ async getSubscriptionById(id) {
110
+ for (const sub of this.subscriptions.values()) {
111
+ if (sub.subscriptionId === id)
112
+ return sub;
113
+ }
114
+ return undefined;
115
+ }
116
+ async listSubscriptions() {
117
+ return [...this.subscriptions.values()];
118
+ }
119
+ async removeSubscription(name) {
120
+ this.subscriptions.delete(name);
121
+ }
122
+ async clearSubscriptions() {
123
+ this.subscriptions.clear();
124
+ this.syncTokens.clear();
125
+ }
126
+ }
127
+ //# sourceMappingURL=inMemoryStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inMemoryStore.js","sourceRoot":"","sources":["../src/inMemoryStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { SyncClient } from "./syncClient.js";
2
+ export { InMemoryStore } from "./inMemoryStore.js";
3
+ export { IdbLocalStore } from "./idbLocalStore.js";
4
+ export { createFetchTransport } from "./fetchTransport.js";
5
+ export type { FetchTransportOptions } from "./fetchTransport.js";
6
+ export type { ClientSubscription, ClientSubscriptionOptions, ILocalStore, PatchListener, PersistedSubscription, SyncQuery, SyncTransport, } from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,SAAS,EACT,aAAa,GACd,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { SyncClient } from "./syncClient.js";
2
+ export { InMemoryStore } from "./inMemoryStore.js";
3
+ export { IdbLocalStore } from "./idbLocalStore.js";
4
+ export { createFetchTransport } from "./fetchTransport.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { type SyncRecord, type SubscriptionFilter, type TableSchema } from "@sync-subscribe/core";
2
+ import type { ClientSubscription, ClientSubscriptionOptions, ILocalStore, PatchListener, SyncQuery, SyncTransport } from "./types.js";
3
+ /**
4
+ * High-level client that manages subscriptions, local state, and sync cycles.
5
+ *
6
+ * Pass a custom `store` to use IndexedDB persistence:
7
+ * new SyncClient(transport, new IdbLocalStore("my-db"))
8
+ *
9
+ * Omit `store` to use the default in-memory store.
10
+ */
11
+ export declare class SyncClient<T extends SyncRecord> {
12
+ private readonly transport;
13
+ private readonly schema?;
14
+ private listeners;
15
+ private activeSubs;
16
+ private subActiveListeners;
17
+ private _stopStream;
18
+ private _streamTimer;
19
+ private pendingPull;
20
+ readonly store: ILocalStore<T>;
21
+ constructor(transport: SyncTransport, store?: ILocalStore<T>, schema?: TableSchema<T> | undefined);
22
+ /** Serial queue — subscribe() calls run one at a time to prevent races. */
23
+ private subscribeQueue;
24
+ subscribe(options: ClientSubscriptionOptions): Promise<ClientSubscription>;
25
+ private _subscribe;
26
+ private checkForEviction;
27
+ /** Checks if the new inputFilter results in a gap we need to fill */
28
+ private checkForGap;
29
+ /**
30
+ * Update an existing subscription to use a new filter.
31
+ * Delegates to subscribe() with previousSubscriptionId set.
32
+ */
33
+ updateSubscription(subscriptionId: string, newFilter: SubscriptionFilter): Promise<ClientSubscription>;
34
+ /**
35
+ * Pull patches for all subscriptions.
36
+ * - ACTIVE subscriptions are included directly.
37
+ * - PENDING subscriptions are excluded; their gap sub is included instead.
38
+ * When a gap sub's token appears in the response the gap is complete:
39
+ * the gap sub is removed, the parent subscription reconstructs its token
40
+ * and transitions to active.
41
+ */
42
+ pull(): Promise<void>;
43
+ /**
44
+ * Debounced pull — collapses multiple rapid calls (e.g. several hooks mounting
45
+ * at the same time) into a single transport request. All callers share the same
46
+ * promise and receive the result of the one batched pull.
47
+ *
48
+ * @param delayMs - How long to wait before issuing the pull (default 20 ms).
49
+ */
50
+ schedulePull(delayMs?: number): Promise<void>;
51
+ private _flushPull;
52
+ /**
53
+ * Write a record locally (read-your-own-writes) then push to server.
54
+ * Returns true on success, false if a conflict was detected (server wins).
55
+ */
56
+ mutate(record: T): Promise<boolean>;
57
+ /**
58
+ * Returns a reactive handle to a filtered view of the local store.
59
+ * Does NOT register a sync subscription — use this when data is already
60
+ * being synced via a separate `subscribe()` call (e.g. a background sync).
61
+ *
62
+ * Loading starts `true`, becomes `false` after the first local read.
63
+ * Re-runs whenever the store changes (pull patches, mutations).
64
+ */
65
+ query(options: {
66
+ filter: SubscriptionFilter<T>;
67
+ }): SyncQuery<T>;
68
+ /**
69
+ * Returns a reactive handle that also manages its own sync subscription.
70
+ * The sync subscription is registered when the first caller subscribes and
71
+ * removed when the last caller unsubscribes.
72
+ *
73
+ * Use this when the sync filter and the query filter are the same.
74
+ * For a narrower in-memory view of a broader background sync, use `query()`.
75
+ */
76
+ liveQuery(options: {
77
+ filter: SubscriptionFilter<T>;
78
+ name?: string;
79
+ }): SyncQuery<T>;
80
+ /**
81
+ * Debounced (re)start of the SSE stream. Collapses multiple rapid calls
82
+ * (e.g. several hooks subscribing at once) into a single connection open.
83
+ * Called automatically by subscribe(), unsubscribe(), and gap-fill completion.
84
+ */
85
+ private _syncStream;
86
+ private _applyStreamState;
87
+ /**
88
+ * Remove a subscription and restart the stream without it.
89
+ * If no active subscriptions remain the stream is stopped.
90
+ */
91
+ unsubscribe(subscriptionId: string): Promise<void>;
92
+ onPatches(listener: PatchListener<T>): () => void;
93
+ /**
94
+ * Register a one-shot listener that fires when the given subscription
95
+ * transitions from pending_gap_fill → active. Useful for restarting the
96
+ * SSE stream to include the newly-active subscription.
97
+ * Returns an unsubscribe function.
98
+ */
99
+ onSubscriptionActive(subscriptionId: string, listener: () => void): () => void;
100
+ private _emitSubscriptionActive;
101
+ private emit;
102
+ /**
103
+ * Look up a subscription by name (for named subs) or by subscriptionId
104
+ * (for unnamed subs, where the key IS the subscriptionId).
105
+ */
106
+ getSubscription(key: string): ClientSubscription | undefined;
107
+ getSubscriptionById(id: string): ClientSubscription | undefined;
108
+ /** Resets sync state (useful for logout / account switch). */
109
+ reset(): Promise<void>;
110
+ }
111
+ //# sourceMappingURL=syncClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncClient.d.ts","sourceRoot":"","sources":["../src/syncClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAOjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,WAAW,EACX,aAAa,EAIb,SAAS,EACT,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB;;;;;;;GAOG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,UAAU;IAiBxC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAnB1B,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,kBAAkB,CAAsC;IAChE,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,WAAW,CAKH;IAEhB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;gBAGZ,SAAS,EAAE,aAAa,EACzC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAEL,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,YAAA;IAS1C,2EAA2E;IAC3E,OAAO,CAAC,cAAc,CAAuC;IAE7D,SAAS,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YAO5D,UAAU;YAuFV,gBAAgB;IAmB9B,qEAAqE;YACvD,WAAW;IAqDzB;;;OAGG;IACG,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,kBAAkB,GAC5B,OAAO,CAAC,kBAAkB,CAAC;IAiB9B;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8E3B;;;;;;OAMG;IACH,YAAY,CAAC,OAAO,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC;YAkB3B,UAAU;IAYxB;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAuCzC;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IA0B/D;;;;;;;OAOG;IACH,SAAS,CAAC,OAAO,EAAE;QACjB,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC,CAAC;IAgEhB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,iBAAiB;IAwCzB;;;OAGG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBxD,SAAS,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAOjD;;;;;OAKG;IACH,oBAAoB,CAClB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,IAAI,GACnB,MAAM,IAAI;IAUb,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,IAAI;IAQZ;;;OAGG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAW5D,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAI/D,8DAA8D;IACxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7B"}