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
@@ -4752,7 +4752,7 @@
4752
4752
  console.debug("Done initial sync");
4753
4753
  }
4754
4754
 
4755
- const USER_INACTIVITY_TIMEOUT = 300000; // 300_000;
4755
+ const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
4756
4756
  const INACTIVE_WAIT_TIME = 20000;
4757
4757
  // This observable will be emitted to later down....
4758
4758
  const userIsActive = new rxjs.BehaviorSubject(true);
@@ -4766,9 +4766,13 @@
4766
4766
  // for just a short time.
4767
4767
  const userIsReallyActive = new rxjs.BehaviorSubject(true);
4768
4768
  userIsActive
4769
- .pipe(switchMap((isActive) => isActive
4770
- ? rxjs.of(true)
4771
- : rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))), distinctUntilChanged())
4769
+ .pipe(switchMap((isActive) => {
4770
+ //console.debug('SyncStatus: DUBB: isActive changed to', isActive);
4771
+ return isActive
4772
+ ? rxjs.of(true)
4773
+ : rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))
4774
+ ;
4775
+ }), distinctUntilChanged())
4772
4776
  .subscribe(userIsReallyActive);
4773
4777
  //
4774
4778
  // First create some corner-stone observables to build the flow on
@@ -4783,7 +4787,7 @@
4783
4787
  const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
4784
4788
  // Any of various user-activity-related events happen:
4785
4789
  const userDoesSomething = typeof window !== 'undefined'
4786
- ? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
4790
+ ? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousedown'), rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
4787
4791
  : rxjs.of({});
4788
4792
  if (typeof document !== 'undefined') {
4789
4793
  //
@@ -4834,6 +4838,7 @@
4834
4838
  constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
4835
4839
  super(() => this.teardown());
4836
4840
  this.id = ++counter;
4841
+ this.reconnecting = false;
4837
4842
  console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
4838
4843
  this.databaseUrl = databaseUrl;
4839
4844
  this.rev = rev;
@@ -4853,7 +4858,7 @@
4853
4858
  this.disconnect();
4854
4859
  }
4855
4860
  disconnect() {
4856
- this.webSocketStatus.next("disconnected");
4861
+ this.webSocketStatus.next('disconnected');
4857
4862
  if (this.pinger) {
4858
4863
  clearInterval(this.pinger);
4859
4864
  this.pinger = null;
@@ -4871,11 +4876,18 @@
4871
4876
  }
4872
4877
  }
4873
4878
  reconnect() {
4874
- this.disconnect();
4875
- this.connect();
4879
+ if (this.reconnecting)
4880
+ return;
4881
+ this.reconnecting = true;
4882
+ try {
4883
+ this.disconnect();
4884
+ }
4885
+ catch { }
4886
+ this.connect()
4887
+ .catch(() => { })
4888
+ .then(() => (this.reconnecting = false)); // finally()
4876
4889
  }
4877
4890
  async connect() {
4878
- this.webSocketStatus.next("connecting");
4879
4891
  this.lastServerActivity = new Date();
4880
4892
  if (this.pauseUntil && this.pauseUntil > new Date()) {
4881
4893
  console.debug('WS not reconnecting just yet', {
@@ -4890,12 +4902,14 @@
4890
4902
  if (!this.databaseUrl)
4891
4903
  throw new Error(`Cannot connect without a database URL`);
4892
4904
  if (this.closed) {
4905
+ //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4893
4906
  return;
4894
4907
  }
4895
4908
  if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4896
4909
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4897
4910
  return;
4898
4911
  }
4912
+ this.webSocketStatus.next('connecting');
4899
4913
  this.pinger = setInterval(async () => {
4900
4914
  if (this.closed) {
4901
4915
  console.debug('pinger check', this.id, 'CLOSED.');
@@ -4942,7 +4956,7 @@
4942
4956
  const searchParams = new URLSearchParams();
4943
4957
  if (this.subscriber.closed)
4944
4958
  return;
4945
- searchParams.set('v', "2");
4959
+ searchParams.set('v', '2');
4946
4960
  searchParams.set('rev', this.rev);
4947
4961
  searchParams.set('realmsHash', this.realmSetHash);
4948
4962
  searchParams.set('clientId', this.clientIdentity);
@@ -4981,23 +4995,30 @@
4981
4995
  }
4982
4996
  };
4983
4997
  try {
4998
+ let everConnected = false;
4984
4999
  await new Promise((resolve, reject) => {
4985
5000
  ws.onopen = (event) => {
4986
5001
  console.debug('dexie-cloud WebSocket onopen');
5002
+ everConnected = true;
4987
5003
  resolve(null);
4988
5004
  };
4989
5005
  ws.onerror = (event) => {
4990
- const error = event.error || new Error('WebSocket Error');
4991
- this.disconnect();
4992
- this.subscriber.error(error);
4993
- this.webSocketStatus.next("error");
4994
- reject(error);
5006
+ if (!everConnected) {
5007
+ const error = event.error || new Error('WebSocket Error');
5008
+ this.subscriber.error(error);
5009
+ this.webSocketStatus.next('error');
5010
+ reject(error);
5011
+ }
5012
+ else {
5013
+ this.reconnect();
5014
+ }
4995
5015
  };
4996
5016
  });
4997
- this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
5017
+ this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
4998
5018
  if (!this.closed) {
4999
- if (msg.type === 'ready' && this.webSocketStatus.value !== 'connected') {
5000
- this.webSocketStatus.next("connected");
5019
+ if (msg.type === 'ready' &&
5020
+ this.webSocketStatus.value !== 'connected') {
5021
+ this.webSocketStatus.next('connected');
5001
5022
  }
5002
5023
  this.ws?.send(TSON.stringify(msg));
5003
5024
  }
@@ -5034,9 +5055,9 @@
5034
5055
  rev: syncState.serverRevision,
5035
5056
  })));
5036
5057
  function createObservable() {
5037
- return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5058
+ return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5038
5059
  take(1), // Don't continue waking up whenever syncState change
5039
- 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]) =>
5060
+ 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]) =>
5040
5061
  // Let server end query changes from last entry of same client-ID and forward.
5041
5062
  // If no new entries, server won't bother the client. If new entries, server sends only those
5042
5063
  // and the baseRev of the last from same client-ID.
@@ -5059,7 +5080,10 @@
5059
5080
  else {
5060
5081
  return rxjs.throwError(error);
5061
5082
  }
5062
- }), catchError((error) => rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()))));
5083
+ }), catchError((error) => {
5084
+ db.cloud.webSocketStatus.next("error");
5085
+ return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
5086
+ }));
5063
5087
  }
5064
5088
  return createObservable().subscribe((msg) => {
5065
5089
  if (msg) {
@@ -5483,6 +5507,21 @@
5483
5507
  return rv;
5484
5508
  }
5485
5509
 
5510
+ const getGlobalRolesObservable = associate((db) => {
5511
+ return createSharedValueObservable(Dexie.liveQuery(() => db.roles
5512
+ .where({ realmId: 'rlm-public' })
5513
+ .toArray()
5514
+ .then((roles) => {
5515
+ const rv = {};
5516
+ for (const role of roles
5517
+ .slice()
5518
+ .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
5519
+ rv[role.name] = role;
5520
+ }
5521
+ return rv;
5522
+ })), {});
5523
+ });
5524
+
5486
5525
  const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
5487
5526
 
5488
5527
  const getInternalAccessControlObservable = associate((db) => {
@@ -5584,18 +5623,38 @@
5584
5623
  }
5585
5624
 
5586
5625
  const getPermissionsLookupObservable = associate((db) => {
5587
- const o = getInternalAccessControlObservable(db._novip);
5588
- return mapValueObservable(o, ({ selfMembers, realms, userId }) => {
5626
+ const o = createSharedValueObservable(rxjs.combineLatest([
5627
+ getInternalAccessControlObservable(db._novip),
5628
+ getGlobalRolesObservable(db._novip),
5629
+ ]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
5630
+ selfMembers,
5631
+ realms,
5632
+ userId,
5633
+ globalRoles,
5634
+ }))), {
5635
+ selfMembers: [],
5636
+ realms: [],
5637
+ userId: UNAUTHORIZED_USER.userId,
5638
+ globalRoles: {},
5639
+ });
5640
+ return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
5589
5641
  const rv = realms
5590
- .map((realm) => ({
5591
- ...realm,
5592
- permissions: realm.owner === userId
5593
- ? { manage: '*' }
5594
- : mergePermissions(...selfMembers
5595
- .filter((m) => m.realmId === realm.realmId)
5596
- .map((m) => m.permissions)
5597
- .filter((p) => p)),
5598
- }))
5642
+ .map((realm) => {
5643
+ const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
5644
+ const directPermissionSets = selfRealmMembers
5645
+ .map((m) => m.permissions)
5646
+ .filter((p) => p);
5647
+ const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
5648
+ .map((role) => globalRoles[role])
5649
+ .filter((role) => role)
5650
+ .map((role) => role.permissions);
5651
+ return {
5652
+ ...realm,
5653
+ permissions: realm.owner === userId
5654
+ ? { manage: '*' }
5655
+ : mergePermissions(...directPermissionSets, ...rolePermissionSets),
5656
+ };
5657
+ })
5599
5658
  .reduce((p, c) => ({ ...p, [c.realmId]: c }), {
5600
5659
  [userId]: {
5601
5660
  realmId: userId,
@@ -5679,7 +5738,7 @@
5679
5738
  const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
5680
5739
  if (!realm)
5681
5740
  return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
5682
- return new PermissionChecker(realm.permissions, tableName, !owner || owner === dexie.cloud.currentUserId);
5741
+ return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
5683
5742
  };
5684
5743
  const o = source.pipe(map(mapper));
5685
5744
  o.getValue = () => mapper(source.getValue());
@@ -5735,7 +5794,7 @@
5735
5794
  currentUserEmitter.next(UNAUTHORIZED_USER);
5736
5795
  });
5737
5796
  dexie.cloud = {
5738
- version: '4.0.0-beta.15',
5797
+ version: '4.0.0-beta.16',
5739
5798
  options: { ...DEFAULT_OPTIONS },
5740
5799
  schema: null,
5741
5800
  serverState: null,
@@ -5756,6 +5815,7 @@
5756
5815
  await login(db, hint);
5757
5816
  },
5758
5817
  invites: getInvitesObservable(dexie),
5818
+ roles: getGlobalRolesObservable(dexie),
5759
5819
  configure(options) {
5760
5820
  options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
5761
5821
  configuredProgramatically = true;
@@ -5976,7 +6036,7 @@
5976
6036
  }
5977
6037
  }
5978
6038
  }
5979
- dexieCloud.version = '4.0.0-beta.15';
6039
+ dexieCloud.version = '4.0.0-beta.16';
5980
6040
  Dexie__default["default"].Cloud = dexieCloud;
5981
6041
 
5982
6042
  // In case the SW lives for a while, let it reuse already opened connections: