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

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.
@@ -3,6 +3,7 @@ import { BehaviorSubject, Observable, Subscriber, Subscription } from 'rxjs';
3
3
  import { DXCWebSocketStatus } from './DXCWebSocketStatus';
4
4
  import type { YClientMessage } from 'dexie-cloud-common';
5
5
  import { DexieCloudDB } from './db/DexieCloudDB';
6
+ import { UserLogin } from './dexie-cloud-client';
6
7
  export type WSClientToServerMsg = ReadyForChangesMessage | YClientMessage;
7
8
  export interface ReadyForChangesMessage {
8
9
  type: 'ready';
@@ -41,7 +42,7 @@ export interface TokenExpiredMessage {
41
42
  type: 'token-expired';
42
43
  }
43
44
  export declare class WSObservable extends Observable<WSConnectionMsg> {
44
- constructor(db: DexieCloudDB, rev: string, realmSetHash: string, clientIdentity: string, messageProducer: Observable<WSClientToServerMsg>, webSocketStatus: BehaviorSubject<DXCWebSocketStatus>, token?: string, tokenExpiration?: Date);
45
+ constructor(db: DexieCloudDB, rev: string, realmSetHash: string, clientIdentity: string, messageProducer: Observable<WSClientToServerMsg>, webSocketStatus: BehaviorSubject<DXCWebSocketStatus>, user: UserLogin);
45
46
  }
46
47
  export declare class WSConnection extends Subscription {
47
48
  db: DexieCloudDB;
@@ -53,8 +54,7 @@ export declare class WSConnection extends Subscription {
53
54
  rev: string;
54
55
  realmSetHash: string;
55
56
  clientIdentity: string;
56
- token: string | undefined;
57
- tokenExpiration: Date | undefined;
57
+ user: UserLogin;
58
58
  subscriber: Subscriber<WSConnectionMsg>;
59
59
  pauseUntil?: Date;
60
60
  messageProducer: Observable<WSClientToServerMsg>;
@@ -62,7 +62,7 @@ export declare class WSConnection extends Subscription {
62
62
  id: number;
63
63
  private pinger;
64
64
  private subscriptions;
65
- constructor(db: DexieCloudDB, rev: string, realmSetHash: string, clientIdentity: string, token: string | undefined, tokenExpiration: Date | undefined, subscriber: Subscriber<WSConnectionMsg>, messageProducer: Observable<WSClientToServerMsg>, webSocketStatus: BehaviorSubject<DXCWebSocketStatus>);
65
+ constructor(db: DexieCloudDB, rev: string, realmSetHash: string, clientIdentity: string, user: UserLogin, subscriber: Subscriber<WSConnectionMsg>, messageProducer: Observable<WSClientToServerMsg>, webSocketStatus: BehaviorSubject<DXCWebSocketStatus>);
66
66
  private teardown;
67
67
  private disconnect;
68
68
  reconnecting: boolean;
@@ -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.5, Mon Oct 14 2024
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -4967,13 +4967,12 @@ function $Y(db) {
4967
4967
  * @param db
4968
4968
  * @returns
4969
4969
  */
4970
- function listYClientMessagesAndStateVector(db) {
4971
- var _a;
4970
+ function listYClientMessagesAndStateVector(db, tablesToSync) {
4972
4971
  return __awaiter(this, void 0, void 0, function* () {
4973
4972
  const result = [];
4974
4973
  const lastUpdateIds = {};
4975
- for (const table of db.tables) {
4976
- if (table.schema.yProps && ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync)) {
4974
+ for (const table of tablesToSync) {
4975
+ if (table.schema.yProps) {
4977
4976
  for (const yProp of table.schema.yProps) {
4978
4977
  const Y = $Y(db); // This is how we retrieve the user-provided Y library
4979
4978
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
@@ -5107,7 +5106,7 @@ function applyYServerMessages(yMessages, db) {
5107
5106
  }
5108
5107
 
5109
5108
  function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
5110
- var _a, _b;
5109
+ var _a, _b, _c, _d, _e;
5111
5110
  return __awaiter(this, void 0, void 0, function* () {
5112
5111
  // We want to update unsentFrom for each yTable to the value specified in first argument
5113
5112
  // because we got those values before we synced with server and here we are back from server
@@ -5115,7 +5114,7 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
5115
5114
  // we can safely store unsentFrom to a value of the last update + 1 here.
5116
5115
  // We also want to update receivedUntil for each yTable to the value specified in the second argument,
5117
5116
  // because that contains the highest resulted id of each update from server after storing it.
5118
- // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
5117
+ // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
5119
5118
  // to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
5120
5119
  // per yTable.
5121
5120
  const mergedSpec = {};
@@ -5127,28 +5126,42 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
5127
5126
  (_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
5128
5127
  mergedSpec[yTable].receivedUntil = lastUpdateId;
5129
5128
  }
5130
- // Now go through the merged map and update YSyncStates accordingly:
5131
- for (const [yTable, { unsentFrom, receivedUntil }] of Object.entries(mergedSpec)) {
5129
+ // Now go through all yTables and update their YSyncStates:
5130
+ const allYTables = Object.values(db.dx._dbSchema)
5131
+ .filter((tblSchema) => tblSchema.yProps)
5132
+ .map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
5133
+ .flat();
5134
+ for (const yTable of allYTables) {
5135
+ const mergedEntry = mergedSpec[yTable];
5136
+ const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
5137
+ const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
5138
+ // from local because we are in the same parent transaction (in sync.ts) that
5139
+ // applied all updates from the server
5140
+ (yield db
5141
+ .table(yTable)
5142
+ .where('i')
5143
+ .between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
5144
+ .reverse()
5145
+ .limit(1)
5146
+ .primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
5132
5147
  // We're already in a transaction, but for the sake of
5133
5148
  // code readability and correctness, let's launch an atomic sub transaction:
5134
5149
  yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
5135
- const state = yield db.table(yTable).get(DEXIE_CLOUD_SYNCER_ID);
5150
+ const state = yield db
5151
+ .table(yTable)
5152
+ .get(DEXIE_CLOUD_SYNCER_ID);
5136
5153
  if (!state) {
5137
5154
  yield db.table(yTable).add({
5138
5155
  i: DEXIE_CLOUD_SYNCER_ID,
5139
- unsentFrom: unsentFrom || 1,
5140
- receivedUntil: receivedUntil || 0,
5156
+ unsentFrom,
5157
+ receivedUntil,
5141
5158
  serverRev: serverRevision,
5142
5159
  });
5143
5160
  }
5144
5161
  else {
5145
- if (unsentFrom) {
5146
- state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
5147
- }
5148
- if (receivedUntil) {
5149
- state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
5150
- state.serverRev = serverRevision;
5151
- }
5162
+ state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
5163
+ state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
5164
+ state.serverRev = serverRevision;
5152
5165
  yield db.table(yTable).put(state);
5153
5166
  }
5154
5167
  }));
@@ -5161,7 +5174,9 @@ const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
5161
5174
  const BINSTREAM_TYPE_DOCUMENT = 3;
5162
5175
  function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms }) {
5163
5176
  return __awaiter(this, void 0, void 0, function* () {
5164
- if (yDownloadedRealms && realms && realms.every(realmId => yDownloadedRealms[realmId] === '*')) {
5177
+ if (yDownloadedRealms &&
5178
+ realms &&
5179
+ realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
5165
5180
  return; // Already done!
5166
5181
  }
5167
5182
  console.debug('Downloading Y.Docs from added realms');
@@ -5201,16 +5216,19 @@ function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms })
5201
5216
  yield yTable.bulkAdd(docsToInsert);
5202
5217
  docsToInsert = [];
5203
5218
  }
5204
- if (currentRealmId && currentTable && currentProp && (lastDoc || completedRealm)) {
5205
- yield db.$syncState.update('syncState', completedRealm
5206
- ? '*'
5207
- : {
5208
- [`yDownloadedRealms.${currentRealmId}`]: {
5219
+ if (currentRealmId &&
5220
+ ((currentTable && currentProp && lastDoc) || completedRealm)) {
5221
+ yield db.$syncState.update('syncState', (syncState) => {
5222
+ const yDownloadedRealms = syncState.yDownloadedRealms || {};
5223
+ yDownloadedRealms[currentRealmId] = completedRealm
5224
+ ? '*'
5225
+ : {
5209
5226
  tbl: currentTable,
5210
5227
  prop: currentProp,
5211
5228
  key: lastDoc.k,
5212
- },
5213
- });
5229
+ };
5230
+ syncState.yDownloadedRealms = yDownloadedRealms;
5231
+ });
5214
5232
  }
5215
5233
  });
5216
5234
  }
@@ -5360,7 +5378,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
5360
5378
  const syncState = yield db.getPersistedSyncState();
5361
5379
  const baseRevs = yield db.$baseRevs.toArray();
5362
5380
  let clientChanges = yield listClientChanges(mutationTables);
5363
- const yResults = yield listYClientMessagesAndStateVector(db);
5381
+ const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
5364
5382
  throwIfCancelled(cancelToken);
5365
5383
  if (doSyncify) {
5366
5384
  const alreadySyncedRealms = [
@@ -6613,7 +6631,7 @@ class TokenExpiredError extends Error {
6613
6631
 
6614
6632
  function createYClientUpdateObservable(db) {
6615
6633
  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; })
6634
+ .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
6635
  .map((table) => table.schema.yProps.map((p) => ({
6618
6636
  table: table.name,
6619
6637
  ydocProp: p.prop,
@@ -6644,7 +6662,12 @@ function createYClientUpdateObservable(db) {
6644
6662
  };
6645
6663
  });
6646
6664
  }));
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.
6665
+ })).pipe(
6666
+ // Flatten the array of messages.
6667
+ // If messageProducer emits empty array, nothing is emitted
6668
+ // but if messageProducer emits array of messages, they are
6669
+ // emitted one by one.
6670
+ mergeMap$1((messages) => messages));
6648
6671
  }
6649
6672
 
6650
6673
  function getAwarenessLibrary(db) {
@@ -6661,25 +6684,24 @@ const SERVER_PING_TIMEOUT = 20000;
6661
6684
  const CLIENT_PING_INTERVAL = 30000;
6662
6685
  const FAIL_RETRY_WAIT_TIME = 60000;
6663
6686
  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));
6687
+ constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
6688
+ super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
6666
6689
  }
6667
6690
  }
6668
6691
  let counter = 0;
6669
6692
  class WSConnection extends Subscription$1 {
6670
- constructor(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
6693
+ constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
6671
6694
  super(() => this.teardown());
6672
6695
  this.id = ++counter;
6673
6696
  this.subscriptions = new Set();
6674
6697
  this.reconnecting = false;
6675
- console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
6698
+ console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
6676
6699
  this.db = db;
6677
6700
  this.databaseUrl = db.cloud.options.databaseUrl;
6678
6701
  this.rev = rev;
6679
6702
  this.realmSetHash = realmSetHash;
6680
6703
  this.clientIdentity = clientIdentity;
6681
- this.token = token;
6682
- this.tokenExpiration = tokenExpiration;
6704
+ this.user = user;
6683
6705
  this.subscriber = subscriber;
6684
6706
  this.lastUserActivity = new Date();
6685
6707
  this.messageProducer = messageProducer;
@@ -6739,7 +6761,8 @@ class WSConnection extends Subscription$1 {
6739
6761
  //console.debug('SyncStatus: DUBB: Ooops it was closed!');
6740
6762
  return;
6741
6763
  }
6742
- if (this.tokenExpiration && this.tokenExpiration < new Date()) {
6764
+ const tokenExpiration = this.user.accessTokenExpiration;
6765
+ if (tokenExpiration && tokenExpiration < new Date()) {
6743
6766
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
6744
6767
  return;
6745
6768
  }
@@ -6794,8 +6817,8 @@ class WSConnection extends Subscription$1 {
6794
6817
  searchParams.set('rev', this.rev);
6795
6818
  searchParams.set('realmsHash', this.realmSetHash);
6796
6819
  searchParams.set('clientId', this.clientIdentity);
6797
- if (this.token) {
6798
- searchParams.set('token', this.token);
6820
+ if (this.user.accessToken) {
6821
+ searchParams.set('token', this.user.accessToken);
6799
6822
  }
6800
6823
  // Connect the WebSocket to given url:
6801
6824
  console.debug('dexie-cloud WebSocket create');
@@ -6881,7 +6904,9 @@ class WSConnection extends Subscription$1 {
6881
6904
  }
6882
6905
  }
6883
6906
  }));
6884
- this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
6907
+ if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
6908
+ this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
6909
+ }
6885
6910
  }
6886
6911
  catch (error) {
6887
6912
  this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
@@ -6961,7 +6986,7 @@ function connectWebSocket(db) {
6961
6986
  // If no new entries, server won't bother the client. If new entries, server sends only those
6962
6987
  // and the baseRev of the last from same client-ID.
6963
6988
  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);
6989
+ return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
6965
6990
  }
6966
6991
  else {
6967
6992
  return from$1([]);
@@ -7768,9 +7793,9 @@ const getInvitesObservable = associate((db) => {
7768
7793
  });
7769
7794
 
7770
7795
  function createYHandler(db) {
7771
- const awap = getAwarenessLibrary(db);
7772
7796
  return (provider) => {
7773
7797
  var _a;
7798
+ const awap = getAwarenessLibrary(db);
7774
7799
  const doc = provider.doc;
7775
7800
  const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7776
7801
  if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
@@ -7782,7 +7807,8 @@ function createYHandler(db) {
7782
7807
  awareness.on('update', ({ added, updated, removed }, origin) => {
7783
7808
  // Send the update
7784
7809
  const changedClients = added.concat(updated).concat(removed);
7785
- if (origin !== 'server') {
7810
+ const user = db.cloud.currentUser.value;
7811
+ if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7786
7812
  const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7787
7813
  db.messageProducer.next({
7788
7814
  type: 'aware',
@@ -7823,7 +7849,8 @@ function createYHandler(db) {
7823
7849
  // Keep "connected" state in a variable so we can check it after async operations
7824
7850
  connected = wsStatus === 'connected';
7825
7851
  // We are or got connected. Open the document on the server.
7826
- if (wsStatus === "connected") {
7852
+ const user = db.cloud.currentUser.value;
7853
+ if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7827
7854
  ++currentFlowId;
7828
7855
  openDocumentOnServer().catch(error => {
7829
7856
  console.warn(`Error catched in createYHandler.ts: ${error}`);
@@ -7936,7 +7963,7 @@ function dexieCloud(dexie) {
7936
7963
  const syncComplete = new Subject();
7937
7964
  dexie.cloud = {
7938
7965
  // @ts-ignore
7939
- version: "4.1.0-alpha.2",
7966
+ version: "4.1.0-alpha.5",
7940
7967
  options: Object.assign({}, DEFAULT_OPTIONS),
7941
7968
  schema: null,
7942
7969
  get currentUserId() {
@@ -8238,7 +8265,7 @@ function dexieCloud(dexie) {
8238
8265
  }
8239
8266
  }
8240
8267
  // @ts-ignore
8241
- dexieCloud.version = "4.1.0-alpha.2";
8268
+ dexieCloud.version = "4.1.0-alpha.5";
8242
8269
  Dexie.Cloud = dexieCloud;
8243
8270
 
8244
8271
  export { dexieCloud as default, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };