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
@@ -4770,7 +4770,7 @@
4770
4770
  console.debug("Done initial sync");
4771
4771
  }
4772
4772
 
4773
- const USER_INACTIVITY_TIMEOUT = 300000; // 300_000;
4773
+ const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
4774
4774
  const INACTIVE_WAIT_TIME = 20000;
4775
4775
  // This observable will be emitted to later down....
4776
4776
  const userIsActive = new rxjs.BehaviorSubject(true);
@@ -4784,9 +4784,13 @@
4784
4784
  // for just a short time.
4785
4785
  const userIsReallyActive = new rxjs.BehaviorSubject(true);
4786
4786
  userIsActive
4787
- .pipe(switchMap((isActive) => isActive
4788
- ? rxjs.of(true)
4789
- : rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))), distinctUntilChanged())
4787
+ .pipe(switchMap((isActive) => {
4788
+ //console.debug('SyncStatus: DUBB: isActive changed to', isActive);
4789
+ return isActive
4790
+ ? rxjs.of(true)
4791
+ : rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))
4792
+ ;
4793
+ }), distinctUntilChanged())
4790
4794
  .subscribe(userIsReallyActive);
4791
4795
  //
4792
4796
  // First create some corner-stone observables to build the flow on
@@ -4801,7 +4805,7 @@
4801
4805
  const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
4802
4806
  // Any of various user-activity-related events happen:
4803
4807
  const userDoesSomething = typeof window !== 'undefined'
4804
- ? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
4808
+ ? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousedown'), rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
4805
4809
  : rxjs.of({});
4806
4810
  if (typeof document !== 'undefined') {
4807
4811
  //
@@ -4852,6 +4856,7 @@
4852
4856
  constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
4853
4857
  super(() => this.teardown());
4854
4858
  this.id = ++counter;
4859
+ this.reconnecting = false;
4855
4860
  console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
4856
4861
  this.databaseUrl = databaseUrl;
4857
4862
  this.rev = rev;
@@ -4871,7 +4876,7 @@
4871
4876
  this.disconnect();
4872
4877
  }
4873
4878
  disconnect() {
4874
- this.webSocketStatus.next("disconnected");
4879
+ this.webSocketStatus.next('disconnected');
4875
4880
  if (this.pinger) {
4876
4881
  clearInterval(this.pinger);
4877
4882
  this.pinger = null;
@@ -4889,11 +4894,18 @@
4889
4894
  }
4890
4895
  }
4891
4896
  reconnect() {
4892
- this.disconnect();
4893
- this.connect();
4897
+ if (this.reconnecting)
4898
+ return;
4899
+ this.reconnecting = true;
4900
+ try {
4901
+ this.disconnect();
4902
+ }
4903
+ catch { }
4904
+ this.connect()
4905
+ .catch(() => { })
4906
+ .then(() => (this.reconnecting = false)); // finally()
4894
4907
  }
4895
4908
  async connect() {
4896
- this.webSocketStatus.next("connecting");
4897
4909
  this.lastServerActivity = new Date();
4898
4910
  if (this.pauseUntil && this.pauseUntil > new Date()) {
4899
4911
  console.debug('WS not reconnecting just yet', {
@@ -4908,12 +4920,14 @@
4908
4920
  if (!this.databaseUrl)
4909
4921
  throw new Error(`Cannot connect without a database URL`);
4910
4922
  if (this.closed) {
4923
+ //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4911
4924
  return;
4912
4925
  }
4913
4926
  if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4914
4927
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4915
4928
  return;
4916
4929
  }
4930
+ this.webSocketStatus.next('connecting');
4917
4931
  this.pinger = setInterval(async () => {
4918
4932
  if (this.closed) {
4919
4933
  console.debug('pinger check', this.id, 'CLOSED.');
@@ -4960,7 +4974,7 @@
4960
4974
  const searchParams = new URLSearchParams();
4961
4975
  if (this.subscriber.closed)
4962
4976
  return;
4963
- searchParams.set('v', "2");
4977
+ searchParams.set('v', '2');
4964
4978
  searchParams.set('rev', this.rev);
4965
4979
  searchParams.set('realmsHash', this.realmSetHash);
4966
4980
  searchParams.set('clientId', this.clientIdentity);
@@ -4999,23 +5013,30 @@
4999
5013
  }
5000
5014
  };
5001
5015
  try {
5016
+ let everConnected = false;
5002
5017
  await new Promise((resolve, reject) => {
5003
5018
  ws.onopen = (event) => {
5004
5019
  console.debug('dexie-cloud WebSocket onopen');
5020
+ everConnected = true;
5005
5021
  resolve(null);
5006
5022
  };
5007
5023
  ws.onerror = (event) => {
5008
- const error = event.error || new Error('WebSocket Error');
5009
- this.disconnect();
5010
- this.subscriber.error(error);
5011
- this.webSocketStatus.next("error");
5012
- reject(error);
5024
+ if (!everConnected) {
5025
+ const error = event.error || new Error('WebSocket Error');
5026
+ this.subscriber.error(error);
5027
+ this.webSocketStatus.next('error');
5028
+ reject(error);
5029
+ }
5030
+ else {
5031
+ this.reconnect();
5032
+ }
5013
5033
  };
5014
5034
  });
5015
- this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
5035
+ this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
5016
5036
  if (!this.closed) {
5017
- if (msg.type === 'ready' && this.webSocketStatus.value !== 'connected') {
5018
- this.webSocketStatus.next("connected");
5037
+ if (msg.type === 'ready' &&
5038
+ this.webSocketStatus.value !== 'connected') {
5039
+ this.webSocketStatus.next('connected');
5019
5040
  }
5020
5041
  this.ws?.send(TSON.stringify(msg));
5021
5042
  }
@@ -5052,9 +5073,9 @@
5052
5073
  rev: syncState.serverRevision,
5053
5074
  })));
5054
5075
  function createObservable() {
5055
- return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5076
+ return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5056
5077
  take(1), // Don't continue waking up whenever syncState change
5057
- 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]) =>
5078
+ 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]) =>
5058
5079
  // Let server end query changes from last entry of same client-ID and forward.
5059
5080
  // If no new entries, server won't bother the client. If new entries, server sends only those
5060
5081
  // and the baseRev of the last from same client-ID.
@@ -5077,7 +5098,10 @@
5077
5098
  else {
5078
5099
  return rxjs.throwError(error);
5079
5100
  }
5080
- }), catchError((error) => rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()))));
5101
+ }), catchError((error) => {
5102
+ db.cloud.webSocketStatus.next("error");
5103
+ return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
5104
+ }));
5081
5105
  }
5082
5106
  return createObservable().subscribe((msg) => {
5083
5107
  if (msg) {
@@ -5501,6 +5525,21 @@
5501
5525
  return rv;
5502
5526
  }
5503
5527
 
5528
+ const getGlobalRolesObservable = associate((db) => {
5529
+ return createSharedValueObservable(Dexie.liveQuery(() => db.roles
5530
+ .where({ realmId: 'rlm-public' })
5531
+ .toArray()
5532
+ .then((roles) => {
5533
+ const rv = {};
5534
+ for (const role of roles
5535
+ .slice()
5536
+ .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
5537
+ rv[role.name] = role;
5538
+ }
5539
+ return rv;
5540
+ })), {});
5541
+ });
5542
+
5504
5543
  const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
5505
5544
 
5506
5545
  const getInternalAccessControlObservable = associate((db) => {
@@ -5602,18 +5641,38 @@
5602
5641
  }
5603
5642
 
5604
5643
  const getPermissionsLookupObservable = associate((db) => {
5605
- const o = getInternalAccessControlObservable(db._novip);
5606
- return mapValueObservable(o, ({ selfMembers, realms, userId }) => {
5644
+ const o = createSharedValueObservable(rxjs.combineLatest([
5645
+ getInternalAccessControlObservable(db._novip),
5646
+ getGlobalRolesObservable(db._novip),
5647
+ ]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
5648
+ selfMembers,
5649
+ realms,
5650
+ userId,
5651
+ globalRoles,
5652
+ }))), {
5653
+ selfMembers: [],
5654
+ realms: [],
5655
+ userId: UNAUTHORIZED_USER.userId,
5656
+ globalRoles: {},
5657
+ });
5658
+ return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
5607
5659
  const rv = realms
5608
- .map((realm) => ({
5609
- ...realm,
5610
- permissions: realm.owner === userId
5611
- ? { manage: '*' }
5612
- : mergePermissions(...selfMembers
5613
- .filter((m) => m.realmId === realm.realmId)
5614
- .map((m) => m.permissions)
5615
- .filter((p) => p)),
5616
- }))
5660
+ .map((realm) => {
5661
+ const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
5662
+ const directPermissionSets = selfRealmMembers
5663
+ .map((m) => m.permissions)
5664
+ .filter((p) => p);
5665
+ const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
5666
+ .map((role) => globalRoles[role])
5667
+ .filter((role) => role)
5668
+ .map((role) => role.permissions);
5669
+ return {
5670
+ ...realm,
5671
+ permissions: realm.owner === userId
5672
+ ? { manage: '*' }
5673
+ : mergePermissions(...directPermissionSets, ...rolePermissionSets),
5674
+ };
5675
+ })
5617
5676
  .reduce((p, c) => ({ ...p, [c.realmId]: c }), {
5618
5677
  [userId]: {
5619
5678
  realmId: userId,
@@ -5697,7 +5756,7 @@
5697
5756
  const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
5698
5757
  if (!realm)
5699
5758
  return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
5700
- return new PermissionChecker(realm.permissions, tableName, !owner || owner === dexie.cloud.currentUserId);
5759
+ return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
5701
5760
  };
5702
5761
  const o = source.pipe(map(mapper));
5703
5762
  o.getValue = () => mapper(source.getValue());
@@ -5781,6 +5840,7 @@
5781
5840
  await login(db, hint);
5782
5841
  },
5783
5842
  invites: getInvitesObservable(dexie),
5843
+ roles: getGlobalRolesObservable(dexie),
5784
5844
  configure(options) {
5785
5845
  options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
5786
5846
  configuredProgramatically = true;