dexie-cloud-addon 4.0.0-beta.15 → 4.0.0-beta.16

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 (43) hide show
  1. package/dist/modern/dexie-cloud-addon.js +96 -36
  2. package/dist/modern/dexie-cloud-addon.js.map +1 -1
  3. package/dist/modern/dexie-cloud-addon.min.js +1 -1
  4. package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
  5. package/dist/modern/service-worker.js +95 -35
  6. package/dist/modern/service-worker.js.map +1 -1
  7. package/dist/modern/service-worker.min.js +1 -1
  8. package/dist/modern/service-worker.min.js.map +1 -1
  9. package/dist/module-es5/dexie-cloud-addon.js +94 -31
  10. package/dist/module-es5/dexie-cloud-addon.js.map +1 -1
  11. package/dist/module-es5/dexie-cloud-addon.min.js +1 -1
  12. package/dist/module-es5/dexie-cloud-addon.min.js.map +1 -1
  13. package/dist/types/DexieCloudAPI.d.ts +4 -1
  14. package/dist/types/DexieCloudEntity.d.ts +8 -0
  15. package/dist/types/WSObservable.d.ts +1 -0
  16. package/dist/types/WebSocketStatus.d.ts +1 -0
  17. package/dist/types/createMyMembersObservable.d.ts +14 -0
  18. package/dist/types/currentUserObservable.d.ts +3 -0
  19. package/dist/types/getGlobalRolesObservable.d.ts +5 -0
  20. package/dist/types/getInvitesObservable.d.ts +1 -1
  21. package/dist/types/helpers/BroadcastedLocalEvent.d.ts +8 -0
  22. package/dist/types/helpers/visibleState.d.ts +1 -0
  23. package/dist/types/permissionsLookup.d.ts +9 -0
  24. package/dist/types/permissionsLookupObservable.d.ts +14 -0
  25. package/dist/types/sync/globalizePrivateIds.d.ts +4 -0
  26. package/dist/types/sync/syncServerToClientOnly.d.ts +3 -0
  27. package/dist/types/types/CloudConnectionStatus.d.ts +0 -0
  28. package/dist/types/types/ConnectionStatus.d.ts +0 -0
  29. package/dist/types/types/LoginState.d.ts +41 -0
  30. package/dist/types/types/SyncConnectionStatus.d.ts +1 -0
  31. package/dist/types/types/SyncFlowStatus.d.ts +6 -0
  32. package/dist/types/types/SyncStatus.d.ts +6 -0
  33. package/dist/umd/dexie-cloud-addon.js +94 -31
  34. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  35. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  36. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  37. package/dist/umd/service-worker.js +95 -35
  38. package/dist/umd/service-worker.js.map +1 -1
  39. package/dist/umd/service-worker.min.js +1 -1
  40. package/dist/umd/service-worker.min.js.map +1 -1
  41. package/dist/umd-modern/dexie-cloud-addon.js +93 -33
  42. package/dist/umd-modern/dexie-cloud-addon.js.map +1 -1
  43. package/package.json +2 -2
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.0.0-beta.15, Mon Dec 20 2021
11
+ * Version 4.0.0-beta.16, Fri Apr 01 2022
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -4763,7 +4763,7 @@ async function performInitialSync(db, cloudOptions, cloudSchema) {
4763
4763
  console.debug("Done initial sync");
4764
4764
  }
4765
4765
 
4766
- const USER_INACTIVITY_TIMEOUT = 300000; // 300_000;
4766
+ const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
4767
4767
  const INACTIVE_WAIT_TIME = 20000;
4768
4768
  // This observable will be emitted to later down....
4769
4769
  const userIsActive = new BehaviorSubject(true);
@@ -4777,9 +4777,13 @@ const userIsActive = new BehaviorSubject(true);
4777
4777
  // for just a short time.
4778
4778
  const userIsReallyActive = new BehaviorSubject(true);
4779
4779
  userIsActive
4780
- .pipe(switchMap((isActive) => isActive
4781
- ? of(true)
4782
- : of(false).pipe(delay(INACTIVE_WAIT_TIME))), distinctUntilChanged())
4780
+ .pipe(switchMap((isActive) => {
4781
+ //console.debug('SyncStatus: DUBB: isActive changed to', isActive);
4782
+ return isActive
4783
+ ? of(true)
4784
+ : of(false).pipe(delay(INACTIVE_WAIT_TIME))
4785
+ ;
4786
+ }), distinctUntilChanged())
4783
4787
  .subscribe(userIsReallyActive);
4784
4788
  //
4785
4789
  // First create some corner-stone observables to build the flow on
@@ -4794,7 +4798,7 @@ const documentBecomesHidden = visibilityStateIsChanged.pipe(filter(() => documen
4794
4798
  const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
4795
4799
  // Any of various user-activity-related events happen:
4796
4800
  const userDoesSomething = typeof window !== 'undefined'
4797
- ? merge(documentBecomesVisible, fromEvent(window, 'mousemove'), fromEvent(window, 'keydown'), fromEvent(window, 'wheel'), fromEvent(window, 'touchmove'))
4801
+ ? merge(documentBecomesVisible, fromEvent(window, 'mousedown'), fromEvent(window, 'mousemove'), fromEvent(window, 'keydown'), fromEvent(window, 'wheel'), fromEvent(window, 'touchmove'))
4798
4802
  : of({});
4799
4803
  if (typeof document !== 'undefined') {
4800
4804
  //
@@ -4845,6 +4849,7 @@ class WSConnection extends Subscription$1 {
4845
4849
  constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
4846
4850
  super(() => this.teardown());
4847
4851
  this.id = ++counter;
4852
+ this.reconnecting = false;
4848
4853
  console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
4849
4854
  this.databaseUrl = databaseUrl;
4850
4855
  this.rev = rev;
@@ -4864,7 +4869,7 @@ class WSConnection extends Subscription$1 {
4864
4869
  this.disconnect();
4865
4870
  }
4866
4871
  disconnect() {
4867
- this.webSocketStatus.next("disconnected");
4872
+ this.webSocketStatus.next('disconnected');
4868
4873
  if (this.pinger) {
4869
4874
  clearInterval(this.pinger);
4870
4875
  this.pinger = null;
@@ -4882,11 +4887,18 @@ class WSConnection extends Subscription$1 {
4882
4887
  }
4883
4888
  }
4884
4889
  reconnect() {
4885
- this.disconnect();
4886
- this.connect();
4890
+ if (this.reconnecting)
4891
+ return;
4892
+ this.reconnecting = true;
4893
+ try {
4894
+ this.disconnect();
4895
+ }
4896
+ catch { }
4897
+ this.connect()
4898
+ .catch(() => { })
4899
+ .then(() => (this.reconnecting = false)); // finally()
4887
4900
  }
4888
4901
  async connect() {
4889
- this.webSocketStatus.next("connecting");
4890
4902
  this.lastServerActivity = new Date();
4891
4903
  if (this.pauseUntil && this.pauseUntil > new Date()) {
4892
4904
  console.debug('WS not reconnecting just yet', {
@@ -4901,12 +4913,14 @@ class WSConnection extends Subscription$1 {
4901
4913
  if (!this.databaseUrl)
4902
4914
  throw new Error(`Cannot connect without a database URL`);
4903
4915
  if (this.closed) {
4916
+ //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4904
4917
  return;
4905
4918
  }
4906
4919
  if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4907
4920
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4908
4921
  return;
4909
4922
  }
4923
+ this.webSocketStatus.next('connecting');
4910
4924
  this.pinger = setInterval(async () => {
4911
4925
  if (this.closed) {
4912
4926
  console.debug('pinger check', this.id, 'CLOSED.');
@@ -4953,7 +4967,7 @@ class WSConnection extends Subscription$1 {
4953
4967
  const searchParams = new URLSearchParams();
4954
4968
  if (this.subscriber.closed)
4955
4969
  return;
4956
- searchParams.set('v', "2");
4970
+ searchParams.set('v', '2');
4957
4971
  searchParams.set('rev', this.rev);
4958
4972
  searchParams.set('realmsHash', this.realmSetHash);
4959
4973
  searchParams.set('clientId', this.clientIdentity);
@@ -4992,23 +5006,30 @@ class WSConnection extends Subscription$1 {
4992
5006
  }
4993
5007
  };
4994
5008
  try {
5009
+ let everConnected = false;
4995
5010
  await new Promise((resolve, reject) => {
4996
5011
  ws.onopen = (event) => {
4997
5012
  console.debug('dexie-cloud WebSocket onopen');
5013
+ everConnected = true;
4998
5014
  resolve(null);
4999
5015
  };
5000
5016
  ws.onerror = (event) => {
5001
- const error = event.error || new Error('WebSocket Error');
5002
- this.disconnect();
5003
- this.subscriber.error(error);
5004
- this.webSocketStatus.next("error");
5005
- reject(error);
5017
+ if (!everConnected) {
5018
+ const error = event.error || new Error('WebSocket Error');
5019
+ this.subscriber.error(error);
5020
+ this.webSocketStatus.next('error');
5021
+ reject(error);
5022
+ }
5023
+ else {
5024
+ this.reconnect();
5025
+ }
5006
5026
  };
5007
5027
  });
5008
- this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
5028
+ this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
5009
5029
  if (!this.closed) {
5010
- if (msg.type === 'ready' && this.webSocketStatus.value !== 'connected') {
5011
- this.webSocketStatus.next("connected");
5030
+ if (msg.type === 'ready' &&
5031
+ this.webSocketStatus.value !== 'connected') {
5032
+ this.webSocketStatus.next('connected');
5012
5033
  }
5013
5034
  this.ws?.send(TSON.stringify(msg));
5014
5035
  }
@@ -5045,9 +5066,9 @@ function connectWebSocket(db) {
5045
5066
  rev: syncState.serverRevision,
5046
5067
  })));
5047
5068
  function createObservable() {
5048
- return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5069
+ return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5049
5070
  take(1), // Don't continue waking up whenever syncState change
5050
- switchMap((syncState) => db.cloud.currentUser.pipe(map(userLogin => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(async ([userLogin, syncState]) => [userLogin, await computeRealmSetHash(syncState)]), switchMap(([userLogin, realmSetHash]) =>
5071
+ switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(async ([userLogin, syncState]) => [userLogin, await computeRealmSetHash(syncState)]), switchMap(([userLogin, realmSetHash]) =>
5051
5072
  // Let server end query changes from last entry of same client-ID and forward.
5052
5073
  // If no new entries, server won't bother the client. If new entries, server sends only those
5053
5074
  // and the baseRev of the last from same client-ID.
@@ -5070,7 +5091,10 @@ function connectWebSocket(db) {
5070
5091
  else {
5071
5092
  return throwError(error);
5072
5093
  }
5073
- }), catchError((error) => from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()))));
5094
+ }), catchError((error) => {
5095
+ db.cloud.webSocketStatus.next("error");
5096
+ return from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
5097
+ }));
5074
5098
  }
5075
5099
  return createObservable().subscribe((msg) => {
5076
5100
  if (msg) {
@@ -5494,6 +5518,21 @@ function createSharedValueObservable(o, defaultValue) {
5494
5518
  return rv;
5495
5519
  }
5496
5520
 
5521
+ const getGlobalRolesObservable = associate((db) => {
5522
+ return createSharedValueObservable(liveQuery(() => db.roles
5523
+ .where({ realmId: 'rlm-public' })
5524
+ .toArray()
5525
+ .then((roles) => {
5526
+ const rv = {};
5527
+ for (const role of roles
5528
+ .slice()
5529
+ .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
5530
+ rv[role.name] = role;
5531
+ }
5532
+ return rv;
5533
+ })), {});
5534
+ });
5535
+
5497
5536
  const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
5498
5537
 
5499
5538
  const getInternalAccessControlObservable = associate((db) => {
@@ -5595,18 +5634,38 @@ function mergePermissions(...permissions) {
5595
5634
  }
5596
5635
 
5597
5636
  const getPermissionsLookupObservable = associate((db) => {
5598
- const o = getInternalAccessControlObservable(db._novip);
5599
- return mapValueObservable(o, ({ selfMembers, realms, userId }) => {
5637
+ const o = createSharedValueObservable(combineLatest([
5638
+ getInternalAccessControlObservable(db._novip),
5639
+ getGlobalRolesObservable(db._novip),
5640
+ ]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
5641
+ selfMembers,
5642
+ realms,
5643
+ userId,
5644
+ globalRoles,
5645
+ }))), {
5646
+ selfMembers: [],
5647
+ realms: [],
5648
+ userId: UNAUTHORIZED_USER.userId,
5649
+ globalRoles: {},
5650
+ });
5651
+ return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
5600
5652
  const rv = realms
5601
- .map((realm) => ({
5602
- ...realm,
5603
- permissions: realm.owner === userId
5604
- ? { manage: '*' }
5605
- : mergePermissions(...selfMembers
5606
- .filter((m) => m.realmId === realm.realmId)
5607
- .map((m) => m.permissions)
5608
- .filter((p) => p)),
5609
- }))
5653
+ .map((realm) => {
5654
+ const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
5655
+ const directPermissionSets = selfRealmMembers
5656
+ .map((m) => m.permissions)
5657
+ .filter((p) => p);
5658
+ const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
5659
+ .map((role) => globalRoles[role])
5660
+ .filter((role) => role)
5661
+ .map((role) => role.permissions);
5662
+ return {
5663
+ ...realm,
5664
+ permissions: realm.owner === userId
5665
+ ? { manage: '*' }
5666
+ : mergePermissions(...directPermissionSets, ...rolePermissionSets),
5667
+ };
5668
+ })
5610
5669
  .reduce((p, c) => ({ ...p, [c.realmId]: c }), {
5611
5670
  [userId]: {
5612
5671
  realmId: userId,
@@ -5690,7 +5749,7 @@ function permissions(dexie, obj, tableName) {
5690
5749
  const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
5691
5750
  if (!realm)
5692
5751
  return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
5693
- return new PermissionChecker(realm.permissions, tableName, !owner || owner === dexie.cloud.currentUserId);
5752
+ return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
5694
5753
  };
5695
5754
  const o = source.pipe(map(mapper));
5696
5755
  o.getValue = () => mapper(source.getValue());
@@ -5753,7 +5812,7 @@ function dexieCloud(dexie) {
5753
5812
  currentUserEmitter.next(UNAUTHORIZED_USER);
5754
5813
  });
5755
5814
  dexie.cloud = {
5756
- version: '4.0.0-beta.15',
5815
+ version: '4.0.0-beta.16',
5757
5816
  options: { ...DEFAULT_OPTIONS },
5758
5817
  schema: null,
5759
5818
  serverState: null,
@@ -5774,6 +5833,7 @@ function dexieCloud(dexie) {
5774
5833
  await login(db, hint);
5775
5834
  },
5776
5835
  invites: getInvitesObservable(dexie),
5836
+ roles: getGlobalRolesObservable(dexie),
5777
5837
  configure(options) {
5778
5838
  options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
5779
5839
  configuredProgramatically = true;
@@ -5994,7 +6054,7 @@ function dexieCloud(dexie) {
5994
6054
  }
5995
6055
  }
5996
6056
  }
5997
- dexieCloud.version = '4.0.0-beta.15';
6057
+ dexieCloud.version = '4.0.0-beta.16';
5998
6058
  Dexie.Cloud = dexieCloud;
5999
6059
 
6000
6060
  export { dexieCloud as default, dexieCloud, getTiedObjectId, getTiedRealmId };