@evolu/web 3.3.0 → 3.4.0
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/src/local-first/Evolu.d.ts +16 -21
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +27 -9
- package/dist/src/local-first/Shared.worker.js +6 -2
- package/package.json +3 -3
- package/src/local-first/Evolu.test.ts +57 -16
- package/src/local-first/Evolu.ts +29 -27
- package/src/local-first/Shared.worker.ts +7 -3
|
@@ -1,27 +1,11 @@
|
|
|
1
1
|
import { type ConsoleDep, type ReloadAppDep } from "@evolu/common";
|
|
2
|
-
import type { EvoluDeps } from "@evolu/common/local-first";
|
|
2
|
+
import type { EvoluDeps, RequestPersistentStorageDep } from "@evolu/common/local-first";
|
|
3
3
|
export interface SharedWorkerUnsupported {
|
|
4
4
|
readonly type: "SharedWorkerUnsupported";
|
|
5
5
|
}
|
|
6
6
|
export interface SharedWorkerUnsupportedDep {
|
|
7
7
|
readonly onSharedWorkerUnsupported: () => void;
|
|
8
8
|
}
|
|
9
|
-
export interface StorageUnavailableDep {
|
|
10
|
-
/**
|
|
11
|
-
* Called when the browser offers no persistent storage, as in Safari's
|
|
12
|
-
* Private Browsing, so every database is kept in memory. See Storage in the
|
|
13
|
-
* Shared module of `@evolu/common`.
|
|
14
|
-
*
|
|
15
|
-
* The app keeps working. Data synced with a relay comes back, as on a new
|
|
16
|
-
* device, and nothing stays on the device once the tabs close. That suits
|
|
17
|
-
* someone checking their app on a borrowed phone, so a message such as
|
|
18
|
-
* "Nothing from this session is kept on this device." tells the user what to
|
|
19
|
-
* expect. Data that exists only locally, or has not synced yet, is lost when
|
|
20
|
-
* the tab hosting the database closes or navigates away, even while other
|
|
21
|
-
* tabs stay open.
|
|
22
|
-
*/
|
|
23
|
-
readonly onStorageUnavailable: () => void;
|
|
24
|
-
}
|
|
25
9
|
/**
|
|
26
10
|
* Creates Evolu dependencies for the web platform.
|
|
27
11
|
*
|
|
@@ -42,14 +26,25 @@ export interface StorageUnavailableDep {
|
|
|
42
26
|
* the user comes back to it.
|
|
43
27
|
*
|
|
44
28
|
* Where the browser offers no persistent storage, as in Safari's Private
|
|
45
|
-
* Browsing, the database is kept in memory, and
|
|
46
|
-
* {@link
|
|
47
|
-
* user.
|
|
29
|
+
* Browsing or a Firefox private window, the database is kept in memory, and
|
|
30
|
+
* {@link Evolu.devicePersistence} resolves to `NotPersisted`, so the app can
|
|
31
|
+
* tell the user. Data that exists only locally, or has not synced yet, is lost
|
|
32
|
+
* when the tab hosting the database closes or navigates away, even while other
|
|
33
|
+
* tabs stay open.
|
|
34
|
+
*
|
|
35
|
+
* After the first local mutation of a database the browser stores, this tab
|
|
36
|
+
* asks the browser once with `navigator.storage.persist()` not to delete the
|
|
37
|
+
* site's data when disk space runs low. Chrome and Safari decide silently, and
|
|
38
|
+
* Firefox asks the user, so every tab asks until the user allows it. A custom
|
|
39
|
+
* {@link RequestPersistentStorageDep.requestPersistentStorage} replaces the
|
|
40
|
+
* request, for example with `constVoid` to never ask. See [Will my data stay on
|
|
41
|
+
* the
|
|
42
|
+
* device?](https://www.evolu.dev/docs/faq#will-my-data-stay-on-the-device).
|
|
48
43
|
*
|
|
49
44
|
* A custom {@link ReloadApp} replaces the default page reload, for example to
|
|
50
45
|
* save state first. It should end by reloading the page, because the other
|
|
51
46
|
* build waits until this tab reloads or closes, and a page restored from the
|
|
52
47
|
* back-forward cache cannot work until it reloads.
|
|
53
48
|
*/
|
|
54
|
-
export declare const createEvoluDeps: (deps?: Partial<ConsoleDep> & Partial<ReloadAppDep> & Partial<SharedWorkerUnsupportedDep> & Partial<
|
|
49
|
+
export declare const createEvoluDeps: (deps?: Partial<ConsoleDep> & Partial<ReloadAppDep> & Partial<SharedWorkerUnsupportedDep> & Partial<RequestPersistentStorageDep>) => EvoluDeps;
|
|
55
50
|
//# sourceMappingURL=Evolu.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAEf,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAKV,SAAS,EACT,2BAA2B,EAI5B,MAAM,2BAA2B,CAAC;AAgBnC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;CAC1C;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,yBAAyB,EAAE,MAAM,IAAI,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,eAAe,UACpB,OAAO,CAAC,UAAU,CAAC,GACvB,OAAO,CAAC,YAAY,CAAC,GACrB,OAAO,CAAC,0BAA0B,CAAC,GACnC,OAAO,CAAC,2BAA2B,CAAC,KACrC,SAsPF,CAAC"}
|
|
@@ -50,7 +50,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
50
50
|
var e = new Error(message);
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
|
-
import { exhaustiveCheck, trySync, } from "@evolu/common";
|
|
53
|
+
import { exhaustiveCheck, tryAsync, trySync, } from "@evolu/common";
|
|
54
54
|
import { BuildWaiting, BuildWaitingRequest, buildsBroadcastChannelName, createEvoluDeps as createCommonEvoluDeps, } from "@evolu/common/local-first";
|
|
55
55
|
import { reloadApp } from "../Platform.js";
|
|
56
56
|
import { createBroadcastChannel, createMessageChannel, createSharedWorker, createWorker, installOneTabSharedWorkerPolyfill, } from "../Worker.js";
|
|
@@ -74,9 +74,20 @@ import { createBroadcastChannel, createMessageChannel, createSharedWorker, creat
|
|
|
74
74
|
* the user comes back to it.
|
|
75
75
|
*
|
|
76
76
|
* Where the browser offers no persistent storage, as in Safari's Private
|
|
77
|
-
* Browsing, the database is kept in memory, and
|
|
78
|
-
* {@link
|
|
79
|
-
* user.
|
|
77
|
+
* Browsing or a Firefox private window, the database is kept in memory, and
|
|
78
|
+
* {@link Evolu.devicePersistence} resolves to `NotPersisted`, so the app can
|
|
79
|
+
* tell the user. Data that exists only locally, or has not synced yet, is lost
|
|
80
|
+
* when the tab hosting the database closes or navigates away, even while other
|
|
81
|
+
* tabs stay open.
|
|
82
|
+
*
|
|
83
|
+
* After the first local mutation of a database the browser stores, this tab
|
|
84
|
+
* asks the browser once with `navigator.storage.persist()` not to delete the
|
|
85
|
+
* site's data when disk space runs low. Chrome and Safari decide silently, and
|
|
86
|
+
* Firefox asks the user, so every tab asks until the user allows it. A custom
|
|
87
|
+
* {@link RequestPersistentStorageDep.requestPersistentStorage} replaces the
|
|
88
|
+
* request, for example with `constVoid` to never ask. See [Will my data stay on
|
|
89
|
+
* the
|
|
90
|
+
* device?](https://www.evolu.dev/docs/faq#will-my-data-stay-on-the-device).
|
|
80
91
|
*
|
|
81
92
|
* A custom {@link ReloadApp} replaces the default page reload, for example to
|
|
82
93
|
* save state first. It should end by reloading the page, because the other
|
|
@@ -105,6 +116,7 @@ export const createEvoluDeps = (deps = {}) => {
|
|
|
105
116
|
const earlyAnnouncedWorkerIds = new Set();
|
|
106
117
|
// Checks whether the user left this tab while another build waits.
|
|
107
118
|
let focusCheckId = null;
|
|
119
|
+
let isPersistentStorageRequested = false;
|
|
108
120
|
const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
|
|
109
121
|
const buildsBroadcastChannel = disposer.use(createBroadcastChannel(buildsBroadcastChannelName));
|
|
110
122
|
const stopFocusCheck = () => {
|
|
@@ -206,11 +218,6 @@ export const createEvoluDeps = (deps = {}) => {
|
|
|
206
218
|
});
|
|
207
219
|
break;
|
|
208
220
|
}
|
|
209
|
-
case "StorageUnavailable": {
|
|
210
|
-
deps.onStorageUnavailable?.();
|
|
211
|
-
forward(message);
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
221
|
case "SharedWorkerUnsupported": {
|
|
215
222
|
if (deps.onSharedWorkerUnsupported) {
|
|
216
223
|
deps.onSharedWorkerUnsupported();
|
|
@@ -259,7 +266,18 @@ export const createEvoluDeps = (deps = {}) => {
|
|
|
259
266
|
},
|
|
260
267
|
},
|
|
261
268
|
};
|
|
269
|
+
const requestPersistentStorage = () => {
|
|
270
|
+
if (isPersistentStorageRequested)
|
|
271
|
+
return;
|
|
272
|
+
isPersistentStorageRequested = true;
|
|
273
|
+
void tryAsync(async () => {
|
|
274
|
+
if (await navigator.storage.persisted())
|
|
275
|
+
return;
|
|
276
|
+
await navigator.storage.persist();
|
|
277
|
+
});
|
|
278
|
+
};
|
|
262
279
|
const evoluDeps = disposer.use(createCommonEvoluDeps({
|
|
280
|
+
requestPersistentStorage,
|
|
263
281
|
...deps,
|
|
264
282
|
createDbWorker,
|
|
265
283
|
createBroadcastChannel,
|
|
@@ -66,8 +66,12 @@ addUncaughtErrorListener(self, (error) => {
|
|
|
66
66
|
const run = createRun({
|
|
67
67
|
...createWorkerDeps(),
|
|
68
68
|
createWebSocket,
|
|
69
|
-
// Safari's Private Browsing
|
|
70
|
-
|
|
69
|
+
// Safari's Private Browsing and Firefox's private windows offer no OPFS, and
|
|
70
|
+
// a browser may delete it, as Chrome's incognito does when the session ends;
|
|
71
|
+
// see Storage in the Shared module.
|
|
72
|
+
getDevicePersistence: async () => (await tryAsync(() => navigator.storage.getDirectory())).ok
|
|
73
|
+
? "Unknown"
|
|
74
|
+
: "NotPersisted",
|
|
71
75
|
lockManager: navigator.locks,
|
|
72
76
|
});
|
|
73
77
|
void run(async (run) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/web",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Evolu for web",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@evolu/sqlite-wasm": "2.2.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@evolu/common": "8.
|
|
38
|
+
"@evolu/common": "8.14.0",
|
|
39
39
|
"@evolu/typescript-config": "0.1.1",
|
|
40
40
|
"@types/node": "^24.10.9",
|
|
41
41
|
"@types/sharedworker": "^0.0.229",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"user-agent-data-types": "^0.4.2"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@evolu/common": "^8.
|
|
47
|
+
"@evolu/common": "^8.14.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
@@ -148,26 +148,59 @@ describe("createEvoluDeps", () => {
|
|
|
148
148
|
});
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
describe("storage
|
|
152
|
-
const
|
|
151
|
+
describe("persistent storage request", () => {
|
|
152
|
+
const setupStorage = (isPersisted: boolean) => ({
|
|
153
|
+
persisted: mock.fn(() => Promise.resolve(isPersisted)),
|
|
154
|
+
persist: mock.fn(() => Promise.resolve(true)),
|
|
155
|
+
});
|
|
153
156
|
|
|
154
|
-
it("
|
|
155
|
-
const
|
|
156
|
-
using setup = setupWebEvoluDeps({
|
|
157
|
-
setup.connect(workerId);
|
|
157
|
+
it("asks the browser once to keep the site's data", async () => {
|
|
158
|
+
const storage = setupStorage(false);
|
|
159
|
+
using setup = setupWebEvoluDeps({ storage });
|
|
158
160
|
|
|
159
|
-
setup.
|
|
161
|
+
setup.deps.requestPersistentStorage?.();
|
|
162
|
+
setup.deps.requestPersistentStorage?.();
|
|
163
|
+
await waitForMacrotask();
|
|
160
164
|
|
|
161
|
-
assertSame(
|
|
165
|
+
assertSame(storage.persisted.mock.callCount(), 1);
|
|
166
|
+
assertSame(storage.persist.mock.callCount(), 1);
|
|
162
167
|
});
|
|
163
168
|
|
|
164
|
-
it("
|
|
165
|
-
|
|
166
|
-
setup
|
|
169
|
+
it("does not ask when the site's data is already persistent", async () => {
|
|
170
|
+
const storage = setupStorage(true);
|
|
171
|
+
using setup = setupWebEvoluDeps({ storage });
|
|
167
172
|
|
|
168
|
-
setup.
|
|
173
|
+
setup.deps.requestPersistentStorage?.();
|
|
174
|
+
await waitForMacrotask();
|
|
169
175
|
|
|
170
|
-
assertSame(
|
|
176
|
+
assertSame(storage.persist.mock.callCount(), 0);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("ignores a browser that refuses to answer", async () => {
|
|
180
|
+
const storage = {
|
|
181
|
+
persisted: mock.fn(() =>
|
|
182
|
+
Promise.reject(new TypeError("Storage is disabled.")),
|
|
183
|
+
),
|
|
184
|
+
persist: mock.fn(() => Promise.resolve(true)),
|
|
185
|
+
};
|
|
186
|
+
using setup = setupWebEvoluDeps({ storage });
|
|
187
|
+
|
|
188
|
+
setup.deps.requestPersistentStorage?.();
|
|
189
|
+
await waitForMacrotask();
|
|
190
|
+
|
|
191
|
+
assertSame(storage.persist.mock.callCount(), 0);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("uses the app's request instead", async () => {
|
|
195
|
+
const storage = setupStorage(false);
|
|
196
|
+
const requestPersistentStorage = mock.fn<() => void>();
|
|
197
|
+
using setup = setupWebEvoluDeps({ storage, requestPersistentStorage });
|
|
198
|
+
|
|
199
|
+
setup.deps.requestPersistentStorage?.();
|
|
200
|
+
await waitForMacrotask();
|
|
201
|
+
|
|
202
|
+
assertSame(requestPersistentStorage.mock.callCount(), 1);
|
|
203
|
+
assertSame(storage.persisted.mock.callCount(), 0);
|
|
171
204
|
});
|
|
172
205
|
});
|
|
173
206
|
|
|
@@ -443,7 +476,8 @@ const setupWebEvoluDeps = ({
|
|
|
443
476
|
isSessionStorageAvailable = true,
|
|
444
477
|
refusalReloads,
|
|
445
478
|
reloadedFor,
|
|
446
|
-
|
|
479
|
+
storage,
|
|
480
|
+
requestPersistentStorage,
|
|
447
481
|
}: {
|
|
448
482
|
hasFocus?: boolean;
|
|
449
483
|
isAutomaticReload?: boolean;
|
|
@@ -452,7 +486,9 @@ const setupWebEvoluDeps = ({
|
|
|
452
486
|
refusalReloads?: string;
|
|
453
487
|
/** The stored waiting workers, as a previous page load left them. */
|
|
454
488
|
reloadedFor?: string;
|
|
455
|
-
|
|
489
|
+
/** Stands in for `navigator.storage`. */
|
|
490
|
+
storage?: Pick<StorageManager, "persist" | "persisted">;
|
|
491
|
+
requestPersistentStorage?: () => void;
|
|
456
492
|
} = {}) => {
|
|
457
493
|
using disposer = new DisposableStack();
|
|
458
494
|
const sharedWorkerPort = createClosableNativePort<unknown>();
|
|
@@ -539,12 +575,17 @@ const setupWebEvoluDeps = ({
|
|
|
539
575
|
);
|
|
540
576
|
let hasFocus = initialHasFocus;
|
|
541
577
|
disposer.use(testStubGlobal("document", { hasFocus: () => hasFocus }));
|
|
578
|
+
if (storage) {
|
|
579
|
+
disposer.use(
|
|
580
|
+
testStubGlobal("navigator", { locks: navigator.locks, storage }),
|
|
581
|
+
);
|
|
582
|
+
}
|
|
542
583
|
const reloadApp = mock.fn<ReloadApp>();
|
|
543
584
|
|
|
544
585
|
const deps = createEvoluDeps({
|
|
545
586
|
console: createConsole({ level: "silent" }),
|
|
546
587
|
reloadApp,
|
|
547
|
-
...(
|
|
588
|
+
...(requestPersistentStorage && { requestPersistentStorage }),
|
|
548
589
|
});
|
|
549
590
|
const builds = channels.find(
|
|
550
591
|
(channel) => channel.name === buildsBroadcastChannelName,
|
package/src/local-first/Evolu.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exhaustiveCheck,
|
|
3
|
+
tryAsync,
|
|
3
4
|
trySync,
|
|
4
5
|
type ConsoleDep,
|
|
5
6
|
type ReloadApp,
|
|
@@ -10,7 +11,9 @@ import type {
|
|
|
10
11
|
SharedWorker as CommonSharedWorker,
|
|
11
12
|
CreateDbWorker,
|
|
12
13
|
DbWorkerInit,
|
|
14
|
+
Evolu,
|
|
13
15
|
EvoluDeps,
|
|
16
|
+
RequestPersistentStorageDep,
|
|
14
17
|
SharedWorkerId,
|
|
15
18
|
SharedWorkerInput,
|
|
16
19
|
SharedWorkerOutput,
|
|
@@ -38,23 +41,6 @@ export interface SharedWorkerUnsupportedDep {
|
|
|
38
41
|
readonly onSharedWorkerUnsupported: () => void;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
export interface StorageUnavailableDep {
|
|
42
|
-
/**
|
|
43
|
-
* Called when the browser offers no persistent storage, as in Safari's
|
|
44
|
-
* Private Browsing, so every database is kept in memory. See Storage in the
|
|
45
|
-
* Shared module of `@evolu/common`.
|
|
46
|
-
*
|
|
47
|
-
* The app keeps working. Data synced with a relay comes back, as on a new
|
|
48
|
-
* device, and nothing stays on the device once the tabs close. That suits
|
|
49
|
-
* someone checking their app on a borrowed phone, so a message such as
|
|
50
|
-
* "Nothing from this session is kept on this device." tells the user what to
|
|
51
|
-
* expect. Data that exists only locally, or has not synced yet, is lost when
|
|
52
|
-
* the tab hosting the database closes or navigates away, even while other
|
|
53
|
-
* tabs stay open.
|
|
54
|
-
*/
|
|
55
|
-
readonly onStorageUnavailable: () => void;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
44
|
/**
|
|
59
45
|
* Creates Evolu dependencies for the web platform.
|
|
60
46
|
*
|
|
@@ -75,9 +61,20 @@ export interface StorageUnavailableDep {
|
|
|
75
61
|
* the user comes back to it.
|
|
76
62
|
*
|
|
77
63
|
* Where the browser offers no persistent storage, as in Safari's Private
|
|
78
|
-
* Browsing, the database is kept in memory, and
|
|
79
|
-
* {@link
|
|
80
|
-
* user.
|
|
64
|
+
* Browsing or a Firefox private window, the database is kept in memory, and
|
|
65
|
+
* {@link Evolu.devicePersistence} resolves to `NotPersisted`, so the app can
|
|
66
|
+
* tell the user. Data that exists only locally, or has not synced yet, is lost
|
|
67
|
+
* when the tab hosting the database closes or navigates away, even while other
|
|
68
|
+
* tabs stay open.
|
|
69
|
+
*
|
|
70
|
+
* After the first local mutation of a database the browser stores, this tab
|
|
71
|
+
* asks the browser once with `navigator.storage.persist()` not to delete the
|
|
72
|
+
* site's data when disk space runs low. Chrome and Safari decide silently, and
|
|
73
|
+
* Firefox asks the user, so every tab asks until the user allows it. A custom
|
|
74
|
+
* {@link RequestPersistentStorageDep.requestPersistentStorage} replaces the
|
|
75
|
+
* request, for example with `constVoid` to never ask. See [Will my data stay on
|
|
76
|
+
* the
|
|
77
|
+
* device?](https://www.evolu.dev/docs/faq#will-my-data-stay-on-the-device).
|
|
81
78
|
*
|
|
82
79
|
* A custom {@link ReloadApp} replaces the default page reload, for example to
|
|
83
80
|
* save state first. It should end by reloading the page, because the other
|
|
@@ -88,7 +85,7 @@ export const createEvoluDeps = (
|
|
|
88
85
|
deps: Partial<ConsoleDep> &
|
|
89
86
|
Partial<ReloadAppDep> &
|
|
90
87
|
Partial<SharedWorkerUnsupportedDep> &
|
|
91
|
-
Partial<
|
|
88
|
+
Partial<RequestPersistentStorageDep> = {},
|
|
92
89
|
): EvoluDeps => {
|
|
93
90
|
installOneTabSharedWorkerPolyfill();
|
|
94
91
|
const reloadThisApp = deps.reloadApp ?? reloadApp;
|
|
@@ -110,6 +107,7 @@ export const createEvoluDeps = (
|
|
|
110
107
|
const earlyAnnouncedWorkerIds = new Set<SharedWorkerId>();
|
|
111
108
|
// Checks whether the user left this tab while another build waits.
|
|
112
109
|
let focusCheckId: ReturnType<typeof setInterval> | null = null;
|
|
110
|
+
let isPersistentStorageRequested = false;
|
|
113
111
|
|
|
114
112
|
using disposer = new DisposableStack();
|
|
115
113
|
const buildsBroadcastChannel = disposer.use(
|
|
@@ -225,12 +223,6 @@ export const createEvoluDeps = (
|
|
|
225
223
|
break;
|
|
226
224
|
}
|
|
227
225
|
|
|
228
|
-
case "StorageUnavailable": {
|
|
229
|
-
deps.onStorageUnavailable?.();
|
|
230
|
-
forward(message);
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
226
|
case "SharedWorkerUnsupported": {
|
|
235
227
|
if (deps.onSharedWorkerUnsupported) {
|
|
236
228
|
deps.onSharedWorkerUnsupported();
|
|
@@ -292,8 +284,18 @@ export const createEvoluDeps = (
|
|
|
292
284
|
},
|
|
293
285
|
};
|
|
294
286
|
|
|
287
|
+
const requestPersistentStorage = (): void => {
|
|
288
|
+
if (isPersistentStorageRequested) return;
|
|
289
|
+
isPersistentStorageRequested = true;
|
|
290
|
+
void tryAsync(async () => {
|
|
291
|
+
if (await navigator.storage.persisted()) return;
|
|
292
|
+
await navigator.storage.persist();
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
|
|
295
296
|
const evoluDeps = disposer.use(
|
|
296
297
|
createCommonEvoluDeps({
|
|
298
|
+
requestPersistentStorage,
|
|
297
299
|
...deps,
|
|
298
300
|
createDbWorker,
|
|
299
301
|
createBroadcastChannel,
|
|
@@ -42,9 +42,13 @@ addUncaughtErrorListener(self, (error) => {
|
|
|
42
42
|
const run = createRun({
|
|
43
43
|
...createWorkerDeps(),
|
|
44
44
|
createWebSocket,
|
|
45
|
-
// Safari's Private Browsing
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// Safari's Private Browsing and Firefox's private windows offer no OPFS, and
|
|
46
|
+
// a browser may delete it, as Chrome's incognito does when the session ends;
|
|
47
|
+
// see Storage in the Shared module.
|
|
48
|
+
getDevicePersistence: async () =>
|
|
49
|
+
(await tryAsync(() => navigator.storage.getDirectory())).ok
|
|
50
|
+
? "Unknown"
|
|
51
|
+
: "NotPersisted",
|
|
48
52
|
lockManager: navigator.locks,
|
|
49
53
|
});
|
|
50
54
|
|