dexie-cloud-addon 4.0.1-beta.34 → 4.0.1-beta.36

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.
@@ -26,7 +26,7 @@
26
26
 
27
27
  var Dexie__default = /*#__PURE__*/_interopDefaultLegacy(Dexie);
28
28
 
29
- /*! *****************************************************************************
29
+ /******************************************************************************
30
30
  Copyright (c) Microsoft Corporation.
31
31
 
32
32
  Permission to use, copy, modify, and/or distribute this software for any
@@ -131,7 +131,7 @@
131
131
  ar[i] = from[i];
132
132
  }
133
133
  }
134
- return to.concat(ar || from);
134
+ return to.concat(ar || Array.prototype.slice.call(from));
135
135
  }
136
136
 
137
137
  function __await(v) {
@@ -1306,7 +1306,6 @@
1306
1306
  return _this;
1307
1307
  }
1308
1308
  AsyncAction.prototype.schedule = function (state, delay) {
1309
- var _a;
1310
1309
  if (delay === void 0) { delay = 0; }
1311
1310
  if (this.closed) {
1312
1311
  return this;
@@ -1319,7 +1318,7 @@
1319
1318
  }
1320
1319
  this.pending = true;
1321
1320
  this.delay = delay;
1322
- this.id = (_a = this.id) !== null && _a !== void 0 ? _a : this.requestAsyncId(scheduler, this.id, delay);
1321
+ this.id = this.id || this.requestAsyncId(scheduler, this.id, delay);
1323
1322
  return this;
1324
1323
  };
1325
1324
  AsyncAction.prototype.requestAsyncId = function (scheduler, _id, delay) {
@@ -1331,9 +1330,7 @@
1331
1330
  if (delay != null && this.delay === delay && this.pending === false) {
1332
1331
  return id;
1333
1332
  }
1334
- if (id != null) {
1335
- intervalProvider.clearInterval(id);
1336
- }
1333
+ intervalProvider.clearInterval(id);
1337
1334
  return undefined;
1338
1335
  };
1339
1336
  AsyncAction.prototype.execute = function (state, delay) {
@@ -1409,6 +1406,7 @@
1409
1406
  var _this = _super.call(this, SchedulerAction, now) || this;
1410
1407
  _this.actions = [];
1411
1408
  _this._active = false;
1409
+ _this._scheduled = undefined;
1412
1410
  return _this;
1413
1411
  }
1414
1412
  AsyncScheduler.prototype.flush = function (action) {
@@ -1818,7 +1816,7 @@
1818
1816
  return concat$1(subscriptionDelay.pipe(take(1), ignoreElements()), source.pipe(delayWhen(delayDurationSelector)));
1819
1817
  };
1820
1818
  }
1821
- return mergeMap(function (value, index) { return innerFrom(delayDurationSelector(value, index)).pipe(take(1), mapTo(value)); });
1819
+ return mergeMap(function (value, index) { return delayDurationSelector(value, index).pipe(take(1), mapTo(value)); });
1822
1820
  }
1823
1821
 
1824
1822
  function delay(due, scheduler) {
@@ -2235,6 +2233,14 @@
2235
2233
  }
2236
2234
  function userAuthenticate(context, fetchToken, userInteraction, hints) {
2237
2235
  return __awaiter(this, void 0, void 0, function* () {
2236
+ if (!crypto.subtle) {
2237
+ if (typeof location !== 'undefined' && location.protocol === 'http:') {
2238
+ throw new Error(`Dexie Cloud Addon needs to use WebCrypto, but your browser has disabled it due to being served from an insecure location. Please serve it from https or http://localhost:<port> (See https://stackoverflow.com/questions/46670556/how-to-enable-crypto-subtle-for-unsecure-origins-in-chrome/46671627#46671627)`);
2239
+ }
2240
+ else {
2241
+ throw new Error(`This browser does not support WebCrypto.`);
2242
+ }
2243
+ }
2238
2244
  const { privateKey, publicKey } = yield crypto.subtle.generateKey({
2239
2245
  name: 'RSASSA-PKCS1-v1_5',
2240
2246
  modulusLength: 2048,
@@ -2280,7 +2286,7 @@
2280
2286
  type: 'error',
2281
2287
  messageCode: 'GENERIC_ERROR',
2282
2288
  message: `We're having a problem authenticating right now.`,
2283
- messageParams: {}
2289
+ messageParams: {},
2284
2290
  }).catch(() => { });
2285
2291
  throw error;
2286
2292
  }
@@ -2511,18 +2517,32 @@
2511
2517
  }
2512
2518
  }
2513
2519
  // Already authenticated according to given hints.
2514
- return;
2520
+ return false;
2515
2521
  }
2516
2522
  const context = new AuthPersistedContext(db, {
2517
2523
  claims: {},
2518
2524
  lastLogin: new Date(0),
2519
2525
  });
2520
2526
  yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
2521
- yield context.save();
2527
+ try {
2528
+ yield context.save();
2529
+ }
2530
+ catch (e) {
2531
+ try {
2532
+ if (e.name === 'DataCloneError') {
2533
+ console.debug(`Login context property names:`, Object.keys(context));
2534
+ console.debug(`Login context:`, context);
2535
+ console.debug(`Login context JSON:`, JSON.stringify(context));
2536
+ }
2537
+ }
2538
+ catch (_a) { }
2539
+ throw e;
2540
+ }
2522
2541
  yield setCurrentUser(db, context);
2523
2542
  // Make sure to resync as the new login will be authorized
2524
2543
  // for new realms.
2525
2544
  triggerSync(db, "pull");
2545
+ return true;
2526
2546
  });
2527
2547
  }
2528
2548
 
@@ -2596,6 +2616,36 @@
2596
2616
  }
2597
2617
  }
2598
2618
 
2619
+ const events = globalThis['lbc-events'] || (globalThis['lbc-events'] = new Map());
2620
+ function addListener(name, listener) {
2621
+ if (events.has(name)) {
2622
+ events.get(name).push(listener);
2623
+ }
2624
+ else {
2625
+ events.set(name, [listener]);
2626
+ }
2627
+ }
2628
+ function removeListener(name, listener) {
2629
+ const listeners = events.get(name);
2630
+ if (listeners) {
2631
+ const idx = listeners.indexOf(listener);
2632
+ if (idx !== -1) {
2633
+ listeners.splice(idx, 1);
2634
+ }
2635
+ }
2636
+ }
2637
+ function dispatch(ev) {
2638
+ const listeners = events.get(ev.type);
2639
+ if (listeners) {
2640
+ listeners.forEach(listener => {
2641
+ try {
2642
+ listener(ev);
2643
+ }
2644
+ catch (_a) {
2645
+ }
2646
+ });
2647
+ }
2648
+ }
2599
2649
  class BroadcastedAndLocalEvent extends rxjs.Observable {
2600
2650
  constructor(name) {
2601
2651
  const bc = typeof BroadcastChannel === "undefined"
@@ -2609,16 +2659,24 @@
2609
2659
  subscriber.next(ev.data);
2610
2660
  }
2611
2661
  let unsubscribe;
2612
- self.addEventListener(`lbc-${name}`, onCustomEvent);
2613
- if (bc instanceof SWBroadcastChannel) {
2614
- unsubscribe = bc.subscribe(message => subscriber.next(message));
2662
+ //self.addEventListener(`lbc-${name}`, onCustomEvent); // Fails in service workers
2663
+ addListener(`lbc-${name}`, onCustomEvent); // Works better in service worker
2664
+ try {
2665
+ if (bc instanceof SWBroadcastChannel) {
2666
+ unsubscribe = bc.subscribe(message => subscriber.next(message));
2667
+ }
2668
+ else {
2669
+ console.debug("BroadcastedAndLocalEvent: bc.addEventListener()", name, "bc is a", bc);
2670
+ bc.addEventListener("message", onMessageEvent);
2671
+ }
2615
2672
  }
2616
- else {
2617
- console.debug("BroadcastedAndLocalEvent: bc.addEventListener()", name, "bc is a", bc);
2618
- bc.addEventListener("message", onMessageEvent);
2673
+ catch (err) {
2674
+ // Service workers might fail to subscribe outside its initial script.
2675
+ console.warn('Failed to subscribe to broadcast channel', err);
2619
2676
  }
2620
2677
  return () => {
2621
- self.removeEventListener(`lbc-${name}`, onCustomEvent);
2678
+ //self.removeEventListener(`lbc-${name}`, onCustomEvent);
2679
+ removeListener(`lbc-${name}`, onCustomEvent);
2622
2680
  if (bc instanceof SWBroadcastChannel) {
2623
2681
  unsubscribe();
2624
2682
  }
@@ -2634,7 +2692,8 @@
2634
2692
  console.debug("BroadcastedAndLocalEvent: bc.postMessage()", Object.assign({}, message), "bc is a", this.bc);
2635
2693
  this.bc.postMessage(message);
2636
2694
  const ev = new CustomEvent(`lbc-${this.name}`, { detail: message });
2637
- self.dispatchEvent(ev);
2695
+ //self.dispatchEvent(ev);
2696
+ dispatch(ev);
2638
2697
  }
2639
2698
  }
2640
2699
 
@@ -3623,7 +3682,7 @@
3623
3682
  return _sync
3624
3683
  .apply(this, arguments)
3625
3684
  .then(() => {
3626
- if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) {
3685
+ if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
3627
3686
  db.syncStateChangedEvent.next({
3628
3687
  phase: 'in-sync',
3629
3688
  });
@@ -3722,12 +3781,12 @@
3722
3781
  }
3723
3782
  return [clientChanges, syncState, baseRevs];
3724
3783
  }));
3725
- const syncIsNeeded = clientChangeSet.some((set) => set.muts.some((mut) => mut.keys.length > 0));
3784
+ const pushSyncIsNeeded = clientChangeSet.some((set) => set.muts.some((mut) => mut.keys.length > 0));
3726
3785
  if (justCheckIfNeeded) {
3727
- console.debug('Sync is needed:', syncIsNeeded);
3728
- return syncIsNeeded;
3786
+ console.debug('Sync is needed:', pushSyncIsNeeded);
3787
+ return pushSyncIsNeeded;
3729
3788
  }
3730
- if (purpose === 'push' && !syncIsNeeded) {
3789
+ if (purpose === 'push' && !pushSyncIsNeeded) {
3731
3790
  // The purpose of this request was to push changes
3732
3791
  return false;
3733
3792
  }
@@ -3845,6 +3904,7 @@
3845
3904
  return yield _sync(db, options, schema, { isInitialSync, cancelToken });
3846
3905
  }
3847
3906
  console.debug('SYNC DONE', { isInitialSync });
3907
+ db.syncCompleteEvent.next();
3848
3908
  return false; // Not needed anymore
3849
3909
  });
3850
3910
  }
@@ -4124,6 +4184,7 @@
4124
4184
  if (!db) {
4125
4185
  const localSyncEvent = new rxjs.Subject();
4126
4186
  let syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
4187
+ let syncCompleteEvent = new BroadcastedAndLocalEvent(`synccomplete-${dx.name}`);
4127
4188
  localSyncEvent['id'] = ++static_counter;
4128
4189
  let initiallySynced = false;
4129
4190
  db = {
@@ -4167,6 +4228,9 @@
4167
4228
  get syncStateChangedEvent() {
4168
4229
  return syncStateChangedEvent;
4169
4230
  },
4231
+ get syncCompleteEvent() {
4232
+ return syncCompleteEvent;
4233
+ },
4170
4234
  dx,
4171
4235
  };
4172
4236
  const helperMethods = {
@@ -4198,6 +4262,7 @@
4198
4262
  },
4199
4263
  reconfigure() {
4200
4264
  syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
4265
+ syncCompleteEvent = new BroadcastedAndLocalEvent(`synccomplete-${dx.name}`);
4201
4266
  },
4202
4267
  };
4203
4268
  Object.assign(db, helperMethods);
@@ -5101,7 +5166,15 @@
5101
5166
  function createObservable() {
5102
5167
  return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState === null || syncState === void 0 ? void 0 : syncState.serverRevision), // Don't connect before there's no initial sync performed.
5103
5168
  take(1), // Don't continue waking up whenever syncState change
5104
- switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(([userLogin, syncState]) => __awaiter(this, void 0, void 0, function* () { return [userLogin, yield computeRealmSetHash(syncState)]; })), switchMap(([userLogin, realmSetHash]) =>
5169
+ switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(([userLogin, syncState]) => {
5170
+ if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
5171
+ // We're in an in-between state when user is logged in but the user's realms are not yet synced.
5172
+ // Don't make this change reconnect the websocket just yet. Wait till syncState is updated
5173
+ // to iclude the user's realm.
5174
+ return db.cloud.persistedSyncState.pipe(filter((syncState) => (syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId)) || false), take(1), map((syncState) => [userLogin, syncState]));
5175
+ }
5176
+ return new rxjs.BehaviorSubject([userLogin, syncState]);
5177
+ }), switchMap(([userLogin, syncState]) => __awaiter(this, void 0, void 0, function* () { return [userLogin, yield computeRealmSetHash(syncState)]; })), switchMap(([userLogin, realmSetHash]) =>
5105
5178
  // Let server end query changes from last entry of same client-ID and forward.
5106
5179
  // If no new entries, server won't bother the client. If new entries, server sends only those
5107
5180
  // and the baseRev of the last from same client-ID.
@@ -5970,6 +6043,7 @@
5970
6043
  localSyncWorker = null;
5971
6044
  currentUserEmitter.next(UNAUTHORIZED_USER);
5972
6045
  });
6046
+ const syncComplete = new rxjs.Subject();
5973
6047
  dexie.cloud = {
5974
6048
  version: '{version}',
5975
6049
  options: Object.assign({}, DEFAULT_OPTIONS),
@@ -5982,6 +6056,9 @@
5982
6056
  phase: 'initial',
5983
6057
  status: 'not-started',
5984
6058
  }),
6059
+ events: {
6060
+ syncComplete,
6061
+ },
5985
6062
  persistedSyncState: new rxjs.BehaviorSubject(undefined),
5986
6063
  userInteraction: new rxjs.BehaviorSubject(undefined),
5987
6064
  webSocketStatus: new rxjs.BehaviorSubject('not-started'),
@@ -6075,6 +6152,8 @@
6075
6152
  if (!db.cloud.isServiceWorkerDB) {
6076
6153
  subscriptions.push(computeSyncState(db).subscribe(dexie.cloud.syncState));
6077
6154
  }
6155
+ // Forward db.syncCompleteEvent to be publicly consumable via db.cloud.events.syncComplete:
6156
+ subscriptions.push(db.syncCompleteEvent.subscribe(syncComplete));
6078
6157
  //verifyConfig(db.cloud.options); Not needed (yet at least!)
6079
6158
  // Verify the user has allowed version increment.
6080
6159
  if (!db.tables.every((table) => table.core)) {
@@ -6182,15 +6261,16 @@
6182
6261
  ]).toPromise();
6183
6262
  }
6184
6263
  // HERE: If requireAuth, do athentication now.
6264
+ let changedUser = false;
6185
6265
  if ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.requireAuth) {
6186
- yield login(db);
6266
+ changedUser = yield login(db);
6187
6267
  }
6188
6268
  if (localSyncWorker)
6189
6269
  localSyncWorker.stop();
6190
6270
  localSyncWorker = null;
6191
6271
  throwIfClosed();
6192
6272
  if (db.cloud.usingServiceWorker && ((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl)) {
6193
- registerSyncEvent(db, 'push').catch(() => { });
6273
+ registerSyncEvent(db, changedUser ? 'pull' : 'push').catch(() => { });
6194
6274
  registerPeriodicSyncEvent(db).catch(() => { });
6195
6275
  }
6196
6276
  else if (((_e = db.cloud.options) === null || _e === void 0 ? void 0 : _e.databaseUrl) &&
@@ -6199,7 +6279,7 @@
6199
6279
  // There's no SW. Start SyncWorker instead.
6200
6280
  localSyncWorker = LocalSyncWorker(db, db.cloud.options, db.cloud.schema);
6201
6281
  localSyncWorker.start();
6202
- triggerSync(db, 'push');
6282
+ triggerSync(db, changedUser ? 'pull' : 'push');
6203
6283
  }
6204
6284
  // Listen to online event and do sync.
6205
6285
  throwIfClosed();