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
@@ -4745,7 +4745,7 @@ async function performInitialSync(db, cloudOptions, cloudSchema) {
4745
4745
  console.debug("Done initial sync");
4746
4746
  }
4747
4747
 
4748
- const USER_INACTIVITY_TIMEOUT = 300000; // 300_000;
4748
+ const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
4749
4749
  const INACTIVE_WAIT_TIME = 20000;
4750
4750
  // This observable will be emitted to later down....
4751
4751
  const userIsActive = new BehaviorSubject(true);
@@ -4759,9 +4759,13 @@ const userIsActive = new BehaviorSubject(true);
4759
4759
  // for just a short time.
4760
4760
  const userIsReallyActive = new BehaviorSubject(true);
4761
4761
  userIsActive
4762
- .pipe(switchMap((isActive) => isActive
4763
- ? of(true)
4764
- : of(false).pipe(delay(INACTIVE_WAIT_TIME))), distinctUntilChanged())
4762
+ .pipe(switchMap((isActive) => {
4763
+ //console.debug('SyncStatus: DUBB: isActive changed to', isActive);
4764
+ return isActive
4765
+ ? of(true)
4766
+ : of(false).pipe(delay(INACTIVE_WAIT_TIME))
4767
+ ;
4768
+ }), distinctUntilChanged())
4765
4769
  .subscribe(userIsReallyActive);
4766
4770
  //
4767
4771
  // First create some corner-stone observables to build the flow on
@@ -4776,7 +4780,7 @@ const documentBecomesHidden = visibilityStateIsChanged.pipe(filter(() => documen
4776
4780
  const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
4777
4781
  // Any of various user-activity-related events happen:
4778
4782
  const userDoesSomething = typeof window !== 'undefined'
4779
- ? merge(documentBecomesVisible, fromEvent(window, 'mousemove'), fromEvent(window, 'keydown'), fromEvent(window, 'wheel'), fromEvent(window, 'touchmove'))
4783
+ ? merge(documentBecomesVisible, fromEvent(window, 'mousedown'), fromEvent(window, 'mousemove'), fromEvent(window, 'keydown'), fromEvent(window, 'wheel'), fromEvent(window, 'touchmove'))
4780
4784
  : of({});
4781
4785
  if (typeof document !== 'undefined') {
4782
4786
  //
@@ -4827,6 +4831,7 @@ class WSConnection extends Subscription$1 {
4827
4831
  constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
4828
4832
  super(() => this.teardown());
4829
4833
  this.id = ++counter;
4834
+ this.reconnecting = false;
4830
4835
  console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
4831
4836
  this.databaseUrl = databaseUrl;
4832
4837
  this.rev = rev;
@@ -4846,7 +4851,7 @@ class WSConnection extends Subscription$1 {
4846
4851
  this.disconnect();
4847
4852
  }
4848
4853
  disconnect() {
4849
- this.webSocketStatus.next("disconnected");
4854
+ this.webSocketStatus.next('disconnected');
4850
4855
  if (this.pinger) {
4851
4856
  clearInterval(this.pinger);
4852
4857
  this.pinger = null;
@@ -4864,11 +4869,18 @@ class WSConnection extends Subscription$1 {
4864
4869
  }
4865
4870
  }
4866
4871
  reconnect() {
4867
- this.disconnect();
4868
- this.connect();
4872
+ if (this.reconnecting)
4873
+ return;
4874
+ this.reconnecting = true;
4875
+ try {
4876
+ this.disconnect();
4877
+ }
4878
+ catch { }
4879
+ this.connect()
4880
+ .catch(() => { })
4881
+ .then(() => (this.reconnecting = false)); // finally()
4869
4882
  }
4870
4883
  async connect() {
4871
- this.webSocketStatus.next("connecting");
4872
4884
  this.lastServerActivity = new Date();
4873
4885
  if (this.pauseUntil && this.pauseUntil > new Date()) {
4874
4886
  console.debug('WS not reconnecting just yet', {
@@ -4883,12 +4895,14 @@ class WSConnection extends Subscription$1 {
4883
4895
  if (!this.databaseUrl)
4884
4896
  throw new Error(`Cannot connect without a database URL`);
4885
4897
  if (this.closed) {
4898
+ //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4886
4899
  return;
4887
4900
  }
4888
4901
  if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4889
4902
  this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4890
4903
  return;
4891
4904
  }
4905
+ this.webSocketStatus.next('connecting');
4892
4906
  this.pinger = setInterval(async () => {
4893
4907
  if (this.closed) {
4894
4908
  console.debug('pinger check', this.id, 'CLOSED.');
@@ -4935,7 +4949,7 @@ class WSConnection extends Subscription$1 {
4935
4949
  const searchParams = new URLSearchParams();
4936
4950
  if (this.subscriber.closed)
4937
4951
  return;
4938
- searchParams.set('v', "2");
4952
+ searchParams.set('v', '2');
4939
4953
  searchParams.set('rev', this.rev);
4940
4954
  searchParams.set('realmsHash', this.realmSetHash);
4941
4955
  searchParams.set('clientId', this.clientIdentity);
@@ -4974,23 +4988,30 @@ class WSConnection extends Subscription$1 {
4974
4988
  }
4975
4989
  };
4976
4990
  try {
4991
+ let everConnected = false;
4977
4992
  await new Promise((resolve, reject) => {
4978
4993
  ws.onopen = (event) => {
4979
4994
  console.debug('dexie-cloud WebSocket onopen');
4995
+ everConnected = true;
4980
4996
  resolve(null);
4981
4997
  };
4982
4998
  ws.onerror = (event) => {
4983
- const error = event.error || new Error('WebSocket Error');
4984
- this.disconnect();
4985
- this.subscriber.error(error);
4986
- this.webSocketStatus.next("error");
4987
- reject(error);
4999
+ if (!everConnected) {
5000
+ const error = event.error || new Error('WebSocket Error');
5001
+ this.subscriber.error(error);
5002
+ this.webSocketStatus.next('error');
5003
+ reject(error);
5004
+ }
5005
+ else {
5006
+ this.reconnect();
5007
+ }
4988
5008
  };
4989
5009
  });
4990
- this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
5010
+ this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
4991
5011
  if (!this.closed) {
4992
- if (msg.type === 'ready' && this.webSocketStatus.value !== 'connected') {
4993
- this.webSocketStatus.next("connected");
5012
+ if (msg.type === 'ready' &&
5013
+ this.webSocketStatus.value !== 'connected') {
5014
+ this.webSocketStatus.next('connected');
4994
5015
  }
4995
5016
  this.ws?.send(TSON.stringify(msg));
4996
5017
  }
@@ -5027,9 +5048,9 @@ function connectWebSocket(db) {
5027
5048
  rev: syncState.serverRevision,
5028
5049
  })));
5029
5050
  function createObservable() {
5030
- return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5051
+ return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5031
5052
  take(1), // Don't continue waking up whenever syncState change
5032
- 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]) =>
5053
+ 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]) =>
5033
5054
  // Let server end query changes from last entry of same client-ID and forward.
5034
5055
  // If no new entries, server won't bother the client. If new entries, server sends only those
5035
5056
  // and the baseRev of the last from same client-ID.
@@ -5052,7 +5073,10 @@ function connectWebSocket(db) {
5052
5073
  else {
5053
5074
  return throwError(error);
5054
5075
  }
5055
- }), catchError((error) => from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()))));
5076
+ }), catchError((error) => {
5077
+ db.cloud.webSocketStatus.next("error");
5078
+ return from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
5079
+ }));
5056
5080
  }
5057
5081
  return createObservable().subscribe((msg) => {
5058
5082
  if (msg) {
@@ -5476,6 +5500,21 @@ function createSharedValueObservable(o, defaultValue) {
5476
5500
  return rv;
5477
5501
  }
5478
5502
 
5503
+ const getGlobalRolesObservable = associate((db) => {
5504
+ return createSharedValueObservable(liveQuery(() => db.roles
5505
+ .where({ realmId: 'rlm-public' })
5506
+ .toArray()
5507
+ .then((roles) => {
5508
+ const rv = {};
5509
+ for (const role of roles
5510
+ .slice()
5511
+ .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
5512
+ rv[role.name] = role;
5513
+ }
5514
+ return rv;
5515
+ })), {});
5516
+ });
5517
+
5479
5518
  const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
5480
5519
 
5481
5520
  const getInternalAccessControlObservable = associate((db) => {
@@ -5577,18 +5616,38 @@ function mergePermissions(...permissions) {
5577
5616
  }
5578
5617
 
5579
5618
  const getPermissionsLookupObservable = associate((db) => {
5580
- const o = getInternalAccessControlObservable(db._novip);
5581
- return mapValueObservable(o, ({ selfMembers, realms, userId }) => {
5619
+ const o = createSharedValueObservable(combineLatest([
5620
+ getInternalAccessControlObservable(db._novip),
5621
+ getGlobalRolesObservable(db._novip),
5622
+ ]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
5623
+ selfMembers,
5624
+ realms,
5625
+ userId,
5626
+ globalRoles,
5627
+ }))), {
5628
+ selfMembers: [],
5629
+ realms: [],
5630
+ userId: UNAUTHORIZED_USER.userId,
5631
+ globalRoles: {},
5632
+ });
5633
+ return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
5582
5634
  const rv = realms
5583
- .map((realm) => ({
5584
- ...realm,
5585
- permissions: realm.owner === userId
5586
- ? { manage: '*' }
5587
- : mergePermissions(...selfMembers
5588
- .filter((m) => m.realmId === realm.realmId)
5589
- .map((m) => m.permissions)
5590
- .filter((p) => p)),
5591
- }))
5635
+ .map((realm) => {
5636
+ const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
5637
+ const directPermissionSets = selfRealmMembers
5638
+ .map((m) => m.permissions)
5639
+ .filter((p) => p);
5640
+ const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
5641
+ .map((role) => globalRoles[role])
5642
+ .filter((role) => role)
5643
+ .map((role) => role.permissions);
5644
+ return {
5645
+ ...realm,
5646
+ permissions: realm.owner === userId
5647
+ ? { manage: '*' }
5648
+ : mergePermissions(...directPermissionSets, ...rolePermissionSets),
5649
+ };
5650
+ })
5592
5651
  .reduce((p, c) => ({ ...p, [c.realmId]: c }), {
5593
5652
  [userId]: {
5594
5653
  realmId: userId,
@@ -5672,7 +5731,7 @@ function permissions(dexie, obj, tableName) {
5672
5731
  const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
5673
5732
  if (!realm)
5674
5733
  return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
5675
- return new PermissionChecker(realm.permissions, tableName, !owner || owner === dexie.cloud.currentUserId);
5734
+ return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
5676
5735
  };
5677
5736
  const o = source.pipe(map(mapper));
5678
5737
  o.getValue = () => mapper(source.getValue());
@@ -5728,7 +5787,7 @@ function dexieCloud(dexie) {
5728
5787
  currentUserEmitter.next(UNAUTHORIZED_USER);
5729
5788
  });
5730
5789
  dexie.cloud = {
5731
- version: '4.0.0-beta.15',
5790
+ version: '4.0.0-beta.16',
5732
5791
  options: { ...DEFAULT_OPTIONS },
5733
5792
  schema: null,
5734
5793
  serverState: null,
@@ -5749,6 +5808,7 @@ function dexieCloud(dexie) {
5749
5808
  await login(db, hint);
5750
5809
  },
5751
5810
  invites: getInvitesObservable(dexie),
5811
+ roles: getGlobalRolesObservable(dexie),
5752
5812
  configure(options) {
5753
5813
  options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
5754
5814
  configuredProgramatically = true;
@@ -5969,7 +6029,7 @@ function dexieCloud(dexie) {
5969
6029
  }
5970
6030
  }
5971
6031
  }
5972
- dexieCloud.version = '4.0.0-beta.15';
6032
+ dexieCloud.version = '4.0.0-beta.16';
5973
6033
  Dexie.Cloud = dexieCloud;
5974
6034
 
5975
6035
  // In case the SW lives for a while, let it reuse already opened connections: