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.
- package/dist/modern/dexie-cloud-addon.js +96 -36
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/service-worker.js +95 -35
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/module-es5/dexie-cloud-addon.js +94 -31
- package/dist/module-es5/dexie-cloud-addon.js.map +1 -1
- package/dist/module-es5/dexie-cloud-addon.min.js +1 -1
- package/dist/module-es5/dexie-cloud-addon.min.js.map +1 -1
- package/dist/types/DexieCloudAPI.d.ts +4 -1
- package/dist/types/DexieCloudEntity.d.ts +8 -0
- package/dist/types/WSObservable.d.ts +1 -0
- package/dist/types/WebSocketStatus.d.ts +1 -0
- package/dist/types/createMyMembersObservable.d.ts +14 -0
- package/dist/types/currentUserObservable.d.ts +3 -0
- package/dist/types/getGlobalRolesObservable.d.ts +5 -0
- package/dist/types/getInvitesObservable.d.ts +1 -1
- package/dist/types/helpers/BroadcastedLocalEvent.d.ts +8 -0
- package/dist/types/helpers/visibleState.d.ts +1 -0
- package/dist/types/permissionsLookup.d.ts +9 -0
- package/dist/types/permissionsLookupObservable.d.ts +14 -0
- package/dist/types/sync/globalizePrivateIds.d.ts +4 -0
- package/dist/types/sync/syncServerToClientOnly.d.ts +3 -0
- package/dist/types/types/CloudConnectionStatus.d.ts +0 -0
- package/dist/types/types/ConnectionStatus.d.ts +0 -0
- package/dist/types/types/LoginState.d.ts +41 -0
- package/dist/types/types/SyncConnectionStatus.d.ts +1 -0
- package/dist/types/types/SyncFlowStatus.d.ts +6 -0
- package/dist/types/types/SyncStatus.d.ts +6 -0
- package/dist/umd/dexie-cloud-addon.js +94 -31
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/service-worker.js +95 -35
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/dist/umd-modern/dexie-cloud-addon.js +93 -33
- package/dist/umd-modern/dexie-cloud-addon.js.map +1 -1
- 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 =
|
|
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) =>
|
|
4763
|
-
|
|
4764
|
-
|
|
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(
|
|
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.
|
|
4868
|
-
|
|
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',
|
|
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
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
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' &&
|
|
4993
|
-
this.webSocketStatus.
|
|
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) =>
|
|
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 =
|
|
5581
|
-
|
|
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
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
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,
|
|
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.
|
|
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.
|
|
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:
|