dexie-cloud-addon 4.1.0-alpha.2 → 4.1.0-alpha.20

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.
Files changed (35) hide show
  1. package/dist/modern/DexieCloudOptions.d.ts +3 -0
  2. package/dist/modern/TSON.d.ts +1 -1
  3. package/dist/modern/WSObservable.d.ts +4 -4
  4. package/dist/modern/define-ydoc-trigger.d.ts +2 -0
  5. package/dist/modern/dexie-cloud-addon.d.ts +1 -0
  6. package/dist/modern/dexie-cloud-addon.js +548 -231
  7. package/dist/modern/dexie-cloud-addon.js.map +1 -1
  8. package/dist/modern/dexie-cloud-addon.min.js +1 -1
  9. package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
  10. package/dist/modern/getInvitesObservable.d.ts +11 -11
  11. package/dist/modern/service-worker.js +390 -229
  12. package/dist/modern/service-worker.js.map +1 -1
  13. package/dist/modern/service-worker.min.js +1 -1
  14. package/dist/modern/service-worker.min.js.map +1 -1
  15. package/dist/modern/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
  16. package/dist/umd/DexieCloudOptions.d.ts +3 -0
  17. package/dist/umd/TSON.d.ts +1 -1
  18. package/dist/umd/WSObservable.d.ts +4 -4
  19. package/dist/umd/define-ydoc-trigger.d.ts +2 -0
  20. package/dist/umd/dexie-cloud-addon.d.ts +1 -0
  21. package/dist/umd/dexie-cloud-addon.js +546 -228
  22. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  23. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  24. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  25. package/dist/umd/getInvitesObservable.d.ts +11 -11
  26. package/dist/umd/service-worker.js +388 -227
  27. package/dist/umd/service-worker.js.map +1 -1
  28. package/dist/umd/service-worker.min.js +1 -1
  29. package/dist/umd/service-worker.min.js.map +1 -1
  30. package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
  31. package/package.json +3 -3
  32. package/dist/modern/yjs/listYClientMessages.d.ts +0 -3
  33. package/dist/umd/yjs/listYClientMessages.d.ts +0 -3
  34. /package/dist/modern/yjs/{y.d.ts → Y.d.ts} +0 -0
  35. /package/dist/umd/yjs/{y.d.ts → Y.d.ts} +0 -0
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.1.0-alpha.2, Mon Oct 07 2024
11
+ * Version 4.1.0-alpha.20, Wed Oct 23 2024
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -1768,8 +1768,8 @@
1768
1768
  });
1769
1769
  }
1770
1770
  function registerPeriodicSyncEvent(db) {
1771
- var _a;
1772
1771
  return __awaiter(this, void 0, void 0, function* () {
1772
+ var _a;
1773
1773
  try {
1774
1774
  // Register periodicSync event to SW:
1775
1775
  // @ts-ignore
@@ -1834,8 +1834,8 @@
1834
1834
  return btoa(strs.join(""));
1835
1835
  };
1836
1836
 
1837
- function computeRealmSetHash({ realms, inviteRealms, }) {
1838
- return __awaiter(this, void 0, void 0, function* () {
1837
+ function computeRealmSetHash(_a) {
1838
+ return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
1839
1839
  const data = JSON.stringify([
1840
1840
  ...realms.map((realmId) => ({ realmId, accepted: true })),
1841
1841
  ...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
@@ -1871,8 +1871,8 @@
1871
1871
  return concat.apply([], a);
1872
1872
  }
1873
1873
 
1874
- function listClientChanges(mutationTables, db, { since = {}, limit = Infinity } = {}) {
1875
- return __awaiter(this, void 0, void 0, function* () {
1874
+ function listClientChanges(mutationTables_1, db_1) {
1875
+ return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
1876
1876
  const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
1877
1877
  const tableName = getTableFromMutationTable(mutationTable.name);
1878
1878
  const lastRevision = since[tableName];
@@ -3519,8 +3519,8 @@
3519
3519
  }
3520
3520
 
3521
3521
  function loadAccessToken(db) {
3522
- var _a, _b, _c;
3523
3522
  return __awaiter(this, void 0, void 0, function* () {
3523
+ var _a, _b, _c;
3524
3524
  const currentUser = yield db.getCurrentUser();
3525
3525
  const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
3526
3526
  if (!accessToken)
@@ -4350,8 +4350,8 @@
4350
4350
  // seconds (given that there is a Ratelimit-Reset header).
4351
4351
  let syncRatelimitDelays = new WeakMap();
4352
4352
  function checkSyncRateLimitDelay(db) {
4353
- var _a, _b;
4354
4353
  return __awaiter(this, void 0, void 0, function* () {
4354
+ var _a, _b;
4355
4355
  const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
4356
4356
  if (delatMilliseconds > 0) {
4357
4357
  console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
@@ -4664,13 +4664,12 @@
4664
4664
  * @param db
4665
4665
  * @returns
4666
4666
  */
4667
- function listYClientMessagesAndStateVector(db) {
4668
- var _a;
4667
+ function listYClientMessagesAndStateVector(db, tablesToSync) {
4669
4668
  return __awaiter(this, void 0, void 0, function* () {
4670
4669
  const result = [];
4671
4670
  const lastUpdateIds = {};
4672
- for (const table of db.tables) {
4673
- if (table.schema.yProps && ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync)) {
4671
+ for (const table of tablesToSync) {
4672
+ if (table.schema.yProps) {
4674
4673
  for (const yProp of table.schema.yProps) {
4675
4674
  const Y = $Y(db); // This is how we retrieve the user-provided Y library
4676
4675
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
@@ -4757,6 +4756,7 @@
4757
4756
 
4758
4757
  function applyYServerMessages(yMessages, db) {
4759
4758
  return __awaiter(this, void 0, void 0, function* () {
4759
+ var _a;
4760
4760
  const result = {};
4761
4761
  for (const m of yMessages) {
4762
4762
  switch (m.type) {
@@ -4787,7 +4787,24 @@
4787
4787
  // See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
4788
4788
  console.debug(`Y update rejected. Deleting it.`);
4789
4789
  const utbl = getUpdatesTable(db, m.table, m.prop);
4790
- yield utbl.delete(m.i);
4790
+ // Delete the rejected update and all local updates since (avoid holes in the CRDT)
4791
+ // and destroy it's open document if there is one.
4792
+ const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
4793
+ if (primaryKey != null) {
4794
+ yield db.transaction('rw', utbl, tx => {
4795
+ // @ts-ignore
4796
+ tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
4797
+ return utbl
4798
+ .where('i')
4799
+ .aboveOrEqual(m.i)
4800
+ .filter(u => Dexie.cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
4801
+ .delete();
4802
+ });
4803
+ // Destroy active doc
4804
+ const activeDoc = Dexie.DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
4805
+ if (activeDoc)
4806
+ activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
4807
+ }
4791
4808
  break;
4792
4809
  }
4793
4810
  case 'in-sync': {
@@ -4804,15 +4821,15 @@
4804
4821
  }
4805
4822
 
4806
4823
  function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
4807
- var _a, _b;
4808
4824
  return __awaiter(this, void 0, void 0, function* () {
4825
+ var _a, _b, _c, _d, _e;
4809
4826
  // We want to update unsentFrom for each yTable to the value specified in first argument
4810
4827
  // because we got those values before we synced with server and here we are back from server
4811
4828
  // that has successfully received all those messages - no matter if the last update was a client or server update,
4812
4829
  // we can safely store unsentFrom to a value of the last update + 1 here.
4813
4830
  // We also want to update receivedUntil for each yTable to the value specified in the second argument,
4814
4831
  // because that contains the highest resulted id of each update from server after storing it.
4815
- // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
4832
+ // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
4816
4833
  // to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
4817
4834
  // per yTable.
4818
4835
  const mergedSpec = {};
@@ -4824,28 +4841,42 @@
4824
4841
  (_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
4825
4842
  mergedSpec[yTable].receivedUntil = lastUpdateId;
4826
4843
  }
4827
- // Now go through the merged map and update YSyncStates accordingly:
4828
- for (const [yTable, { unsentFrom, receivedUntil }] of Object.entries(mergedSpec)) {
4844
+ // Now go through all yTables and update their YSyncStates:
4845
+ const allYTables = Object.values(db.dx._dbSchema)
4846
+ .filter((tblSchema) => tblSchema.yProps)
4847
+ .map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
4848
+ .flat();
4849
+ for (const yTable of allYTables) {
4850
+ const mergedEntry = mergedSpec[yTable];
4851
+ const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
4852
+ const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
4853
+ // from local because we are in the same parent transaction (in sync.ts) that
4854
+ // applied all updates from the server
4855
+ (yield db
4856
+ .table(yTable)
4857
+ .where('i')
4858
+ .between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
4859
+ .reverse()
4860
+ .limit(1)
4861
+ .primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
4829
4862
  // We're already in a transaction, but for the sake of
4830
4863
  // code readability and correctness, let's launch an atomic sub transaction:
4831
4864
  yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
4832
- const state = yield db.table(yTable).get(DEXIE_CLOUD_SYNCER_ID);
4865
+ const state = yield db
4866
+ .table(yTable)
4867
+ .get(DEXIE_CLOUD_SYNCER_ID);
4833
4868
  if (!state) {
4834
4869
  yield db.table(yTable).add({
4835
4870
  i: DEXIE_CLOUD_SYNCER_ID,
4836
- unsentFrom: unsentFrom || 1,
4837
- receivedUntil: receivedUntil || 0,
4871
+ unsentFrom,
4872
+ receivedUntil,
4838
4873
  serverRev: serverRevision,
4839
4874
  });
4840
4875
  }
4841
4876
  else {
4842
- if (unsentFrom) {
4843
- state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
4844
- }
4845
- if (receivedUntil) {
4846
- state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
4847
- state.serverRev = serverRevision;
4848
- }
4877
+ state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
4878
+ state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
4879
+ state.serverRev = serverRevision;
4849
4880
  yield db.table(yTable).put(state);
4850
4881
  }
4851
4882
  }));
@@ -4856,9 +4887,11 @@
4856
4887
  const BINSTREAM_TYPE_REALMID = 1;
4857
4888
  const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
4858
4889
  const BINSTREAM_TYPE_DOCUMENT = 3;
4859
- function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms }) {
4860
- return __awaiter(this, void 0, void 0, function* () {
4861
- if (yDownloadedRealms && realms && realms.every(realmId => yDownloadedRealms[realmId] === '*')) {
4890
+ function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
4891
+ return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
4892
+ if (yDownloadedRealms &&
4893
+ realms &&
4894
+ realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
4862
4895
  return; // Already done!
4863
4896
  }
4864
4897
  console.debug('Downloading Y.Docs from added realms');
@@ -4898,16 +4931,19 @@
4898
4931
  yield yTable.bulkAdd(docsToInsert);
4899
4932
  docsToInsert = [];
4900
4933
  }
4901
- if (currentRealmId && currentTable && currentProp && (lastDoc || completedRealm)) {
4902
- yield db.$syncState.update('syncState', completedRealm
4903
- ? '*'
4904
- : {
4905
- [`yDownloadedRealms.${currentRealmId}`]: {
4934
+ if (currentRealmId &&
4935
+ ((currentTable && currentProp && lastDoc) || completedRealm)) {
4936
+ yield db.$syncState.update('syncState', (syncState) => {
4937
+ const yDownloadedRealms = syncState.yDownloadedRealms || {};
4938
+ yDownloadedRealms[currentRealmId] = completedRealm
4939
+ ? '*'
4940
+ : {
4906
4941
  tbl: currentTable,
4907
4942
  prop: currentProp,
4908
4943
  key: lastDoc.k,
4909
- },
4910
- });
4944
+ };
4945
+ syncState.yDownloadedRealms = yDownloadedRealms;
4946
+ });
4911
4947
  }
4912
4948
  });
4913
4949
  }
@@ -5010,11 +5046,11 @@
5010
5046
  return Promise.reject(error);
5011
5047
  }));
5012
5048
  }
5013
- function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
5014
- isInitialSync: false,
5015
- }) {
5016
- var _a;
5017
- return __awaiter(this, void 0, void 0, function* () {
5049
+ function _sync(db_1, options_1, schema_1) {
5050
+ return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
5051
+ isInitialSync: false,
5052
+ }) {
5053
+ var _a;
5018
5054
  if (!justCheckIfNeeded) {
5019
5055
  console.debug('SYNC STARTED', { isInitialSync, purpose });
5020
5056
  }
@@ -5056,8 +5092,8 @@
5056
5092
  const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
5057
5093
  const syncState = yield db.getPersistedSyncState();
5058
5094
  const baseRevs = yield db.$baseRevs.toArray();
5059
- let clientChanges = yield listClientChanges(mutationTables);
5060
- const yResults = yield listYClientMessagesAndStateVector(db);
5095
+ let clientChanges = yield listClientChanges(mutationTables, db);
5096
+ const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
5061
5097
  throwIfCancelled(cancelToken);
5062
5098
  if (doSyncify) {
5063
5099
  const alreadySyncedRealms = [
@@ -5325,8 +5361,8 @@
5325
5361
  event.next(null);
5326
5362
  }
5327
5363
  function consumeQueue() {
5328
- var _a, _b, _c, _d, _e, _f;
5329
5364
  return __awaiter(this, void 0, void 0, function* () {
5365
+ var _a, _b, _c, _d, _e, _f;
5330
5366
  while (queue.length > 0) {
5331
5367
  const msg = queue.shift();
5332
5368
  try {
@@ -5641,8 +5677,8 @@
5641
5677
  }
5642
5678
  });
5643
5679
  }
5644
- function _logout(db, { deleteUnsyncedData = false } = {}) {
5645
- return __awaiter(this, void 0, void 0, function* () {
5680
+ function _logout(db_1) {
5681
+ return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
5646
5682
  // Clear the database without emptying configuration options.
5647
5683
  const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
5648
5684
  // @ts-ignore
@@ -5680,11 +5716,11 @@
5680
5716
 
5681
5717
  function otpFetchTokenCallback(db) {
5682
5718
  const { userInteraction } = db.cloud;
5683
- return function otpAuthenticate({ public_key, hints }) {
5684
- var _a;
5685
- return __awaiter(this, void 0, void 0, function* () {
5719
+ return function otpAuthenticate(_a) {
5720
+ return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
5721
+ var _b;
5686
5722
  let tokenRequest;
5687
- const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
5723
+ const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
5688
5724
  if (!url)
5689
5725
  throw new Error(`No database URL given.`);
5690
5726
  if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
@@ -5850,8 +5886,8 @@
5850
5886
  }
5851
5887
 
5852
5888
  function login(db, hints) {
5853
- var _a;
5854
5889
  return __awaiter(this, void 0, void 0, function* () {
5890
+ var _a;
5855
5891
  const currentUser = yield db.getCurrentUser();
5856
5892
  const origUserId = currentUser.userId;
5857
5893
  if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
@@ -6288,8 +6324,7 @@
6288
6324
  outstandingTransactions.next(outstandingTransactions.value);
6289
6325
  };
6290
6326
  const txComplete = () => {
6291
- if (tx.mutationsAdded &&
6292
- !isEagerSyncDisabled(db)) {
6327
+ if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
6293
6328
  triggerSync(db, 'push');
6294
6329
  }
6295
6330
  removeTransaction();
@@ -6371,26 +6406,107 @@
6371
6406
  : mutateAndLog(req);
6372
6407
  } }));
6373
6408
  function mutateAndLog(req) {
6409
+ var _a, _b;
6374
6410
  const trans = req.trans;
6375
- trans.mutationsAdded = true;
6411
+ const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
6376
6412
  const { txid, currentUser: { userId }, } = trans;
6377
6413
  const { type } = req;
6378
6414
  const opNo = ++trans.opCount;
6415
+ function stripChangeSpec(changeSpec) {
6416
+ if (!unsyncedProps)
6417
+ return changeSpec;
6418
+ let rv = changeSpec;
6419
+ for (const keyPath of Object.keys(changeSpec)) {
6420
+ if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
6421
+ if (rv === changeSpec)
6422
+ rv = Object.assign({}, changeSpec); // clone on demand
6423
+ delete rv[keyPath];
6424
+ }
6425
+ }
6426
+ return rv;
6427
+ }
6379
6428
  return table.mutate(req).then((res) => {
6429
+ var _a;
6380
6430
  const { numFailures: hasFailures, failures } = res;
6381
6431
  let keys = type === 'delete' ? req.keys : res.results;
6382
6432
  let values = 'values' in req ? req.values : [];
6383
- let updates = 'updates' in req && req.updates;
6433
+ let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
6434
+ let updates = 'updates' in req ? req.updates : undefined;
6384
6435
  if (hasFailures) {
6385
6436
  keys = keys.filter((_, idx) => !failures[idx]);
6386
6437
  values = values.filter((_, idx) => !failures[idx]);
6387
6438
  }
6439
+ if (unsyncedProps) {
6440
+ // Filter out unsynced properties
6441
+ values = values.map((value) => {
6442
+ const newValue = Object.assign({}, value);
6443
+ for (const prop of unsyncedProps) {
6444
+ delete newValue[prop];
6445
+ }
6446
+ return newValue;
6447
+ });
6448
+ if (changeSpec) {
6449
+ // modify operation with criteria and changeSpec.
6450
+ // We must strip out unsynced properties from changeSpec.
6451
+ // We deal with criteria later.
6452
+ changeSpec = stripChangeSpec(changeSpec);
6453
+ if (Object.keys(changeSpec).length === 0) {
6454
+ // Nothing to change on server
6455
+ return res;
6456
+ }
6457
+ }
6458
+ if (updates) {
6459
+ let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
6460
+ let newUpdates = {
6461
+ keys: [],
6462
+ changeSpecs: [],
6463
+ };
6464
+ const validKeys = new Dexie.RangeSet();
6465
+ let anyChangeSpecBecameEmpty = false;
6466
+ for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
6467
+ if (Object.keys(strippedChangeSpecs[i]).length > 0) {
6468
+ newUpdates.keys.push(updates.keys[i]);
6469
+ newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
6470
+ validKeys.addKey(updates.keys[i]);
6471
+ }
6472
+ else {
6473
+ anyChangeSpecBecameEmpty = true;
6474
+ }
6475
+ }
6476
+ updates = newUpdates;
6477
+ if (anyChangeSpecBecameEmpty) {
6478
+ // Some keys were stripped. We must also strip them from keys and values
6479
+ let newKeys = [];
6480
+ let newValues = [];
6481
+ for (let i = 0, l = keys.length; i < l; ++i) {
6482
+ if (validKeys.hasKey(keys[i])) {
6483
+ newKeys.push(keys[i]);
6484
+ newValues.push(values[i]);
6485
+ }
6486
+ }
6487
+ keys = newKeys;
6488
+ values = newValues;
6489
+ }
6490
+ }
6491
+ }
6388
6492
  const ts = Date.now();
6389
6493
  // Canonicalize req.criteria.index to null if it's on the primary key.
6390
- const criteria = 'criteria' in req && req.criteria
6494
+ let criteria = 'criteria' in req && req.criteria
6391
6495
  ? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
6392
6496
  ? null // This will disable the server from trying to log consistent operations where it shouldnt.
6393
6497
  : req.criteria.index }) : undefined;
6498
+ if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
6499
+ const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
6500
+ const involvedProps = keyPaths
6501
+ ? typeof keyPaths === 'string'
6502
+ ? [keyPaths]
6503
+ : keyPaths
6504
+ : [];
6505
+ if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
6506
+ // Don't log criteria on unsynced properties as the server could not test them.
6507
+ criteria = undefined;
6508
+ }
6509
+ }
6394
6510
  const mut = req.type === 'delete'
6395
6511
  ? {
6396
6512
  type: 'delete',
@@ -6411,7 +6527,7 @@
6411
6527
  userId,
6412
6528
  values,
6413
6529
  }
6414
- : criteria && req.changeSpec
6530
+ : criteria && changeSpec
6415
6531
  ? {
6416
6532
  // Common changeSpec for all keys
6417
6533
  type: 'modify',
@@ -6419,37 +6535,51 @@
6419
6535
  opNo,
6420
6536
  keys,
6421
6537
  criteria,
6422
- changeSpec: req.changeSpec,
6538
+ changeSpec,
6423
6539
  txid,
6424
6540
  userId,
6425
6541
  }
6426
- : updates
6542
+ : changeSpec
6427
6543
  ? {
6428
- // One changeSpec per key
6544
+ // In case criteria involved an unsynced property, we go for keys instead.
6429
6545
  type: 'update',
6430
6546
  ts,
6431
6547
  opNo,
6432
- keys: updates.keys,
6433
- changeSpecs: updates.changeSpecs,
6434
- txid,
6435
- userId,
6436
- }
6437
- : {
6438
- type: 'upsert',
6439
- ts,
6440
- opNo,
6441
6548
  keys,
6442
- values,
6549
+ changeSpecs: keys.map(() => changeSpec),
6443
6550
  txid,
6444
6551
  userId,
6445
- };
6552
+ }
6553
+ : updates
6554
+ ? {
6555
+ // One changeSpec per key
6556
+ type: 'update',
6557
+ ts,
6558
+ opNo,
6559
+ keys: updates.keys,
6560
+ changeSpecs: updates.changeSpecs,
6561
+ txid,
6562
+ userId,
6563
+ }
6564
+ : {
6565
+ type: 'upsert',
6566
+ ts,
6567
+ opNo,
6568
+ keys,
6569
+ values,
6570
+ txid,
6571
+ userId,
6572
+ };
6446
6573
  if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
6447
6574
  mut.isAdditionalChunk = true;
6448
6575
  }
6449
6576
  return keys.length > 0 || criteria
6450
6577
  ? mutsTable
6451
6578
  .mutate({ type: 'add', trans, values: [mut] }) // Log entry
6452
- .then(() => res) // Return original response
6579
+ .then(() => {
6580
+ trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
6581
+ return res; // Return original response
6582
+ })
6453
6583
  : res;
6454
6584
  });
6455
6585
  }
@@ -6616,38 +6746,51 @@
6616
6746
 
6617
6747
  function createYClientUpdateObservable(db) {
6618
6748
  const yTableRecords = flatten(db.tables
6619
- .filter((table) => { var _a; return ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync) && table.schema.yProps; })
6749
+ .filter((table) => { var _a, _b; return ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name]) === null || _b === void 0 ? void 0 : _b.markedForSync) && table.schema.yProps; })
6620
6750
  .map((table) => table.schema.yProps.map((p) => ({
6621
6751
  table: table.name,
6622
6752
  ydocProp: p.prop,
6623
6753
  updatesTable: p.updatesTable,
6624
6754
  }))));
6625
6755
  return rxjs.merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
6626
- let currentUnsentFrom = 1;
6627
- return Dexie.liveQuery(() => __awaiter(this, void 0, void 0, function* () {
6628
- const yTbl = db.table(updatesTable);
6629
- const unsentFrom = yield yTbl
6630
- .where({ i: DEXIE_CLOUD_SYNCER_ID })
6631
- .first()
6632
- .then((syncer) => (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1);
6633
- currentUnsentFrom = Math.max(currentUnsentFrom, unsentFrom);
6634
- const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
6635
- // Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
6636
- currentUnsentFrom = Math.max(currentUnsentFrom, ...addedUpdates.map((update) => update.i + 1));
6637
- return addedUpdates
6638
- .filter((update) => update.f && update.f & 1) // Only include local updates
6639
- .map((update) => {
6640
- return {
6641
- type: 'u-c',
6642
- table,
6643
- prop: ydocProp,
6644
- k: update.k,
6645
- u: update.u,
6646
- i: update.i,
6647
- };
6648
- });
6756
+ // Per updates table (table+prop combo), we first read syncer.unsentFrom,
6757
+ // and then start listening for updates since that number.
6758
+ const yTbl = db.table(updatesTable);
6759
+ return rxjs.from(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(rxjs.switchMap((syncer) => {
6760
+ let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
6761
+ return rxjs.from(Dexie.liveQuery(() => __awaiter(this, void 0, void 0, function* () {
6762
+ const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
6763
+ return addedUpdates
6764
+ .filter((update) => update.f && update.f & 1) // Only include local updates
6765
+ .map((update) => {
6766
+ return {
6767
+ type: 'u-c',
6768
+ table,
6769
+ prop: ydocProp,
6770
+ k: update.k,
6771
+ u: update.u,
6772
+ i: update.i,
6773
+ };
6774
+ });
6775
+ }))).pipe(rxjs.tap((addedUpdates) => {
6776
+ // Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
6777
+ // (Before, we did this within the liveQuery, but that caused a bug because
6778
+ // a cancelled emittion of a liveQuery would update the currentUnsentFrom without
6779
+ // emitting anything, leading to that we jumped over some updates. Here we update it
6780
+ // after the liveQuery has emitted its updates)
6781
+ if (addedUpdates.length > 0) {
6782
+ currentUnsentFrom = addedUpdates.at(-1).i + 1;
6783
+ }
6784
+ }));
6649
6785
  }));
6650
- })).pipe(rxjs.mergeMap((messages) => messages)); // Flattens the array of messages. If messageProducer emits empty array, nothing is emitted but if messageProducer emits array of messages, they are emitted one by one.
6786
+ })).pipe(
6787
+ // Flatten the array of messages.
6788
+ // If messageProducer emits empty array, nothing is emitted
6789
+ // but if messageProducer emits array of messages, they are
6790
+ // emitted one by one.
6791
+ rxjs.mergeMap((messages) => messages), rxjs.tap((message) => {
6792
+ console.debug('dexie-cloud emitting y-c', message);
6793
+ }));
6651
6794
  }
6652
6795
 
6653
6796
  function getAwarenessLibrary(db) {
@@ -6664,25 +6807,24 @@
6664
6807
  const CLIENT_PING_INTERVAL = 30000;
6665
6808
  const FAIL_RETRY_WAIT_TIME = 60000;
6666
6809
  class WSObservable extends rxjs.Observable {
6667
- constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, token, tokenExpiration) {
6668
- super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus));
6810
+ constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
6811
+ super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
6669
6812
  }
6670
6813
  }
6671
6814
  let counter = 0;
6672
6815
  class WSConnection extends rxjs.Subscription {
6673
- constructor(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
6816
+ constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
6674
6817
  super(() => this.teardown());
6675
6818
  this.id = ++counter;
6676
6819
  this.subscriptions = new Set();
6677
6820
  this.reconnecting = false;
6678
- console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
6821
+ console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
6679
6822
  this.db = db;
6680
6823
  this.databaseUrl = db.cloud.options.databaseUrl;
6681
6824
  this.rev = rev;
6682
6825
  this.realmSetHash = realmSetHash;
6683
6826
  this.clientIdentity = clientIdentity;
6684
- this.token = token;
6685
- this.tokenExpiration = tokenExpiration;
6827
+ this.user = user;
6686
6828
  this.subscriber = subscriber;
6687
6829
  this.lastUserActivity = new Date();
6688
6830
  this.messageProducer = messageProducer;
@@ -6742,7 +6884,8 @@
6742
6884
  //console.debug('SyncStatus: DUBB: Ooops it was closed!');
6743
6885
  return;
6744
6886
  }
6745
- if (this.tokenExpiration && this.tokenExpiration < new Date()) {
6887
+ const tokenExpiration = this.user.accessTokenExpiration;
6888
+ if (tokenExpiration && tokenExpiration < new Date()) {
6746
6889
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
6747
6890
  return;
6748
6891
  }
@@ -6797,8 +6940,8 @@
6797
6940
  searchParams.set('rev', this.rev);
6798
6941
  searchParams.set('realmsHash', this.realmSetHash);
6799
6942
  searchParams.set('clientId', this.clientIdentity);
6800
- if (this.token) {
6801
- searchParams.set('token', this.token);
6943
+ if (this.user.accessToken) {
6944
+ searchParams.set('token', this.user.accessToken);
6802
6945
  }
6803
6946
  // Connect the WebSocket to given url:
6804
6947
  console.debug('dexie-cloud WebSocket create');
@@ -6813,12 +6956,12 @@
6813
6956
  ws.onmessage = (event) => {
6814
6957
  if (!this.pinger)
6815
6958
  return;
6816
- console.debug('dexie-cloud WebSocket onmessage', event.data);
6817
6959
  this.lastServerActivity = new Date();
6818
6960
  try {
6819
6961
  const msg = typeof event.data === 'string'
6820
6962
  ? TSON.parse(event.data)
6821
6963
  : decodeYMessage(new Uint8Array(event.data));
6964
+ console.debug('dexie-cloud WebSocket onmessage', msg.type, msg);
6822
6965
  if (msg.type === 'error') {
6823
6966
  throw new Error(`Error message from dexie-cloud: ${msg.error}`);
6824
6967
  }
@@ -6874,6 +7017,7 @@
6874
7017
  this.webSocketStatus.value !== 'connected') {
6875
7018
  this.webSocketStatus.next('connected');
6876
7019
  }
7020
+ console.debug('dexie-cloud WebSocket send', msg.type, msg);
6877
7021
  if (msg.type === 'ready') {
6878
7022
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
6879
7023
  }
@@ -6884,7 +7028,9 @@
6884
7028
  }
6885
7029
  }
6886
7030
  }));
6887
- this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
7031
+ if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
7032
+ this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
7033
+ }
6888
7034
  }
6889
7035
  catch (error) {
6890
7036
  this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
@@ -6954,7 +7100,7 @@
6954
7100
  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]));
6955
7101
  }
6956
7102
  return new rxjs.BehaviorSubject([userLogin, syncState]);
6957
- }), switchMap(([userLogin, syncState]) => __awaiter(this, void 0, void 0, function* () { return [userLogin, yield computeRealmSetHash(syncState)]; })), distinctUntilChanged(([prevUser, prevHash], [currUser, currHash]) => prevUser === currUser && prevHash === currHash), switchMap(([userLogin, realmSetHash]) => {
7103
+ }), switchMap((_a) => __awaiter(this, [_a], void 0, function* ([userLogin, syncState]) { return [userLogin, yield computeRealmSetHash(syncState)]; })), distinctUntilChanged(([prevUser, prevHash], [currUser, currHash]) => prevUser === currUser && prevHash === currHash), switchMap(([userLogin, realmSetHash]) => {
6958
7104
  var _a;
6959
7105
  if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
6960
7106
  // Restart the flow if persistedSyncState is not yet available.
@@ -6964,7 +7110,7 @@
6964
7110
  // If no new entries, server won't bother the client. If new entries, server sends only those
6965
7111
  // and the baseRev of the last from same client-ID.
6966
7112
  if (userLogin) {
6967
- return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin.accessToken, userLogin.accessTokenExpiration);
7113
+ return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
6968
7114
  }
6969
7115
  else {
6970
7116
  return rxjs.from([]);
@@ -7015,8 +7161,8 @@
7015
7161
  }
7016
7162
 
7017
7163
  function isSyncNeeded(db) {
7018
- var _a;
7019
7164
  return __awaiter(this, void 0, void 0, function* () {
7165
+ var _a;
7020
7166
  return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
7021
7167
  ? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
7022
7168
  : false;
@@ -7700,6 +7846,7 @@
7700
7846
  // If user can update any prop in any table in this realm, return true unless
7701
7847
  // it regards to ownership change:
7702
7848
  if (this.permissions.update === '*') {
7849
+ // @ts-ignore
7703
7850
  return props.every((prop) => prop !== 'owner');
7704
7851
  }
7705
7852
  const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
@@ -7771,125 +7918,139 @@
7771
7918
  });
7772
7919
 
7773
7920
  function createYHandler(db) {
7774
- const awap = getAwarenessLibrary(db);
7775
7921
  return (provider) => {
7776
7922
  var _a;
7777
7923
  const doc = provider.doc;
7778
- const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7924
+ const { parentTable } = doc.meta || {};
7779
7925
  if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
7780
7926
  return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
7781
7927
  }
7782
- let awareness = new awap.Awareness(doc);
7783
- awarenessWeakMap.set(doc, awareness);
7784
- provider.awareness = awareness;
7785
- awareness.on('update', ({ added, updated, removed }, origin) => {
7786
- // Send the update
7787
- const changedClients = added.concat(updated).concat(removed);
7788
- if (origin !== 'server') {
7789
- const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7928
+ let awareness;
7929
+ Object.defineProperty(provider, "awareness", {
7930
+ get() {
7931
+ if (awareness)
7932
+ return awareness;
7933
+ awareness = createAwareness(db, doc, provider);
7934
+ awarenessWeakMap.set(doc, awareness);
7935
+ return awareness;
7936
+ }
7937
+ });
7938
+ };
7939
+ }
7940
+ function createAwareness(db, doc, provider) {
7941
+ const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7942
+ const awap = getAwarenessLibrary(db);
7943
+ const awareness = new awap.Awareness(doc);
7944
+ awareness.on('update', ({ added, updated, removed }, origin) => {
7945
+ // Send the update
7946
+ const changedClients = added.concat(updated).concat(removed);
7947
+ const user = db.cloud.currentUser.value;
7948
+ if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7949
+ const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7950
+ db.messageProducer.next({
7951
+ type: 'aware',
7952
+ table: parentTable,
7953
+ prop: parentProp,
7954
+ k: doc.meta.parentId,
7955
+ u: update,
7956
+ });
7957
+ if (provider.destroyed) {
7958
+ // We're called from awareness.on('destroy') that did
7959
+ // removeAwarenessStates.
7960
+ // It's time to also send the doc-close message that dexie-cloud understands
7961
+ // and uses to stop subscribing for updates and awareness updates and brings
7962
+ // down the cached information in memory on the WS connection for this.
7790
7963
  db.messageProducer.next({
7791
- type: 'aware',
7964
+ type: 'doc-close',
7792
7965
  table: parentTable,
7793
7966
  prop: parentProp,
7794
- k: doc.meta.parentId,
7795
- u: update,
7967
+ k: doc.meta.parentId
7796
7968
  });
7797
- if (provider.destroyed) {
7798
- // We're called from awareness.on('destroy') that did
7799
- // removeAwarenessStates.
7800
- // It's time to also send the doc-close message that dexie-cloud understands
7801
- // and uses to stop subscribing for updates and awareness updates and brings
7802
- // down the cached information in memory on the WS connection for this.
7803
- db.messageProducer.next({
7804
- type: 'doc-close',
7805
- table: parentTable,
7806
- prop: parentProp,
7807
- k: doc.meta.parentId
7808
- });
7809
- }
7810
7969
  }
7811
- });
7812
- awareness.on('destroy', () => {
7813
- // Signal to server that this provider is destroyed (the update event will be triggered, which
7814
- // in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
7815
- awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
7816
- });
7817
- // Now wait til document is loaded and then open the document on the server
7818
- provider.on('load', () => __awaiter(this, void 0, void 0, function* () {
7970
+ }
7971
+ });
7972
+ awareness.on('destroy', () => {
7973
+ // Signal to server that this provider is destroyed (the update event will be triggered, which
7974
+ // in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
7975
+ awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
7976
+ });
7977
+ // Open the document on the server
7978
+ (() => __awaiter(this, void 0, void 0, function* () {
7979
+ if (provider.destroyed)
7980
+ return;
7981
+ let connected = false;
7982
+ let currentFlowId = 1;
7983
+ const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
7819
7984
  if (provider.destroyed)
7820
7985
  return;
7821
- let connected = false;
7822
- let currentFlowId = 1;
7823
- const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
7824
- if (provider.destroyed)
7986
+ // Keep "connected" state in a variable so we can check it after async operations
7987
+ connected = wsStatus === 'connected';
7988
+ // We are or got connected. Open the document on the server.
7989
+ const user = db.cloud.currentUser.value;
7990
+ if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7991
+ ++currentFlowId;
7992
+ openDocumentOnServer().catch(error => {
7993
+ console.warn(`Error catched in createYHandler.ts: ${error}`);
7994
+ });
7995
+ }
7996
+ });
7997
+ // Wait until WebSocket is connected
7998
+ provider.addCleanupHandler(subscription);
7999
+ /** Sends an 'doc-open' message to server whenever websocket becomes
8000
+ * connected, or if it is already connected.
8001
+ * The flow is aborted in case websocket is disconnected while querying
8002
+ * information required to compute the state vector. Flow is also
8003
+ * aborted in case document or provider has been destroyed during
8004
+ * the async parts of the task.
8005
+ *
8006
+ * The state vector is only computed from the updates that have occured
8007
+ * after the last full sync - which could very often be zero - in which
8008
+ * case no state vector is sent (then the server already knows us by
8009
+ * revision)
8010
+ *
8011
+ * When server gets the doc-open message, it will authorized us for
8012
+ * whether we are allowed to read / write to this document, and then
8013
+ * keep the cached information in memory on the WS connection for this
8014
+ * particular document, as well as subscribe to updates and awareness updates
8015
+ * from other clients on the document.
8016
+ */
8017
+ function openDocumentOnServer(wsStatus) {
8018
+ return __awaiter(this, void 0, void 0, function* () {
8019
+ const myFlow = currentFlowId; // So we can abort when a new flow is started
8020
+ const yTbl = db.table(updatesTable);
8021
+ const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
8022
+ // After every await, check if we still should be working on this task.
8023
+ if (provider.destroyed || currentFlowId !== myFlow || !connected)
7825
8024
  return;
7826
- // Keep "connected" state in a variable so we can check it after async operations
7827
- connected = wsStatus === 'connected';
7828
- // We are or got connected. Open the document on the server.
7829
- if (wsStatus === "connected") {
7830
- ++currentFlowId;
7831
- openDocumentOnServer().catch(error => {
7832
- console.warn(`Error catched in createYHandler.ts: ${error}`);
7833
- });
8025
+ const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
8026
+ const docOpenMsg = {
8027
+ type: 'doc-open',
8028
+ table: parentTable,
8029
+ prop: parentProp,
8030
+ k: parentId,
8031
+ serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
8032
+ };
8033
+ const serverUpdatesSinceLastSync = yield yTbl
8034
+ .where('i')
8035
+ .between(receivedUntil, Infinity, false)
8036
+ .filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
8037
+ ((update.f || 0) & 1) === 0 // Don't include local changes
8038
+ )
8039
+ .toArray();
8040
+ // After every await, check if we still should be working on this task.
8041
+ if (provider.destroyed || currentFlowId !== myFlow || !connected)
8042
+ return;
8043
+ if (serverUpdatesSinceLastSync.length > 0) {
8044
+ const Y = $Y(db); // Get the Yjs library from Dexie constructor options
8045
+ const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
8046
+ const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
8047
+ docOpenMsg.sv = stateVector;
7834
8048
  }
8049
+ db.messageProducer.next(docOpenMsg);
7835
8050
  });
7836
- // Wait until WebSocket is connected
7837
- provider.addCleanupHandler(subscription);
7838
- /** Sends an 'doc-open' message to server whenever websocket becomes
7839
- * connected, or if it is already connected.
7840
- * The flow is aborted in case websocket is disconnected while querying
7841
- * information required to compute the state vector. Flow is also
7842
- * aborted in case document or provider has been destroyed during
7843
- * the async parts of the task.
7844
- *
7845
- * The state vector is only computed from the updates that have occured
7846
- * after the last full sync - which could very often be zero - in which
7847
- * case no state vector is sent (then the server already knows us by
7848
- * revision)
7849
- *
7850
- * When server gets the doc-open message, it will authorized us for
7851
- * whether we are allowed to read / write to this document, and then
7852
- * keep the cached information in memory on the WS connection for this
7853
- * particular document, as well as subscribe to updates and awareness updates
7854
- * from other clients on the document.
7855
- */
7856
- function openDocumentOnServer(wsStatus) {
7857
- return __awaiter(this, void 0, void 0, function* () {
7858
- const myFlow = currentFlowId; // So we can abort when a new flow is started
7859
- const yTbl = db.table(updatesTable);
7860
- const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
7861
- // After every await, check if we still should be working on this task.
7862
- if (provider.destroyed || currentFlowId !== myFlow || !connected)
7863
- return;
7864
- const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
7865
- const docOpenMsg = {
7866
- type: 'doc-open',
7867
- table: parentTable,
7868
- prop: parentProp,
7869
- k: parentId,
7870
- serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
7871
- };
7872
- const serverUpdatesSinceLastSync = yield yTbl
7873
- .where('i')
7874
- .between(receivedUntil, Infinity, false)
7875
- .filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
7876
- ((update.f || 0) & 1) === 0 // Don't include local changes
7877
- )
7878
- .toArray();
7879
- // After every await, check if we still should be working on this task.
7880
- if (provider.destroyed || currentFlowId !== myFlow || !connected)
7881
- return;
7882
- if (serverUpdatesSinceLastSync.length > 0) {
7883
- const Y = $Y(db); // Get the Yjs library from Dexie constructor options
7884
- const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
7885
- const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
7886
- docOpenMsg.sv = stateVector;
7887
- }
7888
- db.messageProducer.next(docOpenMsg);
7889
- });
7890
- }
7891
- }));
7892
- };
8051
+ }
8052
+ }))();
8053
+ return awareness;
7893
8054
  }
7894
8055
 
7895
8056
  const DEFAULT_OPTIONS = {
@@ -7932,7 +8093,7 @@
7932
8093
  const syncComplete = new rxjs.Subject();
7933
8094
  dexie.cloud = {
7934
8095
  // @ts-ignore
7935
- version: "4.1.0-alpha.2",
8096
+ version: "4.1.0-alpha.20",
7936
8097
  options: Object.assign({}, DEFAULT_OPTIONS),
7937
8098
  schema: null,
7938
8099
  get currentUserId() {
@@ -7968,16 +8129,16 @@
7968
8129
  }
7969
8130
  updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
7970
8131
  },
7971
- logout({ force } = {}) {
7972
- return __awaiter(this, void 0, void 0, function* () {
8132
+ logout() {
8133
+ return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
7973
8134
  force
7974
8135
  ? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
7975
8136
  : yield logout(DexieCloudDB(dexie));
7976
8137
  });
7977
8138
  },
7978
- sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
7979
- var _a;
7980
- return __awaiter(this, void 0, void 0, function* () {
8139
+ sync() {
8140
+ return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
8141
+ var _a;
7981
8142
  if (wait === undefined)
7982
8143
  wait = true;
7983
8144
  const db = DexieCloudDB(dexie);
@@ -8035,8 +8196,8 @@
8035
8196
  dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
8036
8197
  dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
8037
8198
  function onDbReady(dexie) {
8038
- var _a, _b, _c, _d, _e, _f, _g;
8039
8199
  return __awaiter(this, void 0, void 0, function* () {
8200
+ var _a, _b, _c, _d, _e, _f, _g;
8040
8201
  closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
8041
8202
  const db = DexieCloudDB(dexie);
8042
8203
  // Setup default GUI:
@@ -8059,7 +8220,7 @@
8059
8220
  ? yield navigator.serviceWorker.getRegistrations()
8060
8221
  : [];
8061
8222
  const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
8062
- var _h, _j;
8223
+ var _a, _b;
8063
8224
  const { options, schema } = db.cloud;
8064
8225
  const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
8065
8226
  db.getOptions(),
@@ -8081,7 +8242,7 @@
8081
8242
  delete newPersistedOptions.awarenessProtocol;
8082
8243
  yield db.$syncState.put(newPersistedOptions, 'options');
8083
8244
  }
8084
- if (((_h = db.cloud.options) === null || _h === void 0 ? void 0 : _h.tryUseServiceWorker) &&
8245
+ if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
8085
8246
  'serviceWorker' in navigator &&
8086
8247
  swRegistrations.length > 0 &&
8087
8248
  !DISABLE_SERVICEWORKER_STRATEGY) {
@@ -8095,7 +8256,7 @@
8095
8256
  // Not configured for using service worker or no service worker
8096
8257
  // registration exists. Don't rely on service worker to do any job.
8097
8258
  // Use LocalSyncWorker instead.
8098
- if (((_j = db.cloud.options) === null || _j === void 0 ? void 0 : _j.tryUseServiceWorker) &&
8259
+ if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
8099
8260
  !db.cloud.isServiceWorkerDB) {
8100
8261
  console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
8101
8262
  ? 'No SW registrations found.'
@@ -8234,7 +8395,7 @@
8234
8395
  }
8235
8396
  }
8236
8397
  // @ts-ignore
8237
- dexieCloud.version = "4.1.0-alpha.2";
8398
+ dexieCloud.version = "4.1.0-alpha.20";
8238
8399
  Dexie.Cloud = dexieCloud;
8239
8400
 
8240
8401
  // In case the SW lives for a while, let it reuse already opened connections:
@@ -8264,8 +8425,8 @@
8264
8425
  }
8265
8426
  return promise;
8266
8427
  function _syncDB(dbName, purpose) {
8267
- var _a;
8268
8428
  return __awaiter(this, void 0, void 0, function* () {
8429
+ var _a;
8269
8430
  let db = managedDBs.get(dbName);
8270
8431
  if (!db) {
8271
8432
  console.debug('Dexie Cloud SW: Creating new Dexie instance for', dbName);