dexie-cloud-addon 4.0.0-beta.14 → 4.0.0-beta.17
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 +194 -138
- 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 +193 -137
- 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 +231 -180
- 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/WSObservable.d.ts +1 -0
- package/dist/types/getGlobalRolesObservable.d.ts +5 -0
- package/dist/types/getInvitesObservable.d.ts +1 -1
- package/dist/umd/dexie-cloud-addon.js +231 -180
- 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 +192 -136
- 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 +190 -134
- package/dist/umd-modern/dexie-cloud-addon.js.map +1 -1
- package/package.json +2 -2
|
@@ -4667,110 +4667,13 @@
|
|
|
4667
4667
|
};
|
|
4668
4668
|
}
|
|
4669
4669
|
|
|
4670
|
-
const SECONDS = 1000;
|
|
4671
|
-
const MINUTES = 60 * SECONDS;
|
|
4672
|
-
|
|
4673
|
-
const myId = randomString(16);
|
|
4674
|
-
|
|
4675
|
-
const GUARDED_JOB_HEARTBEAT = 1 * SECONDS;
|
|
4676
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
4677
|
-
async function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
4678
|
-
// Start working.
|
|
4679
|
-
//
|
|
4680
|
-
// Check if someone else is working on this already.
|
|
4681
|
-
//
|
|
4682
|
-
const jobsTable = db.table(jobsTableName);
|
|
4683
|
-
async function aquireLock() {
|
|
4684
|
-
const gotTheLock = await db.transaction('rw!', jobsTableName, async () => {
|
|
4685
|
-
const currentWork = await jobsTable.get(jobName);
|
|
4686
|
-
if (!currentWork) {
|
|
4687
|
-
// No one else is working. Let's record that we are.
|
|
4688
|
-
await jobsTable.add({
|
|
4689
|
-
nodeId: myId,
|
|
4690
|
-
started: new Date(),
|
|
4691
|
-
heartbeat: new Date()
|
|
4692
|
-
}, jobName);
|
|
4693
|
-
return true;
|
|
4694
|
-
}
|
|
4695
|
-
else if (currentWork.heartbeat.getTime() <
|
|
4696
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
4697
|
-
console.warn(`Latest ${jobName} worker seem to have died.\n`, `The dead job started:`, currentWork.started, `\n`, `Last heart beat was:`, currentWork.heartbeat, '\n', `We're now taking over!`);
|
|
4698
|
-
// Now, take over!
|
|
4699
|
-
await jobsTable.put({
|
|
4700
|
-
nodeId: myId,
|
|
4701
|
-
started: new Date(),
|
|
4702
|
-
heartbeat: new Date()
|
|
4703
|
-
}, jobName);
|
|
4704
|
-
return true;
|
|
4705
|
-
}
|
|
4706
|
-
return false;
|
|
4707
|
-
});
|
|
4708
|
-
if (gotTheLock)
|
|
4709
|
-
return true;
|
|
4710
|
-
// Someone else took the job.
|
|
4711
|
-
if (awaitRemoteJob) {
|
|
4712
|
-
try {
|
|
4713
|
-
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
4714
|
-
await jobDoneObservable.toPromise();
|
|
4715
|
-
return false;
|
|
4716
|
-
}
|
|
4717
|
-
catch (err) {
|
|
4718
|
-
if (err.name !== 'TimeoutError') {
|
|
4719
|
-
throw err;
|
|
4720
|
-
}
|
|
4721
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
4722
|
-
// It will likely succeed this time unless
|
|
4723
|
-
// another client took it.
|
|
4724
|
-
return await aquireLock();
|
|
4725
|
-
}
|
|
4726
|
-
}
|
|
4727
|
-
return false;
|
|
4728
|
-
}
|
|
4729
|
-
if (await aquireLock()) {
|
|
4730
|
-
// We own the lock entry and can do our job undisturbed.
|
|
4731
|
-
// We're not within a transaction, but these type of locks
|
|
4732
|
-
// spans over transactions.
|
|
4733
|
-
// Start our heart beat during the job.
|
|
4734
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
4735
|
-
const heartbeat = setInterval(() => {
|
|
4736
|
-
jobsTable.update(jobName, (job) => {
|
|
4737
|
-
if (job.nodeId === myId) {
|
|
4738
|
-
job.heartbeat = new Date();
|
|
4739
|
-
}
|
|
4740
|
-
});
|
|
4741
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
4742
|
-
try {
|
|
4743
|
-
return await job();
|
|
4744
|
-
}
|
|
4745
|
-
finally {
|
|
4746
|
-
// Stop heartbeat
|
|
4747
|
-
clearInterval(heartbeat);
|
|
4748
|
-
// Remove the persisted job state:
|
|
4749
|
-
await db.transaction('rw!', jobsTableName, async () => {
|
|
4750
|
-
const currentWork = await jobsTable.get(jobName);
|
|
4751
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
4752
|
-
jobsTable.delete(jobName);
|
|
4753
|
-
}
|
|
4754
|
-
});
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
}
|
|
4758
|
-
|
|
4759
4670
|
async function performInitialSync(db, cloudOptions, cloudSchema) {
|
|
4760
|
-
console.debug(
|
|
4761
|
-
await
|
|
4762
|
-
|
|
4763
|
-
// Do check again (now within a transaction) that we really do not have a sync state:
|
|
4764
|
-
const syncState = await db.getPersistedSyncState();
|
|
4765
|
-
if (!syncState?.initiallySynced) {
|
|
4766
|
-
await sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
4767
|
-
}
|
|
4768
|
-
}, { awaitRemoteJob: true } // Don't return until the job is done!
|
|
4769
|
-
);
|
|
4770
|
-
console.debug("Done initial sync");
|
|
4671
|
+
console.debug('Performing initial sync');
|
|
4672
|
+
await sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
4673
|
+
console.debug('Done initial sync');
|
|
4771
4674
|
}
|
|
4772
4675
|
|
|
4773
|
-
const USER_INACTIVITY_TIMEOUT =
|
|
4676
|
+
const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
|
|
4774
4677
|
const INACTIVE_WAIT_TIME = 20000;
|
|
4775
4678
|
// This observable will be emitted to later down....
|
|
4776
4679
|
const userIsActive = new rxjs.BehaviorSubject(true);
|
|
@@ -4784,9 +4687,13 @@
|
|
|
4784
4687
|
// for just a short time.
|
|
4785
4688
|
const userIsReallyActive = new rxjs.BehaviorSubject(true);
|
|
4786
4689
|
userIsActive
|
|
4787
|
-
.pipe(switchMap((isActive) =>
|
|
4788
|
-
|
|
4789
|
-
|
|
4690
|
+
.pipe(switchMap((isActive) => {
|
|
4691
|
+
//console.debug('SyncStatus: DUBB: isActive changed to', isActive);
|
|
4692
|
+
return isActive
|
|
4693
|
+
? rxjs.of(true)
|
|
4694
|
+
: rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))
|
|
4695
|
+
;
|
|
4696
|
+
}), distinctUntilChanged())
|
|
4790
4697
|
.subscribe(userIsReallyActive);
|
|
4791
4698
|
//
|
|
4792
4699
|
// First create some corner-stone observables to build the flow on
|
|
@@ -4801,7 +4708,7 @@
|
|
|
4801
4708
|
const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
|
|
4802
4709
|
// Any of various user-activity-related events happen:
|
|
4803
4710
|
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'))
|
|
4711
|
+
? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousedown'), rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
|
|
4805
4712
|
: rxjs.of({});
|
|
4806
4713
|
if (typeof document !== 'undefined') {
|
|
4807
4714
|
//
|
|
@@ -4852,6 +4759,7 @@
|
|
|
4852
4759
|
constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
|
|
4853
4760
|
super(() => this.teardown());
|
|
4854
4761
|
this.id = ++counter;
|
|
4762
|
+
this.reconnecting = false;
|
|
4855
4763
|
console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
|
|
4856
4764
|
this.databaseUrl = databaseUrl;
|
|
4857
4765
|
this.rev = rev;
|
|
@@ -4871,7 +4779,7 @@
|
|
|
4871
4779
|
this.disconnect();
|
|
4872
4780
|
}
|
|
4873
4781
|
disconnect() {
|
|
4874
|
-
this.webSocketStatus.next(
|
|
4782
|
+
this.webSocketStatus.next('disconnected');
|
|
4875
4783
|
if (this.pinger) {
|
|
4876
4784
|
clearInterval(this.pinger);
|
|
4877
4785
|
this.pinger = null;
|
|
@@ -4889,11 +4797,18 @@
|
|
|
4889
4797
|
}
|
|
4890
4798
|
}
|
|
4891
4799
|
reconnect() {
|
|
4892
|
-
this.
|
|
4893
|
-
|
|
4800
|
+
if (this.reconnecting)
|
|
4801
|
+
return;
|
|
4802
|
+
this.reconnecting = true;
|
|
4803
|
+
try {
|
|
4804
|
+
this.disconnect();
|
|
4805
|
+
}
|
|
4806
|
+
catch { }
|
|
4807
|
+
this.connect()
|
|
4808
|
+
.catch(() => { })
|
|
4809
|
+
.then(() => (this.reconnecting = false)); // finally()
|
|
4894
4810
|
}
|
|
4895
4811
|
async connect() {
|
|
4896
|
-
this.webSocketStatus.next("connecting");
|
|
4897
4812
|
this.lastServerActivity = new Date();
|
|
4898
4813
|
if (this.pauseUntil && this.pauseUntil > new Date()) {
|
|
4899
4814
|
console.debug('WS not reconnecting just yet', {
|
|
@@ -4908,12 +4823,14 @@
|
|
|
4908
4823
|
if (!this.databaseUrl)
|
|
4909
4824
|
throw new Error(`Cannot connect without a database URL`);
|
|
4910
4825
|
if (this.closed) {
|
|
4826
|
+
//console.debug('SyncStatus: DUBB: Ooops it was closed!');
|
|
4911
4827
|
return;
|
|
4912
4828
|
}
|
|
4913
4829
|
if (this.tokenExpiration && this.tokenExpiration < new Date()) {
|
|
4914
4830
|
this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
|
|
4915
4831
|
return;
|
|
4916
4832
|
}
|
|
4833
|
+
this.webSocketStatus.next('connecting');
|
|
4917
4834
|
this.pinger = setInterval(async () => {
|
|
4918
4835
|
if (this.closed) {
|
|
4919
4836
|
console.debug('pinger check', this.id, 'CLOSED.');
|
|
@@ -4960,7 +4877,7 @@
|
|
|
4960
4877
|
const searchParams = new URLSearchParams();
|
|
4961
4878
|
if (this.subscriber.closed)
|
|
4962
4879
|
return;
|
|
4963
|
-
searchParams.set('v',
|
|
4880
|
+
searchParams.set('v', '2');
|
|
4964
4881
|
searchParams.set('rev', this.rev);
|
|
4965
4882
|
searchParams.set('realmsHash', this.realmSetHash);
|
|
4966
4883
|
searchParams.set('clientId', this.clientIdentity);
|
|
@@ -4999,23 +4916,30 @@
|
|
|
4999
4916
|
}
|
|
5000
4917
|
};
|
|
5001
4918
|
try {
|
|
4919
|
+
let everConnected = false;
|
|
5002
4920
|
await new Promise((resolve, reject) => {
|
|
5003
4921
|
ws.onopen = (event) => {
|
|
5004
4922
|
console.debug('dexie-cloud WebSocket onopen');
|
|
4923
|
+
everConnected = true;
|
|
5005
4924
|
resolve(null);
|
|
5006
4925
|
};
|
|
5007
4926
|
ws.onerror = (event) => {
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
4927
|
+
if (!everConnected) {
|
|
4928
|
+
const error = event.error || new Error('WebSocket Error');
|
|
4929
|
+
this.subscriber.error(error);
|
|
4930
|
+
this.webSocketStatus.next('error');
|
|
4931
|
+
reject(error);
|
|
4932
|
+
}
|
|
4933
|
+
else {
|
|
4934
|
+
this.reconnect();
|
|
4935
|
+
}
|
|
5013
4936
|
};
|
|
5014
4937
|
});
|
|
5015
|
-
this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
|
|
4938
|
+
this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
|
|
5016
4939
|
if (!this.closed) {
|
|
5017
|
-
if (msg.type === 'ready' &&
|
|
5018
|
-
this.webSocketStatus.
|
|
4940
|
+
if (msg.type === 'ready' &&
|
|
4941
|
+
this.webSocketStatus.value !== 'connected') {
|
|
4942
|
+
this.webSocketStatus.next('connected');
|
|
5019
4943
|
}
|
|
5020
4944
|
this.ws?.send(TSON.stringify(msg));
|
|
5021
4945
|
}
|
|
@@ -5052,9 +4976,9 @@
|
|
|
5052
4976
|
rev: syncState.serverRevision,
|
|
5053
4977
|
})));
|
|
5054
4978
|
function createObservable() {
|
|
5055
|
-
return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
|
|
4979
|
+
return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
|
|
5056
4980
|
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]) =>
|
|
4981
|
+
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
4982
|
// Let server end query changes from last entry of same client-ID and forward.
|
|
5059
4983
|
// If no new entries, server won't bother the client. If new entries, server sends only those
|
|
5060
4984
|
// and the baseRev of the last from same client-ID.
|
|
@@ -5077,7 +5001,10 @@
|
|
|
5077
5001
|
else {
|
|
5078
5002
|
return rxjs.throwError(error);
|
|
5079
5003
|
}
|
|
5080
|
-
}), catchError((error) =>
|
|
5004
|
+
}), catchError((error) => {
|
|
5005
|
+
db.cloud.webSocketStatus.next("error");
|
|
5006
|
+
return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
5007
|
+
}));
|
|
5081
5008
|
}
|
|
5082
5009
|
return createObservable().subscribe((msg) => {
|
|
5083
5010
|
if (msg) {
|
|
@@ -5097,6 +5024,95 @@
|
|
|
5097
5024
|
: false;
|
|
5098
5025
|
}
|
|
5099
5026
|
|
|
5027
|
+
const SECONDS = 1000;
|
|
5028
|
+
const MINUTES = 60 * SECONDS;
|
|
5029
|
+
|
|
5030
|
+
const myId = randomString(16);
|
|
5031
|
+
|
|
5032
|
+
const GUARDED_JOB_HEARTBEAT = 1 * SECONDS;
|
|
5033
|
+
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
5034
|
+
async function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
5035
|
+
// Start working.
|
|
5036
|
+
//
|
|
5037
|
+
// Check if someone else is working on this already.
|
|
5038
|
+
//
|
|
5039
|
+
const jobsTable = db.table(jobsTableName);
|
|
5040
|
+
async function aquireLock() {
|
|
5041
|
+
const gotTheLock = await db.transaction('rw!', jobsTableName, async () => {
|
|
5042
|
+
const currentWork = await jobsTable.get(jobName);
|
|
5043
|
+
if (!currentWork) {
|
|
5044
|
+
// No one else is working. Let's record that we are.
|
|
5045
|
+
await jobsTable.add({
|
|
5046
|
+
nodeId: myId,
|
|
5047
|
+
started: new Date(),
|
|
5048
|
+
heartbeat: new Date()
|
|
5049
|
+
}, jobName);
|
|
5050
|
+
return true;
|
|
5051
|
+
}
|
|
5052
|
+
else if (currentWork.heartbeat.getTime() <
|
|
5053
|
+
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
5054
|
+
console.warn(`Latest ${jobName} worker seem to have died.\n`, `The dead job started:`, currentWork.started, `\n`, `Last heart beat was:`, currentWork.heartbeat, '\n', `We're now taking over!`);
|
|
5055
|
+
// Now, take over!
|
|
5056
|
+
await jobsTable.put({
|
|
5057
|
+
nodeId: myId,
|
|
5058
|
+
started: new Date(),
|
|
5059
|
+
heartbeat: new Date()
|
|
5060
|
+
}, jobName);
|
|
5061
|
+
return true;
|
|
5062
|
+
}
|
|
5063
|
+
return false;
|
|
5064
|
+
});
|
|
5065
|
+
if (gotTheLock)
|
|
5066
|
+
return true;
|
|
5067
|
+
// Someone else took the job.
|
|
5068
|
+
if (awaitRemoteJob) {
|
|
5069
|
+
try {
|
|
5070
|
+
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
5071
|
+
await jobDoneObservable.toPromise();
|
|
5072
|
+
return false;
|
|
5073
|
+
}
|
|
5074
|
+
catch (err) {
|
|
5075
|
+
if (err.name !== 'TimeoutError') {
|
|
5076
|
+
throw err;
|
|
5077
|
+
}
|
|
5078
|
+
// Timeout stopped us! Try aquire the lock now.
|
|
5079
|
+
// It will likely succeed this time unless
|
|
5080
|
+
// another client took it.
|
|
5081
|
+
return await aquireLock();
|
|
5082
|
+
}
|
|
5083
|
+
}
|
|
5084
|
+
return false;
|
|
5085
|
+
}
|
|
5086
|
+
if (await aquireLock()) {
|
|
5087
|
+
// We own the lock entry and can do our job undisturbed.
|
|
5088
|
+
// We're not within a transaction, but these type of locks
|
|
5089
|
+
// spans over transactions.
|
|
5090
|
+
// Start our heart beat during the job.
|
|
5091
|
+
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
5092
|
+
const heartbeat = setInterval(() => {
|
|
5093
|
+
jobsTable.update(jobName, (job) => {
|
|
5094
|
+
if (job.nodeId === myId) {
|
|
5095
|
+
job.heartbeat = new Date();
|
|
5096
|
+
}
|
|
5097
|
+
});
|
|
5098
|
+
}, GUARDED_JOB_HEARTBEAT);
|
|
5099
|
+
try {
|
|
5100
|
+
return await job();
|
|
5101
|
+
}
|
|
5102
|
+
finally {
|
|
5103
|
+
// Stop heartbeat
|
|
5104
|
+
clearInterval(heartbeat);
|
|
5105
|
+
// Remove the persisted job state:
|
|
5106
|
+
await db.transaction('rw!', jobsTableName, async () => {
|
|
5107
|
+
const currentWork = await jobsTable.get(jobName);
|
|
5108
|
+
if (currentWork && currentWork.nodeId === myId) {
|
|
5109
|
+
await jobsTable.delete(jobName);
|
|
5110
|
+
}
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5100
5116
|
const ongoingSyncs = new WeakMap();
|
|
5101
5117
|
function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
5102
5118
|
const ongoing = ongoingSyncs.get(db);
|
|
@@ -5501,6 +5517,21 @@
|
|
|
5501
5517
|
return rv;
|
|
5502
5518
|
}
|
|
5503
5519
|
|
|
5520
|
+
const getGlobalRolesObservable = associate((db) => {
|
|
5521
|
+
return createSharedValueObservable(Dexie.liveQuery(() => db.roles
|
|
5522
|
+
.where({ realmId: 'rlm-public' })
|
|
5523
|
+
.toArray()
|
|
5524
|
+
.then((roles) => {
|
|
5525
|
+
const rv = {};
|
|
5526
|
+
for (const role of roles
|
|
5527
|
+
.slice()
|
|
5528
|
+
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
|
|
5529
|
+
rv[role.name] = role;
|
|
5530
|
+
}
|
|
5531
|
+
return rv;
|
|
5532
|
+
})), {});
|
|
5533
|
+
});
|
|
5534
|
+
|
|
5504
5535
|
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
5505
5536
|
|
|
5506
5537
|
const getInternalAccessControlObservable = associate((db) => {
|
|
@@ -5602,18 +5633,38 @@
|
|
|
5602
5633
|
}
|
|
5603
5634
|
|
|
5604
5635
|
const getPermissionsLookupObservable = associate((db) => {
|
|
5605
|
-
const o =
|
|
5606
|
-
|
|
5636
|
+
const o = createSharedValueObservable(rxjs.combineLatest([
|
|
5637
|
+
getInternalAccessControlObservable(db._novip),
|
|
5638
|
+
getGlobalRolesObservable(db._novip),
|
|
5639
|
+
]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
|
|
5640
|
+
selfMembers,
|
|
5641
|
+
realms,
|
|
5642
|
+
userId,
|
|
5643
|
+
globalRoles,
|
|
5644
|
+
}))), {
|
|
5645
|
+
selfMembers: [],
|
|
5646
|
+
realms: [],
|
|
5647
|
+
userId: UNAUTHORIZED_USER.userId,
|
|
5648
|
+
globalRoles: {},
|
|
5649
|
+
});
|
|
5650
|
+
return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
|
|
5607
5651
|
const rv = realms
|
|
5608
|
-
.map((realm) =>
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5652
|
+
.map((realm) => {
|
|
5653
|
+
const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
|
|
5654
|
+
const directPermissionSets = selfRealmMembers
|
|
5655
|
+
.map((m) => m.permissions)
|
|
5656
|
+
.filter((p) => p);
|
|
5657
|
+
const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
|
|
5658
|
+
.map((role) => globalRoles[role])
|
|
5659
|
+
.filter((role) => role)
|
|
5660
|
+
.map((role) => role.permissions);
|
|
5661
|
+
return {
|
|
5662
|
+
...realm,
|
|
5663
|
+
permissions: realm.owner === userId
|
|
5664
|
+
? { manage: '*' }
|
|
5665
|
+
: mergePermissions(...directPermissionSets, ...rolePermissionSets),
|
|
5666
|
+
};
|
|
5667
|
+
})
|
|
5617
5668
|
.reduce((p, c) => ({ ...p, [c.realmId]: c }), {
|
|
5618
5669
|
[userId]: {
|
|
5619
5670
|
realmId: userId,
|
|
@@ -5697,7 +5748,7 @@
|
|
|
5697
5748
|
const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
|
|
5698
5749
|
if (!realm)
|
|
5699
5750
|
return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
|
|
5700
|
-
return new PermissionChecker(realm.permissions, tableName,
|
|
5751
|
+
return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
|
|
5701
5752
|
};
|
|
5702
5753
|
const o = source.pipe(map(mapper));
|
|
5703
5754
|
o.getValue = () => mapper(source.getValue());
|
|
@@ -5733,6 +5784,7 @@
|
|
|
5733
5784
|
//
|
|
5734
5785
|
const currentUserEmitter = getCurrentUserEmitter(dexie);
|
|
5735
5786
|
const subscriptions = [];
|
|
5787
|
+
let configuredProgramatically = false;
|
|
5736
5788
|
// local sync worker - used when there's no service worker.
|
|
5737
5789
|
let localSyncWorker = null;
|
|
5738
5790
|
dexie.on('ready', async (dexie) => {
|
|
@@ -5780,8 +5832,10 @@
|
|
|
5780
5832
|
await login(db, hint);
|
|
5781
5833
|
},
|
|
5782
5834
|
invites: getInvitesObservable(dexie),
|
|
5835
|
+
roles: getGlobalRolesObservable(dexie),
|
|
5783
5836
|
configure(options) {
|
|
5784
5837
|
options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
|
|
5838
|
+
configuredProgramatically = true;
|
|
5785
5839
|
if (options.databaseUrl && options.nameSuffix) {
|
|
5786
5840
|
// @ts-ignore
|
|
5787
5841
|
dexie.name = `${origIdbName}-${getDbNameFromDbUrl(options.databaseUrl)}`;
|
|
@@ -5868,7 +5922,7 @@
|
|
|
5868
5922
|
db.getSchema(),
|
|
5869
5923
|
db.getPersistedSyncState(),
|
|
5870
5924
|
]);
|
|
5871
|
-
if (!
|
|
5925
|
+
if (!configuredProgramatically) {
|
|
5872
5926
|
// Options not specified programatically (use case for SW!)
|
|
5873
5927
|
// Take persisted options:
|
|
5874
5928
|
db.cloud.options = persistedOptions || null;
|
|
@@ -5876,6 +5930,8 @@
|
|
|
5876
5930
|
else if (!persistedOptions ||
|
|
5877
5931
|
JSON.stringify(persistedOptions) !== JSON.stringify(options)) {
|
|
5878
5932
|
// Update persisted options:
|
|
5933
|
+
if (!options)
|
|
5934
|
+
throw new Error(`Internal error`); // options cannot be null if configuredProgramatically is set.
|
|
5879
5935
|
await db.$syncState.put(options, 'options');
|
|
5880
5936
|
}
|
|
5881
5937
|
if (db.cloud.options?.tryUseServiceWorker &&
|