dexie-cloud-addon 4.1.0-alpha.21 → 4.1.0-alpha.23

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.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.1.0-alpha.21, Mon Nov 18 2024
11
+ * Version 4.1.0-alpha.23, Wed Nov 27 2024
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import Dexie, { PropModification, cmp, DexieYProvider, RangeSet, liveQuery } from 'dexie';
20
- import { Observable as Observable$1, BehaviorSubject, firstValueFrom, Subject, from as from$1, filter as filter$1, fromEvent, of, merge, switchMap as switchMap$1, tap as tap$1, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
20
+ import { Observable as Observable$1, BehaviorSubject, firstValueFrom, Subject, from as from$1, filter as filter$1, fromEvent, of, merge, switchMap as switchMap$1, tap as tap$1, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1, startWith as startWith$1 } from 'rxjs';
21
21
 
22
22
  /******************************************************************************
23
23
  Copyright (c) Microsoft Corporation.
@@ -2765,6 +2765,9 @@ function encodeYMessage(msg) {
2765
2765
  break;
2766
2766
  case 'outdated-server-rev':
2767
2767
  break;
2768
+ case 'y-complete-sync-done':
2769
+ writeVarString(encoder, msg.yServerRev);
2770
+ break;
2768
2771
  default:
2769
2772
  writeAny(encoder, msg.k);
2770
2773
  switch (msg.type) {
@@ -3111,6 +3114,9 @@ function decodeYMessage(a) {
3111
3114
  if (type === 'outdated-server-rev') {
3112
3115
  return { type };
3113
3116
  }
3117
+ if (type === 'y-complete-sync-done') {
3118
+ return { type, yServerRev: readVarString(decoder) };
3119
+ }
3114
3120
  const table = readVarString(decoder);
3115
3121
  const prop = readVarString(decoder);
3116
3122
  switch (type) {
@@ -4416,6 +4422,7 @@ function syncWithServer(changes, y, syncState, baseRevs, db, databaseUrl, schema
4416
4422
  lastPull: syncState
4417
4423
  ? {
4418
4424
  serverRevision: syncState.serverRevision,
4425
+ yServerRevision: syncState.yServerRevision,
4419
4426
  realms: syncState.realms,
4420
4427
  inviteRealms: syncState.inviteRealms,
4421
4428
  }
@@ -4763,6 +4770,7 @@ function applyYServerMessages(yMessages, db) {
4763
4770
  var _a;
4764
4771
  const receivedUntils = {};
4765
4772
  let resyncNeeded = false;
4773
+ let yServerRevision;
4766
4774
  for (const m of yMessages) {
4767
4775
  switch (m.type) {
4768
4776
  case 'u-s': {
@@ -4819,6 +4827,10 @@ function applyYServerMessages(yMessages, db) {
4819
4827
  }
4820
4828
  break;
4821
4829
  }
4830
+ case 'y-complete-sync-done': {
4831
+ yServerRevision = m.yServerRev;
4832
+ break;
4833
+ }
4822
4834
  case 'outdated-server-rev':
4823
4835
  resyncNeeded = true;
4824
4836
  break;
@@ -4827,11 +4839,12 @@ function applyYServerMessages(yMessages, db) {
4827
4839
  return {
4828
4840
  receivedUntils,
4829
4841
  resyncNeeded,
4842
+ yServerRevision
4830
4843
  };
4831
4844
  });
4832
4845
  }
4833
4846
 
4834
- function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
4847
+ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db) {
4835
4848
  return __awaiter(this, void 0, void 0, function* () {
4836
4849
  var _a, _b, _c, _d, _e;
4837
4850
  // We want to update unsentFrom for each yTable to the value specified in first argument
@@ -4880,14 +4893,12 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
4880
4893
  yield db.table(yTable).add({
4881
4894
  i: DEXIE_CLOUD_SYNCER_ID,
4882
4895
  unsentFrom,
4883
- receivedUntil,
4884
- serverRev: serverRevision,
4896
+ receivedUntil
4885
4897
  });
4886
4898
  }
4887
4899
  else {
4888
4900
  state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
4889
4901
  state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
4890
- state.serverRev = serverRevision;
4891
4902
  yield db.table(yTable).put(state);
4892
4903
  }
4893
4904
  }));
@@ -5223,6 +5234,7 @@ function _sync(db_1, options_1, schema_1) {
5223
5234
  newSyncState.realms = res.realms;
5224
5235
  newSyncState.inviteRealms = res.inviteRealms;
5225
5236
  newSyncState.serverRevision = res.serverRevision;
5237
+ newSyncState.yServerRevision = res.serverRevision;
5226
5238
  newSyncState.timestamp = new Date();
5227
5239
  delete newSyncState.error;
5228
5240
  const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
@@ -5234,11 +5246,14 @@ function _sync(db_1, options_1, schema_1) {
5234
5246
  //
5235
5247
  // apply yMessages
5236
5248
  //
5237
- const { receivedUntils, resyncNeeded } = yield applyYServerMessages(res.yMessages, db);
5249
+ const { receivedUntils, resyncNeeded, yServerRevision } = yield applyYServerMessages(res.yMessages, db);
5250
+ if (yServerRevision) {
5251
+ newSyncState.yServerRevision = yServerRevision;
5252
+ }
5238
5253
  //
5239
5254
  // update Y SyncStates
5240
5255
  //
5241
- yield updateYSyncStates(lastUpdateIds, receivedUntils, db, res.serverRevision);
5256
+ yield updateYSyncStates(lastUpdateIds, receivedUntils, db);
5242
5257
  if (resyncNeeded) {
5243
5258
  newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
5244
5259
  }
@@ -5534,7 +5549,7 @@ function MessagesFromServerConsumer(db) {
5534
5549
  };
5535
5550
  }
5536
5551
 
5537
- const wm$1 = new WeakMap();
5552
+ const wm$2 = new WeakMap();
5538
5553
  const DEXIE_CLOUD_SCHEMA = {
5539
5554
  members: '@id, [userId+realmId], [email+realmId], realmId',
5540
5555
  roles: '[realmId+name]',
@@ -5548,7 +5563,7 @@ let static_counter = 0;
5548
5563
  function DexieCloudDB(dx) {
5549
5564
  if ('vip' in dx)
5550
5565
  dx = dx['vip']; // Avoid race condition. Always map to a vipped dexie that don't block during db.on.ready().
5551
- let db = wm$1.get(dx.cloud);
5566
+ let db = wm$2.get(dx.cloud);
5552
5567
  if (!db) {
5553
5568
  const localSyncEvent = new Subject();
5554
5569
  let syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
@@ -5636,7 +5651,7 @@ function DexieCloudDB(dx) {
5636
5651
  Object.assign(db, helperMethods);
5637
5652
  db.messageConsumer = MessagesFromServerConsumer(db);
5638
5653
  db.messageProducer = new Subject();
5639
- wm$1.set(dx.cloud, db);
5654
+ wm$2.set(dx.cloud, db);
5640
5655
  }
5641
5656
  return db;
5642
5657
  }
@@ -5647,10 +5662,10 @@ function nameFromKeyPath(keyPath) {
5647
5662
  }
5648
5663
 
5649
5664
  // Emulate true-private property db. Why? So it's not stored in DB.
5650
- const wm = new WeakMap();
5665
+ const wm$1 = new WeakMap();
5651
5666
  class AuthPersistedContext {
5652
5667
  constructor(db, userLogin) {
5653
- wm.set(this, db);
5668
+ wm$1.set(this, db);
5654
5669
  Object.assign(this, userLogin);
5655
5670
  }
5656
5671
  static load(db, userId) {
@@ -5667,7 +5682,7 @@ class AuthPersistedContext {
5667
5682
  }
5668
5683
  save() {
5669
5684
  return __awaiter(this, void 0, void 0, function* () {
5670
- const db = wm.get(this);
5685
+ const db = wm$1.get(this);
5671
5686
  db.table("$logins").put(this);
5672
5687
  });
5673
5688
  }
@@ -6802,9 +6817,7 @@ function createYClientUpdateObservable(db) {
6802
6817
  // If messageProducer emits empty array, nothing is emitted
6803
6818
  // but if messageProducer emits array of messages, they are
6804
6819
  // emitted one by one.
6805
- mergeMap$1((messages) => messages), tap$1((message) => {
6806
- console.debug('dexie-cloud emitting y-c', message);
6807
- }));
6820
+ mergeMap$1((messages) => messages));
6808
6821
  }
6809
6822
 
6810
6823
  function getAwarenessLibrary(db) {
@@ -6817,6 +6830,23 @@ function getAwarenessLibrary(db) {
6817
6830
  const awarenessWeakMap = new WeakMap();
6818
6831
  const getDocAwareness = (doc) => awarenessWeakMap.get(doc);
6819
6832
 
6833
+ const wm = new WeakMap();
6834
+ /** A property (package-private) on Y.Doc that is used
6835
+ * to signal that the server wants us to send a 'doc-open' message
6836
+ * to the server for this document.
6837
+ *
6838
+ * @param doc
6839
+ * @returns
6840
+ */
6841
+ function getOpenDocSignal(doc) {
6842
+ let signal = wm.get(doc);
6843
+ if (!signal) {
6844
+ signal = new Subject();
6845
+ wm.set(doc, signal);
6846
+ }
6847
+ return signal;
6848
+ }
6849
+
6820
6850
  const SERVER_PING_TIMEOUT = 20000;
6821
6851
  const CLIENT_PING_INTERVAL = 30000;
6822
6852
  const FAIL_RETRY_WAIT_TIME = 60000;
@@ -6979,9 +7009,6 @@ class WSConnection extends Subscription$1 {
6979
7009
  if (msg.type === 'error') {
6980
7010
  throw new Error(`Error message from dexie-cloud: ${msg.error}`);
6981
7011
  }
6982
- else if (msg.type === 'rev') {
6983
- this.rev = msg.rev; // No meaning but seems reasonable.
6984
- }
6985
7012
  else if (msg.type === 'aware') {
6986
7013
  const docCache = DexieYProvider.getDocCache(this.db.dx);
6987
7014
  const doc = docCache.find(msg.table, msg.k, msg.prop);
@@ -6996,11 +7023,19 @@ class WSConnection extends Subscription$1 {
6996
7023
  else if (msg.type === 'u-ack' || msg.type === 'u-reject' || msg.type === 'u-s' || msg.type === 'in-sync') {
6997
7024
  applyYServerMessages([msg], this.db);
6998
7025
  }
6999
- else if (msg.type === 'outdated-server-rev') {
7026
+ else if (msg.type === 'doc-open') {
7027
+ const docCache = DexieYProvider.getDocCache(this.db.dx);
7028
+ const doc = docCache.find(msg.table, msg.k, msg.prop);
7029
+ if (doc) {
7030
+ getOpenDocSignal(doc).next(); // Make yHandler reopen the document on server.
7031
+ }
7032
+ }
7033
+ else if (msg.type === 'outdated-server-rev' || msg.type === 'y-complete-sync-done') {
7000
7034
  // Won't happen but need this for typing.
7001
- throw new Error('Outdated server revision not expected over WebSocket - only in sync using fetch()');
7035
+ throw new Error('Outdated server revision or y-complete-sync-done not expected over WebSocket - only in sync using fetch()');
7002
7036
  }
7003
7037
  else if (msg.type !== 'pong') {
7038
+ // Forward the request to our subscriber, wich is in messageFromServerQueue.ts (via connectWebSocket's subscribe() at the end!)
7004
7039
  this.subscriber.next(msg);
7005
7040
  }
7006
7041
  }
@@ -7037,24 +7072,21 @@ class WSConnection extends Subscription$1 {
7037
7072
  }
7038
7073
  console.debug('dexie-cloud WebSocket send', msg.type, msg);
7039
7074
  if (msg.type === 'ready') {
7075
+ // Ok, we are certain to have stored everything up until revision msg.rev.
7076
+ // Update this.rev in case of reconnect - remember where we were and don't just start over!
7077
+ this.rev = msg.rev;
7078
+ // ... and then send along the request to the server so it would also be updated!
7040
7079
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
7041
7080
  }
7042
7081
  else {
7043
7082
  // If it's not a "ready" message, it's an YMessage.
7044
7083
  // YMessages can be sent binary encoded.
7045
- if (msg.type === 'u-c') {
7046
- console.log("u-c:B", ++gotClientUpdateB);
7047
- }
7048
7084
  (_b = this.ws) === null || _b === void 0 ? void 0 : _b.send(encodeYMessage(msg));
7049
7085
  }
7050
7086
  }
7051
7087
  }));
7052
7088
  if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
7053
- this.subscriptions.add(createYClientUpdateObservable(this.db).pipe(tap$1((msg) => {
7054
- if (msg.type === 'u-c') {
7055
- console.log("u-c:A", ++gotClientUpdateA, msg.i);
7056
- }
7057
- })).subscribe(this.db.messageProducer));
7089
+ this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
7058
7090
  }
7059
7091
  }
7060
7092
  catch (error) {
@@ -7063,8 +7095,6 @@ class WSConnection extends Subscription$1 {
7063
7095
  });
7064
7096
  }
7065
7097
  }
7066
- let gotClientUpdateA = 0;
7067
- let gotClientUpdateB = 0;
7068
7098
 
7069
7099
  class InvalidLicenseError extends Error {
7070
7100
  constructor(license) {
@@ -7953,14 +7983,14 @@ function createYHandler(db) {
7953
7983
  return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
7954
7984
  }
7955
7985
  let awareness;
7956
- Object.defineProperty(provider, "awareness", {
7986
+ Object.defineProperty(provider, 'awareness', {
7957
7987
  get() {
7958
7988
  if (awareness)
7959
7989
  return awareness;
7960
7990
  awareness = createAwareness(db, doc, provider);
7961
7991
  awarenessWeakMap.set(doc, awareness);
7962
7992
  return awareness;
7963
- }
7993
+ },
7964
7994
  });
7965
7995
  };
7966
7996
  }
@@ -7968,6 +7998,7 @@ function createAwareness(db, doc, provider) {
7968
7998
  const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7969
7999
  const awap = getAwarenessLibrary(db);
7970
8000
  const awareness = new awap.Awareness(doc);
8001
+ const reopenDocSignal = getOpenDocSignal(doc);
7971
8002
  awareness.on('update', ({ added, updated, removed }, origin) => {
7972
8003
  // Send the update
7973
8004
  const changedClients = added.concat(updated).concat(removed);
@@ -7983,7 +8014,7 @@ function createAwareness(db, doc, provider) {
7983
8014
  });
7984
8015
  if (provider.destroyed) {
7985
8016
  // We're called from awareness.on('destroy') that did
7986
- // removeAwarenessStates.
8017
+ // removeAwarenessStates.
7987
8018
  // It's time to also send the doc-close message that dexie-cloud understands
7988
8019
  // and uses to stop subscribing for updates and awareness updates and brings
7989
8020
  // down the cached information in memory on the WS connection for this.
@@ -7991,7 +8022,7 @@ function createAwareness(db, doc, provider) {
7991
8022
  type: 'doc-close',
7992
8023
  table: parentTable,
7993
8024
  prop: parentProp,
7994
- k: doc.meta.parentId
8025
+ k: doc.meta.parentId,
7995
8026
  });
7996
8027
  }
7997
8028
  }
@@ -8007,16 +8038,21 @@ function createAwareness(db, doc, provider) {
8007
8038
  return;
8008
8039
  let connected = false;
8009
8040
  let currentFlowId = 1;
8010
- const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
8041
+ const subscription = combineLatest([
8042
+ db.cloud.webSocketStatus, // Wake up when webSocket status changes
8043
+ reopenDocSignal.pipe(startWith$1(null)), // Wake up when reopenDocSignal emits
8044
+ ]).subscribe(([wsStatus]) => {
8011
8045
  if (provider.destroyed)
8012
8046
  return;
8013
8047
  // Keep "connected" state in a variable so we can check it after async operations
8014
8048
  connected = wsStatus === 'connected';
8015
8049
  // We are or got connected. Open the document on the server.
8016
8050
  const user = db.cloud.currentUser.value;
8017
- if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
8051
+ if (wsStatus === 'connected' &&
8052
+ user.isLoggedIn &&
8053
+ !isEagerSyncDisabled(db)) {
8018
8054
  ++currentFlowId;
8019
- openDocumentOnServer().catch(error => {
8055
+ openDocumentOnServer().catch((error) => {
8020
8056
  console.warn(`Error catched in createYHandler.ts: ${error}`);
8021
8057
  });
8022
8058
  }
@@ -8035,27 +8071,35 @@ function createAwareness(db, doc, provider) {
8035
8071
  * case no state vector is sent (then the server already knows us by
8036
8072
  * revision)
8037
8073
  *
8038
- * When server gets the doc-open message, it will authorized us for
8074
+ * When server gets the doc-open message, it will authorize us for
8039
8075
  * whether we are allowed to read / write to this document, and then
8040
8076
  * keep the cached information in memory on the WS connection for this
8041
8077
  * particular document, as well as subscribe to updates and awareness updates
8042
8078
  * from other clients on the document.
8043
8079
  */
8044
- function openDocumentOnServer(wsStatus) {
8080
+ function openDocumentOnServer() {
8045
8081
  return __awaiter(this, void 0, void 0, function* () {
8046
8082
  const myFlow = currentFlowId; // So we can abort when a new flow is started
8047
8083
  const yTbl = db.table(updatesTable);
8048
- const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
8084
+ const syncStateTbl = db.$syncState;
8085
+ const [receivedUntil, yServerRev] = yield db.transaction('r', syncStateTbl, yTbl, () => __awaiter(this, void 0, void 0, function* () {
8086
+ const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
8087
+ const persistedSyncState = yield syncStateTbl.get('syncState');
8088
+ return [
8089
+ (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0,
8090
+ (persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.yServerRevision) ||
8091
+ (persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.serverRevision),
8092
+ ];
8093
+ }));
8049
8094
  // After every await, check if we still should be working on this task.
8050
8095
  if (provider.destroyed || currentFlowId !== myFlow || !connected)
8051
8096
  return;
8052
- const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
8053
8097
  const docOpenMsg = {
8054
8098
  type: 'doc-open',
8055
8099
  table: parentTable,
8056
8100
  prop: parentProp,
8057
8101
  k: parentId,
8058
- serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
8102
+ serverRev: yServerRev,
8059
8103
  };
8060
8104
  const serverUpdatesSinceLastSync = yield yTbl
8061
8105
  .where('i')
@@ -8120,7 +8164,7 @@ function dexieCloud(dexie) {
8120
8164
  const syncComplete = new Subject();
8121
8165
  dexie.cloud = {
8122
8166
  // @ts-ignore
8123
- version: "4.1.0-alpha.21",
8167
+ version: "4.1.0-alpha.23",
8124
8168
  options: Object.assign({}, DEFAULT_OPTIONS),
8125
8169
  schema: null,
8126
8170
  get currentUserId() {
@@ -8422,7 +8466,7 @@ function dexieCloud(dexie) {
8422
8466
  }
8423
8467
  }
8424
8468
  // @ts-ignore
8425
- dexieCloud.version = "4.1.0-alpha.21";
8469
+ dexieCloud.version = "4.1.0-alpha.23";
8426
8470
  Dexie.Cloud = dexieCloud;
8427
8471
 
8428
8472
  // In case the SW lives for a while, let it reuse already opened connections: