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
|
@@ -4649,110 +4649,13 @@
|
|
|
4649
4649
|
};
|
|
4650
4650
|
}
|
|
4651
4651
|
|
|
4652
|
-
const SECONDS = 1000;
|
|
4653
|
-
const MINUTES = 60 * SECONDS;
|
|
4654
|
-
|
|
4655
|
-
const myId = randomString$1(16);
|
|
4656
|
-
|
|
4657
|
-
const GUARDED_JOB_HEARTBEAT = 1 * SECONDS;
|
|
4658
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
4659
|
-
async function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
4660
|
-
// Start working.
|
|
4661
|
-
//
|
|
4662
|
-
// Check if someone else is working on this already.
|
|
4663
|
-
//
|
|
4664
|
-
const jobsTable = db.table(jobsTableName);
|
|
4665
|
-
async function aquireLock() {
|
|
4666
|
-
const gotTheLock = await db.transaction('rw!', jobsTableName, async () => {
|
|
4667
|
-
const currentWork = await jobsTable.get(jobName);
|
|
4668
|
-
if (!currentWork) {
|
|
4669
|
-
// No one else is working. Let's record that we are.
|
|
4670
|
-
await jobsTable.add({
|
|
4671
|
-
nodeId: myId,
|
|
4672
|
-
started: new Date(),
|
|
4673
|
-
heartbeat: new Date()
|
|
4674
|
-
}, jobName);
|
|
4675
|
-
return true;
|
|
4676
|
-
}
|
|
4677
|
-
else if (currentWork.heartbeat.getTime() <
|
|
4678
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
4679
|
-
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!`);
|
|
4680
|
-
// Now, take over!
|
|
4681
|
-
await jobsTable.put({
|
|
4682
|
-
nodeId: myId,
|
|
4683
|
-
started: new Date(),
|
|
4684
|
-
heartbeat: new Date()
|
|
4685
|
-
}, jobName);
|
|
4686
|
-
return true;
|
|
4687
|
-
}
|
|
4688
|
-
return false;
|
|
4689
|
-
});
|
|
4690
|
-
if (gotTheLock)
|
|
4691
|
-
return true;
|
|
4692
|
-
// Someone else took the job.
|
|
4693
|
-
if (awaitRemoteJob) {
|
|
4694
|
-
try {
|
|
4695
|
-
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
4696
|
-
await jobDoneObservable.toPromise();
|
|
4697
|
-
return false;
|
|
4698
|
-
}
|
|
4699
|
-
catch (err) {
|
|
4700
|
-
if (err.name !== 'TimeoutError') {
|
|
4701
|
-
throw err;
|
|
4702
|
-
}
|
|
4703
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
4704
|
-
// It will likely succeed this time unless
|
|
4705
|
-
// another client took it.
|
|
4706
|
-
return await aquireLock();
|
|
4707
|
-
}
|
|
4708
|
-
}
|
|
4709
|
-
return false;
|
|
4710
|
-
}
|
|
4711
|
-
if (await aquireLock()) {
|
|
4712
|
-
// We own the lock entry and can do our job undisturbed.
|
|
4713
|
-
// We're not within a transaction, but these type of locks
|
|
4714
|
-
// spans over transactions.
|
|
4715
|
-
// Start our heart beat during the job.
|
|
4716
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
4717
|
-
const heartbeat = setInterval(() => {
|
|
4718
|
-
jobsTable.update(jobName, (job) => {
|
|
4719
|
-
if (job.nodeId === myId) {
|
|
4720
|
-
job.heartbeat = new Date();
|
|
4721
|
-
}
|
|
4722
|
-
});
|
|
4723
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
4724
|
-
try {
|
|
4725
|
-
return await job();
|
|
4726
|
-
}
|
|
4727
|
-
finally {
|
|
4728
|
-
// Stop heartbeat
|
|
4729
|
-
clearInterval(heartbeat);
|
|
4730
|
-
// Remove the persisted job state:
|
|
4731
|
-
await db.transaction('rw!', jobsTableName, async () => {
|
|
4732
|
-
const currentWork = await jobsTable.get(jobName);
|
|
4733
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
4734
|
-
jobsTable.delete(jobName);
|
|
4735
|
-
}
|
|
4736
|
-
});
|
|
4737
|
-
}
|
|
4738
|
-
}
|
|
4739
|
-
}
|
|
4740
|
-
|
|
4741
4652
|
async function performInitialSync(db, cloudOptions, cloudSchema) {
|
|
4742
|
-
console.debug(
|
|
4743
|
-
await
|
|
4744
|
-
|
|
4745
|
-
// Do check again (now within a transaction) that we really do not have a sync state:
|
|
4746
|
-
const syncState = await db.getPersistedSyncState();
|
|
4747
|
-
if (!syncState?.initiallySynced) {
|
|
4748
|
-
await sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
4749
|
-
}
|
|
4750
|
-
}, { awaitRemoteJob: true } // Don't return until the job is done!
|
|
4751
|
-
);
|
|
4752
|
-
console.debug("Done initial sync");
|
|
4653
|
+
console.debug('Performing initial sync');
|
|
4654
|
+
await sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
4655
|
+
console.debug('Done initial sync');
|
|
4753
4656
|
}
|
|
4754
4657
|
|
|
4755
|
-
const USER_INACTIVITY_TIMEOUT =
|
|
4658
|
+
const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
|
|
4756
4659
|
const INACTIVE_WAIT_TIME = 20000;
|
|
4757
4660
|
// This observable will be emitted to later down....
|
|
4758
4661
|
const userIsActive = new rxjs.BehaviorSubject(true);
|
|
@@ -4766,9 +4669,13 @@
|
|
|
4766
4669
|
// for just a short time.
|
|
4767
4670
|
const userIsReallyActive = new rxjs.BehaviorSubject(true);
|
|
4768
4671
|
userIsActive
|
|
4769
|
-
.pipe(switchMap((isActive) =>
|
|
4770
|
-
|
|
4771
|
-
|
|
4672
|
+
.pipe(switchMap((isActive) => {
|
|
4673
|
+
//console.debug('SyncStatus: DUBB: isActive changed to', isActive);
|
|
4674
|
+
return isActive
|
|
4675
|
+
? rxjs.of(true)
|
|
4676
|
+
: rxjs.of(false).pipe(delay(INACTIVE_WAIT_TIME))
|
|
4677
|
+
;
|
|
4678
|
+
}), distinctUntilChanged())
|
|
4772
4679
|
.subscribe(userIsReallyActive);
|
|
4773
4680
|
//
|
|
4774
4681
|
// First create some corner-stone observables to build the flow on
|
|
@@ -4783,7 +4690,7 @@
|
|
|
4783
4690
|
const documentBecomesVisible = visibilityStateIsChanged.pipe(filter(() => document.visibilityState === 'visible'));
|
|
4784
4691
|
// Any of various user-activity-related events happen:
|
|
4785
4692
|
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'))
|
|
4693
|
+
? rxjs.merge(documentBecomesVisible, rxjs.fromEvent(window, 'mousedown'), rxjs.fromEvent(window, 'mousemove'), rxjs.fromEvent(window, 'keydown'), rxjs.fromEvent(window, 'wheel'), rxjs.fromEvent(window, 'touchmove'))
|
|
4787
4694
|
: rxjs.of({});
|
|
4788
4695
|
if (typeof document !== 'undefined') {
|
|
4789
4696
|
//
|
|
@@ -4834,6 +4741,7 @@
|
|
|
4834
4741
|
constructor(databaseUrl, rev, realmSetHash, clientIdentity, token, tokenExpiration, subscriber, messageProducer, webSocketStatus) {
|
|
4835
4742
|
super(() => this.teardown());
|
|
4836
4743
|
this.id = ++counter;
|
|
4744
|
+
this.reconnecting = false;
|
|
4837
4745
|
console.debug('New WebSocket Connection', this.id, token ? 'authorized' : 'unauthorized');
|
|
4838
4746
|
this.databaseUrl = databaseUrl;
|
|
4839
4747
|
this.rev = rev;
|
|
@@ -4853,7 +4761,7 @@
|
|
|
4853
4761
|
this.disconnect();
|
|
4854
4762
|
}
|
|
4855
4763
|
disconnect() {
|
|
4856
|
-
this.webSocketStatus.next(
|
|
4764
|
+
this.webSocketStatus.next('disconnected');
|
|
4857
4765
|
if (this.pinger) {
|
|
4858
4766
|
clearInterval(this.pinger);
|
|
4859
4767
|
this.pinger = null;
|
|
@@ -4871,11 +4779,18 @@
|
|
|
4871
4779
|
}
|
|
4872
4780
|
}
|
|
4873
4781
|
reconnect() {
|
|
4874
|
-
this.
|
|
4875
|
-
|
|
4782
|
+
if (this.reconnecting)
|
|
4783
|
+
return;
|
|
4784
|
+
this.reconnecting = true;
|
|
4785
|
+
try {
|
|
4786
|
+
this.disconnect();
|
|
4787
|
+
}
|
|
4788
|
+
catch { }
|
|
4789
|
+
this.connect()
|
|
4790
|
+
.catch(() => { })
|
|
4791
|
+
.then(() => (this.reconnecting = false)); // finally()
|
|
4876
4792
|
}
|
|
4877
4793
|
async connect() {
|
|
4878
|
-
this.webSocketStatus.next("connecting");
|
|
4879
4794
|
this.lastServerActivity = new Date();
|
|
4880
4795
|
if (this.pauseUntil && this.pauseUntil > new Date()) {
|
|
4881
4796
|
console.debug('WS not reconnecting just yet', {
|
|
@@ -4890,12 +4805,14 @@
|
|
|
4890
4805
|
if (!this.databaseUrl)
|
|
4891
4806
|
throw new Error(`Cannot connect without a database URL`);
|
|
4892
4807
|
if (this.closed) {
|
|
4808
|
+
//console.debug('SyncStatus: DUBB: Ooops it was closed!');
|
|
4893
4809
|
return;
|
|
4894
4810
|
}
|
|
4895
4811
|
if (this.tokenExpiration && this.tokenExpiration < new Date()) {
|
|
4896
4812
|
this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
|
|
4897
4813
|
return;
|
|
4898
4814
|
}
|
|
4815
|
+
this.webSocketStatus.next('connecting');
|
|
4899
4816
|
this.pinger = setInterval(async () => {
|
|
4900
4817
|
if (this.closed) {
|
|
4901
4818
|
console.debug('pinger check', this.id, 'CLOSED.');
|
|
@@ -4942,7 +4859,7 @@
|
|
|
4942
4859
|
const searchParams = new URLSearchParams();
|
|
4943
4860
|
if (this.subscriber.closed)
|
|
4944
4861
|
return;
|
|
4945
|
-
searchParams.set('v',
|
|
4862
|
+
searchParams.set('v', '2');
|
|
4946
4863
|
searchParams.set('rev', this.rev);
|
|
4947
4864
|
searchParams.set('realmsHash', this.realmSetHash);
|
|
4948
4865
|
searchParams.set('clientId', this.clientIdentity);
|
|
@@ -4981,23 +4898,30 @@
|
|
|
4981
4898
|
}
|
|
4982
4899
|
};
|
|
4983
4900
|
try {
|
|
4901
|
+
let everConnected = false;
|
|
4984
4902
|
await new Promise((resolve, reject) => {
|
|
4985
4903
|
ws.onopen = (event) => {
|
|
4986
4904
|
console.debug('dexie-cloud WebSocket onopen');
|
|
4905
|
+
everConnected = true;
|
|
4987
4906
|
resolve(null);
|
|
4988
4907
|
};
|
|
4989
4908
|
ws.onerror = (event) => {
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4909
|
+
if (!everConnected) {
|
|
4910
|
+
const error = event.error || new Error('WebSocket Error');
|
|
4911
|
+
this.subscriber.error(error);
|
|
4912
|
+
this.webSocketStatus.next('error');
|
|
4913
|
+
reject(error);
|
|
4914
|
+
}
|
|
4915
|
+
else {
|
|
4916
|
+
this.reconnect();
|
|
4917
|
+
}
|
|
4995
4918
|
};
|
|
4996
4919
|
});
|
|
4997
|
-
this.messageProducerSubscription = this.messageProducer.subscribe(msg => {
|
|
4920
|
+
this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
|
|
4998
4921
|
if (!this.closed) {
|
|
4999
|
-
if (msg.type === 'ready' &&
|
|
5000
|
-
this.webSocketStatus.
|
|
4922
|
+
if (msg.type === 'ready' &&
|
|
4923
|
+
this.webSocketStatus.value !== 'connected') {
|
|
4924
|
+
this.webSocketStatus.next('connected');
|
|
5001
4925
|
}
|
|
5002
4926
|
this.ws?.send(TSON.stringify(msg));
|
|
5003
4927
|
}
|
|
@@ -5034,9 +4958,9 @@
|
|
|
5034
4958
|
rev: syncState.serverRevision,
|
|
5035
4959
|
})));
|
|
5036
4960
|
function createObservable() {
|
|
5037
|
-
return db.cloud.persistedSyncState.pipe(filter(syncState => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
|
|
4961
|
+
return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
|
|
5038
4962
|
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]) =>
|
|
4963
|
+
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
4964
|
// Let server end query changes from last entry of same client-ID and forward.
|
|
5041
4965
|
// If no new entries, server won't bother the client. If new entries, server sends only those
|
|
5042
4966
|
// and the baseRev of the last from same client-ID.
|
|
@@ -5059,7 +4983,10 @@
|
|
|
5059
4983
|
else {
|
|
5060
4984
|
return rxjs.throwError(error);
|
|
5061
4985
|
}
|
|
5062
|
-
}), catchError((error) =>
|
|
4986
|
+
}), catchError((error) => {
|
|
4987
|
+
db.cloud.webSocketStatus.next("error");
|
|
4988
|
+
return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
4989
|
+
}));
|
|
5063
4990
|
}
|
|
5064
4991
|
return createObservable().subscribe((msg) => {
|
|
5065
4992
|
if (msg) {
|
|
@@ -5079,6 +5006,95 @@
|
|
|
5079
5006
|
: false;
|
|
5080
5007
|
}
|
|
5081
5008
|
|
|
5009
|
+
const SECONDS = 1000;
|
|
5010
|
+
const MINUTES = 60 * SECONDS;
|
|
5011
|
+
|
|
5012
|
+
const myId = randomString$1(16);
|
|
5013
|
+
|
|
5014
|
+
const GUARDED_JOB_HEARTBEAT = 1 * SECONDS;
|
|
5015
|
+
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
5016
|
+
async function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
5017
|
+
// Start working.
|
|
5018
|
+
//
|
|
5019
|
+
// Check if someone else is working on this already.
|
|
5020
|
+
//
|
|
5021
|
+
const jobsTable = db.table(jobsTableName);
|
|
5022
|
+
async function aquireLock() {
|
|
5023
|
+
const gotTheLock = await db.transaction('rw!', jobsTableName, async () => {
|
|
5024
|
+
const currentWork = await jobsTable.get(jobName);
|
|
5025
|
+
if (!currentWork) {
|
|
5026
|
+
// No one else is working. Let's record that we are.
|
|
5027
|
+
await jobsTable.add({
|
|
5028
|
+
nodeId: myId,
|
|
5029
|
+
started: new Date(),
|
|
5030
|
+
heartbeat: new Date()
|
|
5031
|
+
}, jobName);
|
|
5032
|
+
return true;
|
|
5033
|
+
}
|
|
5034
|
+
else if (currentWork.heartbeat.getTime() <
|
|
5035
|
+
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
5036
|
+
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!`);
|
|
5037
|
+
// Now, take over!
|
|
5038
|
+
await jobsTable.put({
|
|
5039
|
+
nodeId: myId,
|
|
5040
|
+
started: new Date(),
|
|
5041
|
+
heartbeat: new Date()
|
|
5042
|
+
}, jobName);
|
|
5043
|
+
return true;
|
|
5044
|
+
}
|
|
5045
|
+
return false;
|
|
5046
|
+
});
|
|
5047
|
+
if (gotTheLock)
|
|
5048
|
+
return true;
|
|
5049
|
+
// Someone else took the job.
|
|
5050
|
+
if (awaitRemoteJob) {
|
|
5051
|
+
try {
|
|
5052
|
+
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
5053
|
+
await jobDoneObservable.toPromise();
|
|
5054
|
+
return false;
|
|
5055
|
+
}
|
|
5056
|
+
catch (err) {
|
|
5057
|
+
if (err.name !== 'TimeoutError') {
|
|
5058
|
+
throw err;
|
|
5059
|
+
}
|
|
5060
|
+
// Timeout stopped us! Try aquire the lock now.
|
|
5061
|
+
// It will likely succeed this time unless
|
|
5062
|
+
// another client took it.
|
|
5063
|
+
return await aquireLock();
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
return false;
|
|
5067
|
+
}
|
|
5068
|
+
if (await aquireLock()) {
|
|
5069
|
+
// We own the lock entry and can do our job undisturbed.
|
|
5070
|
+
// We're not within a transaction, but these type of locks
|
|
5071
|
+
// spans over transactions.
|
|
5072
|
+
// Start our heart beat during the job.
|
|
5073
|
+
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
5074
|
+
const heartbeat = setInterval(() => {
|
|
5075
|
+
jobsTable.update(jobName, (job) => {
|
|
5076
|
+
if (job.nodeId === myId) {
|
|
5077
|
+
job.heartbeat = new Date();
|
|
5078
|
+
}
|
|
5079
|
+
});
|
|
5080
|
+
}, GUARDED_JOB_HEARTBEAT);
|
|
5081
|
+
try {
|
|
5082
|
+
return await job();
|
|
5083
|
+
}
|
|
5084
|
+
finally {
|
|
5085
|
+
// Stop heartbeat
|
|
5086
|
+
clearInterval(heartbeat);
|
|
5087
|
+
// Remove the persisted job state:
|
|
5088
|
+
await db.transaction('rw!', jobsTableName, async () => {
|
|
5089
|
+
const currentWork = await jobsTable.get(jobName);
|
|
5090
|
+
if (currentWork && currentWork.nodeId === myId) {
|
|
5091
|
+
await jobsTable.delete(jobName);
|
|
5092
|
+
}
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5082
5098
|
const ongoingSyncs = new WeakMap();
|
|
5083
5099
|
function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
5084
5100
|
const ongoing = ongoingSyncs.get(db);
|
|
@@ -5483,6 +5499,21 @@
|
|
|
5483
5499
|
return rv;
|
|
5484
5500
|
}
|
|
5485
5501
|
|
|
5502
|
+
const getGlobalRolesObservable = associate((db) => {
|
|
5503
|
+
return createSharedValueObservable(Dexie.liveQuery(() => db.roles
|
|
5504
|
+
.where({ realmId: 'rlm-public' })
|
|
5505
|
+
.toArray()
|
|
5506
|
+
.then((roles) => {
|
|
5507
|
+
const rv = {};
|
|
5508
|
+
for (const role of roles
|
|
5509
|
+
.slice()
|
|
5510
|
+
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))) {
|
|
5511
|
+
rv[role.name] = role;
|
|
5512
|
+
}
|
|
5513
|
+
return rv;
|
|
5514
|
+
})), {});
|
|
5515
|
+
});
|
|
5516
|
+
|
|
5486
5517
|
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
5487
5518
|
|
|
5488
5519
|
const getInternalAccessControlObservable = associate((db) => {
|
|
@@ -5584,18 +5615,38 @@
|
|
|
5584
5615
|
}
|
|
5585
5616
|
|
|
5586
5617
|
const getPermissionsLookupObservable = associate((db) => {
|
|
5587
|
-
const o =
|
|
5588
|
-
|
|
5618
|
+
const o = createSharedValueObservable(rxjs.combineLatest([
|
|
5619
|
+
getInternalAccessControlObservable(db._novip),
|
|
5620
|
+
getGlobalRolesObservable(db._novip),
|
|
5621
|
+
]).pipe(map(([{ selfMembers, realms, userId }, globalRoles]) => ({
|
|
5622
|
+
selfMembers,
|
|
5623
|
+
realms,
|
|
5624
|
+
userId,
|
|
5625
|
+
globalRoles,
|
|
5626
|
+
}))), {
|
|
5627
|
+
selfMembers: [],
|
|
5628
|
+
realms: [],
|
|
5629
|
+
userId: UNAUTHORIZED_USER.userId,
|
|
5630
|
+
globalRoles: {},
|
|
5631
|
+
});
|
|
5632
|
+
return mapValueObservable(o, ({ selfMembers, realms, userId, globalRoles }) => {
|
|
5589
5633
|
const rv = realms
|
|
5590
|
-
.map((realm) =>
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5634
|
+
.map((realm) => {
|
|
5635
|
+
const selfRealmMembers = selfMembers.filter((m) => m.realmId === realm.realmId);
|
|
5636
|
+
const directPermissionSets = selfRealmMembers
|
|
5637
|
+
.map((m) => m.permissions)
|
|
5638
|
+
.filter((p) => p);
|
|
5639
|
+
const rolePermissionSets = flatten(selfRealmMembers.map((m) => m.roles).filter((roleName) => roleName))
|
|
5640
|
+
.map((role) => globalRoles[role])
|
|
5641
|
+
.filter((role) => role)
|
|
5642
|
+
.map((role) => role.permissions);
|
|
5643
|
+
return {
|
|
5644
|
+
...realm,
|
|
5645
|
+
permissions: realm.owner === userId
|
|
5646
|
+
? { manage: '*' }
|
|
5647
|
+
: mergePermissions(...directPermissionSets, ...rolePermissionSets),
|
|
5648
|
+
};
|
|
5649
|
+
})
|
|
5599
5650
|
.reduce((p, c) => ({ ...p, [c.realmId]: c }), {
|
|
5600
5651
|
[userId]: {
|
|
5601
5652
|
realmId: userId,
|
|
@@ -5679,7 +5730,7 @@
|
|
|
5679
5730
|
const realm = permissionsLookup[realmId || dexie.cloud.currentUserId];
|
|
5680
5731
|
if (!realm)
|
|
5681
5732
|
return new PermissionChecker({}, tableName, !owner || owner === dexie.cloud.currentUserId);
|
|
5682
|
-
return new PermissionChecker(realm.permissions, tableName,
|
|
5733
|
+
return new PermissionChecker(realm.permissions, tableName, realmId === dexie.cloud.currentUserId || owner === dexie.cloud.currentUserId);
|
|
5683
5734
|
};
|
|
5684
5735
|
const o = source.pipe(map(mapper));
|
|
5685
5736
|
o.getValue = () => mapper(source.getValue());
|
|
@@ -5708,6 +5759,7 @@
|
|
|
5708
5759
|
//
|
|
5709
5760
|
const currentUserEmitter = getCurrentUserEmitter(dexie);
|
|
5710
5761
|
const subscriptions = [];
|
|
5762
|
+
let configuredProgramatically = false;
|
|
5711
5763
|
// local sync worker - used when there's no service worker.
|
|
5712
5764
|
let localSyncWorker = null;
|
|
5713
5765
|
dexie.on('ready', async (dexie) => {
|
|
@@ -5734,7 +5786,7 @@
|
|
|
5734
5786
|
currentUserEmitter.next(UNAUTHORIZED_USER);
|
|
5735
5787
|
});
|
|
5736
5788
|
dexie.cloud = {
|
|
5737
|
-
version: '4.0.0-beta.
|
|
5789
|
+
version: '4.0.0-beta.17',
|
|
5738
5790
|
options: { ...DEFAULT_OPTIONS },
|
|
5739
5791
|
schema: null,
|
|
5740
5792
|
serverState: null,
|
|
@@ -5755,8 +5807,10 @@
|
|
|
5755
5807
|
await login(db, hint);
|
|
5756
5808
|
},
|
|
5757
5809
|
invites: getInvitesObservable(dexie),
|
|
5810
|
+
roles: getGlobalRolesObservable(dexie),
|
|
5758
5811
|
configure(options) {
|
|
5759
5812
|
options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
|
|
5813
|
+
configuredProgramatically = true;
|
|
5760
5814
|
if (options.databaseUrl && options.nameSuffix) {
|
|
5761
5815
|
// @ts-ignore
|
|
5762
5816
|
dexie.name = `${origIdbName}-${getDbNameFromDbUrl(options.databaseUrl)}`;
|
|
@@ -5843,7 +5897,7 @@
|
|
|
5843
5897
|
db.getSchema(),
|
|
5844
5898
|
db.getPersistedSyncState(),
|
|
5845
5899
|
]);
|
|
5846
|
-
if (!
|
|
5900
|
+
if (!configuredProgramatically) {
|
|
5847
5901
|
// Options not specified programatically (use case for SW!)
|
|
5848
5902
|
// Take persisted options:
|
|
5849
5903
|
db.cloud.options = persistedOptions || null;
|
|
@@ -5851,6 +5905,8 @@
|
|
|
5851
5905
|
else if (!persistedOptions ||
|
|
5852
5906
|
JSON.stringify(persistedOptions) !== JSON.stringify(options)) {
|
|
5853
5907
|
// Update persisted options:
|
|
5908
|
+
if (!options)
|
|
5909
|
+
throw new Error(`Internal error`); // options cannot be null if configuredProgramatically is set.
|
|
5854
5910
|
await db.$syncState.put(options, 'options');
|
|
5855
5911
|
}
|
|
5856
5912
|
if (db.cloud.options?.tryUseServiceWorker &&
|
|
@@ -5972,7 +6028,7 @@
|
|
|
5972
6028
|
}
|
|
5973
6029
|
}
|
|
5974
6030
|
}
|
|
5975
|
-
dexieCloud.version = '4.0.0-beta.
|
|
6031
|
+
dexieCloud.version = '4.0.0-beta.17';
|
|
5976
6032
|
Dexie__default["default"].Cloud = dexieCloud;
|
|
5977
6033
|
|
|
5978
6034
|
// In case the SW lives for a while, let it reuse already opened connections:
|