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.
@@ -1,5 +1,6 @@
1
1
  export interface PersistedSyncState {
2
2
  serverRevision?: any;
3
+ yServerRevision?: string;
3
4
  latestRevisions: {
4
5
  [tableName: string]: number;
5
6
  };
@@ -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 { firstValueFrom, from as from$1, filter as filter$1, Observable as Observable$1, BehaviorSubject, Subject, 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 { firstValueFrom, from as from$1, filter as filter$1, Observable as Observable$1, BehaviorSubject, Subject, 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.
@@ -990,6 +990,9 @@ function encodeYMessage(msg) {
990
990
  break;
991
991
  case 'outdated-server-rev':
992
992
  break;
993
+ case 'y-complete-sync-done':
994
+ writeVarString(encoder, msg.yServerRev);
995
+ break;
993
996
  default:
994
997
  writeAny(encoder, msg.k);
995
998
  switch (msg.type) {
@@ -1336,6 +1339,9 @@ function decodeYMessage(a) {
1336
1339
  if (type === 'outdated-server-rev') {
1337
1340
  return { type };
1338
1341
  }
1342
+ if (type === 'y-complete-sync-done') {
1343
+ return { type, yServerRev: readVarString(decoder) };
1344
+ }
1339
1345
  const table = readVarString(decoder);
1340
1346
  const prop = readVarString(decoder);
1341
1347
  switch (type) {
@@ -3421,10 +3427,10 @@ function formatAsPem(str) {
3421
3427
  }
3422
3428
 
3423
3429
  // Emulate true-private property db. Why? So it's not stored in DB.
3424
- const wm$1 = new WeakMap();
3430
+ const wm$2 = new WeakMap();
3425
3431
  class AuthPersistedContext {
3426
3432
  constructor(db, userLogin) {
3427
- wm$1.set(this, db);
3433
+ wm$2.set(this, db);
3428
3434
  Object.assign(this, userLogin);
3429
3435
  }
3430
3436
  static load(db, userId) {
@@ -3441,7 +3447,7 @@ class AuthPersistedContext {
3441
3447
  }
3442
3448
  save() {
3443
3449
  return __awaiter(this, void 0, void 0, function* () {
3444
- const db = wm$1.get(this);
3450
+ const db = wm$2.get(this);
3445
3451
  db.table("$logins").put(this);
3446
3452
  });
3447
3453
  }
@@ -4722,6 +4728,7 @@ function syncWithServer(changes, y, syncState, baseRevs, db, databaseUrl, schema
4722
4728
  lastPull: syncState
4723
4729
  ? {
4724
4730
  serverRevision: syncState.serverRevision,
4731
+ yServerRevision: syncState.yServerRevision,
4725
4732
  realms: syncState.realms,
4726
4733
  inviteRealms: syncState.inviteRealms,
4727
4734
  }
@@ -5069,6 +5076,7 @@ function applyYServerMessages(yMessages, db) {
5069
5076
  var _a;
5070
5077
  const receivedUntils = {};
5071
5078
  let resyncNeeded = false;
5079
+ let yServerRevision;
5072
5080
  for (const m of yMessages) {
5073
5081
  switch (m.type) {
5074
5082
  case 'u-s': {
@@ -5125,6 +5133,10 @@ function applyYServerMessages(yMessages, db) {
5125
5133
  }
5126
5134
  break;
5127
5135
  }
5136
+ case 'y-complete-sync-done': {
5137
+ yServerRevision = m.yServerRev;
5138
+ break;
5139
+ }
5128
5140
  case 'outdated-server-rev':
5129
5141
  resyncNeeded = true;
5130
5142
  break;
@@ -5133,11 +5145,12 @@ function applyYServerMessages(yMessages, db) {
5133
5145
  return {
5134
5146
  receivedUntils,
5135
5147
  resyncNeeded,
5148
+ yServerRevision
5136
5149
  };
5137
5150
  });
5138
5151
  }
5139
5152
 
5140
- function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
5153
+ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db) {
5141
5154
  return __awaiter(this, void 0, void 0, function* () {
5142
5155
  var _a, _b, _c, _d, _e;
5143
5156
  // We want to update unsentFrom for each yTable to the value specified in first argument
@@ -5186,14 +5199,12 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
5186
5199
  yield db.table(yTable).add({
5187
5200
  i: DEXIE_CLOUD_SYNCER_ID,
5188
5201
  unsentFrom,
5189
- receivedUntil,
5190
- serverRev: serverRevision,
5202
+ receivedUntil
5191
5203
  });
5192
5204
  }
5193
5205
  else {
5194
5206
  state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
5195
5207
  state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
5196
- state.serverRev = serverRevision;
5197
5208
  yield db.table(yTable).put(state);
5198
5209
  }
5199
5210
  }));
@@ -5529,6 +5540,7 @@ function _sync(db_1, options_1, schema_1) {
5529
5540
  newSyncState.realms = res.realms;
5530
5541
  newSyncState.inviteRealms = res.inviteRealms;
5531
5542
  newSyncState.serverRevision = res.serverRevision;
5543
+ newSyncState.yServerRevision = res.serverRevision;
5532
5544
  newSyncState.timestamp = new Date();
5533
5545
  delete newSyncState.error;
5534
5546
  const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
@@ -5540,11 +5552,14 @@ function _sync(db_1, options_1, schema_1) {
5540
5552
  //
5541
5553
  // apply yMessages
5542
5554
  //
5543
- const { receivedUntils, resyncNeeded } = yield applyYServerMessages(res.yMessages, db);
5555
+ const { receivedUntils, resyncNeeded, yServerRevision } = yield applyYServerMessages(res.yMessages, db);
5556
+ if (yServerRevision) {
5557
+ newSyncState.yServerRevision = yServerRevision;
5558
+ }
5544
5559
  //
5545
5560
  // update Y SyncStates
5546
5561
  //
5547
- yield updateYSyncStates(lastUpdateIds, receivedUntils, db, res.serverRevision);
5562
+ yield updateYSyncStates(lastUpdateIds, receivedUntils, db);
5548
5563
  if (resyncNeeded) {
5549
5564
  newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
5550
5565
  }
@@ -5840,7 +5855,7 @@ function MessagesFromServerConsumer(db) {
5840
5855
  };
5841
5856
  }
5842
5857
 
5843
- const wm = new WeakMap();
5858
+ const wm$1 = new WeakMap();
5844
5859
  const DEXIE_CLOUD_SCHEMA = {
5845
5860
  members: '@id, [userId+realmId], [email+realmId], realmId',
5846
5861
  roles: '[realmId+name]',
@@ -5854,7 +5869,7 @@ let static_counter = 0;
5854
5869
  function DexieCloudDB(dx) {
5855
5870
  if ('vip' in dx)
5856
5871
  dx = dx['vip']; // Avoid race condition. Always map to a vipped dexie that don't block during db.on.ready().
5857
- let db = wm.get(dx.cloud);
5872
+ let db = wm$1.get(dx.cloud);
5858
5873
  if (!db) {
5859
5874
  const localSyncEvent = new Subject();
5860
5875
  let syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
@@ -5942,7 +5957,7 @@ function DexieCloudDB(dx) {
5942
5957
  Object.assign(db, helperMethods);
5943
5958
  db.messageConsumer = MessagesFromServerConsumer(db);
5944
5959
  db.messageProducer = new Subject();
5945
- wm.set(dx.cloud, db);
5960
+ wm$1.set(dx.cloud, db);
5946
5961
  }
5947
5962
  return db;
5948
5963
  }
@@ -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')
@@ -8127,7 +8171,7 @@ function dexieCloud(dexie) {
8127
8171
  const syncComplete = new Subject();
8128
8172
  dexie.cloud = {
8129
8173
  // @ts-ignore
8130
- version: "4.1.0-alpha.21",
8174
+ version: "4.1.0-alpha.23",
8131
8175
  options: Object.assign({}, DEFAULT_OPTIONS),
8132
8176
  schema: null,
8133
8177
  get currentUserId() {
@@ -8429,7 +8473,7 @@ function dexieCloud(dexie) {
8429
8473
  }
8430
8474
  }
8431
8475
  // @ts-ignore
8432
- dexieCloud.version = "4.1.0-alpha.21";
8476
+ dexieCloud.version = "4.1.0-alpha.23";
8433
8477
  Dexie.Cloud = dexieCloud;
8434
8478
 
8435
8479
  const ydocTriggers = {};