@evolu/web 3.0.2 → 3.1.1
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/README.md +7 -1
- package/dist/src/Platform.d.ts +2 -0
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +6 -1
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +10 -4
- package/dist/src/Task.js +1 -1
- package/dist/src/Worker.d.ts +3 -3
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +11 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/local-first/Db.worker.js +1 -1
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +2 -2
- package/dist/src/local-first/LocalAuth.js +6 -3
- package/dist/src/local-first/Shared.worker.js +2 -2
- package/package.json +6 -5
- package/src/Platform.test.ts +45 -0
- package/src/Platform.ts +8 -1
- package/src/Sqlite.test.ts +356 -0
- package/src/Sqlite.ts +16 -4
- package/src/Task.test.ts +51 -0
- package/src/Task.ts +1 -1
- package/src/Worker.test.ts +473 -0
- package/src/Worker.ts +15 -6
- package/src/index.ts +1 -1
- package/src/local-first/Db.worker.ts +1 -1
- package/src/local-first/Evolu.test.ts +50 -0
- package/src/local-first/Evolu.ts +2 -3
- package/src/local-first/LocalAuth.ts +6 -3
- package/src/local-first/Shared.worker.ts +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Evolu for Web
|
|
2
2
|
|
|
3
|
-
This package provides
|
|
3
|
+
This package provides browser-specific Evolu implementations, including
|
|
4
|
+
OPFS-backed SQLite, Web Workers, Shared Workers, and browser platform
|
|
5
|
+
dependencies.
|
|
6
|
+
|
|
7
|
+
As runtimes increasingly implement the same Web Platform APIs, Evolu's portable
|
|
8
|
+
abstractions for `WebSocket`, `MessagePort`, and Web Locks live in
|
|
9
|
+
`@evolu/common`. Their concrete implementations are supplied by the runtime.
|
|
4
10
|
|
|
5
11
|
## Documentation
|
|
6
12
|
|
package/dist/src/Platform.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { PositiveInt, type ReloadApp } from "@evolu/common";
|
|
2
|
+
/** Returns true when the browser runs on an Apple platform. */
|
|
3
|
+
export declare const isApplePlatform: () => boolean;
|
|
2
4
|
/** Returns the amount of logical processors available to the browser. */
|
|
3
5
|
export declare const availableParallelism: () => PositiveInt;
|
|
4
6
|
export declare const reloadApp: ReloadApp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../../src/Platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE5D,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,QAAO,
|
|
1
|
+
{"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../../src/Platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE5D,+DAA+D;AAC/D,eAAO,MAAM,eAAe,QAAO,OAIlC,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,QAAO,WACY,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,SAMvB,CAAC"}
|
package/dist/src/Platform.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { PositiveInt } from "@evolu/common";
|
|
2
|
+
/** Returns true when the browser runs on an Apple platform. */
|
|
3
|
+
export const isApplePlatform = () => {
|
|
4
|
+
const platform = navigator.userAgentData?.platform ?? navigator.platform;
|
|
5
|
+
return /^(?:Mac|iPhone|iPad|iPod)/iu.test(platform);
|
|
6
|
+
};
|
|
2
7
|
/** Returns the amount of logical processors available to the browser. */
|
|
3
|
-
export const availableParallelism = () => PositiveInt.orThrow(
|
|
8
|
+
export const availableParallelism = () => PositiveInt.orThrow(navigator.hardwareConcurrency);
|
|
4
9
|
export const reloadApp = (url) => {
|
|
5
10
|
if (typeof document === "undefined") {
|
|
6
11
|
return;
|
package/dist/src/Sqlite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAa,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAa,MAAM,eAAe,CAAC;AAiCnE,eAAO,MAAM,sBAAsB,EAAE,kBAoHlC,CAAC"}
|
package/dist/src/Sqlite.js
CHANGED
|
@@ -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 { bytesToHex, createPreparedStatementsCache, ok } from "@evolu/common";
|
|
53
|
+
import { bytesToHex, createPreparedStatementsCache, exhaustiveCheck, ok, } from "@evolu/common";
|
|
54
54
|
import sqlite3InitModule, {} from "@evolu/sqlite-wasm";
|
|
55
55
|
// @ts-expect-error Missing types.
|
|
56
56
|
globalThis.sqlite3ApiConfig = {
|
|
@@ -60,7 +60,7 @@ globalThis.sqlite3ApiConfig = {
|
|
|
60
60
|
if (typeof arg === "string" &&
|
|
61
61
|
arg.startsWith("Ignoring inability to install OPFS sqlite3_vfs"))
|
|
62
62
|
return;
|
|
63
|
-
//
|
|
63
|
+
// oxlint-disable-next-line eslint/no-console
|
|
64
64
|
console.warn(arg);
|
|
65
65
|
},
|
|
66
66
|
};
|
|
@@ -90,16 +90,18 @@ export const createWasmSqliteDriver = (name, options) => async () => {
|
|
|
90
90
|
let db;
|
|
91
91
|
switch (options?.mode) {
|
|
92
92
|
case "memory":
|
|
93
|
+
// oxlint-disable-next-line react/rules-of-hooks -- useDatabase registers disposal and is not a React Hook.
|
|
93
94
|
db = useDatabase(new sqlite3.oo1.DB(":memory:"));
|
|
94
95
|
break;
|
|
95
96
|
case "encrypted": {
|
|
96
97
|
// MultipleCiphers encryption requires its VFS wrapper for OPFS SAH-pool.
|
|
97
98
|
// @ts-expect-error Missing types (update @evolu/sqlite-wasm types)
|
|
98
|
-
//
|
|
99
|
+
// oxlint-disable-next-line typescript/no-unsafe-call
|
|
99
100
|
sqlite3.capi.sqlite3mc_vfs_create("opfs", 1);
|
|
100
101
|
const pool = await createOpfsSAHPoolVfs({
|
|
101
102
|
directory: `.${name}`,
|
|
102
103
|
});
|
|
104
|
+
// oxlint-disable-next-line react/rules-of-hooks -- useDatabase registers disposal and is not a React Hook.
|
|
103
105
|
db = useDatabase(new pool.OpfsSAHPoolDb(
|
|
104
106
|
// SQLite normalizes this URI filename to SAH-pool path "/evolu1.db".
|
|
105
107
|
`file:${fileName}?vfs=multipleciphers-opfs-sahpool`));
|
|
@@ -109,10 +111,14 @@ export const createWasmSqliteDriver = (name, options) => async () => {
|
|
|
109
111
|
`);
|
|
110
112
|
break;
|
|
111
113
|
}
|
|
112
|
-
|
|
114
|
+
case undefined: {
|
|
113
115
|
const pool = await createOpfsSAHPoolVfs({ name });
|
|
116
|
+
// oxlint-disable-next-line react/rules-of-hooks -- useDatabase registers disposal and is not a React Hook.
|
|
114
117
|
db = useDatabase(new pool.OpfsSAHPoolDb(`file:${fileName}`));
|
|
118
|
+
break;
|
|
115
119
|
}
|
|
120
|
+
default:
|
|
121
|
+
exhaustiveCheck(options);
|
|
116
122
|
}
|
|
117
123
|
const cache = disposer.use(createPreparedStatementsCache((sql) => db.prepare(sql), (statement) => {
|
|
118
124
|
statement.finalize();
|
package/dist/src/Task.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { createRun as createCommonRun, } from "@evolu/common";
|
|
7
7
|
export function createRun(deps) {
|
|
8
8
|
const reportDefect = (reported) => {
|
|
9
|
-
|
|
9
|
+
reportError(reported);
|
|
10
10
|
};
|
|
11
11
|
return deps === undefined
|
|
12
12
|
? createCommonRun({ reportDefect })
|
package/dist/src/Worker.d.ts
CHANGED
|
@@ -39,14 +39,14 @@ export declare const createBroadcastChannel: CreateBroadcastChannel;
|
|
|
39
39
|
* Creates an Evolu {@link WorkerSelf} from a Web `DedicatedWorkerGlobalScope`
|
|
40
40
|
* (`self` inside a dedicated worker).
|
|
41
41
|
*/
|
|
42
|
-
export declare const createWorkerSelf: <Input, Output = never>(nativeSelf:
|
|
42
|
+
export declare const createWorkerSelf: <Input, Output = never>(nativeSelf: DedicatedWorkerGlobalScope) => WorkerSelf<Input, Output>;
|
|
43
43
|
/**
|
|
44
44
|
* Creates an Evolu {@link SharedWorkerSelf} from a Web `SharedWorkerGlobalScope`
|
|
45
45
|
* (`self` inside a shared worker).
|
|
46
46
|
*
|
|
47
47
|
* Disposing closes the shared worker scope for all connected clients.
|
|
48
48
|
*/
|
|
49
|
-
export declare const createSharedWorkerSelf: <Input, Output = never>(nativeSelf:
|
|
49
|
+
export declare const createSharedWorkerSelf: <Input, Output = never>(nativeSelf: SharedWorkerGlobalScope) => SharedWorkerSelf<Input, Output>;
|
|
50
50
|
/**
|
|
51
51
|
* Creates a one-tab {@link SharedWorkerSelf} polyfill for browsers without
|
|
52
52
|
* native SharedWorker support.
|
|
@@ -54,7 +54,7 @@ export declare const createSharedWorkerSelf: <Input, Output = never>(nativeSelf:
|
|
|
54
54
|
* Delivers one synthetic `onConnect` port, queues startup messages until
|
|
55
55
|
* `onMessage` is set, and does not share state across tabs.
|
|
56
56
|
*/
|
|
57
|
-
export declare const createOneTabSharedWorkerSelfPolyfill: <Input, Output = never>(nativeSelf:
|
|
57
|
+
export declare const createOneTabSharedWorkerSelfPolyfill: <Input, Output = never>(nativeSelf: DedicatedWorkerGlobalScope) => SharedWorkerSelf<Input, Output>;
|
|
58
58
|
/** Creates deps shared by web worker entry points. */
|
|
59
59
|
export declare const createWorkerDeps: () => WorkerDeps & CreateBroadcastChannelDep & CreateMessageChannelDep;
|
|
60
60
|
//# sourceMappingURL=Worker.d.ts.map
|
package/dist/src/Worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EAGd,YAAY,EACZ,gBAAgB,EAEhB,MAAM,EACN,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AAQvB,kDAAkD;AAClD,eAAO,MAAM,YAAY,GAAI,KAAK,EAAE,MAAM,gBAC1B,UAAU,CAAC,MAAM,KAC9B,MAAM,CAAC,KAAK,EAAE,MAAM,CAAuB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,QAAO,IAiBpD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,sBAClC,UAAU,CAAC,YAAY,KAC1C,YAAY,CAAC,KAAK,EAAE,MAAM,CAM5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,OAAK,cAAc,CAC3E,KAAK,EACL,MAAM,CAcP,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,iBACuB,CAAC;AAExD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EAGd,YAAY,EACZ,gBAAgB,EAEhB,MAAM,EACN,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AAQvB,kDAAkD;AAClD,eAAO,MAAM,YAAY,GAAI,KAAK,EAAE,MAAM,gBAC1B,UAAU,CAAC,MAAM,KAC9B,MAAM,CAAC,KAAK,EAAE,MAAM,CAAuB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,QAAO,IAiBpD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,sBAClC,UAAU,CAAC,YAAY,KAC1C,YAAY,CAAC,KAAK,EAAE,MAAM,CAM5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,OAAK,cAAc,CAC3E,KAAK,EACL,MAAM,CAcP,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,iBACuB,CAAC;AAExD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,EAAE,sBAwCpC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,cACxC,0BAA0B,KACrC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAoC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,cAC9C,uBAAuB,KAClC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAoBhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAAI,KAAK,EAAE,MAAM,GAAG,KAAK,cAC5D,0BAA0B,KACrC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CA2EhC,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,QAAO,UAAU,GAC5C,yBAAyB,GACzB,uBAWD,CAAC"}
|
package/dist/src/Worker.js
CHANGED
|
@@ -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 {
|
|
53
|
+
import { assertNonNullable, createConsole, createConsoleStoreOutput, disposable, } from "@evolu/common";
|
|
54
54
|
/** Creates a {@link Worker} from a Web Worker. */
|
|
55
55
|
export const createWorker = (nativeWorker) => wrap(nativeWorker);
|
|
56
56
|
/**
|
|
@@ -134,6 +134,7 @@ export const createBroadcastChannel = (name) => {
|
|
|
134
134
|
let disposed = false;
|
|
135
135
|
disposer.defer(() => {
|
|
136
136
|
disposed = true;
|
|
137
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
137
138
|
nativeBroadcastChannel.onmessage = null;
|
|
138
139
|
nativeBroadcastChannel.close();
|
|
139
140
|
});
|
|
@@ -149,6 +150,7 @@ export const createBroadcastChannel = (name) => {
|
|
|
149
150
|
if (disposed)
|
|
150
151
|
return;
|
|
151
152
|
onMessageHandler = fn;
|
|
153
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
152
154
|
nativeBroadcastChannel.onmessage = fn
|
|
153
155
|
? (event) => {
|
|
154
156
|
fn(event.data);
|
|
@@ -180,12 +182,14 @@ export const createSharedWorkerSelf = (nativeSelf) => {
|
|
|
180
182
|
const self = {
|
|
181
183
|
onConnect: null,
|
|
182
184
|
[Symbol.dispose]: () => {
|
|
185
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
183
186
|
nativeSelf.onconnect = null;
|
|
184
187
|
nativeSelf.close();
|
|
185
188
|
},
|
|
186
189
|
};
|
|
190
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
187
191
|
nativeSelf.onconnect = (e) => {
|
|
188
|
-
|
|
192
|
+
assertNonNullable(self.onConnect, "onConnect must be set before receiving connections");
|
|
189
193
|
self.onConnect(wrap(e.ports[0]));
|
|
190
194
|
};
|
|
191
195
|
return self;
|
|
@@ -209,10 +213,12 @@ export const createOneTabSharedWorkerSelfPolyfill = (nativeSelf) => {
|
|
|
209
213
|
messages.length = 0;
|
|
210
214
|
onConnectHandler = null;
|
|
211
215
|
onMessageHandler = null;
|
|
216
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
212
217
|
nativeSelf.onmessage = null;
|
|
213
218
|
nativeSelf.close();
|
|
214
219
|
});
|
|
215
220
|
const disposables = disposer.move();
|
|
221
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
216
222
|
nativeSelf.onmessage = (event) => {
|
|
217
223
|
if (disposables.disposed)
|
|
218
224
|
return;
|
|
@@ -307,16 +313,19 @@ const wrap = (native) => {
|
|
|
307
313
|
onMessageHandler = fn;
|
|
308
314
|
if (fn) {
|
|
309
315
|
// Messages are queued until onMessage is assigned.
|
|
316
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
310
317
|
native.onmessage = (event) => {
|
|
311
318
|
fn(event.data);
|
|
312
319
|
};
|
|
313
320
|
}
|
|
314
321
|
else {
|
|
322
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
315
323
|
native.onmessage = null;
|
|
316
324
|
}
|
|
317
325
|
},
|
|
318
326
|
native: native,
|
|
319
327
|
[Symbol.dispose]: () => {
|
|
328
|
+
// oxlint-disable-next-line unicorn/prefer-add-event-listener -- This adapter owns and clears one handler.
|
|
320
329
|
native.onmessage = null;
|
|
321
330
|
if ("terminate" in native)
|
|
322
331
|
native.terminate();
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACtE,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { createWorkerDeps, createWorkerSelf } from "../Worker.js";
|
|
|
9
9
|
const run = createRun({
|
|
10
10
|
...createWorkerDeps(),
|
|
11
11
|
createSqliteDriver: createWasmSqliteDriver,
|
|
12
|
-
lockManager:
|
|
12
|
+
lockManager: navigator.locks,
|
|
13
13
|
randomBytes: createRandomBytes(),
|
|
14
14
|
});
|
|
15
15
|
void run(startDbWorker(createWorkerSelf(self)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAIV,SAAS,EAGV,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAIV,SAAS,EAGV,MAAM,2BAA2B,CAAC;AAiBnC,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,uDAAuD;AACvD,eAAO,MAAM,eAAe,UACpB,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,0BAA0B,CAAC,KAC9D,SAkEF,CAAC"}
|
|
@@ -33,7 +33,7 @@ export const createEvoluDeps = (deps = {}) => {
|
|
|
33
33
|
deps.onSharedWorkerUnsupported();
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
|
|
36
|
+
alert("This browser supports Evolu in one tab only. Close this tab and use the already open tab.");
|
|
37
37
|
}
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
@@ -50,7 +50,7 @@ export const createEvoluDeps = (deps = {}) => {
|
|
|
50
50
|
createDbWorker,
|
|
51
51
|
createBroadcastChannel,
|
|
52
52
|
createMessageChannel,
|
|
53
|
-
lockManager:
|
|
53
|
+
lockManager: navigator.locks,
|
|
54
54
|
reloadApp,
|
|
55
55
|
sharedWorker,
|
|
56
56
|
});
|
|
@@ -124,9 +124,12 @@ const createCredentialCreationOptions = (deps) => (username, seed, relyingPartyI
|
|
|
124
124
|
displayName: username,
|
|
125
125
|
},
|
|
126
126
|
pubKeyCredParams: [
|
|
127
|
-
|
|
128
|
-
{ type: "public-key", alg: -
|
|
129
|
-
|
|
127
|
+
// Ed25519
|
|
128
|
+
{ type: "public-key", alg: -8 },
|
|
129
|
+
// ES256
|
|
130
|
+
{ type: "public-key", alg: -7 },
|
|
131
|
+
// RS256
|
|
132
|
+
{ type: "public-key", alg: -257 },
|
|
130
133
|
],
|
|
131
134
|
attestation: "none",
|
|
132
135
|
authenticatorSelection: {
|
|
@@ -60,7 +60,7 @@ import { createOneTabSharedWorkerSelfPolyfill, createSharedWorkerSelf, createWor
|
|
|
60
60
|
const run = createRun({
|
|
61
61
|
...createWorkerDeps(),
|
|
62
62
|
createWebSocket,
|
|
63
|
-
lockManager:
|
|
63
|
+
lockManager: navigator.locks,
|
|
64
64
|
});
|
|
65
65
|
void run(async (run) => {
|
|
66
66
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
@@ -82,7 +82,7 @@ void run(async (run) => {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
const workerSelf = __addDisposableResource(env_1, createOneTabSharedWorkerSelfPolyfill(self), false);
|
|
85
|
-
await
|
|
85
|
+
await navigator.locks.request("evolu-one-tab-sharedworker-polyfill", { ifAvailable: true, mode: "exclusive" }, async (lock) => {
|
|
86
86
|
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
87
87
|
try {
|
|
88
88
|
if (!lock) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/web",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Evolu for web",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist/src/**",
|
|
30
|
+
"!dist/**/*.test.*",
|
|
30
31
|
"src/**",
|
|
31
32
|
"README.md"
|
|
32
33
|
],
|
|
@@ -35,13 +36,13 @@
|
|
|
35
36
|
"idb-keyval": "^6.2.2"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
39
|
+
"@evolu/common": "8.8.0",
|
|
40
|
+
"@evolu/typescript-config": "0.1.1",
|
|
38
41
|
"@types/node": "^24.10.9",
|
|
39
42
|
"@types/sharedworker": "^0.0.229",
|
|
40
43
|
"@types/web-locks-api": "^0.0.5",
|
|
41
44
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
42
|
-
"user-agent-data-types": "^0.4.2"
|
|
43
|
-
"@evolu/common": "8.3.0",
|
|
44
|
-
"@evolu/typescript-config": "0.0.2"
|
|
45
|
+
"user-agent-data-types": "^0.4.2"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
48
|
"@evolu/common": "^8.0.0"
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
"access": "public"
|
|
51
52
|
},
|
|
52
53
|
"engines": {
|
|
53
|
-
"node": ">=24.
|
|
54
|
+
"node": ">=24.20.0"
|
|
54
55
|
},
|
|
55
56
|
"sideEffects": false,
|
|
56
57
|
"scripts": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertEqual,
|
|
3
|
+
assertFalse,
|
|
4
|
+
assertTrue,
|
|
5
|
+
assertType,
|
|
6
|
+
PositiveInt,
|
|
7
|
+
testStubGlobal,
|
|
8
|
+
} from "@evolu/common";
|
|
9
|
+
import { test } from "node:test";
|
|
10
|
+
import { availableParallelism, isApplePlatform } from "./index.ts";
|
|
11
|
+
|
|
12
|
+
for (const platform of ["macOS", "MacIntel", "iPhone", "iPad", "iPod"]) {
|
|
13
|
+
test(`isApplePlatform recognizes ${platform}`, () => {
|
|
14
|
+
using _navigator = testStubGlobal("navigator", {
|
|
15
|
+
platform: "Win32",
|
|
16
|
+
userAgentData: { platform },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
assertTrue(isApplePlatform());
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("isApplePlatform recognizes a non-Apple platform", () => {
|
|
24
|
+
using _navigator = testStubGlobal("navigator", {
|
|
25
|
+
platform: "MacIntel",
|
|
26
|
+
userAgentData: { platform: "Windows" },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
assertFalse(isApplePlatform());
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("isApplePlatform falls back to navigator.platform", () => {
|
|
33
|
+
using _navigator = testStubGlobal("navigator", { platform: "MacIntel" });
|
|
34
|
+
|
|
35
|
+
assertTrue(isApplePlatform());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("availableParallelism returns the validated browser value", () => {
|
|
39
|
+
using _navigator = testStubGlobal("navigator", { hardwareConcurrency: 128 });
|
|
40
|
+
|
|
41
|
+
const parallelism = availableParallelism();
|
|
42
|
+
|
|
43
|
+
assertType<typeof parallelism, PositiveInt>();
|
|
44
|
+
assertEqual(parallelism, 128);
|
|
45
|
+
});
|
package/src/Platform.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { PositiveInt, type ReloadApp } from "@evolu/common";
|
|
2
2
|
|
|
3
|
+
/** Returns true when the browser runs on an Apple platform. */
|
|
4
|
+
export const isApplePlatform = (): boolean => {
|
|
5
|
+
const platform = navigator.userAgentData?.platform ?? navigator.platform;
|
|
6
|
+
|
|
7
|
+
return /^(?:Mac|iPhone|iPad|iPod)/iu.test(platform);
|
|
8
|
+
};
|
|
9
|
+
|
|
3
10
|
/** Returns the amount of logical processors available to the browser. */
|
|
4
11
|
export const availableParallelism = (): PositiveInt =>
|
|
5
|
-
PositiveInt.orThrow(
|
|
12
|
+
PositiveInt.orThrow(navigator.hardwareConcurrency);
|
|
6
13
|
|
|
7
14
|
export const reloadApp: ReloadApp = (url) => {
|
|
8
15
|
if (typeof document === "undefined") {
|