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.
@@ -1,5 +1,7 @@
1
+ import type { Table } from 'dexie';
1
2
  import type { YClientMessage } from 'dexie-cloud-common';
2
3
  import { DexieCloudDB } from '../db/DexieCloudDB';
4
+ import { EntityCommon } from '../db/entities/EntityCommon';
3
5
  /** Queries the local database for YMessages to send to server.
4
6
  *
5
7
  * There are 2 messages that this function can provide:
@@ -16,7 +18,7 @@ import { DexieCloudDB } from '../db/DexieCloudDB';
16
18
  * @param db
17
19
  * @returns
18
20
  */
19
- export declare function listYClientMessagesAndStateVector(db: DexieCloudDB): Promise<{
21
+ export declare function listYClientMessagesAndStateVector(db: DexieCloudDB, tablesToSync: Table<EntityCommon>[]): Promise<{
20
22
  yMessages: YClientMessage[];
21
23
  lastUpdateIds: {
22
24
  [yTable: string]: number;
@@ -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
  *
@@ -4970,13 +4970,12 @@
4970
4970
  * @param db
4971
4971
  * @returns
4972
4972
  */
4973
- function listYClientMessagesAndStateVector(db) {
4974
- var _a;
4973
+ function listYClientMessagesAndStateVector(db, tablesToSync) {
4975
4974
  return __awaiter(this, void 0, void 0, function* () {
4976
4975
  const result = [];
4977
4976
  const lastUpdateIds = {};
4978
- for (const table of db.tables) {
4979
- if (table.schema.yProps && ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync)) {
4977
+ for (const table of tablesToSync) {
4978
+ if (table.schema.yProps) {
4980
4979
  for (const yProp of table.schema.yProps) {
4981
4980
  const Y = $Y(db); // This is how we retrieve the user-provided Y library
4982
4981
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
@@ -5110,7 +5109,7 @@
5110
5109
  }
5111
5110
 
5112
5111
  function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
5113
- var _a, _b;
5112
+ var _a, _b, _c, _d, _e;
5114
5113
  return __awaiter(this, void 0, void 0, function* () {
5115
5114
  // We want to update unsentFrom for each yTable to the value specified in first argument
5116
5115
  // because we got those values before we synced with server and here we are back from server
@@ -5118,7 +5117,7 @@
5118
5117
  // we can safely store unsentFrom to a value of the last update + 1 here.
5119
5118
  // We also want to update receivedUntil for each yTable to the value specified in the second argument,
5120
5119
  // because that contains the highest resulted id of each update from server after storing it.
5121
- // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
5120
+ // We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
5122
5121
  // to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
5123
5122
  // per yTable.
5124
5123
  const mergedSpec = {};
@@ -5130,28 +5129,42 @@
5130
5129
  (_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
5131
5130
  mergedSpec[yTable].receivedUntil = lastUpdateId;
5132
5131
  }
5133
- // Now go through the merged map and update YSyncStates accordingly:
5134
- for (const [yTable, { unsentFrom, receivedUntil }] of Object.entries(mergedSpec)) {
5132
+ // Now go through all yTables and update their YSyncStates:
5133
+ const allYTables = Object.values(db.dx._dbSchema)
5134
+ .filter((tblSchema) => tblSchema.yProps)
5135
+ .map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
5136
+ .flat();
5137
+ for (const yTable of allYTables) {
5138
+ const mergedEntry = mergedSpec[yTable];
5139
+ const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
5140
+ const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
5141
+ // from local because we are in the same parent transaction (in sync.ts) that
5142
+ // applied all updates from the server
5143
+ (yield db
5144
+ .table(yTable)
5145
+ .where('i')
5146
+ .between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
5147
+ .reverse()
5148
+ .limit(1)
5149
+ .primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
5135
5150
  // We're already in a transaction, but for the sake of
5136
5151
  // code readability and correctness, let's launch an atomic sub transaction:
5137
5152
  yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
5138
- const state = yield db.table(yTable).get(DEXIE_CLOUD_SYNCER_ID);
5153
+ const state = yield db
5154
+ .table(yTable)
5155
+ .get(DEXIE_CLOUD_SYNCER_ID);
5139
5156
  if (!state) {
5140
5157
  yield db.table(yTable).add({
5141
5158
  i: DEXIE_CLOUD_SYNCER_ID,
5142
- unsentFrom: unsentFrom || 1,
5143
- receivedUntil: receivedUntil || 0,
5159
+ unsentFrom,
5160
+ receivedUntil,
5144
5161
  serverRev: serverRevision,
5145
5162
  });
5146
5163
  }
5147
5164
  else {
5148
- if (unsentFrom) {
5149
- state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
5150
- }
5151
- if (receivedUntil) {
5152
- state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
5153
- state.serverRev = serverRevision;
5154
- }
5165
+ state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
5166
+ state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
5167
+ state.serverRev = serverRevision;
5155
5168
  yield db.table(yTable).put(state);
5156
5169
  }
5157
5170
  }));
@@ -5164,7 +5177,9 @@
5164
5177
  const BINSTREAM_TYPE_DOCUMENT = 3;
5165
5178
  function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms }) {
5166
5179
  return __awaiter(this, void 0, void 0, function* () {
5167
- if (yDownloadedRealms && realms && realms.every(realmId => yDownloadedRealms[realmId] === '*')) {
5180
+ if (yDownloadedRealms &&
5181
+ realms &&
5182
+ realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
5168
5183
  return; // Already done!
5169
5184
  }
5170
5185
  console.debug('Downloading Y.Docs from added realms');
@@ -5204,16 +5219,19 @@
5204
5219
  yield yTable.bulkAdd(docsToInsert);
5205
5220
  docsToInsert = [];
5206
5221
  }
5207
- if (currentRealmId && currentTable && currentProp && (lastDoc || completedRealm)) {
5208
- yield db.$syncState.update('syncState', completedRealm
5209
- ? '*'
5210
- : {
5211
- [`yDownloadedRealms.${currentRealmId}`]: {
5222
+ if (currentRealmId &&
5223
+ ((currentTable && currentProp && lastDoc) || completedRealm)) {
5224
+ yield db.$syncState.update('syncState', (syncState) => {
5225
+ const yDownloadedRealms = syncState.yDownloadedRealms || {};
5226
+ yDownloadedRealms[currentRealmId] = completedRealm
5227
+ ? '*'
5228
+ : {
5212
5229
  tbl: currentTable,
5213
5230
  prop: currentProp,
5214
5231
  key: lastDoc.k,
5215
- },
5216
- });
5232
+ };
5233
+ syncState.yDownloadedRealms = yDownloadedRealms;
5234
+ });
5217
5235
  }
5218
5236
  });
5219
5237
  }
@@ -5363,7 +5381,7 @@
5363
5381
  const syncState = yield db.getPersistedSyncState();
5364
5382
  const baseRevs = yield db.$baseRevs.toArray();
5365
5383
  let clientChanges = yield listClientChanges(mutationTables);
5366
- const yResults = yield listYClientMessagesAndStateVector(db);
5384
+ const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
5367
5385
  throwIfCancelled(cancelToken);
5368
5386
  if (doSyncify) {
5369
5387
  const alreadySyncedRealms = [
@@ -6616,7 +6634,7 @@
6616
6634
 
6617
6635
  function createYClientUpdateObservable(db) {
6618
6636
  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; })
6637
+ .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
6638
  .map((table) => table.schema.yProps.map((p) => ({
6621
6639
  table: table.name,
6622
6640
  ydocProp: p.prop,
@@ -6647,7 +6665,12 @@
6647
6665
  };
6648
6666
  });
6649
6667
  }));
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.
6668
+ })).pipe(
6669
+ // Flatten the array of messages.
6670
+ // If messageProducer emits empty array, nothing is emitted
6671
+ // but if messageProducer emits array of messages, they are
6672
+ // emitted one by one.
6673
+ rxjs.mergeMap((messages) => messages));
6651
6674
  }
6652
6675
 
6653
6676
  function getAwarenessLibrary(db) {
@@ -6664,25 +6687,24 @@
6664
6687
  const CLIENT_PING_INTERVAL = 30000;
6665
6688
  const FAIL_RETRY_WAIT_TIME = 60000;
6666
6689
  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));
6690
+ constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
6691
+ super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
6669
6692
  }
6670
6693
  }
6671
6694
  let counter = 0;
6672
6695
  class WSConnection extends rxjs.Subscription {
6673
- constructor(db, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
6696
+ constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
6674
6697
  super(() => this.teardown());
6675
6698
  this.id = ++counter;
6676
6699
  this.subscriptions = new Set();
6677
6700
  this.reconnecting = false;
6678
- console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
6701
+ console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
6679
6702
  this.db = db;
6680
6703
  this.databaseUrl = db.cloud.options.databaseUrl;
6681
6704
  this.rev = rev;
6682
6705
  this.realmSetHash = realmSetHash;
6683
6706
  this.clientIdentity = clientIdentity;
6684
- this.token = token;
6685
- this.tokenExpiration = tokenExpiration;
6707
+ this.user = user;
6686
6708
  this.subscriber = subscriber;
6687
6709
  this.lastUserActivity = new Date();
6688
6710
  this.messageProducer = messageProducer;
@@ -6742,7 +6764,8 @@
6742
6764
  //console.debug('SyncStatus: DUBB: Ooops it was closed!');
6743
6765
  return;
6744
6766
  }
6745
- if (this.tokenExpiration && this.tokenExpiration < new Date()) {
6767
+ const tokenExpiration = this.user.accessTokenExpiration;
6768
+ if (tokenExpiration && tokenExpiration < new Date()) {
6746
6769
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
6747
6770
  return;
6748
6771
  }
@@ -6797,8 +6820,8 @@
6797
6820
  searchParams.set('rev', this.rev);
6798
6821
  searchParams.set('realmsHash', this.realmSetHash);
6799
6822
  searchParams.set('clientId', this.clientIdentity);
6800
- if (this.token) {
6801
- searchParams.set('token', this.token);
6823
+ if (this.user.accessToken) {
6824
+ searchParams.set('token', this.user.accessToken);
6802
6825
  }
6803
6826
  // Connect the WebSocket to given url:
6804
6827
  console.debug('dexie-cloud WebSocket create');
@@ -6884,7 +6907,9 @@
6884
6907
  }
6885
6908
  }
6886
6909
  }));
6887
- this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
6910
+ if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
6911
+ this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
6912
+ }
6888
6913
  }
6889
6914
  catch (error) {
6890
6915
  this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
@@ -6964,7 +6989,7 @@
6964
6989
  // If no new entries, server won't bother the client. If new entries, server sends only those
6965
6990
  // and the baseRev of the last from same client-ID.
6966
6991
  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);
6992
+ return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
6968
6993
  }
6969
6994
  else {
6970
6995
  return rxjs.from([]);
@@ -7771,9 +7796,9 @@
7771
7796
  });
7772
7797
 
7773
7798
  function createYHandler(db) {
7774
- const awap = getAwarenessLibrary(db);
7775
7799
  return (provider) => {
7776
7800
  var _a;
7801
+ const awap = getAwarenessLibrary(db);
7777
7802
  const doc = provider.doc;
7778
7803
  const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
7779
7804
  if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
@@ -7785,7 +7810,8 @@
7785
7810
  awareness.on('update', ({ added, updated, removed }, origin) => {
7786
7811
  // Send the update
7787
7812
  const changedClients = added.concat(updated).concat(removed);
7788
- if (origin !== 'server') {
7813
+ const user = db.cloud.currentUser.value;
7814
+ if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7789
7815
  const update = awap.encodeAwarenessUpdate(awareness, changedClients);
7790
7816
  db.messageProducer.next({
7791
7817
  type: 'aware',
@@ -7826,7 +7852,8 @@
7826
7852
  // Keep "connected" state in a variable so we can check it after async operations
7827
7853
  connected = wsStatus === 'connected';
7828
7854
  // We are or got connected. Open the document on the server.
7829
- if (wsStatus === "connected") {
7855
+ const user = db.cloud.currentUser.value;
7856
+ if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
7830
7857
  ++currentFlowId;
7831
7858
  openDocumentOnServer().catch(error => {
7832
7859
  console.warn(`Error catched in createYHandler.ts: ${error}`);
@@ -7939,7 +7966,7 @@
7939
7966
  const syncComplete = new rxjs.Subject();
7940
7967
  dexie.cloud = {
7941
7968
  // @ts-ignore
7942
- version: "4.1.0-alpha.2",
7969
+ version: "4.1.0-alpha.5",
7943
7970
  options: Object.assign({}, DEFAULT_OPTIONS),
7944
7971
  schema: null,
7945
7972
  get currentUserId() {
@@ -8241,7 +8268,7 @@
8241
8268
  }
8242
8269
  }
8243
8270
  // @ts-ignore
8244
- dexieCloud.version = "4.1.0-alpha.2";
8271
+ dexieCloud.version = "4.1.0-alpha.5";
8245
8272
  Dexie.Cloud = dexieCloud;
8246
8273
 
8247
8274
  exports.default = dexieCloud;