@signaldb/sync 1.2.2 → 1.3.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.
@@ -12,7 +12,10 @@ interface Options<CollectionOptions extends Record<string, any>, ItemType extend
12
12
  lastFinishedSyncEnd?: number;
13
13
  }) => Promise<LoadResponse<ItemType>>;
14
14
  push: (collectionOptions: SyncOptions<CollectionOptions>, pushParameters: {
15
- changes: Changeset<ItemType>;
15
+ rawChanges: Omit<Change, 'id' | 'collectionName'>[];
16
+ changes: Changeset<ItemType> & {
17
+ modifiedFields: Map<IdType, string[]>;
18
+ };
16
19
  }) => Promise<void>;
17
20
  registerRemoteChange?: (collectionOptions: SyncOptions<CollectionOptions>, onChange: (data?: LoadResponse<ItemType>) => Promise<void>) => CleanupFunction | Promise<CleanupFunction>;
18
21
  id?: string;
@@ -1,11 +1,20 @@
1
+ import type { BaseItem } from '@signaldb/core';
2
+ /**
3
+ * Computes the modified fields between two items recursively.
4
+ * @param oldItem The old item
5
+ * @param newItem The new item
6
+ * @returns The modified fields
7
+ */
8
+ export declare function computeModifiedFields<T extends Record<string, any>>(oldItem: T, newItem: T): string[];
1
9
  /**
2
10
  * Compute changes between two arrays of items.
3
11
  * @param oldItems Array of the old items
4
12
  * @param newItems Array of the new items
5
13
  * @returns The changeset
6
14
  */
7
- export default function computeChanges<T extends Record<string, any>>(oldItems: T[], newItems: T[]): {
8
- added: T[];
9
- modified: T[];
10
- removed: T[];
15
+ export default function computeChanges<ItemType extends BaseItem<IdType>, IdType>(oldItems: ItemType[], newItems: ItemType[]): {
16
+ added: ItemType[];
17
+ modified: ItemType[];
18
+ modifiedFields: Map<IdType, string[]>;
19
+ removed: ItemType[];
11
20
  };
package/dist/index.mjs CHANGED
@@ -1,22 +1,22 @@
1
- var $ = Object.defineProperty;
2
- var E = (r, e, t) => e in r ? $(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
- var d = (r, e, t) => E(r, typeof e != "symbol" ? e + "" : e, t);
1
+ var E = Object.defineProperty;
2
+ var M = (r, e, t) => e in r ? E(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var u = (r, e, t) => M(r, typeof e != "symbol" ? e + "" : e, t);
4
4
  import { isEqual as S, modify as C, randomId as D, Collection as m, createIndex as y } from "@signaldb/core";
5
- function I(r, e, t = {}) {
5
+ function x(r, e, t = {}) {
6
6
  let s, n;
7
- const { leading: a = !1, trailing: o = !0 } = t;
7
+ const { leading: o = !1, trailing: a = !0 } = t;
8
8
  function c(...h) {
9
- const i = a && !s, u = o && !s;
9
+ const i = o && !s, l = a && !s;
10
10
  return s && clearTimeout(s), s = setTimeout(() => {
11
- s = null, o && !i && (n = r.apply(this, h));
12
- }, e), i ? n = r.apply(this, h) : u || (n = null), n;
11
+ s = null, a && !i && (n = r.apply(this, h));
12
+ }, e), i ? n = r.apply(this, h) : l || (n = null), n;
13
13
  }
14
14
  return c;
15
15
  }
16
- class M {
16
+ class I {
17
17
  constructor() {
18
- d(this, "queue", []);
19
- d(this, "pendingPromise", !1);
18
+ u(this, "queue", []);
19
+ u(this, "pendingPromise", !1);
20
20
  }
21
21
  /**
22
22
  * Method to add a new promise to the queue and returns a promise that resolves when this task is done
@@ -51,28 +51,45 @@ class M {
51
51
  }));
52
52
  }
53
53
  }
54
+ function N(r, e) {
55
+ const t = [], s = Object.keys(r), n = Object.keys(e), o = /* @__PURE__ */ new Set([...s, ...n]);
56
+ for (const a of o)
57
+ if (e[a] !== r[a])
58
+ if (typeof e[a] == "object" && typeof r[a] == "object") {
59
+ const c = N(r[a], e[a]);
60
+ for (const h of c)
61
+ t.push(`${a}.${h}`);
62
+ } else
63
+ t.push(a);
64
+ return t;
65
+ }
54
66
  function g(r, e) {
55
- const t = [], s = [], n = [], a = new Map(r.map((c) => [c.id, c])), o = new Map(e.map((c) => [c.id, c]));
56
- for (const [c, h] of a) {
57
- const i = o.get(c);
58
- i ? S(i, h) || s.push(i) : n.push(h);
67
+ const t = [], s = [], n = /* @__PURE__ */ new Map(), o = [], a = new Map(r.map((h) => [h.id, h])), c = new Map(e.map((h) => [h.id, h]));
68
+ for (const [h, i] of a) {
69
+ const l = c.get(h);
70
+ l ? S(l, i) || (n.set(l.id, N(i, l)), s.push(l)) : o.push(i);
59
71
  }
60
- for (const [c, h] of o)
61
- a.has(c) || t.push(h);
62
- return { added: t, modified: s, removed: n };
72
+ for (const [h, i] of c)
73
+ a.has(h) || t.push(i);
74
+ return {
75
+ added: t,
76
+ modified: s,
77
+ modifiedFields: n,
78
+ removed: o
79
+ };
63
80
  }
64
81
  function P(r, e) {
65
82
  if (e.items != null)
66
83
  return e.items;
67
84
  const t = r || [];
68
85
  return e.changes.added.forEach((s) => {
69
- const n = t.findIndex((a) => a.id === s.id);
86
+ const n = t.findIndex((o) => o.id === s.id);
70
87
  n === -1 ? t.push(s) : t[n] = s;
71
88
  }), e.changes.modified.forEach((s) => {
72
- const n = t.findIndex((a) => a.id === s.id);
89
+ const n = t.findIndex((o) => o.id === s.id);
73
90
  n === -1 ? t.push(s) : t[n] = s;
74
91
  }), e.changes.removed.forEach((s) => {
75
- const n = t.findIndex((a) => a.id === s.id);
92
+ const n = t.findIndex((o) => o.id === s.id);
76
93
  n !== -1 && t.splice(n, 1);
77
94
  }), t;
78
95
  }
@@ -90,27 +107,27 @@ function v(r, e) {
90
107
  }
91
108
  }), [...t.values()];
92
109
  }
93
- function N(r) {
110
+ function O(r) {
94
111
  return r.added.length > 0 || r.modified.length > 0 || r.removed.length > 0;
95
112
  }
96
- function x(r, e) {
97
- return N(g(r, e));
113
+ function R(r, e) {
114
+ return O(g(r, e));
98
115
  }
99
- async function R({ changes: r, lastSnapshot: e, data: t, pull: s, push: n, insert: a, update: o, remove: c, batch: h }) {
100
- let i = t, u = e || [], f = P(e, i);
116
+ async function b({ changes: r, lastSnapshot: e, data: t, pull: s, push: n, insert: o, update: a, remove: c, batch: h }) {
117
+ let i = t, l = e || [], f = P(e, i);
101
118
  if (r.length > 0) {
102
- const p = v(u, r);
103
- if (x(u, p)) {
104
- const O = v(f, r), w = g(f, O);
105
- N(w) && (await n(w), i = await s(), f = P(f, i)), u = p;
119
+ const p = v(l, r);
120
+ if (R(l, p)) {
121
+ const $ = v(f, r), w = g(f, $);
122
+ O(w) && (await n(w), i = await s(), f = P(f, i)), l = p;
106
123
  }
107
124
  }
108
- const l = i.changes == null ? g(u, i.items) : i.changes;
125
+ const d = i.changes == null ? g(l, i.items) : i.changes;
109
126
  return h(() => {
110
- l.added.forEach((p) => a(p)), l.modified.forEach((p) => o(p.id, { $set: p })), l.removed.forEach((p) => c(p.id));
127
+ d.added.forEach((p) => o(p)), d.modified.forEach((p) => a(p.id, { $set: p })), d.removed.forEach((p) => c(p.id));
111
128
  }), f;
112
129
  }
113
- class b {
130
+ class T {
114
131
  /**
115
132
  * @param options Collection options
116
133
  * @param options.pull Function to pull data from remote source.
@@ -124,24 +141,24 @@ class b {
124
141
  * @param [options.debounceTime] The time in milliseconds to debounce push operations.
125
142
  */
126
143
  constructor(e) {
127
- d(this, "options");
128
- d(this, "collections", /* @__PURE__ */ new Map());
129
- d(this, "changes");
130
- d(this, "snapshots");
131
- d(this, "syncOperations");
132
- d(this, "scheduledPushes", /* @__PURE__ */ new Set());
133
- d(this, "remoteChanges", []);
134
- d(this, "syncQueues", /* @__PURE__ */ new Map());
135
- d(this, "persistenceReady");
136
- d(this, "isDisposed", !1);
137
- d(this, "instanceId", D());
138
- d(this, "id");
139
- d(this, "debouncedFlush");
144
+ u(this, "options");
145
+ u(this, "collections", /* @__PURE__ */ new Map());
146
+ u(this, "changes");
147
+ u(this, "snapshots");
148
+ u(this, "syncOperations");
149
+ u(this, "scheduledPushes", /* @__PURE__ */ new Set());
150
+ u(this, "remoteChanges", []);
151
+ u(this, "syncQueues", /* @__PURE__ */ new Map());
152
+ u(this, "persistenceReady");
153
+ u(this, "isDisposed", !1);
154
+ u(this, "instanceId", D());
155
+ u(this, "id");
156
+ u(this, "debouncedFlush");
140
157
  this.options = {
141
158
  autostart: !0,
142
159
  ...e
143
160
  }, this.id = this.options.id || "default-sync-manager";
144
- const { reactivity: t } = this.options, s = this.createPersistenceAdapter("changes"), n = this.createPersistenceAdapter("snapshots"), a = this.createPersistenceAdapter("sync-operations");
161
+ const { reactivity: t } = this.options, s = this.createPersistenceAdapter("changes"), n = this.createPersistenceAdapter("snapshots"), o = this.createPersistenceAdapter("sync-operations");
145
162
  this.changes = new m({
146
163
  name: `${this.options.id}-changes`,
147
164
  persistence: s == null ? void 0 : s.adapter,
@@ -154,15 +171,15 @@ class b {
154
171
  reactivity: t
155
172
  }), this.syncOperations = new m({
156
173
  name: `${this.options.id}-sync-operations`,
157
- persistence: a == null ? void 0 : a.adapter,
174
+ persistence: o == null ? void 0 : o.adapter,
158
175
  indices: [y("collectionName"), y("status")],
159
176
  reactivity: t
160
- }), this.changes.on("persistence.error", (o) => s == null ? void 0 : s.handler(o)), this.snapshots.on("persistence.error", (o) => n == null ? void 0 : n.handler(o)), this.syncOperations.on("persistence.error", (o) => a == null ? void 0 : a.handler(o)), this.persistenceReady = Promise.all([
177
+ }), this.changes.on("persistence.error", (a) => s == null ? void 0 : s.handler(a)), this.snapshots.on("persistence.error", (a) => n == null ? void 0 : n.handler(a)), this.syncOperations.on("persistence.error", (a) => o == null ? void 0 : o.handler(a)), this.persistenceReady = Promise.all([
161
178
  this.syncOperations.isReady(),
162
179
  this.changes.isReady(),
163
180
  this.snapshots.isReady()
164
181
  ]).then(() => {
165
- }), this.changes.setMaxListeners(1e3), this.snapshots.setMaxListeners(1e3), this.syncOperations.setMaxListeners(1e3), this.debouncedFlush = I(this.flushScheduledPushes, this.options.debounceTime ?? 100);
182
+ }), this.changes.setMaxListeners(1e3), this.snapshots.setMaxListeners(1e3), this.syncOperations.setMaxListeners(1e3), this.debouncedFlush = x(this.flushScheduledPushes, this.options.debounceTime ?? 100);
166
183
  }
167
184
  createPersistenceAdapter(e) {
168
185
  if (this.options.persistenceAdapter == null)
@@ -177,7 +194,7 @@ class b {
177
194
  };
178
195
  }
179
196
  getSyncQueue(e) {
180
- return this.syncQueues.get(e) == null && this.syncQueues.set(e, new M()), this.syncQueues.get(e);
197
+ return this.syncQueues.get(e) == null && this.syncQueues.set(e, new I()), this.syncQueues.get(e);
181
198
  }
182
199
  /**
183
200
  * Clears all internal data structures
@@ -228,34 +245,34 @@ class b {
228
245
  syncPaused: !0
229
246
  // always start paused as the autostart will start it
230
247
  });
231
- const s = (a) => {
232
- for (const o of this.remoteChanges)
233
- if (o != null && o.collectionName === a.collectionName && o.type === a.type && !(a.type === "remove" && o.data !== a.data) && o.data.id === a.data.id)
248
+ const s = (o) => {
249
+ for (const a of this.remoteChanges)
250
+ if (a != null && a.collectionName === o.collectionName && a.type === o.type && !(o.type === "remove" && a.data !== o.data) && a.data.id === o.data.id)
234
251
  return !0;
235
252
  return !1;
236
- }, n = (a, o) => {
253
+ }, n = (o, a) => {
237
254
  const c = [...this.remoteChanges];
238
255
  for (let h = 0; h < c.length; h += 1) {
239
256
  const i = c[h];
240
- i != null && i.collectionName === a && (i.type === "remove" && i.data !== o || i.data.id === o && (c[h] = null));
257
+ i != null && i.collectionName === o && (i.type === "remove" && i.data !== a || i.data.id === a && (c[h] = null));
241
258
  }
242
259
  this.remoteChanges = c.filter((h) => h != null);
243
260
  };
244
- e.on("added", (a) => {
245
- if (s({ collectionName: t.name, type: "insert", data: a })) {
246
- n(t.name, a.id);
261
+ e.on("added", (o) => {
262
+ if (s({ collectionName: t.name, type: "insert", data: o })) {
263
+ n(t.name, o.id);
247
264
  return;
248
265
  }
249
266
  this.changes.insert({
250
267
  collectionName: t.name,
251
268
  time: Date.now(),
252
269
  type: "insert",
253
- data: a
270
+ data: o
254
271
  }), !this.getCollectionProperties(t.name).syncPaused && this.schedulePush(t.name);
255
- }), e.on("changed", ({ id: a }, o) => {
256
- const c = { id: a, modifier: o };
272
+ }), e.on("changed", ({ id: o }, a) => {
273
+ const c = { id: o, modifier: a };
257
274
  if (s({ collectionName: t.name, type: "update", data: c })) {
258
- n(t.name, a);
275
+ n(t.name, o);
259
276
  return;
260
277
  }
261
278
  this.changes.insert({
@@ -264,19 +281,19 @@ class b {
264
281
  type: "update",
265
282
  data: c
266
283
  }), !this.getCollectionProperties(t.name).syncPaused && this.schedulePush(t.name);
267
- }), e.on("removed", ({ id: a }) => {
268
- if (s({ collectionName: t.name, type: "remove", data: a })) {
269
- n(t.name, a);
284
+ }), e.on("removed", ({ id: o }) => {
285
+ if (s({ collectionName: t.name, type: "remove", data: o })) {
286
+ n(t.name, o);
270
287
  return;
271
288
  }
272
289
  this.changes.insert({
273
290
  collectionName: t.name,
274
291
  time: Date.now(),
275
292
  type: "remove",
276
- data: a
293
+ data: o
277
294
  }), !this.getCollectionProperties(t.name).syncPaused && this.schedulePush(t.name);
278
- }), this.options.autostart && this.startSync(t.name).catch((a) => {
279
- this.options.onError && this.options.onError(this.getCollectionProperties(t.name).options, a);
295
+ }), this.options.autostart && this.startSync(t.name).catch((o) => {
296
+ this.options.onError && this.options.onError(this.getCollectionProperties(t.name).options, o);
280
297
  });
281
298
  }
282
299
  flushScheduledPushes() {
@@ -309,25 +326,25 @@ class b {
309
326
  if (n == null)
310
327
  await this.sync(e);
311
328
  else {
312
- const a = Date.now(), o = this.syncOperations.insert({
313
- start: a,
329
+ const o = Date.now(), a = this.syncOperations.insert({
330
+ start: o,
314
331
  collectionName: e,
315
332
  instanceId: this.instanceId,
316
333
  status: "active"
317
334
  });
318
335
  await this.syncWithData(e, n).then(() => {
319
336
  this.syncOperations.removeMany({
320
- id: { $ne: o },
337
+ id: { $ne: a },
321
338
  collectionName: e,
322
339
  $or: [
323
- { end: { $lte: a } },
340
+ { end: { $lte: o } },
324
341
  { status: "active" }
325
342
  ]
326
- }), this.syncOperations.updateOne({ id: o }, {
343
+ }), this.syncOperations.updateOne({ id: a }, {
327
344
  $set: { status: "done", end: Date.now() }
328
345
  });
329
346
  }).catch((c) => {
330
- throw this.options.onError && this.options.onError(this.getCollectionProperties(e).options, c), this.syncOperations.updateOne({ id: o }, {
347
+ throw this.options.onError && this.options.onError(this.getCollectionProperties(e).options, c), this.syncOperations.updateOne({ id: a }, {
331
348
  $set: { status: "error", end: Date.now(), error: c.stack || c.message }
332
349
  }), c;
333
350
  });
@@ -407,13 +424,13 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
407
424
  await this.isReady();
408
425
  const { options: s, readyPromise: n } = this.getCollectionProperties(e);
409
426
  await n;
410
- const a = this.syncOperations.find({
427
+ const o = this.syncOperations.find({
411
428
  collectionName: e,
412
429
  instanceId: this.instanceId,
413
430
  status: "active"
414
431
  }, {
415
432
  reactive: !1
416
- }).count() > 0, o = Date.now();
433
+ }).count() > 0, a = Date.now();
417
434
  let c = null;
418
435
  await new Promise((i) => {
419
436
  setTimeout(i, 0);
@@ -428,23 +445,23 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
428
445
  });
429
446
  if (t != null && t.onlyWithChanges && this.changes.find({
430
447
  collectionName: e,
431
- time: { $lte: o }
448
+ time: { $lte: a }
432
449
  }, {
433
450
  sort: { time: 1 },
434
451
  reactive: !1
435
452
  }).count() === 0)
436
453
  return;
437
- a || (c = this.syncOperations.insert({
438
- start: o,
454
+ o || (c = this.syncOperations.insert({
455
+ start: a,
439
456
  collectionName: e,
440
457
  instanceId: this.instanceId,
441
458
  status: "active"
442
459
  }));
443
- const u = await this.options.pull(s, {
460
+ const l = await this.options.pull(s, {
444
461
  lastFinishedSyncStart: i == null ? void 0 : i.start,
445
462
  lastFinishedSyncEnd: i == null ? void 0 : i.end
446
463
  });
447
- await this.syncWithData(e, u);
464
+ await this.syncWithData(e, l);
448
465
  };
449
466
  await (t != null && t.force ? h() : this.getSyncQueue(e).add(h)).catch((i) => {
450
467
  throw c != null && (this.options.onError && this.options.onError(s, i), this.syncOperations.updateOne({ id: c }, {
@@ -454,7 +471,7 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
454
471
  id: { $ne: c },
455
472
  collectionName: e,
456
473
  $or: [
457
- { end: { $lte: o } },
474
+ { end: { $lte: a } },
458
475
  { status: "active" }
459
476
  ]
460
477
  }), this.syncOperations.updateOne({ id: c }, {
@@ -471,7 +488,7 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
471
488
  });
472
489
  }
473
490
  async syncWithData(e, t) {
474
- const { collection: s, options: n } = this.getCollectionProperties(e), a = Date.now(), o = this.syncOperations.findOne({
491
+ const { collection: s, options: n } = this.getCollectionProperties(e), o = Date.now(), a = this.syncOperations.findOne({
475
492
  collectionName: e,
476
493
  status: "done"
477
494
  }, {
@@ -484,20 +501,23 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
484
501
  reactive: !1
485
502
  }), h = this.changes.find({
486
503
  collectionName: e,
487
- time: { $lte: a }
504
+ time: { $lte: o }
488
505
  }, {
489
506
  sort: { time: 1 },
490
507
  reactive: !1
491
508
  }).fetch();
492
- await R({
509
+ await b({
493
510
  changes: h,
494
511
  lastSnapshot: c == null ? void 0 : c.items,
495
512
  data: t,
496
513
  pull: () => this.options.pull(n, {
497
- lastFinishedSyncStart: o == null ? void 0 : o.start,
498
- lastFinishedSyncEnd: o == null ? void 0 : o.end
514
+ lastFinishedSyncStart: a == null ? void 0 : a.start,
515
+ lastFinishedSyncEnd: a == null ? void 0 : a.end
516
+ }),
517
+ push: (i) => this.options.push(n, {
518
+ changes: i,
519
+ rawChanges: h
499
520
  }),
500
- push: (i) => this.options.push(n, { changes: i }),
501
521
  insert: (i) => {
502
522
  this.remoteChanges.push({
503
523
  collectionName: e,
@@ -509,17 +529,17 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
509
529
  data: { id: i.id, modifier: { $set: i } }
510
530
  }), s.replaceOne({ id: i.id }, i, { upsert: !0 });
511
531
  },
512
- update: (i, u) => {
532
+ update: (i, l) => {
513
533
  this.remoteChanges.push({
514
534
  collectionName: e,
515
535
  type: "insert",
516
- data: { id: i, ...u.$set }
536
+ data: { id: i, ...l.$set }
517
537
  }, {
518
538
  collectionName: e,
519
539
  type: "update",
520
- data: { id: i, modifier: u }
540
+ data: { id: i, modifier: l }
521
541
  }), s.updateOne({ id: i }, {
522
- ...u,
542
+ ...l,
523
543
  $setOnInsert: { id: i }
524
544
  }, { upsert: !0 });
525
545
  },
@@ -540,16 +560,16 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
540
560
  }).then(async (i) => {
541
561
  if (this.snapshots.removeMany({
542
562
  collectionName: e,
543
- time: { $lte: a }
563
+ time: { $lte: o }
544
564
  }), this.changes.removeMany({
545
565
  collectionName: e,
546
- id: { $in: h.map((l) => l.id) }
566
+ id: { $in: h.map((d) => d.id) }
547
567
  }), this.snapshots.insert({
548
- time: a,
568
+ time: o,
549
569
  collectionName: e,
550
570
  items: i
551
- }), await new Promise((l) => {
552
- setTimeout(l, 0);
571
+ }), await new Promise((d) => {
572
+ setTimeout(d, 0);
553
573
  }), this.changes.find({
554
574
  collectionName: e
555
575
  }, { reactive: !1 }).count() > 0) {
@@ -560,29 +580,29 @@ ${e.map((t) => `${t.id}: ${t.error.message}`).join(`
560
580
  return;
561
581
  }
562
582
  const f = s.find({
563
- id: { $nin: i.map((l) => l.id) }
583
+ id: { $nin: i.map((d) => d.id) }
564
584
  }, {
565
585
  reactive: !1
566
- }).map((l) => l.id);
586
+ }).map((d) => d.id);
567
587
  s.batch(() => {
568
- i.forEach((l) => {
588
+ i.forEach((d) => {
569
589
  this.remoteChanges.push({
570
590
  collectionName: e,
571
591
  type: "insert",
572
- data: l
592
+ data: d
573
593
  }, {
574
594
  collectionName: e,
575
595
  type: "update",
576
- data: { id: l.id, modifier: { $set: l } }
577
- }), s.replaceOne({ id: l.id }, l, { upsert: !0 });
578
- }), f.forEach((l) => {
579
- s.removeOne({ id: l });
596
+ data: { id: d.id, modifier: { $set: d } }
597
+ }), s.replaceOne({ id: d.id }, d, { upsert: !0 });
598
+ }), f.forEach((d) => {
599
+ s.removeOne({ id: d });
580
600
  });
581
601
  });
582
602
  });
583
603
  }
584
604
  }
585
605
  export {
586
- b as SyncManager
606
+ T as SyncManager
587
607
  };
588
608
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/utils/debounce.ts","../src/utils/PromiseQueue.ts","../src/computeChanges.ts","../src/getSnapshot.ts","../src/applyChanges.ts","../src/sync.ts","../src/SyncManager.ts"],"sourcesContent":["/**\n * Debounces a function.\n * @param fn Function to debounce\n * @param wait Time to wait before calling the function.\n * @param [options] Debounce options\n * @param [options.leading] Whether to call the function on the leading edge of the wait interval.\n * @param [options.trailing] Whether to call the function on the trailing edge of the wait interval.\n * @returns The debounced function.\n */\nexport default function debounce(fn, wait, options = {}) {\n let timeout;\n let result;\n const { leading = false, trailing = true } = options;\n /**\n * The debounced function that will be returned.\n * @param this The context to bind the function to.\n * @param args The arguments to pass to the function.\n * @returns The result of the debounced function.\n */\n function debounced(...args) {\n const shouldCallImmediately = leading && !timeout;\n const shouldCallTrailing = trailing && !timeout;\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(() => {\n timeout = null;\n if (trailing && !shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n }, wait);\n if (shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n else if (!shouldCallTrailing) {\n result = null;\n }\n return result;\n }\n return debounced;\n}\n","/**\n * Class for queuing promises to be executed one after the other.\n * This is useful for tasks that should not be executed in parallel.\n * @example\n * const queue = new PromiseQueue();\n * queue.add(() => fetch('https://example.com/api/endpoint1'));\n * queue.add(() => fetch('https://example.com/api/endpoint2'));\n * // The second fetch will only be executed after the first one is done.\n */\nexport default class PromiseQueue {\n queue = [];\n pendingPromise = false;\n /**\n * Method to add a new promise to the queue and returns a promise that resolves when this task is done\n * @param task Function that returns a promise that will be added to the queue\n * @returns Promise that resolves when the task is done\n */\n add(task) {\n return new Promise((resolve, reject) => {\n // Wrap the task with the resolve and reject to control its completion from the outside\n this.queue.push(() => task()\n .then(resolve)\n .catch((error) => {\n reject(error);\n throw error;\n }));\n this.dequeue();\n });\n }\n /**\n * Method to check if there is a pending promise in the queue\n * @returns True if there is a pending promise, false otherwise\n */\n hasPendingPromise() {\n return this.pendingPromise;\n }\n /**\n * Method to process the queue\n */\n dequeue() {\n if (this.pendingPromise || this.queue.length === 0) {\n return;\n }\n const task = this.queue.shift();\n if (!task)\n return;\n this.pendingPromise = true;\n task()\n .then(() => {\n this.pendingPromise = false;\n this.dequeue();\n })\n .catch(() => {\n this.pendingPromise = false;\n this.dequeue();\n });\n }\n}\n","import { isEqual } from '@signaldb/core';\n/**\n * Compute changes between two arrays of items.\n * @param oldItems Array of the old items\n * @param newItems Array of the new items\n * @returns The changeset\n */\nexport default function computeChanges(oldItems, newItems) {\n const added = [];\n const modified = [];\n const removed = [];\n const oldItemsMap = new Map(oldItems.map(item => [item.id, item]));\n const newItemsMap = new Map(newItems.map(item => [item.id, item]));\n for (const [id, oldItem] of oldItemsMap) {\n const newItem = newItemsMap.get(id);\n if (!newItem) {\n removed.push(oldItem);\n }\n else if (!isEqual(newItem, oldItem)) {\n modified.push(newItem);\n }\n }\n for (const [id, newItem] of newItemsMap) {\n if (!oldItemsMap.has(id)) {\n added.push(newItem);\n }\n }\n return { added, modified, removed };\n}\n","/**\n * Gets the snapshot of items from the last snapshot and the changes.\n * @param lastSnapshot The last snapshot of items\n * @param data The changes to apply to the last snapshot\n * @returns The new snapshot of items\n */\nexport default function getSnapshot(lastSnapshot, data) {\n if (data.items != null)\n return data.items;\n const items = lastSnapshot || [];\n data.changes.added.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.modified.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.removed.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index !== -1)\n items.splice(index, 1);\n });\n return items;\n}\n","import { modify } from '@signaldb/core';\n/**\n * applies changes to a collection of items\n * @param items The items to apply the changes to\n * @param changes The changes to apply to the items\n * @returns The new items after applying the changes\n */\nexport default function applyChanges(items, changes) {\n // Create initial map of items by ID\n const itemMap = new Map(items.map(item => [item.id, item]));\n changes.forEach((change) => {\n if (change.type === 'remove') {\n itemMap.delete(change.data);\n }\n else if (change.type === 'insert') {\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem ? { ...existingItem, ...change.data } : change.data);\n }\n else { // change.type === 'update'\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem\n ? modify(existingItem, change.data.modifier)\n : modify({ id: change.data.id }, change.data.modifier));\n }\n });\n // Convert map back to array\n return [...itemMap.values()];\n}\n","import computeChanges from './computeChanges';\nimport getSnapshot from './getSnapshot';\nimport applyChanges from './applyChanges';\n/**\n * Checks if there are any changes in the given changeset.\n * @param changes The changeset to check.\n * @returns True if there are changes, false otherwise.\n */\nfunction hasChanges(changes) {\n return changes.added.length > 0\n || changes.modified.length > 0\n || changes.removed.length > 0;\n}\n/**\n * Checks if there is a difference between the old items and the new items.\n * @param oldItems The old items.\n * @param newItems The new items.\n * @returns True if there is a difference, false otherwise.\n */\nfunction hasDifference(oldItems, newItems) {\n return hasChanges(computeChanges(oldItems, newItems));\n}\n/**\n * Does a sync operation based on the provided options. If changes are supplied, these will be rebased on the new data.\n * Afterwards the push method will be called with the remaining changes. A new snapshot will be created and returned.\n * @param options Sync options\n * @param options.changes Changes to call the push method with\n * @param [options.lastSnapshot] The last snapshot\n * @param options.data The new data\n * @param options.pull Method to pull new data\n * @param options.push Method to push changes\n * @param options.insert Method to insert an item\n * @param options.update Method to update an item\n * @param options.remove Method to remove an item\n * @param options.batch Method to batch multiple operations\n * @returns The new snapshot\n */\nexport default async function sync({ changes, lastSnapshot, data, pull, push, insert, update, remove, batch, }) {\n let newData = data;\n let previousSnapshot = lastSnapshot || [];\n let newSnapshot = getSnapshot(lastSnapshot, newData);\n if (changes.length > 0) {\n // apply changes on last snapshot and check if there is a difference\n const lastSnapshotWithChanges = applyChanges(previousSnapshot, changes);\n if (hasDifference(previousSnapshot, lastSnapshotWithChanges)) {\n // if yes, apply the changes on the newSnapshot and check if there is a difference\n const newSnapshotWithChanges = applyChanges(newSnapshot, changes);\n const changesToPush = computeChanges(newSnapshot, newSnapshotWithChanges);\n if (hasChanges(changesToPush)) {\n // if yes, push the changes to the server\n await push(changesToPush);\n // pull new data afterwards to ensure that all server changes are applied\n newData = await pull();\n newSnapshot = getSnapshot(newSnapshot, newData);\n }\n previousSnapshot = lastSnapshotWithChanges;\n }\n }\n // apply the new changes on the collection\n const newChanges = newData.changes == null\n ? computeChanges(previousSnapshot, newData.items)\n : newData.changes;\n batch(() => {\n newChanges.added.forEach(item => insert(item));\n newChanges.modified.forEach(item => update(item.id, { $set: item }));\n newChanges.removed.forEach(item => remove(item.id));\n });\n return newSnapshot;\n}\n","import { Collection, randomId, createIndex } from '@signaldb/core';\nimport debounce from './utils/debounce';\nimport PromiseQueue from './utils/PromiseQueue';\nimport sync from './sync';\n/**\n * Class to manage syncing of collections.\n * @template CollectionOptions\n * @template ItemType\n * @template IdType\n * @example\n * const syncManager = new SyncManager({\n * pull: async (collectionOptions) => {\n * const response = await fetch(`/api/collections/${collectionOptions.name}`)\n * return await response.json()\n * },\n * push: async (collectionOptions, { changes }) => {\n * await fetch(`/api/collections/${collectionOptions.name}`, {\n * method: 'POST',\n * body: JSON.stringify(changes),\n * })\n * },\n * })\n *\n * const collection = new Collection()\n * syncManager.addCollection(collection, {\n * name: 'todos',\n * })\n *\n * syncManager.sync('todos')\n */\nexport default class SyncManager {\n options;\n collections = new Map();\n changes;\n snapshots;\n syncOperations;\n scheduledPushes = new Set();\n remoteChanges = [];\n syncQueues = new Map();\n persistenceReady;\n isDisposed = false;\n instanceId = randomId();\n id;\n debouncedFlush;\n /**\n * @param options Collection options\n * @param options.pull Function to pull data from remote source.\n * @param options.push Function to push data to remote source.\n * @param [options.registerRemoteChange] Function to register a callback for remote changes.\n * @param [options.id] Unique identifier for this sync manager. Only nessesary if you have multiple sync managers.\n * @param [options.persistenceAdapter] Persistence adapter to use for storing changes, snapshots and sync operations.\n * @param [options.reactivity] Reactivity adapter to use for reactivity.\n * @param [options.onError] Function to handle errors that occur async during syncing.\n * @param [options.autostart] Whether to automatically start syncing new collections.\n * @param [options.debounceTime] The time in milliseconds to debounce push operations.\n */\n constructor(options) {\n this.options = {\n autostart: true,\n ...options,\n };\n this.id = this.options.id || 'default-sync-manager';\n const { reactivity } = this.options;\n const changesPersistence = this.createPersistenceAdapter('changes');\n const snapshotsPersistence = this.createPersistenceAdapter('snapshots');\n const syncOperationsPersistence = this.createPersistenceAdapter('sync-operations');\n this.changes = new Collection({\n name: `${this.options.id}-changes`,\n persistence: changesPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.snapshots = new Collection({\n name: `${this.options.id}-snapshots`,\n persistence: snapshotsPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.syncOperations = new Collection({\n name: `${this.options.id}-sync-operations`,\n persistence: syncOperationsPersistence?.adapter,\n indices: [createIndex('collectionName'), createIndex('status')],\n reactivity,\n });\n this.changes.on('persistence.error', error => changesPersistence?.handler(error));\n this.snapshots.on('persistence.error', error => snapshotsPersistence?.handler(error));\n this.syncOperations.on('persistence.error', error => syncOperationsPersistence?.handler(error));\n this.persistenceReady = Promise.all([\n this.syncOperations.isReady(),\n this.changes.isReady(),\n this.snapshots.isReady(),\n ]).then(() => { });\n this.changes.setMaxListeners(1000);\n this.snapshots.setMaxListeners(1000);\n this.syncOperations.setMaxListeners(1000);\n this.debouncedFlush = debounce(this.flushScheduledPushes, this.options.debounceTime ?? 100);\n }\n createPersistenceAdapter(name) {\n if (this.options.persistenceAdapter == null)\n return;\n let errorHandler = () => { };\n const adapter = this.options.persistenceAdapter(`${this.id}-${name}`, (handler) => {\n errorHandler = handler;\n });\n return {\n adapter,\n handler: (error) => errorHandler(error),\n };\n }\n getSyncQueue(name) {\n if (this.syncQueues.get(name) == null) {\n this.syncQueues.set(name, new PromiseQueue());\n }\n return this.syncQueues.get(name);\n }\n /**\n * Clears all internal data structures\n */\n async dispose() {\n this.collections.clear();\n this.syncQueues.clear();\n this.remoteChanges.splice(0, this.remoteChanges.length);\n await Promise.all([\n this.changes.dispose(),\n this.snapshots.dispose(),\n this.syncOperations.dispose(),\n ]);\n this.isDisposed = true;\n }\n /**\n * Gets a collection with it's options by name\n * @deprecated Use getCollectionProperties instead.\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns Tuple of collection and options\n */\n getCollection(name) {\n const { collection, options } = this.getCollectionProperties(name);\n return [collection, options];\n }\n /**\n * Gets collection options by name\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns An object of all properties of the collection\n */\n getCollectionProperties(name) {\n const collectionParameters = this.collections.get(name);\n if (collectionParameters == null)\n throw new Error(`Collection with id '${name}' not found`);\n return collectionParameters;\n }\n /**\n * Adds a collection to the sync manager.\n * @param collection Collection to add\n * @param options Options for the collection. The object needs at least a `name` property.\n * @param options.name Unique name of the collection\n */\n addCollection(collection, options) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n this.collections.set(options.name, {\n collection,\n options,\n readyPromise: collection.isReady(),\n syncPaused: true, // always start paused as the autostart will start it\n });\n const hasRemoteChange = (change) => {\n for (const remoteChange of this.remoteChanges) {\n if (remoteChange == null)\n continue;\n if (remoteChange.collectionName !== change.collectionName)\n continue;\n if (remoteChange.type !== change.type)\n continue;\n if (change.type === 'remove' && remoteChange.data !== change.data)\n continue;\n if (remoteChange.data.id !== change.data.id)\n continue;\n return true;\n }\n return false;\n };\n const removeRemoteChanges = (collectionName, id) => {\n const newRemoteChanges = [...this.remoteChanges];\n for (let i = 0; i < newRemoteChanges.length; i += 1) {\n const item = newRemoteChanges[i];\n if (item == null)\n continue;\n if (item.collectionName !== collectionName)\n continue;\n if (item.type === 'remove' && item.data !== id)\n continue;\n if (item.data.id !== id)\n continue;\n newRemoteChanges[i] = null;\n }\n this.remoteChanges = newRemoteChanges.filter(item => item != null);\n };\n collection.on('added', (item) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'insert', data: item })) {\n removeRemoteChanges(options.name, item.id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'insert',\n data: item,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('changed', ({ id }, modifier) => {\n const data = { id, modifier };\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'update', data })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'update',\n data,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('removed', ({ id }) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'remove', data: id })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'remove',\n data: id,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n if (this.options.autostart) {\n this.startSync(options.name)\n .catch((error) => {\n if (!this.options.onError)\n return;\n this.options.onError(this.getCollectionProperties(options.name).options, error);\n });\n }\n }\n flushScheduledPushes() {\n this.scheduledPushes.forEach((name) => {\n this.pushChanges(name).catch(() => { });\n });\n this.scheduledPushes.clear();\n }\n schedulePush(name) {\n this.scheduledPushes.add(name);\n this.debouncedFlush();\n }\n /**\n * Setup all collections to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n */\n async startAll() {\n await Promise.all([...this.collections.keys()].map(id => this.startSync(id)));\n }\n /**\n * Setup a collection to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n * @param name Name of the collection\n */\n async startSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (!collectionParameters.syncPaused)\n return; // already started\n this.schedulePush(name); // push changes that were made while paused\n const cleanupFunction = this.options.registerRemoteChange\n ? await this.options.registerRemoteChange(collectionParameters.options, async (data) => {\n if (data == null) {\n await this.sync(name);\n }\n else {\n const syncTime = Date.now();\n const syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n await this.syncWithData(name, data)\n .then(() => {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n })\n .catch((error) => {\n if (this.options.onError) {\n this.options.onError(this.getCollectionProperties(name).options, error);\n }\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n throw error;\n });\n }\n })\n : undefined;\n this.collections.set(name, {\n ...collectionParameters,\n syncPaused: false,\n cleanupFunction,\n });\n }\n /**\n * Pauses the sync process for all collections.\n * This means that the collections will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n */\n async pauseAll() {\n await Promise.all([...this.collections.keys()].map(id => this.pauseSync(id)));\n }\n /**\n * Pauses the sync process for a collection.\n * This means that the collection will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n * @param name Name of the collection\n */\n async pauseSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (collectionParameters.syncPaused)\n return; // already paused\n if (collectionParameters.cleanupFunction)\n await collectionParameters.cleanupFunction();\n this.collections.set(name, {\n ...collectionParameters,\n cleanupFunction: undefined,\n syncPaused: true,\n });\n }\n /**\n * Starts the sync process for all collections\n */\n async syncAll() {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n const errors = [];\n await Promise.all([...this.collections.keys()].map(id => this.sync(id).catch((error) => {\n errors.push({ id, error });\n })));\n if (errors.length > 0)\n throw new Error(`Error while syncing collections:\\n${errors.map(error => `${error.id}: ${error.error.message}`).join('\\n\\n')}`);\n }\n /**\n * Checks if a collection is currently beeing synced\n * @param [name] Name of the collection. If not provided, it will check if any collection is currently beeing synced.\n * @returns True if the collection is currently beeing synced, false otherwise.\n */\n isSyncing(name) {\n return this.syncOperations.findOne({\n ...name ? { collectionName: name } : {},\n status: 'active',\n }, { fields: { status: 1 } }) != null;\n }\n /**\n * Checks if the sync manager is ready to sync.\n * @returns A promise that resolves when the sync manager is ready to sync.\n */\n async isReady() {\n await this.persistenceReady;\n }\n /**\n * Starts the sync process for a collection\n * @param name Name of the collection\n * @param options Options for the sync process.\n * @param options.force If true, the sync process will be started even if there are no changes and onlyWithChanges is true.\n * @param options.onlyWithChanges If true, the sync process will only be started if there are changes.\n */\n async sync(name, options = {}) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n await this.isReady();\n const { options: collectionOptions, readyPromise } = this.getCollectionProperties(name);\n await readyPromise;\n const hasActiveSyncs = this.syncOperations.find({\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n }, {\n reactive: false,\n }).count() > 0;\n const syncTime = Date.now();\n let syncId = null;\n // schedule for next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const doSync = async () => {\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n if (options?.onlyWithChanges) {\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).count();\n if (currentChanges === 0)\n return;\n }\n if (!hasActiveSyncs) {\n syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n }\n const data = await this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n });\n await this.syncWithData(name, data);\n };\n await (options?.force ? doSync() : this.getSyncQueue(name).add(doSync))\n .catch((error) => {\n if (syncId != null) {\n if (this.options.onError)\n this.options.onError(collectionOptions, error);\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n }\n throw error;\n });\n if (syncId != null) {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n }\n }\n /**\n * Starts the push process for a collection (sync process but only if there are changes)\n * @param name Name of the collection\n */\n async pushChanges(name) {\n await this.sync(name, {\n onlyWithChanges: true,\n });\n }\n async syncWithData(name, data) {\n const { collection, options: collectionOptions } = this.getCollectionProperties(name);\n const syncTime = Date.now();\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n const lastSnapshot = this.snapshots.findOne({\n collectionName: name,\n }, {\n sort: { time: -1 },\n reactive: false,\n });\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).fetch();\n await sync({\n changes: currentChanges,\n lastSnapshot: lastSnapshot?.items,\n data,\n pull: () => this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n }),\n push: changes => this.options.push(collectionOptions, { changes }),\n insert: (item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n },\n update: (itemId, modifier) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: { id: itemId, ...modifier.$set },\n }, {\n collectionName: name,\n type: 'update',\n data: { id: itemId, modifier },\n });\n collection.updateOne({ id: itemId }, {\n ...modifier,\n $setOnInsert: { id: itemId },\n }, { upsert: true });\n },\n remove: (itemId) => {\n const itemExists = !!collection.findOne({\n id: itemId,\n }, { reactive: false });\n if (!itemExists)\n return;\n this.remoteChanges.push({\n collectionName: name,\n type: 'remove',\n data: itemId,\n });\n collection.removeOne({ id: itemId });\n },\n batch: (fn) => {\n collection.batch(() => {\n fn();\n });\n },\n })\n .then(async (snapshot) => {\n // clean up old snapshots\n this.snapshots.removeMany({\n collectionName: name,\n time: { $lte: syncTime },\n });\n // clean up processed changes\n this.changes.removeMany({\n collectionName: name,\n id: { $in: currentChanges.map(c => c.id) },\n });\n // insert new snapshot\n this.snapshots.insert({\n time: syncTime,\n collectionName: name,\n items: snapshot,\n });\n // delay sync operation update to next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const hasChanges = this.changes.find({\n collectionName: name,\n }, { reactive: false }).count() > 0;\n if (hasChanges) {\n // check if there are unsynced changes to push\n // and sync again if there are any\n await this.sync(name, {\n force: true,\n onlyWithChanges: true,\n });\n return;\n }\n // if there are no unsynced changes apply the last snapshot\n // to make sure that collection and snapshot are in sync\n // find all items that are not in the snapshot\n const nonExistingItemIds = collection.find({\n id: { $nin: snapshot.map(item => item.id) },\n }, {\n reactive: false,\n }).map(item => item.id);\n collection.batch(() => {\n // update all items that are in the snapshot\n snapshot.forEach((item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n });\n // remove all items that are not in the snapshot\n nonExistingItemIds.forEach((id) => {\n collection.removeOne({ id });\n });\n });\n });\n }\n}\n"],"names":["debounce","fn","wait","options","timeout","result","leading","trailing","debounced","args","shouldCallImmediately","shouldCallTrailing","PromiseQueue","__publicField","task","resolve","reject","error","computeChanges","oldItems","newItems","added","modified","removed","oldItemsMap","item","newItemsMap","id","oldItem","newItem","isEqual","getSnapshot","lastSnapshot","data","items","index","i","applyChanges","changes","itemMap","change","existingItem","modify","hasChanges","hasDifference","sync","pull","push","insert","update","remove","batch","newData","previousSnapshot","newSnapshot","lastSnapshotWithChanges","newSnapshotWithChanges","changesToPush","newChanges","SyncManager","randomId","reactivity","changesPersistence","snapshotsPersistence","syncOperationsPersistence","Collection","createIndex","name","errorHandler","handler","collection","collectionParameters","hasRemoteChange","remoteChange","removeRemoteChanges","collectionName","newRemoteChanges","modifier","cleanupFunction","syncTime","syncId","errors","collectionOptions","readyPromise","hasActiveSyncs","doSync","lastFinishedSync","currentChanges","itemId","snapshot","c","nonExistingItemIds"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/utils/debounce.ts","../src/utils/PromiseQueue.ts","../src/computeChanges.ts","../src/getSnapshot.ts","../src/applyChanges.ts","../src/sync.ts","../src/SyncManager.ts"],"sourcesContent":["/**\n * Debounces a function.\n * @param fn Function to debounce\n * @param wait Time to wait before calling the function.\n * @param [options] Debounce options\n * @param [options.leading] Whether to call the function on the leading edge of the wait interval.\n * @param [options.trailing] Whether to call the function on the trailing edge of the wait interval.\n * @returns The debounced function.\n */\nexport default function debounce(fn, wait, options = {}) {\n let timeout;\n let result;\n const { leading = false, trailing = true } = options;\n /**\n * The debounced function that will be returned.\n * @param this The context to bind the function to.\n * @param args The arguments to pass to the function.\n * @returns The result of the debounced function.\n */\n function debounced(...args) {\n const shouldCallImmediately = leading && !timeout;\n const shouldCallTrailing = trailing && !timeout;\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(() => {\n timeout = null;\n if (trailing && !shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n }, wait);\n if (shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n else if (!shouldCallTrailing) {\n result = null;\n }\n return result;\n }\n return debounced;\n}\n","/**\n * Class for queuing promises to be executed one after the other.\n * This is useful for tasks that should not be executed in parallel.\n * @example\n * const queue = new PromiseQueue();\n * queue.add(() => fetch('https://example.com/api/endpoint1'));\n * queue.add(() => fetch('https://example.com/api/endpoint2'));\n * // The second fetch will only be executed after the first one is done.\n */\nexport default class PromiseQueue {\n queue = [];\n pendingPromise = false;\n /**\n * Method to add a new promise to the queue and returns a promise that resolves when this task is done\n * @param task Function that returns a promise that will be added to the queue\n * @returns Promise that resolves when the task is done\n */\n add(task) {\n return new Promise((resolve, reject) => {\n // Wrap the task with the resolve and reject to control its completion from the outside\n this.queue.push(() => task()\n .then(resolve)\n .catch((error) => {\n reject(error);\n throw error;\n }));\n this.dequeue();\n });\n }\n /**\n * Method to check if there is a pending promise in the queue\n * @returns True if there is a pending promise, false otherwise\n */\n hasPendingPromise() {\n return this.pendingPromise;\n }\n /**\n * Method to process the queue\n */\n dequeue() {\n if (this.pendingPromise || this.queue.length === 0) {\n return;\n }\n const task = this.queue.shift();\n if (!task)\n return;\n this.pendingPromise = true;\n task()\n .then(() => {\n this.pendingPromise = false;\n this.dequeue();\n })\n .catch(() => {\n this.pendingPromise = false;\n this.dequeue();\n });\n }\n}\n","import { isEqual } from '@signaldb/core';\n/**\n * Computes the modified fields between two items recursively.\n * @param oldItem The old item\n * @param newItem The new item\n * @returns The modified fields\n */\nexport function computeModifiedFields(oldItem, newItem) {\n const modifiedFields = [];\n const oldKeys = Object.keys(oldItem);\n const newKeys = Object.keys(newItem);\n const allKeys = new Set([...oldKeys, ...newKeys]);\n for (const key of allKeys) {\n if (newItem[key] !== oldItem[key]) {\n if (typeof newItem[key] === 'object' && typeof oldItem[key] === 'object') {\n const nestedModifiedFields = computeModifiedFields(oldItem[key], newItem[key]);\n for (const nestedField of nestedModifiedFields) {\n modifiedFields.push(`${key}.${nestedField}`);\n }\n }\n else {\n modifiedFields.push(key);\n }\n }\n }\n return modifiedFields;\n}\n/**\n * Compute changes between two arrays of items.\n * @param oldItems Array of the old items\n * @param newItems Array of the new items\n * @returns The changeset\n */\nexport default function computeChanges(oldItems, newItems) {\n const added = [];\n const modified = [];\n const modifiedFields = new Map();\n const removed = [];\n const oldItemsMap = new Map(oldItems.map(item => [item.id, item]));\n const newItemsMap = new Map(newItems.map(item => [item.id, item]));\n for (const [id, oldItem] of oldItemsMap) {\n const newItem = newItemsMap.get(id);\n if (!newItem) {\n removed.push(oldItem);\n }\n else if (!isEqual(newItem, oldItem)) {\n modifiedFields.set(newItem.id, computeModifiedFields(oldItem, newItem));\n modified.push(newItem);\n }\n }\n for (const [id, newItem] of newItemsMap) {\n if (!oldItemsMap.has(id)) {\n added.push(newItem);\n }\n }\n return {\n added,\n modified,\n modifiedFields,\n removed,\n };\n}\n","/**\n * Gets the snapshot of items from the last snapshot and the changes.\n * @param lastSnapshot The last snapshot of items\n * @param data The changes to apply to the last snapshot\n * @returns The new snapshot of items\n */\nexport default function getSnapshot(lastSnapshot, data) {\n if (data.items != null)\n return data.items;\n const items = lastSnapshot || [];\n data.changes.added.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.modified.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.removed.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index !== -1)\n items.splice(index, 1);\n });\n return items;\n}\n","import { modify } from '@signaldb/core';\n/**\n * applies changes to a collection of items\n * @param items The items to apply the changes to\n * @param changes The changes to apply to the items\n * @returns The new items after applying the changes\n */\nexport default function applyChanges(items, changes) {\n // Create initial map of items by ID\n const itemMap = new Map(items.map(item => [item.id, item]));\n changes.forEach((change) => {\n if (change.type === 'remove') {\n itemMap.delete(change.data);\n }\n else if (change.type === 'insert') {\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem ? { ...existingItem, ...change.data } : change.data);\n }\n else { // change.type === 'update'\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem\n ? modify(existingItem, change.data.modifier)\n : modify({ id: change.data.id }, change.data.modifier));\n }\n });\n // Convert map back to array\n return [...itemMap.values()];\n}\n","import computeChanges from './computeChanges';\nimport getSnapshot from './getSnapshot';\nimport applyChanges from './applyChanges';\n/**\n * Checks if there are any changes in the given changeset.\n * @param changes The changeset to check.\n * @returns True if there are changes, false otherwise.\n */\nfunction hasChanges(changes) {\n return changes.added.length > 0\n || changes.modified.length > 0\n || changes.removed.length > 0;\n}\n/**\n * Checks if there is a difference between the old items and the new items.\n * @param oldItems The old items.\n * @param newItems The new items.\n * @returns True if there is a difference, false otherwise.\n */\nfunction hasDifference(oldItems, newItems) {\n return hasChanges(computeChanges(oldItems, newItems));\n}\n/**\n * Does a sync operation based on the provided options. If changes are supplied, these will be rebased on the new data.\n * Afterwards the push method will be called with the remaining changes. A new snapshot will be created and returned.\n * @param options Sync options\n * @param options.changes Changes to call the push method with\n * @param [options.lastSnapshot] The last snapshot\n * @param options.data The new data\n * @param options.pull Method to pull new data\n * @param options.push Method to push changes\n * @param options.insert Method to insert an item\n * @param options.update Method to update an item\n * @param options.remove Method to remove an item\n * @param options.batch Method to batch multiple operations\n * @returns The new snapshot\n */\nexport default async function sync({ changes, lastSnapshot, data, pull, push, insert, update, remove, batch, }) {\n let newData = data;\n let previousSnapshot = lastSnapshot || [];\n let newSnapshot = getSnapshot(lastSnapshot, newData);\n if (changes.length > 0) {\n // apply changes on last snapshot and check if there is a difference\n const lastSnapshotWithChanges = applyChanges(previousSnapshot, changes);\n if (hasDifference(previousSnapshot, lastSnapshotWithChanges)) {\n // if yes, apply the changes on the newSnapshot and check if there is a difference\n const newSnapshotWithChanges = applyChanges(newSnapshot, changes);\n const changesToPush = computeChanges(newSnapshot, newSnapshotWithChanges);\n if (hasChanges(changesToPush)) {\n // if yes, push the changes to the server\n await push(changesToPush);\n // pull new data afterwards to ensure that all server changes are applied\n newData = await pull();\n newSnapshot = getSnapshot(newSnapshot, newData);\n }\n previousSnapshot = lastSnapshotWithChanges;\n }\n }\n // apply the new changes on the collection\n const newChanges = newData.changes == null\n ? computeChanges(previousSnapshot, newData.items)\n : newData.changes;\n batch(() => {\n newChanges.added.forEach(item => insert(item));\n newChanges.modified.forEach(item => update(item.id, { $set: item }));\n newChanges.removed.forEach(item => remove(item.id));\n });\n return newSnapshot;\n}\n","import { Collection, randomId, createIndex } from '@signaldb/core';\nimport debounce from './utils/debounce';\nimport PromiseQueue from './utils/PromiseQueue';\nimport sync from './sync';\n/**\n * Class to manage syncing of collections.\n * @template CollectionOptions\n * @template ItemType\n * @template IdType\n * @example\n * const syncManager = new SyncManager({\n * pull: async (collectionOptions) => {\n * const response = await fetch(`/api/collections/${collectionOptions.name}`)\n * return await response.json()\n * },\n * push: async (collectionOptions, { changes }) => {\n * await fetch(`/api/collections/${collectionOptions.name}`, {\n * method: 'POST',\n * body: JSON.stringify(changes),\n * })\n * },\n * })\n *\n * const collection = new Collection()\n * syncManager.addCollection(collection, {\n * name: 'todos',\n * })\n *\n * syncManager.sync('todos')\n */\nexport default class SyncManager {\n options;\n collections = new Map();\n changes;\n snapshots;\n syncOperations;\n scheduledPushes = new Set();\n remoteChanges = [];\n syncQueues = new Map();\n persistenceReady;\n isDisposed = false;\n instanceId = randomId();\n id;\n debouncedFlush;\n /**\n * @param options Collection options\n * @param options.pull Function to pull data from remote source.\n * @param options.push Function to push data to remote source.\n * @param [options.registerRemoteChange] Function to register a callback for remote changes.\n * @param [options.id] Unique identifier for this sync manager. Only nessesary if you have multiple sync managers.\n * @param [options.persistenceAdapter] Persistence adapter to use for storing changes, snapshots and sync operations.\n * @param [options.reactivity] Reactivity adapter to use for reactivity.\n * @param [options.onError] Function to handle errors that occur async during syncing.\n * @param [options.autostart] Whether to automatically start syncing new collections.\n * @param [options.debounceTime] The time in milliseconds to debounce push operations.\n */\n constructor(options) {\n this.options = {\n autostart: true,\n ...options,\n };\n this.id = this.options.id || 'default-sync-manager';\n const { reactivity } = this.options;\n const changesPersistence = this.createPersistenceAdapter('changes');\n const snapshotsPersistence = this.createPersistenceAdapter('snapshots');\n const syncOperationsPersistence = this.createPersistenceAdapter('sync-operations');\n this.changes = new Collection({\n name: `${this.options.id}-changes`,\n persistence: changesPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.snapshots = new Collection({\n name: `${this.options.id}-snapshots`,\n persistence: snapshotsPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.syncOperations = new Collection({\n name: `${this.options.id}-sync-operations`,\n persistence: syncOperationsPersistence?.adapter,\n indices: [createIndex('collectionName'), createIndex('status')],\n reactivity,\n });\n this.changes.on('persistence.error', error => changesPersistence?.handler(error));\n this.snapshots.on('persistence.error', error => snapshotsPersistence?.handler(error));\n this.syncOperations.on('persistence.error', error => syncOperationsPersistence?.handler(error));\n this.persistenceReady = Promise.all([\n this.syncOperations.isReady(),\n this.changes.isReady(),\n this.snapshots.isReady(),\n ]).then(() => { });\n this.changes.setMaxListeners(1000);\n this.snapshots.setMaxListeners(1000);\n this.syncOperations.setMaxListeners(1000);\n this.debouncedFlush = debounce(this.flushScheduledPushes, this.options.debounceTime ?? 100);\n }\n createPersistenceAdapter(name) {\n if (this.options.persistenceAdapter == null)\n return;\n let errorHandler = () => { };\n const adapter = this.options.persistenceAdapter(`${this.id}-${name}`, (handler) => {\n errorHandler = handler;\n });\n return {\n adapter,\n handler: (error) => errorHandler(error),\n };\n }\n getSyncQueue(name) {\n if (this.syncQueues.get(name) == null) {\n this.syncQueues.set(name, new PromiseQueue());\n }\n return this.syncQueues.get(name);\n }\n /**\n * Clears all internal data structures\n */\n async dispose() {\n this.collections.clear();\n this.syncQueues.clear();\n this.remoteChanges.splice(0, this.remoteChanges.length);\n await Promise.all([\n this.changes.dispose(),\n this.snapshots.dispose(),\n this.syncOperations.dispose(),\n ]);\n this.isDisposed = true;\n }\n /**\n * Gets a collection with it's options by name\n * @deprecated Use getCollectionProperties instead.\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns Tuple of collection and options\n */\n getCollection(name) {\n const { collection, options } = this.getCollectionProperties(name);\n return [collection, options];\n }\n /**\n * Gets collection options by name\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns An object of all properties of the collection\n */\n getCollectionProperties(name) {\n const collectionParameters = this.collections.get(name);\n if (collectionParameters == null)\n throw new Error(`Collection with id '${name}' not found`);\n return collectionParameters;\n }\n /**\n * Adds a collection to the sync manager.\n * @param collection Collection to add\n * @param options Options for the collection. The object needs at least a `name` property.\n * @param options.name Unique name of the collection\n */\n addCollection(collection, options) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n this.collections.set(options.name, {\n collection,\n options,\n readyPromise: collection.isReady(),\n syncPaused: true, // always start paused as the autostart will start it\n });\n const hasRemoteChange = (change) => {\n for (const remoteChange of this.remoteChanges) {\n if (remoteChange == null)\n continue;\n if (remoteChange.collectionName !== change.collectionName)\n continue;\n if (remoteChange.type !== change.type)\n continue;\n if (change.type === 'remove' && remoteChange.data !== change.data)\n continue;\n if (remoteChange.data.id !== change.data.id)\n continue;\n return true;\n }\n return false;\n };\n const removeRemoteChanges = (collectionName, id) => {\n const newRemoteChanges = [...this.remoteChanges];\n for (let i = 0; i < newRemoteChanges.length; i += 1) {\n const item = newRemoteChanges[i];\n if (item == null)\n continue;\n if (item.collectionName !== collectionName)\n continue;\n if (item.type === 'remove' && item.data !== id)\n continue;\n if (item.data.id !== id)\n continue;\n newRemoteChanges[i] = null;\n }\n this.remoteChanges = newRemoteChanges.filter(item => item != null);\n };\n collection.on('added', (item) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'insert', data: item })) {\n removeRemoteChanges(options.name, item.id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'insert',\n data: item,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('changed', ({ id }, modifier) => {\n const data = { id, modifier };\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'update', data })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'update',\n data,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('removed', ({ id }) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'remove', data: id })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'remove',\n data: id,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n if (this.options.autostart) {\n this.startSync(options.name)\n .catch((error) => {\n if (!this.options.onError)\n return;\n this.options.onError(this.getCollectionProperties(options.name).options, error);\n });\n }\n }\n flushScheduledPushes() {\n this.scheduledPushes.forEach((name) => {\n this.pushChanges(name).catch(() => { });\n });\n this.scheduledPushes.clear();\n }\n schedulePush(name) {\n this.scheduledPushes.add(name);\n this.debouncedFlush();\n }\n /**\n * Setup all collections to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n */\n async startAll() {\n await Promise.all([...this.collections.keys()].map(id => this.startSync(id)));\n }\n /**\n * Setup a collection to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n * @param name Name of the collection\n */\n async startSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (!collectionParameters.syncPaused)\n return; // already started\n this.schedulePush(name); // push changes that were made while paused\n const cleanupFunction = this.options.registerRemoteChange\n ? await this.options.registerRemoteChange(collectionParameters.options, async (data) => {\n if (data == null) {\n await this.sync(name);\n }\n else {\n const syncTime = Date.now();\n const syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n await this.syncWithData(name, data)\n .then(() => {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n })\n .catch((error) => {\n if (this.options.onError) {\n this.options.onError(this.getCollectionProperties(name).options, error);\n }\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n throw error;\n });\n }\n })\n : undefined;\n this.collections.set(name, {\n ...collectionParameters,\n syncPaused: false,\n cleanupFunction,\n });\n }\n /**\n * Pauses the sync process for all collections.\n * This means that the collections will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n */\n async pauseAll() {\n await Promise.all([...this.collections.keys()].map(id => this.pauseSync(id)));\n }\n /**\n * Pauses the sync process for a collection.\n * This means that the collection will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n * @param name Name of the collection\n */\n async pauseSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (collectionParameters.syncPaused)\n return; // already paused\n if (collectionParameters.cleanupFunction)\n await collectionParameters.cleanupFunction();\n this.collections.set(name, {\n ...collectionParameters,\n cleanupFunction: undefined,\n syncPaused: true,\n });\n }\n /**\n * Starts the sync process for all collections\n */\n async syncAll() {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n const errors = [];\n await Promise.all([...this.collections.keys()].map(id => this.sync(id).catch((error) => {\n errors.push({ id, error });\n })));\n if (errors.length > 0)\n throw new Error(`Error while syncing collections:\\n${errors.map(error => `${error.id}: ${error.error.message}`).join('\\n\\n')}`);\n }\n /**\n * Checks if a collection is currently beeing synced\n * @param [name] Name of the collection. If not provided, it will check if any collection is currently beeing synced.\n * @returns True if the collection is currently beeing synced, false otherwise.\n */\n isSyncing(name) {\n return this.syncOperations.findOne({\n ...name ? { collectionName: name } : {},\n status: 'active',\n }, { fields: { status: 1 } }) != null;\n }\n /**\n * Checks if the sync manager is ready to sync.\n * @returns A promise that resolves when the sync manager is ready to sync.\n */\n async isReady() {\n await this.persistenceReady;\n }\n /**\n * Starts the sync process for a collection\n * @param name Name of the collection\n * @param options Options for the sync process.\n * @param options.force If true, the sync process will be started even if there are no changes and onlyWithChanges is true.\n * @param options.onlyWithChanges If true, the sync process will only be started if there are changes.\n */\n async sync(name, options = {}) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n await this.isReady();\n const { options: collectionOptions, readyPromise } = this.getCollectionProperties(name);\n await readyPromise;\n const hasActiveSyncs = this.syncOperations.find({\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n }, {\n reactive: false,\n }).count() > 0;\n const syncTime = Date.now();\n let syncId = null;\n // schedule for next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const doSync = async () => {\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n if (options?.onlyWithChanges) {\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).count();\n if (currentChanges === 0)\n return;\n }\n if (!hasActiveSyncs) {\n syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n }\n const data = await this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n });\n await this.syncWithData(name, data);\n };\n await (options?.force ? doSync() : this.getSyncQueue(name).add(doSync))\n .catch((error) => {\n if (syncId != null) {\n if (this.options.onError)\n this.options.onError(collectionOptions, error);\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n }\n throw error;\n });\n if (syncId != null) {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n }\n }\n /**\n * Starts the push process for a collection (sync process but only if there are changes)\n * @param name Name of the collection\n */\n async pushChanges(name) {\n await this.sync(name, {\n onlyWithChanges: true,\n });\n }\n async syncWithData(name, data) {\n const { collection, options: collectionOptions } = this.getCollectionProperties(name);\n const syncTime = Date.now();\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n const lastSnapshot = this.snapshots.findOne({\n collectionName: name,\n }, {\n sort: { time: -1 },\n reactive: false,\n });\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).fetch();\n await sync({\n changes: currentChanges,\n lastSnapshot: lastSnapshot?.items,\n data,\n pull: () => this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n }),\n push: changes => this.options.push(collectionOptions, {\n changes,\n rawChanges: currentChanges,\n }),\n insert: (item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n },\n update: (itemId, modifier) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: { id: itemId, ...modifier.$set },\n }, {\n collectionName: name,\n type: 'update',\n data: { id: itemId, modifier },\n });\n collection.updateOne({ id: itemId }, {\n ...modifier,\n $setOnInsert: { id: itemId },\n }, { upsert: true });\n },\n remove: (itemId) => {\n const itemExists = !!collection.findOne({\n id: itemId,\n }, { reactive: false });\n if (!itemExists)\n return;\n this.remoteChanges.push({\n collectionName: name,\n type: 'remove',\n data: itemId,\n });\n collection.removeOne({ id: itemId });\n },\n batch: (fn) => {\n collection.batch(() => {\n fn();\n });\n },\n })\n .then(async (snapshot) => {\n // clean up old snapshots\n this.snapshots.removeMany({\n collectionName: name,\n time: { $lte: syncTime },\n });\n // clean up processed changes\n this.changes.removeMany({\n collectionName: name,\n id: { $in: currentChanges.map(c => c.id) },\n });\n // insert new snapshot\n this.snapshots.insert({\n time: syncTime,\n collectionName: name,\n items: snapshot,\n });\n // delay sync operation update to next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const hasChanges = this.changes.find({\n collectionName: name,\n }, { reactive: false }).count() > 0;\n if (hasChanges) {\n // check if there are unsynced changes to push\n // and sync again if there are any\n await this.sync(name, {\n force: true,\n onlyWithChanges: true,\n });\n return;\n }\n // if there are no unsynced changes apply the last snapshot\n // to make sure that collection and snapshot are in sync\n // find all items that are not in the snapshot\n const nonExistingItemIds = collection.find({\n id: { $nin: snapshot.map(item => item.id) },\n }, {\n reactive: false,\n }).map(item => item.id);\n collection.batch(() => {\n // update all items that are in the snapshot\n snapshot.forEach((item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n });\n // remove all items that are not in the snapshot\n nonExistingItemIds.forEach((id) => {\n collection.removeOne({ id });\n });\n });\n });\n }\n}\n"],"names":["debounce","fn","wait","options","timeout","result","leading","trailing","debounced","args","shouldCallImmediately","shouldCallTrailing","PromiseQueue","__publicField","task","resolve","reject","error","computeModifiedFields","oldItem","newItem","modifiedFields","oldKeys","newKeys","allKeys","key","nestedModifiedFields","nestedField","computeChanges","oldItems","newItems","added","modified","removed","oldItemsMap","item","newItemsMap","id","isEqual","getSnapshot","lastSnapshot","data","items","index","i","applyChanges","changes","itemMap","change","existingItem","modify","hasChanges","hasDifference","sync","pull","push","insert","update","remove","batch","newData","previousSnapshot","newSnapshot","lastSnapshotWithChanges","newSnapshotWithChanges","changesToPush","newChanges","SyncManager","randomId","reactivity","changesPersistence","snapshotsPersistence","syncOperationsPersistence","Collection","createIndex","name","errorHandler","handler","collection","collectionParameters","hasRemoteChange","remoteChange","removeRemoteChanges","collectionName","newRemoteChanges","modifier","cleanupFunction","syncTime","syncId","errors","collectionOptions","readyPromise","hasActiveSyncs","doSync","lastFinishedSync","currentChanges","itemId","snapshot","c","nonExistingItemIds"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
- (function(f,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("@signaldb/core")):typeof define=="function"&&define.amd?define(["exports","@signaldb/core"],r):(f=typeof globalThis<"u"?globalThis:f||self,r(f.SignalDB={},f.core))})(this,function(f,r){"use strict";var D=Object.defineProperty;var M=(f,r,g)=>r in f?D(f,r,{enumerable:!0,configurable:!0,writable:!0,value:g}):f[r]=g;var u=(f,r,g)=>M(f,typeof r!="symbol"?r+"":r,g);function g(l,e,t={}){let s,n;const{leading:a=!1,trailing:o=!0}=t;function c(...h){const i=a&&!s,p=o&&!s;return s&&clearTimeout(s),s=setTimeout(()=>{s=null,o&&!i&&(n=l.apply(this,h))},e),i?n=l.apply(this,h):p||(n=null),n}return c}class O{constructor(){u(this,"queue",[]);u(this,"pendingPromise",!1)}add(e){return new Promise((t,s)=>{this.queue.push(()=>e().then(t).catch(n=>{throw s(n),n})),this.dequeue()})}hasPendingPromise(){return this.pendingPromise}dequeue(){if(this.pendingPromise||this.queue.length===0)return;const e=this.queue.shift();e&&(this.pendingPromise=!0,e().then(()=>{this.pendingPromise=!1,this.dequeue()}).catch(()=>{this.pendingPromise=!1,this.dequeue()}))}}function w(l,e){const t=[],s=[],n=[],a=new Map(l.map(c=>[c.id,c])),o=new Map(e.map(c=>[c.id,c]));for(const[c,h]of a){const i=o.get(c);i?r.isEqual(i,h)||s.push(i):n.push(h)}for(const[c,h]of o)a.has(c)||t.push(h);return{added:t,modified:s,removed:n}}function C(l,e){if(e.items!=null)return e.items;const t=l||[];return e.changes.added.forEach(s=>{const n=t.findIndex(a=>a.id===s.id);n===-1?t.push(s):t[n]=s}),e.changes.modified.forEach(s=>{const n=t.findIndex(a=>a.id===s.id);n===-1?t.push(s):t[n]=s}),e.changes.removed.forEach(s=>{const n=t.findIndex(a=>a.id===s.id);n!==-1&&t.splice(n,1)}),t}function P(l,e){const t=new Map(l.map(s=>[s.id,s]));return e.forEach(s=>{if(s.type==="remove")t.delete(s.data);else if(s.type==="insert"){const n=t.get(s.data.id);t.set(s.data.id,n?{...n,...s.data}:s.data)}else{const n=t.get(s.data.id);t.set(s.data.id,n?r.modify(n,s.data.modifier):r.modify({id:s.data.id},s.data.modifier))}}),[...t.values()]}function v(l){return l.added.length>0||l.modified.length>0||l.removed.length>0}function $(l,e){return v(w(l,e))}async function E({changes:l,lastSnapshot:e,data:t,pull:s,push:n,insert:a,update:o,remove:c,batch:h}){let i=t,p=e||[],m=C(e,i);if(l.length>0){const y=P(p,l);if($(p,y)){const I=P(m,l),N=w(m,I);v(N)&&(await n(N),i=await s(),m=C(m,i)),p=y}}const d=i.changes==null?w(p,i.items):i.changes;return h(()=>{d.added.forEach(y=>a(y)),d.modified.forEach(y=>o(y.id,{$set:y})),d.removed.forEach(y=>c(y.id))}),m}class S{constructor(e){u(this,"options");u(this,"collections",new Map);u(this,"changes");u(this,"snapshots");u(this,"syncOperations");u(this,"scheduledPushes",new Set);u(this,"remoteChanges",[]);u(this,"syncQueues",new Map);u(this,"persistenceReady");u(this,"isDisposed",!1);u(this,"instanceId",r.randomId());u(this,"id");u(this,"debouncedFlush");this.options={autostart:!0,...e},this.id=this.options.id||"default-sync-manager";const{reactivity:t}=this.options,s=this.createPersistenceAdapter("changes"),n=this.createPersistenceAdapter("snapshots"),a=this.createPersistenceAdapter("sync-operations");this.changes=new r.Collection({name:`${this.options.id}-changes`,persistence:s==null?void 0:s.adapter,indices:[r.createIndex("collectionName")],reactivity:t}),this.snapshots=new r.Collection({name:`${this.options.id}-snapshots`,persistence:n==null?void 0:n.adapter,indices:[r.createIndex("collectionName")],reactivity:t}),this.syncOperations=new r.Collection({name:`${this.options.id}-sync-operations`,persistence:a==null?void 0:a.adapter,indices:[r.createIndex("collectionName"),r.createIndex("status")],reactivity:t}),this.changes.on("persistence.error",o=>s==null?void 0:s.handler(o)),this.snapshots.on("persistence.error",o=>n==null?void 0:n.handler(o)),this.syncOperations.on("persistence.error",o=>a==null?void 0:a.handler(o)),this.persistenceReady=Promise.all([this.syncOperations.isReady(),this.changes.isReady(),this.snapshots.isReady()]).then(()=>{}),this.changes.setMaxListeners(1e3),this.snapshots.setMaxListeners(1e3),this.syncOperations.setMaxListeners(1e3),this.debouncedFlush=g(this.flushScheduledPushes,this.options.debounceTime??100)}createPersistenceAdapter(e){if(this.options.persistenceAdapter==null)return;let t=()=>{};return{adapter:this.options.persistenceAdapter(`${this.id}-${e}`,n=>{t=n}),handler:n=>t(n)}}getSyncQueue(e){return this.syncQueues.get(e)==null&&this.syncQueues.set(e,new O),this.syncQueues.get(e)}async dispose(){this.collections.clear(),this.syncQueues.clear(),this.remoteChanges.splice(0,this.remoteChanges.length),await Promise.all([this.changes.dispose(),this.snapshots.dispose(),this.syncOperations.dispose()]),this.isDisposed=!0}getCollection(e){const{collection:t,options:s}=this.getCollectionProperties(e);return[t,s]}getCollectionProperties(e){const t=this.collections.get(e);if(t==null)throw new Error(`Collection with id '${e}' not found`);return t}addCollection(e,t){if(this.isDisposed)throw new Error("SyncManager is disposed");this.collections.set(t.name,{collection:e,options:t,readyPromise:e.isReady(),syncPaused:!0});const s=a=>{for(const o of this.remoteChanges)if(o!=null&&o.collectionName===a.collectionName&&o.type===a.type&&!(a.type==="remove"&&o.data!==a.data)&&o.data.id===a.data.id)return!0;return!1},n=(a,o)=>{const c=[...this.remoteChanges];for(let h=0;h<c.length;h+=1){const i=c[h];i!=null&&i.collectionName===a&&(i.type==="remove"&&i.data!==o||i.data.id===o&&(c[h]=null))}this.remoteChanges=c.filter(h=>h!=null)};e.on("added",a=>{if(s({collectionName:t.name,type:"insert",data:a})){n(t.name,a.id);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"insert",data:a}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),e.on("changed",({id:a},o)=>{const c={id:a,modifier:o};if(s({collectionName:t.name,type:"update",data:c})){n(t.name,a);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"update",data:c}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),e.on("removed",({id:a})=>{if(s({collectionName:t.name,type:"remove",data:a})){n(t.name,a);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"remove",data:a}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),this.options.autostart&&this.startSync(t.name).catch(a=>{this.options.onError&&this.options.onError(this.getCollectionProperties(t.name).options,a)})}flushScheduledPushes(){this.scheduledPushes.forEach(e=>{this.pushChanges(e).catch(()=>{})}),this.scheduledPushes.clear()}schedulePush(e){this.scheduledPushes.add(e),this.debouncedFlush()}async startAll(){await Promise.all([...this.collections.keys()].map(e=>this.startSync(e)))}async startSync(e){const t=this.getCollectionProperties(e);if(!t.syncPaused)return;this.schedulePush(e);const s=this.options.registerRemoteChange?await this.options.registerRemoteChange(t.options,async n=>{if(n==null)await this.sync(e);else{const a=Date.now(),o=this.syncOperations.insert({start:a,collectionName:e,instanceId:this.instanceId,status:"active"});await this.syncWithData(e,n).then(()=>{this.syncOperations.removeMany({id:{$ne:o},collectionName:e,$or:[{end:{$lte:a}},{status:"active"}]}),this.syncOperations.updateOne({id:o},{$set:{status:"done",end:Date.now()}})}).catch(c=>{throw this.options.onError&&this.options.onError(this.getCollectionProperties(e).options,c),this.syncOperations.updateOne({id:o},{$set:{status:"error",end:Date.now(),error:c.stack||c.message}}),c})}}):void 0;this.collections.set(e,{...t,syncPaused:!1,cleanupFunction:s})}async pauseAll(){await Promise.all([...this.collections.keys()].map(e=>this.pauseSync(e)))}async pauseSync(e){const t=this.getCollectionProperties(e);t.syncPaused||(t.cleanupFunction&&await t.cleanupFunction(),this.collections.set(e,{...t,cleanupFunction:void 0,syncPaused:!0}))}async syncAll(){if(this.isDisposed)throw new Error("SyncManager is disposed");const e=[];if(await Promise.all([...this.collections.keys()].map(t=>this.sync(t).catch(s=>{e.push({id:t,error:s})}))),e.length>0)throw new Error(`Error while syncing collections:
1
+ (function(f,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@signaldb/core")):typeof define=="function"&&define.amd?define(["exports","@signaldb/core"],l):(f=typeof globalThis<"u"?globalThis:f||self,l(f.SignalDB={},f.core))})(this,function(f,l){"use strict";var x=Object.defineProperty;var b=(f,l,g)=>l in f?x(f,l,{enumerable:!0,configurable:!0,writable:!0,value:g}):f[l]=g;var p=(f,l,g)=>b(f,typeof l!="symbol"?l+"":l,g);function g(d,e,t={}){let s,n;const{leading:o=!1,trailing:a=!0}=t;function c(...r){const i=o&&!s,h=a&&!s;return s&&clearTimeout(s),s=setTimeout(()=>{s=null,a&&!i&&(n=d.apply(this,r))},e),i?n=d.apply(this,r):h||(n=null),n}return c}class ${constructor(){p(this,"queue",[]);p(this,"pendingPromise",!1)}add(e){return new Promise((t,s)=>{this.queue.push(()=>e().then(t).catch(n=>{throw s(n),n})),this.dequeue()})}hasPendingPromise(){return this.pendingPromise}dequeue(){if(this.pendingPromise||this.queue.length===0)return;const e=this.queue.shift();e&&(this.pendingPromise=!0,e().then(()=>{this.pendingPromise=!1,this.dequeue()}).catch(()=>{this.pendingPromise=!1,this.dequeue()}))}}function C(d,e){const t=[],s=Object.keys(d),n=Object.keys(e),o=new Set([...s,...n]);for(const a of o)if(e[a]!==d[a])if(typeof e[a]=="object"&&typeof d[a]=="object"){const c=C(d[a],e[a]);for(const r of c)t.push(`${a}.${r}`)}else t.push(a);return t}function w(d,e){const t=[],s=[],n=new Map,o=[],a=new Map(d.map(r=>[r.id,r])),c=new Map(e.map(r=>[r.id,r]));for(const[r,i]of a){const h=c.get(r);h?l.isEqual(h,i)||(n.set(h.id,C(i,h)),s.push(h)):o.push(i)}for(const[r,i]of c)a.has(r)||t.push(i);return{added:t,modified:s,modifiedFields:n,removed:o}}function P(d,e){if(e.items!=null)return e.items;const t=d||[];return e.changes.added.forEach(s=>{const n=t.findIndex(o=>o.id===s.id);n===-1?t.push(s):t[n]=s}),e.changes.modified.forEach(s=>{const n=t.findIndex(o=>o.id===s.id);n===-1?t.push(s):t[n]=s}),e.changes.removed.forEach(s=>{const n=t.findIndex(o=>o.id===s.id);n!==-1&&t.splice(n,1)}),t}function v(d,e){const t=new Map(d.map(s=>[s.id,s]));return e.forEach(s=>{if(s.type==="remove")t.delete(s.data);else if(s.type==="insert"){const n=t.get(s.data.id);t.set(s.data.id,n?{...n,...s.data}:s.data)}else{const n=t.get(s.data.id);t.set(s.data.id,n?l.modify(n,s.data.modifier):l.modify({id:s.data.id},s.data.modifier))}}),[...t.values()]}function O(d){return d.added.length>0||d.modified.length>0||d.removed.length>0}function E(d,e){return O(w(d,e))}async function S({changes:d,lastSnapshot:e,data:t,pull:s,push:n,insert:o,update:a,remove:c,batch:r}){let i=t,h=e||[],m=P(e,i);if(d.length>0){const y=v(h,d);if(E(h,y)){const D=v(m,d),N=w(m,D);O(N)&&(await n(N),i=await s(),m=P(m,i)),h=y}}const u=i.changes==null?w(h,i.items):i.changes;return r(()=>{u.added.forEach(y=>o(y)),u.modified.forEach(y=>a(y.id,{$set:y})),u.removed.forEach(y=>c(y.id))}),m}class M{constructor(e){p(this,"options");p(this,"collections",new Map);p(this,"changes");p(this,"snapshots");p(this,"syncOperations");p(this,"scheduledPushes",new Set);p(this,"remoteChanges",[]);p(this,"syncQueues",new Map);p(this,"persistenceReady");p(this,"isDisposed",!1);p(this,"instanceId",l.randomId());p(this,"id");p(this,"debouncedFlush");this.options={autostart:!0,...e},this.id=this.options.id||"default-sync-manager";const{reactivity:t}=this.options,s=this.createPersistenceAdapter("changes"),n=this.createPersistenceAdapter("snapshots"),o=this.createPersistenceAdapter("sync-operations");this.changes=new l.Collection({name:`${this.options.id}-changes`,persistence:s==null?void 0:s.adapter,indices:[l.createIndex("collectionName")],reactivity:t}),this.snapshots=new l.Collection({name:`${this.options.id}-snapshots`,persistence:n==null?void 0:n.adapter,indices:[l.createIndex("collectionName")],reactivity:t}),this.syncOperations=new l.Collection({name:`${this.options.id}-sync-operations`,persistence:o==null?void 0:o.adapter,indices:[l.createIndex("collectionName"),l.createIndex("status")],reactivity:t}),this.changes.on("persistence.error",a=>s==null?void 0:s.handler(a)),this.snapshots.on("persistence.error",a=>n==null?void 0:n.handler(a)),this.syncOperations.on("persistence.error",a=>o==null?void 0:o.handler(a)),this.persistenceReady=Promise.all([this.syncOperations.isReady(),this.changes.isReady(),this.snapshots.isReady()]).then(()=>{}),this.changes.setMaxListeners(1e3),this.snapshots.setMaxListeners(1e3),this.syncOperations.setMaxListeners(1e3),this.debouncedFlush=g(this.flushScheduledPushes,this.options.debounceTime??100)}createPersistenceAdapter(e){if(this.options.persistenceAdapter==null)return;let t=()=>{};return{adapter:this.options.persistenceAdapter(`${this.id}-${e}`,n=>{t=n}),handler:n=>t(n)}}getSyncQueue(e){return this.syncQueues.get(e)==null&&this.syncQueues.set(e,new $),this.syncQueues.get(e)}async dispose(){this.collections.clear(),this.syncQueues.clear(),this.remoteChanges.splice(0,this.remoteChanges.length),await Promise.all([this.changes.dispose(),this.snapshots.dispose(),this.syncOperations.dispose()]),this.isDisposed=!0}getCollection(e){const{collection:t,options:s}=this.getCollectionProperties(e);return[t,s]}getCollectionProperties(e){const t=this.collections.get(e);if(t==null)throw new Error(`Collection with id '${e}' not found`);return t}addCollection(e,t){if(this.isDisposed)throw new Error("SyncManager is disposed");this.collections.set(t.name,{collection:e,options:t,readyPromise:e.isReady(),syncPaused:!0});const s=o=>{for(const a of this.remoteChanges)if(a!=null&&a.collectionName===o.collectionName&&a.type===o.type&&!(o.type==="remove"&&a.data!==o.data)&&a.data.id===o.data.id)return!0;return!1},n=(o,a)=>{const c=[...this.remoteChanges];for(let r=0;r<c.length;r+=1){const i=c[r];i!=null&&i.collectionName===o&&(i.type==="remove"&&i.data!==a||i.data.id===a&&(c[r]=null))}this.remoteChanges=c.filter(r=>r!=null)};e.on("added",o=>{if(s({collectionName:t.name,type:"insert",data:o})){n(t.name,o.id);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"insert",data:o}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),e.on("changed",({id:o},a)=>{const c={id:o,modifier:a};if(s({collectionName:t.name,type:"update",data:c})){n(t.name,o);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"update",data:c}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),e.on("removed",({id:o})=>{if(s({collectionName:t.name,type:"remove",data:o})){n(t.name,o);return}this.changes.insert({collectionName:t.name,time:Date.now(),type:"remove",data:o}),!this.getCollectionProperties(t.name).syncPaused&&this.schedulePush(t.name)}),this.options.autostart&&this.startSync(t.name).catch(o=>{this.options.onError&&this.options.onError(this.getCollectionProperties(t.name).options,o)})}flushScheduledPushes(){this.scheduledPushes.forEach(e=>{this.pushChanges(e).catch(()=>{})}),this.scheduledPushes.clear()}schedulePush(e){this.scheduledPushes.add(e),this.debouncedFlush()}async startAll(){await Promise.all([...this.collections.keys()].map(e=>this.startSync(e)))}async startSync(e){const t=this.getCollectionProperties(e);if(!t.syncPaused)return;this.schedulePush(e);const s=this.options.registerRemoteChange?await this.options.registerRemoteChange(t.options,async n=>{if(n==null)await this.sync(e);else{const o=Date.now(),a=this.syncOperations.insert({start:o,collectionName:e,instanceId:this.instanceId,status:"active"});await this.syncWithData(e,n).then(()=>{this.syncOperations.removeMany({id:{$ne:a},collectionName:e,$or:[{end:{$lte:o}},{status:"active"}]}),this.syncOperations.updateOne({id:a},{$set:{status:"done",end:Date.now()}})}).catch(c=>{throw this.options.onError&&this.options.onError(this.getCollectionProperties(e).options,c),this.syncOperations.updateOne({id:a},{$set:{status:"error",end:Date.now(),error:c.stack||c.message}}),c})}}):void 0;this.collections.set(e,{...t,syncPaused:!1,cleanupFunction:s})}async pauseAll(){await Promise.all([...this.collections.keys()].map(e=>this.pauseSync(e)))}async pauseSync(e){const t=this.getCollectionProperties(e);t.syncPaused||(t.cleanupFunction&&await t.cleanupFunction(),this.collections.set(e,{...t,cleanupFunction:void 0,syncPaused:!0}))}async syncAll(){if(this.isDisposed)throw new Error("SyncManager is disposed");const e=[];if(await Promise.all([...this.collections.keys()].map(t=>this.sync(t).catch(s=>{e.push({id:t,error:s})}))),e.length>0)throw new Error(`Error while syncing collections:
2
2
  ${e.map(t=>`${t.id}: ${t.error.message}`).join(`
3
3
 
4
- `)}`)}isSyncing(e){return this.syncOperations.findOne({...e?{collectionName:e}:{},status:"active"},{fields:{status:1}})!=null}async isReady(){await this.persistenceReady}async sync(e,t={}){if(this.isDisposed)throw new Error("SyncManager is disposed");await this.isReady();const{options:s,readyPromise:n}=this.getCollectionProperties(e);await n;const a=this.syncOperations.find({collectionName:e,instanceId:this.instanceId,status:"active"},{reactive:!1}).count()>0,o=Date.now();let c=null;await new Promise(i=>{setTimeout(i,0)});const h=async()=>{const i=this.syncOperations.findOne({collectionName:e,status:"done"},{sort:{end:-1},reactive:!1});if(t!=null&&t.onlyWithChanges&&this.changes.find({collectionName:e,time:{$lte:o}},{sort:{time:1},reactive:!1}).count()===0)return;a||(c=this.syncOperations.insert({start:o,collectionName:e,instanceId:this.instanceId,status:"active"}));const p=await this.options.pull(s,{lastFinishedSyncStart:i==null?void 0:i.start,lastFinishedSyncEnd:i==null?void 0:i.end});await this.syncWithData(e,p)};await(t!=null&&t.force?h():this.getSyncQueue(e).add(h)).catch(i=>{throw c!=null&&(this.options.onError&&this.options.onError(s,i),this.syncOperations.updateOne({id:c},{$set:{status:"error",end:Date.now(),error:i.stack||i.message}})),i}),c!=null&&(this.syncOperations.removeMany({id:{$ne:c},collectionName:e,$or:[{end:{$lte:o}},{status:"active"}]}),this.syncOperations.updateOne({id:c},{$set:{status:"done",end:Date.now()}}))}async pushChanges(e){await this.sync(e,{onlyWithChanges:!0})}async syncWithData(e,t){const{collection:s,options:n}=this.getCollectionProperties(e),a=Date.now(),o=this.syncOperations.findOne({collectionName:e,status:"done"},{sort:{end:-1},reactive:!1}),c=this.snapshots.findOne({collectionName:e},{sort:{time:-1},reactive:!1}),h=this.changes.find({collectionName:e,time:{$lte:a}},{sort:{time:1},reactive:!1}).fetch();await E({changes:h,lastSnapshot:c==null?void 0:c.items,data:t,pull:()=>this.options.pull(n,{lastFinishedSyncStart:o==null?void 0:o.start,lastFinishedSyncEnd:o==null?void 0:o.end}),push:i=>this.options.push(n,{changes:i}),insert:i=>{this.remoteChanges.push({collectionName:e,type:"insert",data:i},{collectionName:e,type:"update",data:{id:i.id,modifier:{$set:i}}}),s.replaceOne({id:i.id},i,{upsert:!0})},update:(i,p)=>{this.remoteChanges.push({collectionName:e,type:"insert",data:{id:i,...p.$set}},{collectionName:e,type:"update",data:{id:i,modifier:p}}),s.updateOne({id:i},{...p,$setOnInsert:{id:i}},{upsert:!0})},remove:i=>{s.findOne({id:i},{reactive:!1})&&(this.remoteChanges.push({collectionName:e,type:"remove",data:i}),s.removeOne({id:i}))},batch:i=>{s.batch(()=>{i()})}}).then(async i=>{if(this.snapshots.removeMany({collectionName:e,time:{$lte:a}}),this.changes.removeMany({collectionName:e,id:{$in:h.map(d=>d.id)}}),this.snapshots.insert({time:a,collectionName:e,items:i}),await new Promise(d=>{setTimeout(d,0)}),this.changes.find({collectionName:e},{reactive:!1}).count()>0){await this.sync(e,{force:!0,onlyWithChanges:!0});return}const m=s.find({id:{$nin:i.map(d=>d.id)}},{reactive:!1}).map(d=>d.id);s.batch(()=>{i.forEach(d=>{this.remoteChanges.push({collectionName:e,type:"insert",data:d},{collectionName:e,type:"update",data:{id:d.id,modifier:{$set:d}}}),s.replaceOne({id:d.id},d,{upsert:!0})}),m.forEach(d=>{s.removeOne({id:d})})})})}}f.SyncManager=S,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
4
+ `)}`)}isSyncing(e){return this.syncOperations.findOne({...e?{collectionName:e}:{},status:"active"},{fields:{status:1}})!=null}async isReady(){await this.persistenceReady}async sync(e,t={}){if(this.isDisposed)throw new Error("SyncManager is disposed");await this.isReady();const{options:s,readyPromise:n}=this.getCollectionProperties(e);await n;const o=this.syncOperations.find({collectionName:e,instanceId:this.instanceId,status:"active"},{reactive:!1}).count()>0,a=Date.now();let c=null;await new Promise(i=>{setTimeout(i,0)});const r=async()=>{const i=this.syncOperations.findOne({collectionName:e,status:"done"},{sort:{end:-1},reactive:!1});if(t!=null&&t.onlyWithChanges&&this.changes.find({collectionName:e,time:{$lte:a}},{sort:{time:1},reactive:!1}).count()===0)return;o||(c=this.syncOperations.insert({start:a,collectionName:e,instanceId:this.instanceId,status:"active"}));const h=await this.options.pull(s,{lastFinishedSyncStart:i==null?void 0:i.start,lastFinishedSyncEnd:i==null?void 0:i.end});await this.syncWithData(e,h)};await(t!=null&&t.force?r():this.getSyncQueue(e).add(r)).catch(i=>{throw c!=null&&(this.options.onError&&this.options.onError(s,i),this.syncOperations.updateOne({id:c},{$set:{status:"error",end:Date.now(),error:i.stack||i.message}})),i}),c!=null&&(this.syncOperations.removeMany({id:{$ne:c},collectionName:e,$or:[{end:{$lte:a}},{status:"active"}]}),this.syncOperations.updateOne({id:c},{$set:{status:"done",end:Date.now()}}))}async pushChanges(e){await this.sync(e,{onlyWithChanges:!0})}async syncWithData(e,t){const{collection:s,options:n}=this.getCollectionProperties(e),o=Date.now(),a=this.syncOperations.findOne({collectionName:e,status:"done"},{sort:{end:-1},reactive:!1}),c=this.snapshots.findOne({collectionName:e},{sort:{time:-1},reactive:!1}),r=this.changes.find({collectionName:e,time:{$lte:o}},{sort:{time:1},reactive:!1}).fetch();await S({changes:r,lastSnapshot:c==null?void 0:c.items,data:t,pull:()=>this.options.pull(n,{lastFinishedSyncStart:a==null?void 0:a.start,lastFinishedSyncEnd:a==null?void 0:a.end}),push:i=>this.options.push(n,{changes:i,rawChanges:r}),insert:i=>{this.remoteChanges.push({collectionName:e,type:"insert",data:i},{collectionName:e,type:"update",data:{id:i.id,modifier:{$set:i}}}),s.replaceOne({id:i.id},i,{upsert:!0})},update:(i,h)=>{this.remoteChanges.push({collectionName:e,type:"insert",data:{id:i,...h.$set}},{collectionName:e,type:"update",data:{id:i,modifier:h}}),s.updateOne({id:i},{...h,$setOnInsert:{id:i}},{upsert:!0})},remove:i=>{s.findOne({id:i},{reactive:!1})&&(this.remoteChanges.push({collectionName:e,type:"remove",data:i}),s.removeOne({id:i}))},batch:i=>{s.batch(()=>{i()})}}).then(async i=>{if(this.snapshots.removeMany({collectionName:e,time:{$lte:o}}),this.changes.removeMany({collectionName:e,id:{$in:r.map(u=>u.id)}}),this.snapshots.insert({time:o,collectionName:e,items:i}),await new Promise(u=>{setTimeout(u,0)}),this.changes.find({collectionName:e},{reactive:!1}).count()>0){await this.sync(e,{force:!0,onlyWithChanges:!0});return}const m=s.find({id:{$nin:i.map(u=>u.id)}},{reactive:!1}).map(u=>u.id);s.batch(()=>{i.forEach(u=>{this.remoteChanges.push({collectionName:e,type:"insert",data:u},{collectionName:e,type:"update",data:{id:u.id,modifier:{$set:u}}}),s.replaceOne({id:u.id},u,{upsert:!0})}),m.forEach(u=>{s.removeOne({id:u})})})})}}f.SyncManager=M,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
5
5
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/utils/debounce.ts","../src/utils/PromiseQueue.ts","../src/computeChanges.ts","../src/getSnapshot.ts","../src/applyChanges.ts","../src/sync.ts","../src/SyncManager.ts"],"sourcesContent":["/**\n * Debounces a function.\n * @param fn Function to debounce\n * @param wait Time to wait before calling the function.\n * @param [options] Debounce options\n * @param [options.leading] Whether to call the function on the leading edge of the wait interval.\n * @param [options.trailing] Whether to call the function on the trailing edge of the wait interval.\n * @returns The debounced function.\n */\nexport default function debounce(fn, wait, options = {}) {\n let timeout;\n let result;\n const { leading = false, trailing = true } = options;\n /**\n * The debounced function that will be returned.\n * @param this The context to bind the function to.\n * @param args The arguments to pass to the function.\n * @returns The result of the debounced function.\n */\n function debounced(...args) {\n const shouldCallImmediately = leading && !timeout;\n const shouldCallTrailing = trailing && !timeout;\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(() => {\n timeout = null;\n if (trailing && !shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n }, wait);\n if (shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n else if (!shouldCallTrailing) {\n result = null;\n }\n return result;\n }\n return debounced;\n}\n","/**\n * Class for queuing promises to be executed one after the other.\n * This is useful for tasks that should not be executed in parallel.\n * @example\n * const queue = new PromiseQueue();\n * queue.add(() => fetch('https://example.com/api/endpoint1'));\n * queue.add(() => fetch('https://example.com/api/endpoint2'));\n * // The second fetch will only be executed after the first one is done.\n */\nexport default class PromiseQueue {\n queue = [];\n pendingPromise = false;\n /**\n * Method to add a new promise to the queue and returns a promise that resolves when this task is done\n * @param task Function that returns a promise that will be added to the queue\n * @returns Promise that resolves when the task is done\n */\n add(task) {\n return new Promise((resolve, reject) => {\n // Wrap the task with the resolve and reject to control its completion from the outside\n this.queue.push(() => task()\n .then(resolve)\n .catch((error) => {\n reject(error);\n throw error;\n }));\n this.dequeue();\n });\n }\n /**\n * Method to check if there is a pending promise in the queue\n * @returns True if there is a pending promise, false otherwise\n */\n hasPendingPromise() {\n return this.pendingPromise;\n }\n /**\n * Method to process the queue\n */\n dequeue() {\n if (this.pendingPromise || this.queue.length === 0) {\n return;\n }\n const task = this.queue.shift();\n if (!task)\n return;\n this.pendingPromise = true;\n task()\n .then(() => {\n this.pendingPromise = false;\n this.dequeue();\n })\n .catch(() => {\n this.pendingPromise = false;\n this.dequeue();\n });\n }\n}\n","import { isEqual } from '@signaldb/core';\n/**\n * Compute changes between two arrays of items.\n * @param oldItems Array of the old items\n * @param newItems Array of the new items\n * @returns The changeset\n */\nexport default function computeChanges(oldItems, newItems) {\n const added = [];\n const modified = [];\n const removed = [];\n const oldItemsMap = new Map(oldItems.map(item => [item.id, item]));\n const newItemsMap = new Map(newItems.map(item => [item.id, item]));\n for (const [id, oldItem] of oldItemsMap) {\n const newItem = newItemsMap.get(id);\n if (!newItem) {\n removed.push(oldItem);\n }\n else if (!isEqual(newItem, oldItem)) {\n modified.push(newItem);\n }\n }\n for (const [id, newItem] of newItemsMap) {\n if (!oldItemsMap.has(id)) {\n added.push(newItem);\n }\n }\n return { added, modified, removed };\n}\n","/**\n * Gets the snapshot of items from the last snapshot and the changes.\n * @param lastSnapshot The last snapshot of items\n * @param data The changes to apply to the last snapshot\n * @returns The new snapshot of items\n */\nexport default function getSnapshot(lastSnapshot, data) {\n if (data.items != null)\n return data.items;\n const items = lastSnapshot || [];\n data.changes.added.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.modified.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.removed.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index !== -1)\n items.splice(index, 1);\n });\n return items;\n}\n","import { modify } from '@signaldb/core';\n/**\n * applies changes to a collection of items\n * @param items The items to apply the changes to\n * @param changes The changes to apply to the items\n * @returns The new items after applying the changes\n */\nexport default function applyChanges(items, changes) {\n // Create initial map of items by ID\n const itemMap = new Map(items.map(item => [item.id, item]));\n changes.forEach((change) => {\n if (change.type === 'remove') {\n itemMap.delete(change.data);\n }\n else if (change.type === 'insert') {\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem ? { ...existingItem, ...change.data } : change.data);\n }\n else { // change.type === 'update'\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem\n ? modify(existingItem, change.data.modifier)\n : modify({ id: change.data.id }, change.data.modifier));\n }\n });\n // Convert map back to array\n return [...itemMap.values()];\n}\n","import computeChanges from './computeChanges';\nimport getSnapshot from './getSnapshot';\nimport applyChanges from './applyChanges';\n/**\n * Checks if there are any changes in the given changeset.\n * @param changes The changeset to check.\n * @returns True if there are changes, false otherwise.\n */\nfunction hasChanges(changes) {\n return changes.added.length > 0\n || changes.modified.length > 0\n || changes.removed.length > 0;\n}\n/**\n * Checks if there is a difference between the old items and the new items.\n * @param oldItems The old items.\n * @param newItems The new items.\n * @returns True if there is a difference, false otherwise.\n */\nfunction hasDifference(oldItems, newItems) {\n return hasChanges(computeChanges(oldItems, newItems));\n}\n/**\n * Does a sync operation based on the provided options. If changes are supplied, these will be rebased on the new data.\n * Afterwards the push method will be called with the remaining changes. A new snapshot will be created and returned.\n * @param options Sync options\n * @param options.changes Changes to call the push method with\n * @param [options.lastSnapshot] The last snapshot\n * @param options.data The new data\n * @param options.pull Method to pull new data\n * @param options.push Method to push changes\n * @param options.insert Method to insert an item\n * @param options.update Method to update an item\n * @param options.remove Method to remove an item\n * @param options.batch Method to batch multiple operations\n * @returns The new snapshot\n */\nexport default async function sync({ changes, lastSnapshot, data, pull, push, insert, update, remove, batch, }) {\n let newData = data;\n let previousSnapshot = lastSnapshot || [];\n let newSnapshot = getSnapshot(lastSnapshot, newData);\n if (changes.length > 0) {\n // apply changes on last snapshot and check if there is a difference\n const lastSnapshotWithChanges = applyChanges(previousSnapshot, changes);\n if (hasDifference(previousSnapshot, lastSnapshotWithChanges)) {\n // if yes, apply the changes on the newSnapshot and check if there is a difference\n const newSnapshotWithChanges = applyChanges(newSnapshot, changes);\n const changesToPush = computeChanges(newSnapshot, newSnapshotWithChanges);\n if (hasChanges(changesToPush)) {\n // if yes, push the changes to the server\n await push(changesToPush);\n // pull new data afterwards to ensure that all server changes are applied\n newData = await pull();\n newSnapshot = getSnapshot(newSnapshot, newData);\n }\n previousSnapshot = lastSnapshotWithChanges;\n }\n }\n // apply the new changes on the collection\n const newChanges = newData.changes == null\n ? computeChanges(previousSnapshot, newData.items)\n : newData.changes;\n batch(() => {\n newChanges.added.forEach(item => insert(item));\n newChanges.modified.forEach(item => update(item.id, { $set: item }));\n newChanges.removed.forEach(item => remove(item.id));\n });\n return newSnapshot;\n}\n","import { Collection, randomId, createIndex } from '@signaldb/core';\nimport debounce from './utils/debounce';\nimport PromiseQueue from './utils/PromiseQueue';\nimport sync from './sync';\n/**\n * Class to manage syncing of collections.\n * @template CollectionOptions\n * @template ItemType\n * @template IdType\n * @example\n * const syncManager = new SyncManager({\n * pull: async (collectionOptions) => {\n * const response = await fetch(`/api/collections/${collectionOptions.name}`)\n * return await response.json()\n * },\n * push: async (collectionOptions, { changes }) => {\n * await fetch(`/api/collections/${collectionOptions.name}`, {\n * method: 'POST',\n * body: JSON.stringify(changes),\n * })\n * },\n * })\n *\n * const collection = new Collection()\n * syncManager.addCollection(collection, {\n * name: 'todos',\n * })\n *\n * syncManager.sync('todos')\n */\nexport default class SyncManager {\n options;\n collections = new Map();\n changes;\n snapshots;\n syncOperations;\n scheduledPushes = new Set();\n remoteChanges = [];\n syncQueues = new Map();\n persistenceReady;\n isDisposed = false;\n instanceId = randomId();\n id;\n debouncedFlush;\n /**\n * @param options Collection options\n * @param options.pull Function to pull data from remote source.\n * @param options.push Function to push data to remote source.\n * @param [options.registerRemoteChange] Function to register a callback for remote changes.\n * @param [options.id] Unique identifier for this sync manager. Only nessesary if you have multiple sync managers.\n * @param [options.persistenceAdapter] Persistence adapter to use for storing changes, snapshots and sync operations.\n * @param [options.reactivity] Reactivity adapter to use for reactivity.\n * @param [options.onError] Function to handle errors that occur async during syncing.\n * @param [options.autostart] Whether to automatically start syncing new collections.\n * @param [options.debounceTime] The time in milliseconds to debounce push operations.\n */\n constructor(options) {\n this.options = {\n autostart: true,\n ...options,\n };\n this.id = this.options.id || 'default-sync-manager';\n const { reactivity } = this.options;\n const changesPersistence = this.createPersistenceAdapter('changes');\n const snapshotsPersistence = this.createPersistenceAdapter('snapshots');\n const syncOperationsPersistence = this.createPersistenceAdapter('sync-operations');\n this.changes = new Collection({\n name: `${this.options.id}-changes`,\n persistence: changesPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.snapshots = new Collection({\n name: `${this.options.id}-snapshots`,\n persistence: snapshotsPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.syncOperations = new Collection({\n name: `${this.options.id}-sync-operations`,\n persistence: syncOperationsPersistence?.adapter,\n indices: [createIndex('collectionName'), createIndex('status')],\n reactivity,\n });\n this.changes.on('persistence.error', error => changesPersistence?.handler(error));\n this.snapshots.on('persistence.error', error => snapshotsPersistence?.handler(error));\n this.syncOperations.on('persistence.error', error => syncOperationsPersistence?.handler(error));\n this.persistenceReady = Promise.all([\n this.syncOperations.isReady(),\n this.changes.isReady(),\n this.snapshots.isReady(),\n ]).then(() => { });\n this.changes.setMaxListeners(1000);\n this.snapshots.setMaxListeners(1000);\n this.syncOperations.setMaxListeners(1000);\n this.debouncedFlush = debounce(this.flushScheduledPushes, this.options.debounceTime ?? 100);\n }\n createPersistenceAdapter(name) {\n if (this.options.persistenceAdapter == null)\n return;\n let errorHandler = () => { };\n const adapter = this.options.persistenceAdapter(`${this.id}-${name}`, (handler) => {\n errorHandler = handler;\n });\n return {\n adapter,\n handler: (error) => errorHandler(error),\n };\n }\n getSyncQueue(name) {\n if (this.syncQueues.get(name) == null) {\n this.syncQueues.set(name, new PromiseQueue());\n }\n return this.syncQueues.get(name);\n }\n /**\n * Clears all internal data structures\n */\n async dispose() {\n this.collections.clear();\n this.syncQueues.clear();\n this.remoteChanges.splice(0, this.remoteChanges.length);\n await Promise.all([\n this.changes.dispose(),\n this.snapshots.dispose(),\n this.syncOperations.dispose(),\n ]);\n this.isDisposed = true;\n }\n /**\n * Gets a collection with it's options by name\n * @deprecated Use getCollectionProperties instead.\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns Tuple of collection and options\n */\n getCollection(name) {\n const { collection, options } = this.getCollectionProperties(name);\n return [collection, options];\n }\n /**\n * Gets collection options by name\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns An object of all properties of the collection\n */\n getCollectionProperties(name) {\n const collectionParameters = this.collections.get(name);\n if (collectionParameters == null)\n throw new Error(`Collection with id '${name}' not found`);\n return collectionParameters;\n }\n /**\n * Adds a collection to the sync manager.\n * @param collection Collection to add\n * @param options Options for the collection. The object needs at least a `name` property.\n * @param options.name Unique name of the collection\n */\n addCollection(collection, options) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n this.collections.set(options.name, {\n collection,\n options,\n readyPromise: collection.isReady(),\n syncPaused: true, // always start paused as the autostart will start it\n });\n const hasRemoteChange = (change) => {\n for (const remoteChange of this.remoteChanges) {\n if (remoteChange == null)\n continue;\n if (remoteChange.collectionName !== change.collectionName)\n continue;\n if (remoteChange.type !== change.type)\n continue;\n if (change.type === 'remove' && remoteChange.data !== change.data)\n continue;\n if (remoteChange.data.id !== change.data.id)\n continue;\n return true;\n }\n return false;\n };\n const removeRemoteChanges = (collectionName, id) => {\n const newRemoteChanges = [...this.remoteChanges];\n for (let i = 0; i < newRemoteChanges.length; i += 1) {\n const item = newRemoteChanges[i];\n if (item == null)\n continue;\n if (item.collectionName !== collectionName)\n continue;\n if (item.type === 'remove' && item.data !== id)\n continue;\n if (item.data.id !== id)\n continue;\n newRemoteChanges[i] = null;\n }\n this.remoteChanges = newRemoteChanges.filter(item => item != null);\n };\n collection.on('added', (item) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'insert', data: item })) {\n removeRemoteChanges(options.name, item.id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'insert',\n data: item,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('changed', ({ id }, modifier) => {\n const data = { id, modifier };\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'update', data })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'update',\n data,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('removed', ({ id }) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'remove', data: id })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'remove',\n data: id,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n if (this.options.autostart) {\n this.startSync(options.name)\n .catch((error) => {\n if (!this.options.onError)\n return;\n this.options.onError(this.getCollectionProperties(options.name).options, error);\n });\n }\n }\n flushScheduledPushes() {\n this.scheduledPushes.forEach((name) => {\n this.pushChanges(name).catch(() => { });\n });\n this.scheduledPushes.clear();\n }\n schedulePush(name) {\n this.scheduledPushes.add(name);\n this.debouncedFlush();\n }\n /**\n * Setup all collections to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n */\n async startAll() {\n await Promise.all([...this.collections.keys()].map(id => this.startSync(id)));\n }\n /**\n * Setup a collection to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n * @param name Name of the collection\n */\n async startSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (!collectionParameters.syncPaused)\n return; // already started\n this.schedulePush(name); // push changes that were made while paused\n const cleanupFunction = this.options.registerRemoteChange\n ? await this.options.registerRemoteChange(collectionParameters.options, async (data) => {\n if (data == null) {\n await this.sync(name);\n }\n else {\n const syncTime = Date.now();\n const syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n await this.syncWithData(name, data)\n .then(() => {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n })\n .catch((error) => {\n if (this.options.onError) {\n this.options.onError(this.getCollectionProperties(name).options, error);\n }\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n throw error;\n });\n }\n })\n : undefined;\n this.collections.set(name, {\n ...collectionParameters,\n syncPaused: false,\n cleanupFunction,\n });\n }\n /**\n * Pauses the sync process for all collections.\n * This means that the collections will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n */\n async pauseAll() {\n await Promise.all([...this.collections.keys()].map(id => this.pauseSync(id)));\n }\n /**\n * Pauses the sync process for a collection.\n * This means that the collection will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n * @param name Name of the collection\n */\n async pauseSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (collectionParameters.syncPaused)\n return; // already paused\n if (collectionParameters.cleanupFunction)\n await collectionParameters.cleanupFunction();\n this.collections.set(name, {\n ...collectionParameters,\n cleanupFunction: undefined,\n syncPaused: true,\n });\n }\n /**\n * Starts the sync process for all collections\n */\n async syncAll() {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n const errors = [];\n await Promise.all([...this.collections.keys()].map(id => this.sync(id).catch((error) => {\n errors.push({ id, error });\n })));\n if (errors.length > 0)\n throw new Error(`Error while syncing collections:\\n${errors.map(error => `${error.id}: ${error.error.message}`).join('\\n\\n')}`);\n }\n /**\n * Checks if a collection is currently beeing synced\n * @param [name] Name of the collection. If not provided, it will check if any collection is currently beeing synced.\n * @returns True if the collection is currently beeing synced, false otherwise.\n */\n isSyncing(name) {\n return this.syncOperations.findOne({\n ...name ? { collectionName: name } : {},\n status: 'active',\n }, { fields: { status: 1 } }) != null;\n }\n /**\n * Checks if the sync manager is ready to sync.\n * @returns A promise that resolves when the sync manager is ready to sync.\n */\n async isReady() {\n await this.persistenceReady;\n }\n /**\n * Starts the sync process for a collection\n * @param name Name of the collection\n * @param options Options for the sync process.\n * @param options.force If true, the sync process will be started even if there are no changes and onlyWithChanges is true.\n * @param options.onlyWithChanges If true, the sync process will only be started if there are changes.\n */\n async sync(name, options = {}) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n await this.isReady();\n const { options: collectionOptions, readyPromise } = this.getCollectionProperties(name);\n await readyPromise;\n const hasActiveSyncs = this.syncOperations.find({\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n }, {\n reactive: false,\n }).count() > 0;\n const syncTime = Date.now();\n let syncId = null;\n // schedule for next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const doSync = async () => {\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n if (options?.onlyWithChanges) {\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).count();\n if (currentChanges === 0)\n return;\n }\n if (!hasActiveSyncs) {\n syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n }\n const data = await this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n });\n await this.syncWithData(name, data);\n };\n await (options?.force ? doSync() : this.getSyncQueue(name).add(doSync))\n .catch((error) => {\n if (syncId != null) {\n if (this.options.onError)\n this.options.onError(collectionOptions, error);\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n }\n throw error;\n });\n if (syncId != null) {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n }\n }\n /**\n * Starts the push process for a collection (sync process but only if there are changes)\n * @param name Name of the collection\n */\n async pushChanges(name) {\n await this.sync(name, {\n onlyWithChanges: true,\n });\n }\n async syncWithData(name, data) {\n const { collection, options: collectionOptions } = this.getCollectionProperties(name);\n const syncTime = Date.now();\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n const lastSnapshot = this.snapshots.findOne({\n collectionName: name,\n }, {\n sort: { time: -1 },\n reactive: false,\n });\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).fetch();\n await sync({\n changes: currentChanges,\n lastSnapshot: lastSnapshot?.items,\n data,\n pull: () => this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n }),\n push: changes => this.options.push(collectionOptions, { changes }),\n insert: (item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n },\n update: (itemId, modifier) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: { id: itemId, ...modifier.$set },\n }, {\n collectionName: name,\n type: 'update',\n data: { id: itemId, modifier },\n });\n collection.updateOne({ id: itemId }, {\n ...modifier,\n $setOnInsert: { id: itemId },\n }, { upsert: true });\n },\n remove: (itemId) => {\n const itemExists = !!collection.findOne({\n id: itemId,\n }, { reactive: false });\n if (!itemExists)\n return;\n this.remoteChanges.push({\n collectionName: name,\n type: 'remove',\n data: itemId,\n });\n collection.removeOne({ id: itemId });\n },\n batch: (fn) => {\n collection.batch(() => {\n fn();\n });\n },\n })\n .then(async (snapshot) => {\n // clean up old snapshots\n this.snapshots.removeMany({\n collectionName: name,\n time: { $lte: syncTime },\n });\n // clean up processed changes\n this.changes.removeMany({\n collectionName: name,\n id: { $in: currentChanges.map(c => c.id) },\n });\n // insert new snapshot\n this.snapshots.insert({\n time: syncTime,\n collectionName: name,\n items: snapshot,\n });\n // delay sync operation update to next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const hasChanges = this.changes.find({\n collectionName: name,\n }, { reactive: false }).count() > 0;\n if (hasChanges) {\n // check if there are unsynced changes to push\n // and sync again if there are any\n await this.sync(name, {\n force: true,\n onlyWithChanges: true,\n });\n return;\n }\n // if there are no unsynced changes apply the last snapshot\n // to make sure that collection and snapshot are in sync\n // find all items that are not in the snapshot\n const nonExistingItemIds = collection.find({\n id: { $nin: snapshot.map(item => item.id) },\n }, {\n reactive: false,\n }).map(item => item.id);\n collection.batch(() => {\n // update all items that are in the snapshot\n snapshot.forEach((item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n });\n // remove all items that are not in the snapshot\n nonExistingItemIds.forEach((id) => {\n collection.removeOne({ id });\n });\n });\n });\n }\n}\n"],"names":["debounce","fn","wait","options","timeout","result","leading","trailing","debounced","args","shouldCallImmediately","shouldCallTrailing","PromiseQueue","__publicField","task","resolve","reject","error","computeChanges","oldItems","newItems","added","modified","removed","oldItemsMap","item","newItemsMap","id","oldItem","newItem","isEqual","getSnapshot","lastSnapshot","data","items","index","i","applyChanges","changes","itemMap","change","existingItem","modify","hasChanges","hasDifference","sync","pull","push","insert","update","remove","batch","newData","previousSnapshot","newSnapshot","lastSnapshotWithChanges","newSnapshotWithChanges","changesToPush","newChanges","SyncManager","randomId","reactivity","changesPersistence","snapshotsPersistence","syncOperationsPersistence","Collection","createIndex","name","errorHandler","handler","collection","collectionParameters","hasRemoteChange","remoteChange","removeRemoteChanges","collectionName","newRemoteChanges","modifier","cleanupFunction","syncTime","syncId","errors","collectionOptions","readyPromise","hasActiveSyncs","doSync","lastFinishedSync","currentChanges","itemId","snapshot","c","nonExistingItemIds"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/utils/debounce.ts","../src/utils/PromiseQueue.ts","../src/computeChanges.ts","../src/getSnapshot.ts","../src/applyChanges.ts","../src/sync.ts","../src/SyncManager.ts"],"sourcesContent":["/**\n * Debounces a function.\n * @param fn Function to debounce\n * @param wait Time to wait before calling the function.\n * @param [options] Debounce options\n * @param [options.leading] Whether to call the function on the leading edge of the wait interval.\n * @param [options.trailing] Whether to call the function on the trailing edge of the wait interval.\n * @returns The debounced function.\n */\nexport default function debounce(fn, wait, options = {}) {\n let timeout;\n let result;\n const { leading = false, trailing = true } = options;\n /**\n * The debounced function that will be returned.\n * @param this The context to bind the function to.\n * @param args The arguments to pass to the function.\n * @returns The result of the debounced function.\n */\n function debounced(...args) {\n const shouldCallImmediately = leading && !timeout;\n const shouldCallTrailing = trailing && !timeout;\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(() => {\n timeout = null;\n if (trailing && !shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n }, wait);\n if (shouldCallImmediately) {\n result = fn.apply(this, args);\n }\n else if (!shouldCallTrailing) {\n result = null;\n }\n return result;\n }\n return debounced;\n}\n","/**\n * Class for queuing promises to be executed one after the other.\n * This is useful for tasks that should not be executed in parallel.\n * @example\n * const queue = new PromiseQueue();\n * queue.add(() => fetch('https://example.com/api/endpoint1'));\n * queue.add(() => fetch('https://example.com/api/endpoint2'));\n * // The second fetch will only be executed after the first one is done.\n */\nexport default class PromiseQueue {\n queue = [];\n pendingPromise = false;\n /**\n * Method to add a new promise to the queue and returns a promise that resolves when this task is done\n * @param task Function that returns a promise that will be added to the queue\n * @returns Promise that resolves when the task is done\n */\n add(task) {\n return new Promise((resolve, reject) => {\n // Wrap the task with the resolve and reject to control its completion from the outside\n this.queue.push(() => task()\n .then(resolve)\n .catch((error) => {\n reject(error);\n throw error;\n }));\n this.dequeue();\n });\n }\n /**\n * Method to check if there is a pending promise in the queue\n * @returns True if there is a pending promise, false otherwise\n */\n hasPendingPromise() {\n return this.pendingPromise;\n }\n /**\n * Method to process the queue\n */\n dequeue() {\n if (this.pendingPromise || this.queue.length === 0) {\n return;\n }\n const task = this.queue.shift();\n if (!task)\n return;\n this.pendingPromise = true;\n task()\n .then(() => {\n this.pendingPromise = false;\n this.dequeue();\n })\n .catch(() => {\n this.pendingPromise = false;\n this.dequeue();\n });\n }\n}\n","import { isEqual } from '@signaldb/core';\n/**\n * Computes the modified fields between two items recursively.\n * @param oldItem The old item\n * @param newItem The new item\n * @returns The modified fields\n */\nexport function computeModifiedFields(oldItem, newItem) {\n const modifiedFields = [];\n const oldKeys = Object.keys(oldItem);\n const newKeys = Object.keys(newItem);\n const allKeys = new Set([...oldKeys, ...newKeys]);\n for (const key of allKeys) {\n if (newItem[key] !== oldItem[key]) {\n if (typeof newItem[key] === 'object' && typeof oldItem[key] === 'object') {\n const nestedModifiedFields = computeModifiedFields(oldItem[key], newItem[key]);\n for (const nestedField of nestedModifiedFields) {\n modifiedFields.push(`${key}.${nestedField}`);\n }\n }\n else {\n modifiedFields.push(key);\n }\n }\n }\n return modifiedFields;\n}\n/**\n * Compute changes between two arrays of items.\n * @param oldItems Array of the old items\n * @param newItems Array of the new items\n * @returns The changeset\n */\nexport default function computeChanges(oldItems, newItems) {\n const added = [];\n const modified = [];\n const modifiedFields = new Map();\n const removed = [];\n const oldItemsMap = new Map(oldItems.map(item => [item.id, item]));\n const newItemsMap = new Map(newItems.map(item => [item.id, item]));\n for (const [id, oldItem] of oldItemsMap) {\n const newItem = newItemsMap.get(id);\n if (!newItem) {\n removed.push(oldItem);\n }\n else if (!isEqual(newItem, oldItem)) {\n modifiedFields.set(newItem.id, computeModifiedFields(oldItem, newItem));\n modified.push(newItem);\n }\n }\n for (const [id, newItem] of newItemsMap) {\n if (!oldItemsMap.has(id)) {\n added.push(newItem);\n }\n }\n return {\n added,\n modified,\n modifiedFields,\n removed,\n };\n}\n","/**\n * Gets the snapshot of items from the last snapshot and the changes.\n * @param lastSnapshot The last snapshot of items\n * @param data The changes to apply to the last snapshot\n * @returns The new snapshot of items\n */\nexport default function getSnapshot(lastSnapshot, data) {\n if (data.items != null)\n return data.items;\n const items = lastSnapshot || [];\n data.changes.added.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.modified.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index === -1) {\n items.push(item);\n }\n else {\n items[index] = item;\n }\n });\n data.changes.removed.forEach((item) => {\n const index = items.findIndex(i => i.id === item.id);\n if (index !== -1)\n items.splice(index, 1);\n });\n return items;\n}\n","import { modify } from '@signaldb/core';\n/**\n * applies changes to a collection of items\n * @param items The items to apply the changes to\n * @param changes The changes to apply to the items\n * @returns The new items after applying the changes\n */\nexport default function applyChanges(items, changes) {\n // Create initial map of items by ID\n const itemMap = new Map(items.map(item => [item.id, item]));\n changes.forEach((change) => {\n if (change.type === 'remove') {\n itemMap.delete(change.data);\n }\n else if (change.type === 'insert') {\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem ? { ...existingItem, ...change.data } : change.data);\n }\n else { // change.type === 'update'\n const existingItem = itemMap.get(change.data.id);\n itemMap.set(change.data.id, existingItem\n ? modify(existingItem, change.data.modifier)\n : modify({ id: change.data.id }, change.data.modifier));\n }\n });\n // Convert map back to array\n return [...itemMap.values()];\n}\n","import computeChanges from './computeChanges';\nimport getSnapshot from './getSnapshot';\nimport applyChanges from './applyChanges';\n/**\n * Checks if there are any changes in the given changeset.\n * @param changes The changeset to check.\n * @returns True if there are changes, false otherwise.\n */\nfunction hasChanges(changes) {\n return changes.added.length > 0\n || changes.modified.length > 0\n || changes.removed.length > 0;\n}\n/**\n * Checks if there is a difference between the old items and the new items.\n * @param oldItems The old items.\n * @param newItems The new items.\n * @returns True if there is a difference, false otherwise.\n */\nfunction hasDifference(oldItems, newItems) {\n return hasChanges(computeChanges(oldItems, newItems));\n}\n/**\n * Does a sync operation based on the provided options. If changes are supplied, these will be rebased on the new data.\n * Afterwards the push method will be called with the remaining changes. A new snapshot will be created and returned.\n * @param options Sync options\n * @param options.changes Changes to call the push method with\n * @param [options.lastSnapshot] The last snapshot\n * @param options.data The new data\n * @param options.pull Method to pull new data\n * @param options.push Method to push changes\n * @param options.insert Method to insert an item\n * @param options.update Method to update an item\n * @param options.remove Method to remove an item\n * @param options.batch Method to batch multiple operations\n * @returns The new snapshot\n */\nexport default async function sync({ changes, lastSnapshot, data, pull, push, insert, update, remove, batch, }) {\n let newData = data;\n let previousSnapshot = lastSnapshot || [];\n let newSnapshot = getSnapshot(lastSnapshot, newData);\n if (changes.length > 0) {\n // apply changes on last snapshot and check if there is a difference\n const lastSnapshotWithChanges = applyChanges(previousSnapshot, changes);\n if (hasDifference(previousSnapshot, lastSnapshotWithChanges)) {\n // if yes, apply the changes on the newSnapshot and check if there is a difference\n const newSnapshotWithChanges = applyChanges(newSnapshot, changes);\n const changesToPush = computeChanges(newSnapshot, newSnapshotWithChanges);\n if (hasChanges(changesToPush)) {\n // if yes, push the changes to the server\n await push(changesToPush);\n // pull new data afterwards to ensure that all server changes are applied\n newData = await pull();\n newSnapshot = getSnapshot(newSnapshot, newData);\n }\n previousSnapshot = lastSnapshotWithChanges;\n }\n }\n // apply the new changes on the collection\n const newChanges = newData.changes == null\n ? computeChanges(previousSnapshot, newData.items)\n : newData.changes;\n batch(() => {\n newChanges.added.forEach(item => insert(item));\n newChanges.modified.forEach(item => update(item.id, { $set: item }));\n newChanges.removed.forEach(item => remove(item.id));\n });\n return newSnapshot;\n}\n","import { Collection, randomId, createIndex } from '@signaldb/core';\nimport debounce from './utils/debounce';\nimport PromiseQueue from './utils/PromiseQueue';\nimport sync from './sync';\n/**\n * Class to manage syncing of collections.\n * @template CollectionOptions\n * @template ItemType\n * @template IdType\n * @example\n * const syncManager = new SyncManager({\n * pull: async (collectionOptions) => {\n * const response = await fetch(`/api/collections/${collectionOptions.name}`)\n * return await response.json()\n * },\n * push: async (collectionOptions, { changes }) => {\n * await fetch(`/api/collections/${collectionOptions.name}`, {\n * method: 'POST',\n * body: JSON.stringify(changes),\n * })\n * },\n * })\n *\n * const collection = new Collection()\n * syncManager.addCollection(collection, {\n * name: 'todos',\n * })\n *\n * syncManager.sync('todos')\n */\nexport default class SyncManager {\n options;\n collections = new Map();\n changes;\n snapshots;\n syncOperations;\n scheduledPushes = new Set();\n remoteChanges = [];\n syncQueues = new Map();\n persistenceReady;\n isDisposed = false;\n instanceId = randomId();\n id;\n debouncedFlush;\n /**\n * @param options Collection options\n * @param options.pull Function to pull data from remote source.\n * @param options.push Function to push data to remote source.\n * @param [options.registerRemoteChange] Function to register a callback for remote changes.\n * @param [options.id] Unique identifier for this sync manager. Only nessesary if you have multiple sync managers.\n * @param [options.persistenceAdapter] Persistence adapter to use for storing changes, snapshots and sync operations.\n * @param [options.reactivity] Reactivity adapter to use for reactivity.\n * @param [options.onError] Function to handle errors that occur async during syncing.\n * @param [options.autostart] Whether to automatically start syncing new collections.\n * @param [options.debounceTime] The time in milliseconds to debounce push operations.\n */\n constructor(options) {\n this.options = {\n autostart: true,\n ...options,\n };\n this.id = this.options.id || 'default-sync-manager';\n const { reactivity } = this.options;\n const changesPersistence = this.createPersistenceAdapter('changes');\n const snapshotsPersistence = this.createPersistenceAdapter('snapshots');\n const syncOperationsPersistence = this.createPersistenceAdapter('sync-operations');\n this.changes = new Collection({\n name: `${this.options.id}-changes`,\n persistence: changesPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.snapshots = new Collection({\n name: `${this.options.id}-snapshots`,\n persistence: snapshotsPersistence?.adapter,\n indices: [createIndex('collectionName')],\n reactivity,\n });\n this.syncOperations = new Collection({\n name: `${this.options.id}-sync-operations`,\n persistence: syncOperationsPersistence?.adapter,\n indices: [createIndex('collectionName'), createIndex('status')],\n reactivity,\n });\n this.changes.on('persistence.error', error => changesPersistence?.handler(error));\n this.snapshots.on('persistence.error', error => snapshotsPersistence?.handler(error));\n this.syncOperations.on('persistence.error', error => syncOperationsPersistence?.handler(error));\n this.persistenceReady = Promise.all([\n this.syncOperations.isReady(),\n this.changes.isReady(),\n this.snapshots.isReady(),\n ]).then(() => { });\n this.changes.setMaxListeners(1000);\n this.snapshots.setMaxListeners(1000);\n this.syncOperations.setMaxListeners(1000);\n this.debouncedFlush = debounce(this.flushScheduledPushes, this.options.debounceTime ?? 100);\n }\n createPersistenceAdapter(name) {\n if (this.options.persistenceAdapter == null)\n return;\n let errorHandler = () => { };\n const adapter = this.options.persistenceAdapter(`${this.id}-${name}`, (handler) => {\n errorHandler = handler;\n });\n return {\n adapter,\n handler: (error) => errorHandler(error),\n };\n }\n getSyncQueue(name) {\n if (this.syncQueues.get(name) == null) {\n this.syncQueues.set(name, new PromiseQueue());\n }\n return this.syncQueues.get(name);\n }\n /**\n * Clears all internal data structures\n */\n async dispose() {\n this.collections.clear();\n this.syncQueues.clear();\n this.remoteChanges.splice(0, this.remoteChanges.length);\n await Promise.all([\n this.changes.dispose(),\n this.snapshots.dispose(),\n this.syncOperations.dispose(),\n ]);\n this.isDisposed = true;\n }\n /**\n * Gets a collection with it's options by name\n * @deprecated Use getCollectionProperties instead.\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns Tuple of collection and options\n */\n getCollection(name) {\n const { collection, options } = this.getCollectionProperties(name);\n return [collection, options];\n }\n /**\n * Gets collection options by name\n * @param name Name of the collection\n * @throws Will throw an error if the name wasn't found\n * @returns An object of all properties of the collection\n */\n getCollectionProperties(name) {\n const collectionParameters = this.collections.get(name);\n if (collectionParameters == null)\n throw new Error(`Collection with id '${name}' not found`);\n return collectionParameters;\n }\n /**\n * Adds a collection to the sync manager.\n * @param collection Collection to add\n * @param options Options for the collection. The object needs at least a `name` property.\n * @param options.name Unique name of the collection\n */\n addCollection(collection, options) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n this.collections.set(options.name, {\n collection,\n options,\n readyPromise: collection.isReady(),\n syncPaused: true, // always start paused as the autostart will start it\n });\n const hasRemoteChange = (change) => {\n for (const remoteChange of this.remoteChanges) {\n if (remoteChange == null)\n continue;\n if (remoteChange.collectionName !== change.collectionName)\n continue;\n if (remoteChange.type !== change.type)\n continue;\n if (change.type === 'remove' && remoteChange.data !== change.data)\n continue;\n if (remoteChange.data.id !== change.data.id)\n continue;\n return true;\n }\n return false;\n };\n const removeRemoteChanges = (collectionName, id) => {\n const newRemoteChanges = [...this.remoteChanges];\n for (let i = 0; i < newRemoteChanges.length; i += 1) {\n const item = newRemoteChanges[i];\n if (item == null)\n continue;\n if (item.collectionName !== collectionName)\n continue;\n if (item.type === 'remove' && item.data !== id)\n continue;\n if (item.data.id !== id)\n continue;\n newRemoteChanges[i] = null;\n }\n this.remoteChanges = newRemoteChanges.filter(item => item != null);\n };\n collection.on('added', (item) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'insert', data: item })) {\n removeRemoteChanges(options.name, item.id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'insert',\n data: item,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('changed', ({ id }, modifier) => {\n const data = { id, modifier };\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'update', data })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'update',\n data,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n collection.on('removed', ({ id }) => {\n // skip the change if it was a remote change\n if (hasRemoteChange({ collectionName: options.name, type: 'remove', data: id })) {\n removeRemoteChanges(options.name, id);\n return;\n }\n this.changes.insert({\n collectionName: options.name,\n time: Date.now(),\n type: 'remove',\n data: id,\n });\n if (this.getCollectionProperties(options.name).syncPaused)\n return;\n this.schedulePush(options.name);\n });\n if (this.options.autostart) {\n this.startSync(options.name)\n .catch((error) => {\n if (!this.options.onError)\n return;\n this.options.onError(this.getCollectionProperties(options.name).options, error);\n });\n }\n }\n flushScheduledPushes() {\n this.scheduledPushes.forEach((name) => {\n this.pushChanges(name).catch(() => { });\n });\n this.scheduledPushes.clear();\n }\n schedulePush(name) {\n this.scheduledPushes.add(name);\n this.debouncedFlush();\n }\n /**\n * Setup all collections to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n */\n async startAll() {\n await Promise.all([...this.collections.keys()].map(id => this.startSync(id)));\n }\n /**\n * Setup a collection to be synced with remote changes\n * and enable automatic pushing changes to the remote source.\n * @param name Name of the collection\n */\n async startSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (!collectionParameters.syncPaused)\n return; // already started\n this.schedulePush(name); // push changes that were made while paused\n const cleanupFunction = this.options.registerRemoteChange\n ? await this.options.registerRemoteChange(collectionParameters.options, async (data) => {\n if (data == null) {\n await this.sync(name);\n }\n else {\n const syncTime = Date.now();\n const syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n await this.syncWithData(name, data)\n .then(() => {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n })\n .catch((error) => {\n if (this.options.onError) {\n this.options.onError(this.getCollectionProperties(name).options, error);\n }\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n throw error;\n });\n }\n })\n : undefined;\n this.collections.set(name, {\n ...collectionParameters,\n syncPaused: false,\n cleanupFunction,\n });\n }\n /**\n * Pauses the sync process for all collections.\n * This means that the collections will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n */\n async pauseAll() {\n await Promise.all([...this.collections.keys()].map(id => this.pauseSync(id)));\n }\n /**\n * Pauses the sync process for a collection.\n * This means that the collection will not be synced with remote changes\n * and changes will not automatically be pushed to the remote source.\n * @param name Name of the collection\n */\n async pauseSync(name) {\n const collectionParameters = this.getCollectionProperties(name);\n if (collectionParameters.syncPaused)\n return; // already paused\n if (collectionParameters.cleanupFunction)\n await collectionParameters.cleanupFunction();\n this.collections.set(name, {\n ...collectionParameters,\n cleanupFunction: undefined,\n syncPaused: true,\n });\n }\n /**\n * Starts the sync process for all collections\n */\n async syncAll() {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n const errors = [];\n await Promise.all([...this.collections.keys()].map(id => this.sync(id).catch((error) => {\n errors.push({ id, error });\n })));\n if (errors.length > 0)\n throw new Error(`Error while syncing collections:\\n${errors.map(error => `${error.id}: ${error.error.message}`).join('\\n\\n')}`);\n }\n /**\n * Checks if a collection is currently beeing synced\n * @param [name] Name of the collection. If not provided, it will check if any collection is currently beeing synced.\n * @returns True if the collection is currently beeing synced, false otherwise.\n */\n isSyncing(name) {\n return this.syncOperations.findOne({\n ...name ? { collectionName: name } : {},\n status: 'active',\n }, { fields: { status: 1 } }) != null;\n }\n /**\n * Checks if the sync manager is ready to sync.\n * @returns A promise that resolves when the sync manager is ready to sync.\n */\n async isReady() {\n await this.persistenceReady;\n }\n /**\n * Starts the sync process for a collection\n * @param name Name of the collection\n * @param options Options for the sync process.\n * @param options.force If true, the sync process will be started even if there are no changes and onlyWithChanges is true.\n * @param options.onlyWithChanges If true, the sync process will only be started if there are changes.\n */\n async sync(name, options = {}) {\n if (this.isDisposed)\n throw new Error('SyncManager is disposed');\n await this.isReady();\n const { options: collectionOptions, readyPromise } = this.getCollectionProperties(name);\n await readyPromise;\n const hasActiveSyncs = this.syncOperations.find({\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n }, {\n reactive: false,\n }).count() > 0;\n const syncTime = Date.now();\n let syncId = null;\n // schedule for next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const doSync = async () => {\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n if (options?.onlyWithChanges) {\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).count();\n if (currentChanges === 0)\n return;\n }\n if (!hasActiveSyncs) {\n syncId = this.syncOperations.insert({\n start: syncTime,\n collectionName: name,\n instanceId: this.instanceId,\n status: 'active',\n });\n }\n const data = await this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n });\n await this.syncWithData(name, data);\n };\n await (options?.force ? doSync() : this.getSyncQueue(name).add(doSync))\n .catch((error) => {\n if (syncId != null) {\n if (this.options.onError)\n this.options.onError(collectionOptions, error);\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'error', end: Date.now(), error: error.stack || error.message },\n });\n }\n throw error;\n });\n if (syncId != null) {\n // clean up old sync operations\n this.syncOperations.removeMany({\n id: { $ne: syncId },\n collectionName: name,\n $or: [\n { end: { $lte: syncTime } },\n { status: 'active' },\n ],\n });\n // update sync operation status to done after everthing was finished\n this.syncOperations.updateOne({ id: syncId }, {\n $set: { status: 'done', end: Date.now() },\n });\n }\n }\n /**\n * Starts the push process for a collection (sync process but only if there are changes)\n * @param name Name of the collection\n */\n async pushChanges(name) {\n await this.sync(name, {\n onlyWithChanges: true,\n });\n }\n async syncWithData(name, data) {\n const { collection, options: collectionOptions } = this.getCollectionProperties(name);\n const syncTime = Date.now();\n const lastFinishedSync = this.syncOperations.findOne({\n collectionName: name,\n status: 'done',\n }, {\n sort: { end: -1 },\n reactive: false,\n });\n const lastSnapshot = this.snapshots.findOne({\n collectionName: name,\n }, {\n sort: { time: -1 },\n reactive: false,\n });\n const currentChanges = this.changes.find({\n collectionName: name,\n time: { $lte: syncTime },\n }, {\n sort: { time: 1 },\n reactive: false,\n }).fetch();\n await sync({\n changes: currentChanges,\n lastSnapshot: lastSnapshot?.items,\n data,\n pull: () => this.options.pull(collectionOptions, {\n lastFinishedSyncStart: lastFinishedSync?.start,\n lastFinishedSyncEnd: lastFinishedSync?.end,\n }),\n push: changes => this.options.push(collectionOptions, {\n changes,\n rawChanges: currentChanges,\n }),\n insert: (item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n },\n update: (itemId, modifier) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: { id: itemId, ...modifier.$set },\n }, {\n collectionName: name,\n type: 'update',\n data: { id: itemId, modifier },\n });\n collection.updateOne({ id: itemId }, {\n ...modifier,\n $setOnInsert: { id: itemId },\n }, { upsert: true });\n },\n remove: (itemId) => {\n const itemExists = !!collection.findOne({\n id: itemId,\n }, { reactive: false });\n if (!itemExists)\n return;\n this.remoteChanges.push({\n collectionName: name,\n type: 'remove',\n data: itemId,\n });\n collection.removeOne({ id: itemId });\n },\n batch: (fn) => {\n collection.batch(() => {\n fn();\n });\n },\n })\n .then(async (snapshot) => {\n // clean up old snapshots\n this.snapshots.removeMany({\n collectionName: name,\n time: { $lte: syncTime },\n });\n // clean up processed changes\n this.changes.removeMany({\n collectionName: name,\n id: { $in: currentChanges.map(c => c.id) },\n });\n // insert new snapshot\n this.snapshots.insert({\n time: syncTime,\n collectionName: name,\n items: snapshot,\n });\n // delay sync operation update to next tick to allow other tasks to run first\n await new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n const hasChanges = this.changes.find({\n collectionName: name,\n }, { reactive: false }).count() > 0;\n if (hasChanges) {\n // check if there are unsynced changes to push\n // and sync again if there are any\n await this.sync(name, {\n force: true,\n onlyWithChanges: true,\n });\n return;\n }\n // if there are no unsynced changes apply the last snapshot\n // to make sure that collection and snapshot are in sync\n // find all items that are not in the snapshot\n const nonExistingItemIds = collection.find({\n id: { $nin: snapshot.map(item => item.id) },\n }, {\n reactive: false,\n }).map(item => item.id);\n collection.batch(() => {\n // update all items that are in the snapshot\n snapshot.forEach((item) => {\n // add multiple remote changes as we don't know if the item will be updated or inserted during replace\n this.remoteChanges.push({\n collectionName: name,\n type: 'insert',\n data: item,\n }, {\n collectionName: name,\n type: 'update',\n data: { id: item.id, modifier: { $set: item } },\n });\n // replace the item\n collection.replaceOne({ id: item.id }, item, { upsert: true });\n });\n // remove all items that are not in the snapshot\n nonExistingItemIds.forEach((id) => {\n collection.removeOne({ id });\n });\n });\n });\n }\n}\n"],"names":["debounce","fn","wait","options","timeout","result","leading","trailing","debounced","args","shouldCallImmediately","shouldCallTrailing","PromiseQueue","__publicField","task","resolve","reject","error","computeModifiedFields","oldItem","newItem","modifiedFields","oldKeys","newKeys","allKeys","key","nestedModifiedFields","nestedField","computeChanges","oldItems","newItems","added","modified","removed","oldItemsMap","item","newItemsMap","id","isEqual","getSnapshot","lastSnapshot","data","items","index","i","applyChanges","changes","itemMap","change","existingItem","modify","hasChanges","hasDifference","sync","pull","push","insert","update","remove","batch","newData","previousSnapshot","newSnapshot","lastSnapshotWithChanges","newSnapshotWithChanges","changesToPush","newChanges","SyncManager","randomId","reactivity","changesPersistence","snapshotsPersistence","syncOperationsPersistence","Collection","createIndex","name","errorHandler","handler","collection","collectionParameters","hasRemoteChange","remoteChange","removeRemoteChanges","collectionName","newRemoteChanges","modifier","cleanupFunction","syncTime","syncId","errors","collectionOptions","readyPromise","hasActiveSyncs","doSync","lastFinishedSync","currentChanges","itemId","snapshot","c","nonExistingItemIds"],"mappings":"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"}
package/dist/sync.d.ts CHANGED
@@ -5,7 +5,9 @@ interface Options<ItemType extends BaseItem<IdType>, IdType> {
5
5
  lastSnapshot?: ItemType[];
6
6
  data: LoadResponse<ItemType>;
7
7
  pull: () => Promise<LoadResponse<ItemType>>;
8
- push: (changes: Changeset<ItemType>) => Promise<void>;
8
+ push: (changes: Changeset<ItemType> & {
9
+ modifiedFields: Map<IdType, string[]>;
10
+ }) => Promise<void>;
9
11
  insert: (item: ItemType) => void;
10
12
  update: (id: IdType, modifier: Modifier<ItemType>) => void;
11
13
  remove: (id: IdType) => void;
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@signaldb/sync",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "scripts": {
6
- "build": "rimraf dist && vite build"
6
+ "build": "rimraf dist && vite build",
7
+ "analyze-bundle": "bundle-analyzer ./dist --upload-token=$BUNDLE_ANALYZER_UPLOAD_TOKEN --bundle-name=@signaldb/sync",
8
+ "test": "vitest"
7
9
  },
8
10
  "repository": {
9
11
  "type": "git",