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

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 (37) 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 +584 -237
  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 +426 -235
  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/applyYMessages.d.ts +4 -1
  16. package/dist/modern/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
  17. package/dist/umd/DexieCloudOptions.d.ts +3 -0
  18. package/dist/umd/TSON.d.ts +1 -1
  19. package/dist/umd/WSObservable.d.ts +4 -4
  20. package/dist/umd/define-ydoc-trigger.d.ts +2 -0
  21. package/dist/umd/dexie-cloud-addon.d.ts +1 -0
  22. package/dist/umd/dexie-cloud-addon.js +582 -234
  23. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  24. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  25. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  26. package/dist/umd/getInvitesObservable.d.ts +11 -11
  27. package/dist/umd/service-worker.js +424 -233
  28. package/dist/umd/service-worker.js.map +1 -1
  29. package/dist/umd/service-worker.min.js +1 -1
  30. package/dist/umd/service-worker.min.js.map +1 -1
  31. package/dist/umd/yjs/applyYMessages.d.ts +4 -1
  32. package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
  33. package/package.json +4 -4
  34. package/dist/modern/yjs/listYClientMessages.d.ts +0 -3
  35. package/dist/umd/yjs/listYClientMessages.d.ts +0 -3
  36. /package/dist/modern/yjs/{y.d.ts → Y.d.ts} +0 -0
  37. /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.21, Mon Nov 18 2024
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -16,8 +16,8 @@
16
16
  *
17
17
  */
18
18
 
19
- import Dexie, { PropModification, cmp, DexieYProvider, liveQuery } from 'dexie';
20
- import { Observable as Observable$1, BehaviorSubject, firstValueFrom, Subject, from as from$1, filter as filter$1, fromEvent, of, merge, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
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';
21
21
 
22
22
  /******************************************************************************
23
23
  Copyright (c) Microsoft Corporation.
@@ -1765,8 +1765,8 @@ function registerSyncEvent(db, purpose) {
1765
1765
  });
1766
1766
  }
1767
1767
  function registerPeriodicSyncEvent(db) {
1768
- var _a;
1769
1768
  return __awaiter(this, void 0, void 0, function* () {
1769
+ var _a;
1770
1770
  try {
1771
1771
  // Register periodicSync event to SW:
1772
1772
  // @ts-ignore
@@ -1831,8 +1831,8 @@ const b64encode = typeof Buffer !== "undefined"
1831
1831
  return btoa(strs.join(""));
1832
1832
  };
1833
1833
 
1834
- function computeRealmSetHash({ realms, inviteRealms, }) {
1835
- return __awaiter(this, void 0, void 0, function* () {
1834
+ function computeRealmSetHash(_a) {
1835
+ return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
1836
1836
  const data = JSON.stringify([
1837
1837
  ...realms.map((realmId) => ({ realmId, accepted: true })),
1838
1838
  ...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
@@ -1868,8 +1868,8 @@ function flatten(a) {
1868
1868
  return concat.apply([], a);
1869
1869
  }
1870
1870
 
1871
- function listClientChanges(mutationTables, db, { since = {}, limit = Infinity } = {}) {
1872
- return __awaiter(this, void 0, void 0, function* () {
1871
+ function listClientChanges(mutationTables_1, db_1) {
1872
+ return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
1873
1873
  const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
1874
1874
  const tableName = getTableFromMutationTable(mutationTable.name);
1875
1875
  const lastRevision = since[tableName];
@@ -2754,13 +2754,17 @@ const writeAny = (encoder, data) => {
2754
2754
  function encodeYMessage(msg) {
2755
2755
  const encoder = new Encoder();
2756
2756
  writeVarString(encoder, msg.type);
2757
- writeVarString(encoder, msg.table);
2758
- writeVarString(encoder, msg.prop);
2757
+ if ('table' in msg)
2758
+ writeVarString(encoder, msg.table);
2759
+ if ('prop' in msg)
2760
+ writeVarString(encoder, msg.prop);
2759
2761
  switch (msg.type) {
2760
2762
  case 'u-ack':
2761
2763
  case 'u-reject':
2762
2764
  writeBigUint64(encoder, BigInt(msg.i));
2763
2765
  break;
2766
+ case 'outdated-server-rev':
2767
+ break;
2764
2768
  default:
2765
2769
  writeAny(encoder, msg.k);
2766
2770
  switch (msg.type) {
@@ -3104,6 +3108,9 @@ const readAny = decoder => readAnyLookupTable[127 - readUint8(decoder)](decoder)
3104
3108
  function decodeYMessage(a) {
3105
3109
  const decoder = new Decoder(a);
3106
3110
  const type = readVarString(decoder);
3111
+ if (type === 'outdated-server-rev') {
3112
+ return { type };
3113
+ }
3107
3114
  const table = readVarString(decoder);
3108
3115
  const prop = readVarString(decoder);
3109
3116
  switch (type) {
@@ -3516,8 +3523,8 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
3516
3523
  }
3517
3524
 
3518
3525
  function loadAccessToken(db) {
3519
- var _a, _b, _c;
3520
3526
  return __awaiter(this, void 0, void 0, function* () {
3527
+ var _a, _b, _c;
3521
3528
  const currentUser = yield db.getCurrentUser();
3522
3529
  const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
3523
3530
  if (!accessToken)
@@ -4347,8 +4354,8 @@ function cloneChange(change, rewriteValues) {
4347
4354
  // seconds (given that there is a Ratelimit-Reset header).
4348
4355
  let syncRatelimitDelays = new WeakMap();
4349
4356
  function checkSyncRateLimitDelay(db) {
4350
- var _a, _b;
4351
4357
  return __awaiter(this, void 0, void 0, function* () {
4358
+ var _a, _b;
4352
4359
  const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
4353
4360
  if (delatMilliseconds > 0) {
4354
4361
  console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
@@ -4661,13 +4668,12 @@ function $Y(db) {
4661
4668
  * @param db
4662
4669
  * @returns
4663
4670
  */
4664
- function listYClientMessagesAndStateVector(db) {
4665
- var _a;
4671
+ function listYClientMessagesAndStateVector(db, tablesToSync) {
4666
4672
  return __awaiter(this, void 0, void 0, function* () {
4667
4673
  const result = [];
4668
4674
  const lastUpdateIds = {};
4669
- for (const table of db.tables) {
4670
- if (table.schema.yProps && ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync)) {
4675
+ for (const table of tablesToSync) {
4676
+ if (table.schema.yProps) {
4671
4677
  for (const yProp of table.schema.yProps) {
4672
4678
  const Y = $Y(db); // This is how we retrieve the user-provided Y library
4673
4679
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
@@ -4754,12 +4760,14 @@ function getUpdatesTable(db, table, ydocProp) {
4754
4760
 
4755
4761
  function applyYServerMessages(yMessages, db) {
4756
4762
  return __awaiter(this, void 0, void 0, function* () {
4757
- const result = {};
4763
+ var _a;
4764
+ const receivedUntils = {};
4765
+ let resyncNeeded = false;
4758
4766
  for (const m of yMessages) {
4759
4767
  switch (m.type) {
4760
4768
  case 'u-s': {
4761
4769
  const utbl = getUpdatesTable(db, m.table, m.prop);
4762
- result[utbl.name] = yield utbl.add({
4770
+ receivedUntils[utbl.name] = yield utbl.add({
4763
4771
  k: m.k,
4764
4772
  u: m.u,
4765
4773
  });
@@ -4784,7 +4792,24 @@ function applyYServerMessages(yMessages, db) {
4784
4792
  // See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
4785
4793
  console.debug(`Y update rejected. Deleting it.`);
4786
4794
  const utbl = getUpdatesTable(db, m.table, m.prop);
4787
- yield utbl.delete(m.i);
4795
+ // Delete the rejected update and all local updates since (avoid holes in the CRDT)
4796
+ // and destroy it's open document if there is one.
4797
+ const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
4798
+ if (primaryKey != null) {
4799
+ yield db.transaction('rw', utbl, (tx) => {
4800
+ // @ts-ignore
4801
+ tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
4802
+ return utbl
4803
+ .where('i')
4804
+ .aboveOrEqual(m.i)
4805
+ .filter((u) => cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
4806
+ .delete();
4807
+ });
4808
+ // Destroy active doc
4809
+ const activeDoc = DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
4810
+ if (activeDoc)
4811
+ activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
4812
+ }
4788
4813
  break;
4789
4814
  }
4790
4815
  case 'in-sync': {
@@ -4794,22 +4819,28 @@ function applyYServerMessages(yMessages, db) {
4794
4819
  }
4795
4820
  break;
4796
4821
  }
4822
+ case 'outdated-server-rev':
4823
+ resyncNeeded = true;
4824
+ break;
4797
4825
  }
4798
4826
  }
4799
- return result;
4827
+ return {
4828
+ receivedUntils,
4829
+ resyncNeeded,
4830
+ };
4800
4831
  });
4801
4832
  }
4802
4833
 
4803
4834
  function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
4804
- var _a, _b;
4805
4835
  return __awaiter(this, void 0, void 0, function* () {
4836
+ var _a, _b, _c, _d, _e;
4806
4837
  // We want to update unsentFrom for each yTable to the value specified in first argument
4807
4838
  // because we got those values before we synced with server and here we are back from server
4808
4839
  // that has successfully received all those messages - no matter if the last update was a client or server update,
4809
4840
  // we can safely store unsentFrom to a value of the last update + 1 here.
4810
4841
  // We also want to update receivedUntil for each yTable to the value specified in the second argument,
4811
4842
  // because that contains the highest resulted id of each update from server after storing it.
4812
- // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
4843
+ // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
4813
4844
  // to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
4814
4845
  // per yTable.
4815
4846
  const mergedSpec = {};
@@ -4821,28 +4852,42 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
4821
4852
  (_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
4822
4853
  mergedSpec[yTable].receivedUntil = lastUpdateId;
4823
4854
  }
4824
- // Now go through the merged map and update YSyncStates accordingly:
4825
- for (const [yTable, { unsentFrom, receivedUntil }] of Object.entries(mergedSpec)) {
4855
+ // Now go through all yTables and update their YSyncStates:
4856
+ const allYTables = Object.values(db.dx._dbSchema)
4857
+ .filter((tblSchema) => tblSchema.yProps)
4858
+ .map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
4859
+ .flat();
4860
+ for (const yTable of allYTables) {
4861
+ const mergedEntry = mergedSpec[yTable];
4862
+ const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
4863
+ const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
4864
+ // from local because we are in the same parent transaction (in sync.ts) that
4865
+ // applied all updates from the server
4866
+ (yield db
4867
+ .table(yTable)
4868
+ .where('i')
4869
+ .between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
4870
+ .reverse()
4871
+ .limit(1)
4872
+ .primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
4826
4873
  // We're already in a transaction, but for the sake of
4827
4874
  // code readability and correctness, let's launch an atomic sub transaction:
4828
4875
  yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
4829
- const state = yield db.table(yTable).get(DEXIE_CLOUD_SYNCER_ID);
4876
+ const state = yield db
4877
+ .table(yTable)
4878
+ .get(DEXIE_CLOUD_SYNCER_ID);
4830
4879
  if (!state) {
4831
4880
  yield db.table(yTable).add({
4832
4881
  i: DEXIE_CLOUD_SYNCER_ID,
4833
- unsentFrom: unsentFrom || 1,
4834
- receivedUntil: receivedUntil || 0,
4882
+ unsentFrom,
4883
+ receivedUntil,
4835
4884
  serverRev: serverRevision,
4836
4885
  });
4837
4886
  }
4838
4887
  else {
4839
- if (unsentFrom) {
4840
- state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
4841
- }
4842
- if (receivedUntil) {
4843
- state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
4844
- state.serverRev = serverRevision;
4845
- }
4888
+ state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
4889
+ state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
4890
+ state.serverRev = serverRevision;
4846
4891
  yield db.table(yTable).put(state);
4847
4892
  }
4848
4893
  }));
@@ -4853,9 +4898,11 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
4853
4898
  const BINSTREAM_TYPE_REALMID = 1;
4854
4899
  const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
4855
4900
  const BINSTREAM_TYPE_DOCUMENT = 3;
4856
- function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms }) {
4857
- return __awaiter(this, void 0, void 0, function* () {
4858
- if (yDownloadedRealms && realms && realms.every(realmId => yDownloadedRealms[realmId] === '*')) {
4901
+ function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
4902
+ return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
4903
+ if (yDownloadedRealms &&
4904
+ realms &&
4905
+ realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
4859
4906
  return; // Already done!
4860
4907
  }
4861
4908
  console.debug('Downloading Y.Docs from added realms');
@@ -4895,16 +4942,19 @@ function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms })
4895
4942
  yield yTable.bulkAdd(docsToInsert);
4896
4943
  docsToInsert = [];
4897
4944
  }
4898
- if (currentRealmId && currentTable && currentProp && (lastDoc || completedRealm)) {
4899
- yield db.$syncState.update('syncState', completedRealm
4900
- ? '*'
4901
- : {
4902
- [`yDownloadedRealms.${currentRealmId}`]: {
4945
+ if (currentRealmId &&
4946
+ ((currentTable && currentProp && lastDoc) || completedRealm)) {
4947
+ yield db.$syncState.update('syncState', (syncState) => {
4948
+ const yDownloadedRealms = syncState.yDownloadedRealms || {};
4949
+ yDownloadedRealms[currentRealmId] = completedRealm
4950
+ ? '*'
4951
+ : {
4903
4952
  tbl: currentTable,
4904
4953
  prop: currentProp,
4905
4954
  key: lastDoc.k,
4906
- },
4907
- });
4955
+ };
4956
+ syncState.yDownloadedRealms = yDownloadedRealms;
4957
+ });
4908
4958
  }
4909
4959
  });
4910
4960
  }
@@ -5007,11 +5057,11 @@ function sync(db, options, schema, syncOptions) {
5007
5057
  return Promise.reject(error);
5008
5058
  }));
5009
5059
  }
5010
- function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
5011
- isInitialSync: false,
5012
- }) {
5013
- var _a;
5014
- return __awaiter(this, void 0, void 0, function* () {
5060
+ function _sync(db_1, options_1, schema_1) {
5061
+ return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
5062
+ isInitialSync: false,
5063
+ }) {
5064
+ var _a;
5015
5065
  if (!justCheckIfNeeded) {
5016
5066
  console.debug('SYNC STARTED', { isInitialSync, purpose });
5017
5067
  }
@@ -5053,8 +5103,8 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
5053
5103
  const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
5054
5104
  const syncState = yield db.getPersistedSyncState();
5055
5105
  const baseRevs = yield db.$baseRevs.toArray();
5056
- let clientChanges = yield listClientChanges(mutationTables);
5057
- const yResults = yield listYClientMessagesAndStateVector(db);
5106
+ let clientChanges = yield listClientChanges(mutationTables, db);
5107
+ const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
5058
5108
  throwIfCancelled(cancelToken);
5059
5109
  if (doSyncify) {
5060
5110
  const alreadySyncedRealms = [
@@ -5184,11 +5234,14 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
5184
5234
  //
5185
5235
  // apply yMessages
5186
5236
  //
5187
- const receivedUntils = yield applyYServerMessages(res.yMessages, db);
5237
+ const { receivedUntils, resyncNeeded } = yield applyYServerMessages(res.yMessages, db);
5188
5238
  //
5189
5239
  // update Y SyncStates
5190
5240
  //
5191
5241
  yield updateYSyncStates(lastUpdateIds, receivedUntils, db, res.serverRevision);
5242
+ if (resyncNeeded) {
5243
+ newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
5244
+ }
5192
5245
  }
5193
5246
  //
5194
5247
  // Update regular syncState
@@ -5322,8 +5375,8 @@ function MessagesFromServerConsumer(db) {
5322
5375
  event.next(null);
5323
5376
  }
5324
5377
  function consumeQueue() {
5325
- var _a, _b, _c, _d, _e, _f;
5326
5378
  return __awaiter(this, void 0, void 0, function* () {
5379
+ var _a, _b, _c, _d, _e, _f;
5327
5380
  while (queue.length > 0) {
5328
5381
  const msg = queue.shift();
5329
5382
  try {
@@ -5638,8 +5691,8 @@ function logout(db) {
5638
5691
  }
5639
5692
  });
5640
5693
  }
5641
- function _logout(db, { deleteUnsyncedData = false } = {}) {
5642
- return __awaiter(this, void 0, void 0, function* () {
5694
+ function _logout(db_1) {
5695
+ return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
5643
5696
  // Clear the database without emptying configuration options.
5644
5697
  const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
5645
5698
  // @ts-ignore
@@ -5677,11 +5730,11 @@ function _logout(db, { deleteUnsyncedData = false } = {}) {
5677
5730
 
5678
5731
  function otpFetchTokenCallback(db) {
5679
5732
  const { userInteraction } = db.cloud;
5680
- return function otpAuthenticate({ public_key, hints }) {
5681
- var _a;
5682
- return __awaiter(this, void 0, void 0, function* () {
5733
+ return function otpAuthenticate(_a) {
5734
+ return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
5735
+ var _b;
5683
5736
  let tokenRequest;
5684
- const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
5737
+ const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
5685
5738
  if (!url)
5686
5739
  throw new Error(`No database URL given.`);
5687
5740
  if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
@@ -5847,8 +5900,8 @@ function setCurrentUser(db, user) {
5847
5900
  }
5848
5901
 
5849
5902
  function login(db, hints) {
5850
- var _a;
5851
5903
  return __awaiter(this, void 0, void 0, function* () {
5904
+ var _a;
5852
5905
  const currentUser = yield db.getCurrentUser();
5853
5906
  const origUserId = currentUser.userId;
5854
5907
  if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
@@ -6285,8 +6338,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6285
6338
  outstandingTransactions.next(outstandingTransactions.value);
6286
6339
  };
6287
6340
  const txComplete = () => {
6288
- if (tx.mutationsAdded &&
6289
- !isEagerSyncDisabled(db)) {
6341
+ if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
6290
6342
  triggerSync(db, 'push');
6291
6343
  }
6292
6344
  removeTransaction();
@@ -6368,26 +6420,107 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6368
6420
  : mutateAndLog(req);
6369
6421
  } }));
6370
6422
  function mutateAndLog(req) {
6423
+ var _a, _b;
6371
6424
  const trans = req.trans;
6372
- trans.mutationsAdded = true;
6425
+ const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
6373
6426
  const { txid, currentUser: { userId }, } = trans;
6374
6427
  const { type } = req;
6375
6428
  const opNo = ++trans.opCount;
6429
+ function stripChangeSpec(changeSpec) {
6430
+ if (!unsyncedProps)
6431
+ return changeSpec;
6432
+ let rv = changeSpec;
6433
+ for (const keyPath of Object.keys(changeSpec)) {
6434
+ if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
6435
+ if (rv === changeSpec)
6436
+ rv = Object.assign({}, changeSpec); // clone on demand
6437
+ delete rv[keyPath];
6438
+ }
6439
+ }
6440
+ return rv;
6441
+ }
6376
6442
  return table.mutate(req).then((res) => {
6443
+ var _a;
6377
6444
  const { numFailures: hasFailures, failures } = res;
6378
6445
  let keys = type === 'delete' ? req.keys : res.results;
6379
6446
  let values = 'values' in req ? req.values : [];
6380
- let updates = 'updates' in req && req.updates;
6447
+ let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
6448
+ let updates = 'updates' in req ? req.updates : undefined;
6381
6449
  if (hasFailures) {
6382
6450
  keys = keys.filter((_, idx) => !failures[idx]);
6383
6451
  values = values.filter((_, idx) => !failures[idx]);
6384
6452
  }
6453
+ if (unsyncedProps) {
6454
+ // Filter out unsynced properties
6455
+ values = values.map((value) => {
6456
+ const newValue = Object.assign({}, value);
6457
+ for (const prop of unsyncedProps) {
6458
+ delete newValue[prop];
6459
+ }
6460
+ return newValue;
6461
+ });
6462
+ if (changeSpec) {
6463
+ // modify operation with criteria and changeSpec.
6464
+ // We must strip out unsynced properties from changeSpec.
6465
+ // We deal with criteria later.
6466
+ changeSpec = stripChangeSpec(changeSpec);
6467
+ if (Object.keys(changeSpec).length === 0) {
6468
+ // Nothing to change on server
6469
+ return res;
6470
+ }
6471
+ }
6472
+ if (updates) {
6473
+ let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
6474
+ let newUpdates = {
6475
+ keys: [],
6476
+ changeSpecs: [],
6477
+ };
6478
+ const validKeys = new RangeSet();
6479
+ let anyChangeSpecBecameEmpty = false;
6480
+ for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
6481
+ if (Object.keys(strippedChangeSpecs[i]).length > 0) {
6482
+ newUpdates.keys.push(updates.keys[i]);
6483
+ newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
6484
+ validKeys.addKey(updates.keys[i]);
6485
+ }
6486
+ else {
6487
+ anyChangeSpecBecameEmpty = true;
6488
+ }
6489
+ }
6490
+ updates = newUpdates;
6491
+ if (anyChangeSpecBecameEmpty) {
6492
+ // Some keys were stripped. We must also strip them from keys and values
6493
+ let newKeys = [];
6494
+ let newValues = [];
6495
+ for (let i = 0, l = keys.length; i < l; ++i) {
6496
+ if (validKeys.hasKey(keys[i])) {
6497
+ newKeys.push(keys[i]);
6498
+ newValues.push(values[i]);
6499
+ }
6500
+ }
6501
+ keys = newKeys;
6502
+ values = newValues;
6503
+ }
6504
+ }
6505
+ }
6385
6506
  const ts = Date.now();
6386
6507
  // Canonicalize req.criteria.index to null if it's on the primary key.
6387
- const criteria = 'criteria' in req && req.criteria
6508
+ let criteria = 'criteria' in req && req.criteria
6388
6509
  ? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
6389
6510
  ? null // This will disable the server from trying to log consistent operations where it shouldnt.
6390
6511
  : req.criteria.index }) : undefined;
6512
+ if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
6513
+ const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
6514
+ const involvedProps = keyPaths
6515
+ ? typeof keyPaths === 'string'
6516
+ ? [keyPaths]
6517
+ : keyPaths
6518
+ : [];
6519
+ if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
6520
+ // Don't log criteria on unsynced properties as the server could not test them.
6521
+ criteria = undefined;
6522
+ }
6523
+ }
6391
6524
  const mut = req.type === 'delete'
6392
6525
  ? {
6393
6526
  type: 'delete',
@@ -6408,7 +6541,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6408
6541
  userId,
6409
6542
  values,
6410
6543
  }
6411
- : criteria && req.changeSpec
6544
+ : criteria && changeSpec
6412
6545
  ? {
6413
6546
  // Common changeSpec for all keys
6414
6547
  type: 'modify',
@@ -6416,37 +6549,51 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6416
6549
  opNo,
6417
6550
  keys,
6418
6551
  criteria,
6419
- changeSpec: req.changeSpec,
6552
+ changeSpec,
6420
6553
  txid,
6421
6554
  userId,
6422
6555
  }
6423
- : updates
6556
+ : changeSpec
6424
6557
  ? {
6425
- // One changeSpec per key
6558
+ // In case criteria involved an unsynced property, we go for keys instead.
6426
6559
  type: 'update',
6427
6560
  ts,
6428
6561
  opNo,
6429
- keys: updates.keys,
6430
- changeSpecs: updates.changeSpecs,
6431
- txid,
6432
- userId,
6433
- }
6434
- : {
6435
- type: 'upsert',
6436
- ts,
6437
- opNo,
6438
6562
  keys,
6439
- values,
6563
+ changeSpecs: keys.map(() => changeSpec),
6440
6564
  txid,
6441
6565
  userId,
6442
- };
6566
+ }
6567
+ : updates
6568
+ ? {
6569
+ // One changeSpec per key
6570
+ type: 'update',
6571
+ ts,
6572
+ opNo,
6573
+ keys: updates.keys,
6574
+ changeSpecs: updates.changeSpecs,
6575
+ txid,
6576
+ userId,
6577
+ }
6578
+ : {
6579
+ type: 'upsert',
6580
+ ts,
6581
+ opNo,
6582
+ keys,
6583
+ values,
6584
+ txid,
6585
+ userId,
6586
+ };
6443
6587
  if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
6444
6588
  mut.isAdditionalChunk = true;
6445
6589
  }
6446
6590
  return keys.length > 0 || criteria
6447
6591
  ? mutsTable
6448
6592
  .mutate({ type: 'add', trans, values: [mut] }) // Log entry
6449
- .then(() => res) // Return original response
6593
+ .then(() => {
6594
+ trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
6595
+ return res; // Return original response
6596
+ })
6450
6597
  : res;
6451
6598
  });
6452
6599
  }
@@ -6613,38 +6760,51 @@ class TokenExpiredError extends Error {
6613
6760
 
6614
6761
  function createYClientUpdateObservable(db) {
6615
6762
  const yTableRecords = flatten(db.tables
6616
- .filter((table) => { var _a; return ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync) && table.schema.yProps; })
6763
+ .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; })
6617
6764
  .map((table) => table.schema.yProps.map((p) => ({
6618
6765
  table: table.name,
6619
6766
  ydocProp: p.prop,
6620
6767
  updatesTable: p.updatesTable,
6621
6768
  }))));
6622
6769
  return merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
6623
- let currentUnsentFrom = 1;
6624
- return liveQuery(() => __awaiter(this, void 0, void 0, function* () {
6625
- const yTbl = db.table(updatesTable);
6626
- const unsentFrom = yield yTbl
6627
- .where({ i: DEXIE_CLOUD_SYNCER_ID })
6628
- .first()
6629
- .then((syncer) => (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1);
6630
- currentUnsentFrom = Math.max(currentUnsentFrom, unsentFrom);
6631
- const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
6632
- // Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
6633
- currentUnsentFrom = Math.max(currentUnsentFrom, ...addedUpdates.map((update) => update.i + 1));
6634
- return addedUpdates
6635
- .filter((update) => update.f && update.f & 1) // Only include local updates
6636
- .map((update) => {
6637
- return {
6638
- type: 'u-c',
6639
- table,
6640
- prop: ydocProp,
6641
- k: update.k,
6642
- u: update.u,
6643
- i: update.i,
6644
- };
6645
- });
6770
+ // Per updates table (table+prop combo), we first read syncer.unsentFrom,
6771
+ // and then start listening for updates since that number.
6772
+ const yTbl = db.table(updatesTable);
6773
+ return from$1(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(switchMap$1((syncer) => {
6774
+ let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
6775
+ return from$1(liveQuery(() => __awaiter(this, void 0, void 0, function* () {
6776
+ const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
6777
+ return addedUpdates
6778
+ .filter((update) => update.f && update.f & 1) // Only include local updates
6779
+ .map((update) => {
6780
+ return {
6781
+ type: 'u-c',
6782
+ table,
6783
+ prop: ydocProp,
6784
+ k: update.k,
6785
+ u: update.u,
6786
+ i: update.i,
6787
+ };
6788
+ });
6789
+ }))).pipe(tap$1((addedUpdates) => {
6790
+ // Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
6791
+ // (Before, we did this within the liveQuery, but that caused a bug because
6792
+ // a cancelled emittion of a liveQuery would update the currentUnsentFrom without
6793
+ // emitting anything, leading to that we jumped over some updates. Here we update it
6794
+ // after the liveQuery has emitted its updates)
6795
+ if (addedUpdates.length > 0) {
6796
+ currentUnsentFrom = addedUpdates.at(-1).i + 1;
6797
+ }
6798
+ }));
6646
6799
  }));
6647
- })).pipe(mergeMap$1((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.
6800
+ })).pipe(
6801
+ // Flatten the array of messages.
6802
+ // If messageProducer emits empty array, nothing is emitted
6803
+ // but if messageProducer emits array of messages, they are
6804
+ // emitted one by one.
6805
+ mergeMap$1((messages) => messages), tap$1((message) => {
6806
+ console.debug('dexie-cloud emitting y-c', message);
6807
+ }));
6648
6808
  }
6649
6809
 
6650
6810
  function getAwarenessLibrary(db) {
@@ -6661,25 +6821,24 @@ const SERVER_PING_TIMEOUT = 20000;
6661
6821
  const CLIENT_PING_INTERVAL = 30000;
6662
6822
  const FAIL_RETRY_WAIT_TIME = 60000;
6663
6823
  class WSObservable extends Observable$1 {
6664
- constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, token, tokenExpiration) {
6665
- super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus));
6824
+ constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
6825
+ super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
6666
6826
  }
6667
6827
  }
6668
6828
  let counter = 0;
6669
6829
  class WSConnection extends Subscription$1 {
6670
- constructor(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
6830
+ constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
6671
6831
  super(() => this.teardown());
6672
6832
  this.id = ++counter;
6673
6833
  this.subscriptions = new Set();
6674
6834
  this.reconnecting = false;
6675
- console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
6835
+ console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
6676
6836
  this.db = db;
6677
6837
  this.databaseUrl = db.cloud.options.databaseUrl;
6678
6838
  this.rev = rev;
6679
6839
  this.realmSetHash = realmSetHash;
6680
6840
  this.clientIdentity = clientIdentity;
6681
- this.token = token;
6682
- this.tokenExpiration = tokenExpiration;
6841
+ this.user = user;
6683
6842
  this.subscriber = subscriber;
6684
6843
  this.lastUserActivity = new Date();
6685
6844
  this.messageProducer = messageProducer;
@@ -6739,7 +6898,8 @@ class WSConnection extends Subscription$1 {
6739
6898
  //console.debug('SyncStatus: DUBB: Ooops it was closed!');
6740
6899
  return;
6741
6900
  }
6742
- if (this.tokenExpiration && this.tokenExpiration < new Date()) {
6901
+ const tokenExpiration = this.user.accessTokenExpiration;
6902
+ if (tokenExpiration && tokenExpiration < new Date()) {
6743
6903
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
6744
6904
  return;
6745
6905
  }
@@ -6794,8 +6954,8 @@ class WSConnection extends Subscription$1 {
6794
6954
  searchParams.set('rev', this.rev);
6795
6955
  searchParams.set('realmsHash', this.realmSetHash);
6796
6956
  searchParams.set('clientId', this.clientIdentity);
6797
- if (this.token) {
6798
- searchParams.set('token', this.token);
6957
+ if (this.user.accessToken) {
6958
+ searchParams.set('token', this.user.accessToken);
6799
6959
  }
6800
6960
  // Connect the WebSocket to given url:
6801
6961
  console.debug('dexie-cloud WebSocket create');
@@ -6810,12 +6970,12 @@ class WSConnection extends Subscription$1 {
6810
6970
  ws.onmessage = (event) => {
6811
6971
  if (!this.pinger)
6812
6972
  return;
6813
- console.debug('dexie-cloud WebSocket onmessage', event.data);
6814
6973
  this.lastServerActivity = new Date();
6815
6974
  try {
6816
6975
  const msg = typeof event.data === 'string'
6817
6976
  ? TSON.parse(event.data)
6818
6977
  : decodeYMessage(new Uint8Array(event.data));
6978
+ console.debug('dexie-cloud WebSocket onmessage', msg.type, msg);
6819
6979
  if (msg.type === 'error') {
6820
6980
  throw new Error(`Error message from dexie-cloud: ${msg.error}`);
6821
6981
  }
@@ -6836,6 +6996,10 @@ class WSConnection extends Subscription$1 {
6836
6996
  else if (msg.type === 'u-ack' || msg.type === 'u-reject' || msg.type === 'u-s' || msg.type === 'in-sync') {
6837
6997
  applyYServerMessages([msg], this.db);
6838
6998
  }
6999
+ else if (msg.type === 'outdated-server-rev') {
7000
+ // Won't happen but need this for typing.
7001
+ throw new Error('Outdated server revision not expected over WebSocket - only in sync using fetch()');
7002
+ }
6839
7003
  else if (msg.type !== 'pong') {
6840
7004
  this.subscriber.next(msg);
6841
7005
  }
@@ -6871,17 +7035,27 @@ class WSConnection extends Subscription$1 {
6871
7035
  this.webSocketStatus.value !== 'connected') {
6872
7036
  this.webSocketStatus.next('connected');
6873
7037
  }
7038
+ console.debug('dexie-cloud WebSocket send', msg.type, msg);
6874
7039
  if (msg.type === 'ready') {
6875
7040
  (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
6876
7041
  }
6877
7042
  else {
6878
7043
  // If it's not a "ready" message, it's an YMessage.
6879
7044
  // YMessages can be sent binary encoded.
7045
+ if (msg.type === 'u-c') {
7046
+ console.log("u-c:B", ++gotClientUpdateB);
7047
+ }
6880
7048
  (_b = this.ws) === null || _b === void 0 ? void 0 : _b.send(encodeYMessage(msg));
6881
7049
  }
6882
7050
  }
6883
7051
  }));
6884
- this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
7052
+ 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));
7058
+ }
6885
7059
  }
6886
7060
  catch (error) {
6887
7061
  this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
@@ -6889,6 +7063,8 @@ class WSConnection extends Subscription$1 {
6889
7063
  });
6890
7064
  }
6891
7065
  }
7066
+ let gotClientUpdateA = 0;
7067
+ let gotClientUpdateB = 0;
6892
7068
 
6893
7069
  class InvalidLicenseError extends Error {
6894
7070
  constructor(license) {
@@ -6951,7 +7127,7 @@ function connectWebSocket(db) {
6951
7127
  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]));
6952
7128
  }
6953
7129
  return new BehaviorSubject([userLogin, syncState]);
6954
- }), 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]) => {
7130
+ }), 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]) => {
6955
7131
  var _a;
6956
7132
  if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
6957
7133
  // Restart the flow if persistedSyncState is not yet available.
@@ -6961,7 +7137,7 @@ function connectWebSocket(db) {
6961
7137
  // If no new entries, server won't bother the client. If new entries, server sends only those
6962
7138
  // and the baseRev of the last from same client-ID.
6963
7139
  if (userLogin) {
6964
- return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin.accessToken, userLogin.accessTokenExpiration);
7140
+ return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
6965
7141
  }
6966
7142
  else {
6967
7143
  return from$1([]);
@@ -7012,8 +7188,8 @@ function connectWebSocket(db) {
7012
7188
  }
7013
7189
 
7014
7190
  function isSyncNeeded(db) {
7015
- var _a;
7016
7191
  return __awaiter(this, void 0, void 0, function* () {
7192
+ var _a;
7017
7193
  return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
7018
7194
  ? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
7019
7195
  : false;
@@ -7697,6 +7873,7 @@ class PermissionChecker {
7697
7873
  // If user can update any prop in any table in this realm, return true unless
7698
7874
  // it regards to ownership change:
7699
7875
  if (this.permissions.update === '*') {
7876
+ // @ts-ignore
7700
7877
  return props.every((prop) => prop !== 'owner');
7701
7878
  }
7702
7879
  const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
@@ -7768,125 +7945,139 @@ const getInvitesObservable = associate((db) => {
7768
7945
  });
7769
7946
 
7770
7947
  function createYHandler(db) {
7771
- const awap = getAwarenessLibrary(db);
7772
7948
  return (provider) => {
7773
7949
  var _a;
7774
7950
  const doc = provider.doc;
7775
- const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7951
+ const { parentTable } = doc.meta || {};
7776
7952
  if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
7777
7953
  return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
7778
7954
  }
7779
- let awareness = new awap.Awareness(doc);
7780
- awarenessWeakMap.set(doc, awareness);
7781
- provider.awareness = awareness;
7782
- awareness.on('update', ({ added, updated, removed }, origin) => {
7783
- // Send the update
7784
- const changedClients = added.concat(updated).concat(removed);
7785
- if (origin !== 'server') {
7786
- const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7955
+ let awareness;
7956
+ Object.defineProperty(provider, "awareness", {
7957
+ get() {
7958
+ if (awareness)
7959
+ return awareness;
7960
+ awareness = createAwareness(db, doc, provider);
7961
+ awarenessWeakMap.set(doc, awareness);
7962
+ return awareness;
7963
+ }
7964
+ });
7965
+ };
7966
+ }
7967
+ function createAwareness(db, doc, provider) {
7968
+ const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7969
+ const awap = getAwarenessLibrary(db);
7970
+ const awareness = new awap.Awareness(doc);
7971
+ awareness.on('update', ({ added, updated, removed }, origin) => {
7972
+ // Send the update
7973
+ const changedClients = added.concat(updated).concat(removed);
7974
+ const user = db.cloud.currentUser.value;
7975
+ if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7976
+ const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7977
+ db.messageProducer.next({
7978
+ type: 'aware',
7979
+ table: parentTable,
7980
+ prop: parentProp,
7981
+ k: doc.meta.parentId,
7982
+ u: update,
7983
+ });
7984
+ if (provider.destroyed) {
7985
+ // We're called from awareness.on('destroy') that did
7986
+ // removeAwarenessStates.
7987
+ // It's time to also send the doc-close message that dexie-cloud understands
7988
+ // and uses to stop subscribing for updates and awareness updates and brings
7989
+ // down the cached information in memory on the WS connection for this.
7787
7990
  db.messageProducer.next({
7788
- type: 'aware',
7991
+ type: 'doc-close',
7789
7992
  table: parentTable,
7790
7993
  prop: parentProp,
7791
- k: doc.meta.parentId,
7792
- u: update,
7994
+ k: doc.meta.parentId
7793
7995
  });
7794
- if (provider.destroyed) {
7795
- // We're called from awareness.on('destroy') that did
7796
- // removeAwarenessStates.
7797
- // It's time to also send the doc-close message that dexie-cloud understands
7798
- // and uses to stop subscribing for updates and awareness updates and brings
7799
- // down the cached information in memory on the WS connection for this.
7800
- db.messageProducer.next({
7801
- type: 'doc-close',
7802
- table: parentTable,
7803
- prop: parentProp,
7804
- k: doc.meta.parentId
7805
- });
7806
- }
7807
7996
  }
7808
- });
7809
- awareness.on('destroy', () => {
7810
- // Signal to server that this provider is destroyed (the update event will be triggered, which
7811
- // in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
7812
- awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
7813
- });
7814
- // Now wait til document is loaded and then open the document on the server
7815
- provider.on('load', () => __awaiter(this, void 0, void 0, function* () {
7997
+ }
7998
+ });
7999
+ awareness.on('destroy', () => {
8000
+ // Signal to server that this provider is destroyed (the update event will be triggered, which
8001
+ // in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
8002
+ awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
8003
+ });
8004
+ // Open the document on the server
8005
+ (() => __awaiter(this, void 0, void 0, function* () {
8006
+ if (provider.destroyed)
8007
+ return;
8008
+ let connected = false;
8009
+ let currentFlowId = 1;
8010
+ const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
7816
8011
  if (provider.destroyed)
7817
8012
  return;
7818
- let connected = false;
7819
- let currentFlowId = 1;
7820
- const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
7821
- if (provider.destroyed)
8013
+ // Keep "connected" state in a variable so we can check it after async operations
8014
+ connected = wsStatus === 'connected';
8015
+ // We are or got connected. Open the document on the server.
8016
+ const user = db.cloud.currentUser.value;
8017
+ if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
8018
+ ++currentFlowId;
8019
+ openDocumentOnServer().catch(error => {
8020
+ console.warn(`Error catched in createYHandler.ts: ${error}`);
8021
+ });
8022
+ }
8023
+ });
8024
+ // Wait until WebSocket is connected
8025
+ provider.addCleanupHandler(subscription);
8026
+ /** Sends an 'doc-open' message to server whenever websocket becomes
8027
+ * connected, or if it is already connected.
8028
+ * The flow is aborted in case websocket is disconnected while querying
8029
+ * information required to compute the state vector. Flow is also
8030
+ * aborted in case document or provider has been destroyed during
8031
+ * the async parts of the task.
8032
+ *
8033
+ * The state vector is only computed from the updates that have occured
8034
+ * after the last full sync - which could very often be zero - in which
8035
+ * case no state vector is sent (then the server already knows us by
8036
+ * revision)
8037
+ *
8038
+ * When server gets the doc-open message, it will authorized us for
8039
+ * whether we are allowed to read / write to this document, and then
8040
+ * keep the cached information in memory on the WS connection for this
8041
+ * particular document, as well as subscribe to updates and awareness updates
8042
+ * from other clients on the document.
8043
+ */
8044
+ function openDocumentOnServer(wsStatus) {
8045
+ return __awaiter(this, void 0, void 0, function* () {
8046
+ const myFlow = currentFlowId; // So we can abort when a new flow is started
8047
+ const yTbl = db.table(updatesTable);
8048
+ const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
8049
+ // After every await, check if we still should be working on this task.
8050
+ if (provider.destroyed || currentFlowId !== myFlow || !connected)
7822
8051
  return;
7823
- // Keep "connected" state in a variable so we can check it after async operations
7824
- connected = wsStatus === 'connected';
7825
- // We are or got connected. Open the document on the server.
7826
- if (wsStatus === "connected") {
7827
- ++currentFlowId;
7828
- openDocumentOnServer().catch(error => {
7829
- console.warn(`Error catched in createYHandler.ts: ${error}`);
7830
- });
8052
+ const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
8053
+ const docOpenMsg = {
8054
+ type: 'doc-open',
8055
+ table: parentTable,
8056
+ prop: parentProp,
8057
+ k: parentId,
8058
+ serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
8059
+ };
8060
+ const serverUpdatesSinceLastSync = yield yTbl
8061
+ .where('i')
8062
+ .between(receivedUntil, Infinity, false)
8063
+ .filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
8064
+ ((update.f || 0) & 1) === 0 // Don't include local changes
8065
+ )
8066
+ .toArray();
8067
+ // After every await, check if we still should be working on this task.
8068
+ if (provider.destroyed || currentFlowId !== myFlow || !connected)
8069
+ return;
8070
+ if (serverUpdatesSinceLastSync.length > 0) {
8071
+ const Y = $Y(db); // Get the Yjs library from Dexie constructor options
8072
+ const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
8073
+ const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
8074
+ docOpenMsg.sv = stateVector;
7831
8075
  }
8076
+ db.messageProducer.next(docOpenMsg);
7832
8077
  });
7833
- // Wait until WebSocket is connected
7834
- provider.addCleanupHandler(subscription);
7835
- /** Sends an 'doc-open' message to server whenever websocket becomes
7836
- * connected, or if it is already connected.
7837
- * The flow is aborted in case websocket is disconnected while querying
7838
- * information required to compute the state vector. Flow is also
7839
- * aborted in case document or provider has been destroyed during
7840
- * the async parts of the task.
7841
- *
7842
- * The state vector is only computed from the updates that have occured
7843
- * after the last full sync - which could very often be zero - in which
7844
- * case no state vector is sent (then the server already knows us by
7845
- * revision)
7846
- *
7847
- * When server gets the doc-open message, it will authorized us for
7848
- * whether we are allowed to read / write to this document, and then
7849
- * keep the cached information in memory on the WS connection for this
7850
- * particular document, as well as subscribe to updates and awareness updates
7851
- * from other clients on the document.
7852
- */
7853
- function openDocumentOnServer(wsStatus) {
7854
- return __awaiter(this, void 0, void 0, function* () {
7855
- const myFlow = currentFlowId; // So we can abort when a new flow is started
7856
- const yTbl = db.table(updatesTable);
7857
- const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
7858
- // After every await, check if we still should be working on this task.
7859
- if (provider.destroyed || currentFlowId !== myFlow || !connected)
7860
- return;
7861
- const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
7862
- const docOpenMsg = {
7863
- type: 'doc-open',
7864
- table: parentTable,
7865
- prop: parentProp,
7866
- k: parentId,
7867
- serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
7868
- };
7869
- const serverUpdatesSinceLastSync = yield yTbl
7870
- .where('i')
7871
- .between(receivedUntil, Infinity, false)
7872
- .filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
7873
- ((update.f || 0) & 1) === 0 // Don't include local changes
7874
- )
7875
- .toArray();
7876
- // After every await, check if we still should be working on this task.
7877
- if (provider.destroyed || currentFlowId !== myFlow || !connected)
7878
- return;
7879
- if (serverUpdatesSinceLastSync.length > 0) {
7880
- const Y = $Y(db); // Get the Yjs library from Dexie constructor options
7881
- const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
7882
- const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
7883
- docOpenMsg.sv = stateVector;
7884
- }
7885
- db.messageProducer.next(docOpenMsg);
7886
- });
7887
- }
7888
- }));
7889
- };
8078
+ }
8079
+ }))();
8080
+ return awareness;
7890
8081
  }
7891
8082
 
7892
8083
  const DEFAULT_OPTIONS = {
@@ -7929,7 +8120,7 @@ function dexieCloud(dexie) {
7929
8120
  const syncComplete = new Subject();
7930
8121
  dexie.cloud = {
7931
8122
  // @ts-ignore
7932
- version: "4.1.0-alpha.2",
8123
+ version: "4.1.0-alpha.21",
7933
8124
  options: Object.assign({}, DEFAULT_OPTIONS),
7934
8125
  schema: null,
7935
8126
  get currentUserId() {
@@ -7965,16 +8156,16 @@ function dexieCloud(dexie) {
7965
8156
  }
7966
8157
  updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
7967
8158
  },
7968
- logout({ force } = {}) {
7969
- return __awaiter(this, void 0, void 0, function* () {
8159
+ logout() {
8160
+ return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
7970
8161
  force
7971
8162
  ? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
7972
8163
  : yield logout(DexieCloudDB(dexie));
7973
8164
  });
7974
8165
  },
7975
- sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
7976
- var _a;
7977
- return __awaiter(this, void 0, void 0, function* () {
8166
+ sync() {
8167
+ return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
8168
+ var _a;
7978
8169
  if (wait === undefined)
7979
8170
  wait = true;
7980
8171
  const db = DexieCloudDB(dexie);
@@ -8032,8 +8223,8 @@ function dexieCloud(dexie) {
8032
8223
  dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
8033
8224
  dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
8034
8225
  function onDbReady(dexie) {
8035
- var _a, _b, _c, _d, _e, _f, _g;
8036
8226
  return __awaiter(this, void 0, void 0, function* () {
8227
+ var _a, _b, _c, _d, _e, _f, _g;
8037
8228
  closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
8038
8229
  const db = DexieCloudDB(dexie);
8039
8230
  // Setup default GUI:
@@ -8056,7 +8247,7 @@ function dexieCloud(dexie) {
8056
8247
  ? yield navigator.serviceWorker.getRegistrations()
8057
8248
  : [];
8058
8249
  const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
8059
- var _h, _j;
8250
+ var _a, _b;
8060
8251
  const { options, schema } = db.cloud;
8061
8252
  const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
8062
8253
  db.getOptions(),
@@ -8078,7 +8269,7 @@ function dexieCloud(dexie) {
8078
8269
  delete newPersistedOptions.awarenessProtocol;
8079
8270
  yield db.$syncState.put(newPersistedOptions, 'options');
8080
8271
  }
8081
- if (((_h = db.cloud.options) === null || _h === void 0 ? void 0 : _h.tryUseServiceWorker) &&
8272
+ if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
8082
8273
  'serviceWorker' in navigator &&
8083
8274
  swRegistrations.length > 0 &&
8084
8275
  !DISABLE_SERVICEWORKER_STRATEGY) {
@@ -8092,7 +8283,7 @@ function dexieCloud(dexie) {
8092
8283
  // Not configured for using service worker or no service worker
8093
8284
  // registration exists. Don't rely on service worker to do any job.
8094
8285
  // Use LocalSyncWorker instead.
8095
- if (((_j = db.cloud.options) === null || _j === void 0 ? void 0 : _j.tryUseServiceWorker) &&
8286
+ if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
8096
8287
  !db.cloud.isServiceWorkerDB) {
8097
8288
  console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
8098
8289
  ? 'No SW registrations found.'
@@ -8231,7 +8422,7 @@ function dexieCloud(dexie) {
8231
8422
  }
8232
8423
  }
8233
8424
  // @ts-ignore
8234
- dexieCloud.version = "4.1.0-alpha.2";
8425
+ dexieCloud.version = "4.1.0-alpha.21";
8235
8426
  Dexie.Cloud = dexieCloud;
8236
8427
 
8237
8428
  // In case the SW lives for a while, let it reuse already opened connections:
@@ -8261,8 +8452,8 @@ function syncDB(dbName, purpose) {
8261
8452
  }
8262
8453
  return promise;
8263
8454
  function _syncDB(dbName, purpose) {
8264
- var _a;
8265
8455
  return __awaiter(this, void 0, void 0, function* () {
8456
+ var _a;
8266
8457
  let db = managedDBs.get(dbName);
8267
8458
  if (!db) {
8268
8459
  console.debug('Dexie Cloud SW: Creating new Dexie instance for', dbName);