@evolu/common 7.4.0 → 8.0.0-next.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/README.md +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +160 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +192 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +27 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +299 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2263 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1488 -348
- package/dist/src/Test.d.ts +122 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +66 -0
- package/dist/src/Time.d.ts +201 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +190 -86
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +582 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +362 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +113 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +210 -118
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +622 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +18 -38
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -171
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +50 -53
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +121 -173
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +36 -26
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +415 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +465 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3685 -740
- package/src/Test.ts +162 -0
- package/src/Time.ts +348 -145
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +1064 -630
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +311 -178
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +871 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -823
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +440 -303
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +91 -252
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +209 -282
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/LICENSE +0 -21
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -529
- package/src/Instances.ts +0 -90
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -960
package/dist/src/WebSocket.js
CHANGED
|
@@ -1,131 +1,223 @@
|
|
|
1
|
-
import { err, ok } from "./Result.js";
|
|
2
|
-
import { retry } from "./Task.js";
|
|
3
|
-
import { maxPositiveInt } from "./Type.js";
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* The default behavior is that WebSocket tries to reconnect repeatedly in case
|
|
8
|
-
* the application is offline, because online events (both web and native) are
|
|
9
|
-
* not reliable. Once it connects and the connection is closed, it tries to
|
|
10
|
-
* reconnect again. Retrying the connection can be controlled using the
|
|
11
|
-
* retryOptions retryable predicate.
|
|
12
|
-
*
|
|
13
|
-
* ### How Binary Messages Work in WebSockets
|
|
14
|
-
*
|
|
15
|
-
* The Server Chooses the Message Type:
|
|
16
|
-
*
|
|
17
|
-
* - Text (0x1) → Sent as UTF-8 encoded text (always received as a string in the
|
|
18
|
-
* browser).
|
|
19
|
-
* - Binary (0x2) → Sent as raw binary data (received as a Blob or ArrayBuffer,
|
|
20
|
-
* depending on binaryType).
|
|
21
|
-
*
|
|
22
|
-
* The Client's binaryType Controls How Binary Data is Processed:
|
|
23
|
-
*
|
|
24
|
-
* - If the server sends a text frame (0x1), the browser always delivers
|
|
25
|
-
* event.data as a string, regardless of binaryType.
|
|
26
|
-
* - If the server sends a binary frame (0x2), the browser delivers event.data as:
|
|
27
|
-
*
|
|
28
|
-
* - A Blob (default: "blob")
|
|
29
|
-
* - An ArrayBuffer ("arraybuffer")
|
|
2
|
+
* WebSocket with auto-reconnect.
|
|
30
3
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* TODO:
|
|
4
|
+
* @module
|
|
34
5
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
6
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
7
|
+
if (value !== null && value !== void 0) {
|
|
8
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
9
|
+
var dispose, inner;
|
|
10
|
+
if (async) {
|
|
11
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
12
|
+
dispose = value[Symbol.asyncDispose];
|
|
13
|
+
}
|
|
14
|
+
if (dispose === void 0) {
|
|
15
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
16
|
+
dispose = value[Symbol.dispose];
|
|
17
|
+
if (async) inner = dispose;
|
|
18
|
+
}
|
|
19
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
20
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
21
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
22
|
+
}
|
|
23
|
+
else if (async) {
|
|
24
|
+
env.stack.push({ async: true });
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
29
|
+
return function (env) {
|
|
30
|
+
function fail(e) {
|
|
31
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
32
|
+
env.hasError = true;
|
|
33
|
+
}
|
|
34
|
+
var r, s = 0;
|
|
35
|
+
function next() {
|
|
36
|
+
while (r = env.stack.pop()) {
|
|
37
|
+
try {
|
|
38
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
39
|
+
if (r.dispose) {
|
|
40
|
+
var result = r.dispose.call(r.value);
|
|
41
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
42
|
+
}
|
|
43
|
+
else s |= 1;
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
fail(e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
50
|
+
if (env.hasError) throw env.error;
|
|
53
51
|
}
|
|
54
|
-
|
|
52
|
+
return next();
|
|
55
53
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
socket.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
socket.onerror = (event) => {
|
|
84
|
-
const error = isOpen
|
|
85
|
-
? { type: "WebSocketConnectionError", event }
|
|
86
|
-
: { type: "WebSocketConnectError", event };
|
|
87
|
-
onError?.(error);
|
|
88
|
-
// Trigger reconnect only on WebSocketConnectError.
|
|
89
|
-
if (error.type === "WebSocketConnectError") {
|
|
90
|
-
resolve(err(error));
|
|
54
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
55
|
+
var e = new Error(message);
|
|
56
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
|
+
});
|
|
58
|
+
import { lazyTrue } from "./Function.js";
|
|
59
|
+
import { err, ok } from "./Result.js";
|
|
60
|
+
import { exponential, jitter, maxDelay } from "./Schedule.js";
|
|
61
|
+
import { callback, retry } from "./Task.js";
|
|
62
|
+
import { String } from "./Type.js";
|
|
63
|
+
import { assert } from "./Assert.js";
|
|
64
|
+
/** Create a new {@link WebSocket}. */
|
|
65
|
+
export const createWebSocket = (url, { protocols, binaryType, onOpen, onClose, shouldRetryOnClose = lazyTrue, onMessage, onError, schedule = jitter(1)(maxDelay("30s")(exponential("100ms"))), WebSocketConstructor = globalThis.WebSocket, } = {}) => async (run) => {
|
|
66
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
67
|
+
try {
|
|
68
|
+
const stack = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
|
|
69
|
+
let socket = null;
|
|
70
|
+
let disposed = false;
|
|
71
|
+
const closeSocket = () => {
|
|
72
|
+
if (!socket)
|
|
73
|
+
return;
|
|
74
|
+
socket.onopen = null;
|
|
75
|
+
socket.onclose = null;
|
|
76
|
+
socket.onmessage = null;
|
|
77
|
+
socket.onerror = null;
|
|
78
|
+
if (socket.readyState !== socket.CLOSING &&
|
|
79
|
+
socket.readyState !== socket.CLOSED) {
|
|
80
|
+
socket.close();
|
|
91
81
|
}
|
|
82
|
+
socket = null;
|
|
92
83
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
84
|
+
/**
|
|
85
|
+
* A task that connects and stays connected until the connection closes or
|
|
86
|
+
* errors. Returns error to trigger retry.
|
|
87
|
+
*/
|
|
88
|
+
const connect = callback(({ err, ok }) => {
|
|
89
|
+
closeSocket();
|
|
90
|
+
socket = new WebSocketConstructor(url, String.is(protocols) ? protocols : protocols && [...protocols]);
|
|
91
|
+
if (binaryType)
|
|
92
|
+
socket.binaryType = binaryType;
|
|
93
|
+
let isOpen = false;
|
|
94
|
+
socket.onopen = () => {
|
|
95
|
+
isOpen = true;
|
|
96
|
+
onOpen?.();
|
|
97
|
+
};
|
|
98
|
+
socket.onclose = (event) => {
|
|
99
|
+
onClose?.(event);
|
|
100
|
+
if (shouldRetryOnClose(event)) {
|
|
101
|
+
err({ type: "WebSocketConnectionCloseError", event });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
ok();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
socket.onmessage = (event) => {
|
|
108
|
+
onMessage?.(event.data);
|
|
109
|
+
};
|
|
110
|
+
socket.onerror = (event) => {
|
|
111
|
+
const error = isOpen
|
|
112
|
+
? { type: "WebSocketConnectionError", event }
|
|
113
|
+
: { type: "WebSocketConnectError", event };
|
|
114
|
+
onError?.(error);
|
|
115
|
+
if (error.type === "WebSocketConnectError")
|
|
116
|
+
err(error);
|
|
117
|
+
};
|
|
118
|
+
return closeSocket;
|
|
119
|
+
});
|
|
120
|
+
const retryFiber = stack.use(run.daemon(retry(connect, schedule)));
|
|
121
|
+
// Report RetryError (schedule exhausted) via onError callback
|
|
122
|
+
void retryFiber.then((result) => {
|
|
123
|
+
if (!result.ok && result.error.type === "RetryError") {
|
|
124
|
+
onError?.(result.error);
|
|
110
125
|
}
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
});
|
|
127
|
+
const moved = stack.move();
|
|
128
|
+
return ok({
|
|
129
|
+
send: (data) => {
|
|
130
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
|
|
131
|
+
if (!socket || socket.readyState === socket.CONNECTING) {
|
|
132
|
+
return err({ type: "WebSocketSendError" });
|
|
133
|
+
}
|
|
134
|
+
socket.send(data);
|
|
135
|
+
return ok();
|
|
136
|
+
},
|
|
137
|
+
getReadyState: () => {
|
|
138
|
+
if (disposed)
|
|
139
|
+
return "closed";
|
|
140
|
+
return socket ? nativeToStringState[socket.readyState] : "connecting";
|
|
141
|
+
},
|
|
142
|
+
isOpen: () => !disposed && socket?.readyState === globalThis.WebSocket.OPEN,
|
|
143
|
+
[Symbol.asyncDispose]: async () => {
|
|
144
|
+
disposed = true;
|
|
145
|
+
await moved.disposeAsync();
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (e_1) {
|
|
150
|
+
env_1.error = e_1;
|
|
151
|
+
env_1.hasError = true;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
const result_1 = __disposeResources(env_1);
|
|
155
|
+
if (result_1)
|
|
156
|
+
await result_1;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
export const testCreateWebSocket = (options = {}) => {
|
|
160
|
+
const createdUrls = [];
|
|
161
|
+
const sentMessages = [];
|
|
162
|
+
const stateByUrl = new Map();
|
|
163
|
+
const getState = (url) => {
|
|
164
|
+
const state = stateByUrl.get(url);
|
|
165
|
+
assert(state, `Test WebSocket for ${url} does not exist.`);
|
|
166
|
+
return state;
|
|
167
|
+
};
|
|
168
|
+
const createWebSocket = (url, socketOptions) => () => {
|
|
169
|
+
if (options.throwOnCreate) {
|
|
170
|
+
throw new Error("testCreateWebSocket is configured to throw on create");
|
|
171
|
+
}
|
|
172
|
+
createdUrls.push(url);
|
|
173
|
+
stateByUrl.set(url, {
|
|
174
|
+
options: socketOptions,
|
|
175
|
+
isOpen: options.isOpen ?? true,
|
|
176
|
+
isDisposed: false,
|
|
177
|
+
});
|
|
178
|
+
return ok({
|
|
179
|
+
send: (data) => {
|
|
180
|
+
const state = getState(url);
|
|
181
|
+
if (state.isDisposed || !state.isOpen) {
|
|
182
|
+
return err({ type: "WebSocketSendError" });
|
|
183
|
+
}
|
|
184
|
+
sentMessages.push({ url, data });
|
|
185
|
+
return ok();
|
|
186
|
+
},
|
|
187
|
+
getReadyState: () => {
|
|
188
|
+
const state = getState(url);
|
|
189
|
+
if (state.isDisposed)
|
|
190
|
+
return "closed";
|
|
191
|
+
return state.isOpen ? "open" : "closed";
|
|
192
|
+
},
|
|
193
|
+
isOpen: () => {
|
|
194
|
+
const state = getState(url);
|
|
195
|
+
return !state.isDisposed && state.isOpen;
|
|
196
|
+
},
|
|
197
|
+
[Symbol.asyncDispose]: () => {
|
|
198
|
+
const state = getState(url);
|
|
199
|
+
state.isDisposed = true;
|
|
200
|
+
state.isOpen = false;
|
|
201
|
+
return Promise.resolve();
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
return Object.assign(createWebSocket, {
|
|
206
|
+
createdUrls,
|
|
207
|
+
sentMessages,
|
|
208
|
+
message: (url, data) => {
|
|
209
|
+
getState(url).options?.onMessage?.(data);
|
|
113
210
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return;
|
|
119
|
-
isDisposed = true;
|
|
120
|
-
reconnectController.abort();
|
|
121
|
-
disposeSocket();
|
|
122
|
-
disposePromise?.();
|
|
211
|
+
open: (url) => {
|
|
212
|
+
const state = getState(url);
|
|
213
|
+
state.isOpen = true;
|
|
214
|
+
state.options?.onOpen?.();
|
|
123
215
|
},
|
|
124
|
-
};
|
|
216
|
+
});
|
|
125
217
|
};
|
|
126
218
|
const nativeToStringState = {
|
|
127
|
-
[WebSocket.CONNECTING]: "connecting",
|
|
128
|
-
[WebSocket.OPEN]: "open",
|
|
129
|
-
[WebSocket.CLOSING]: "closing",
|
|
130
|
-
[WebSocket.CLOSED]: "closed",
|
|
219
|
+
[globalThis.WebSocket.CONNECTING]: "connecting",
|
|
220
|
+
[globalThis.WebSocket.OPEN]: "open",
|
|
221
|
+
[globalThis.WebSocket.CLOSING]: "closing",
|
|
222
|
+
[globalThis.WebSocket.CLOSED]: "closed",
|
|
131
223
|
};
|
package/dist/src/Worker.d.ts
CHANGED
|
@@ -1,67 +1,252 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Platform-agnostic Worker abstractions.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { Brand } from "./Brand.js";
|
|
7
|
+
import type { ConsoleDep, ConsoleStoreOutputEntryDep } from "./Console.js";
|
|
8
|
+
/**
|
|
9
|
+
* Platform-agnostic Worker.
|
|
10
|
+
*
|
|
11
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Worker
|
|
12
|
+
*/
|
|
13
|
+
export interface Worker<Input, Output = never> extends MessagePort<Input, Output> {
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Platform-agnostic SharedWorker.
|
|
17
|
+
*
|
|
18
|
+
* A shared worker is shared across multiple clients (tabs, windows, iframes)
|
|
19
|
+
* and provides a port for bidirectional communication with each client.
|
|
20
|
+
*
|
|
21
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
|
|
22
|
+
*/
|
|
23
|
+
export interface SharedWorker<Input, Output = never> extends Disposable {
|
|
24
|
+
/** Port for communicating with the shared worker. */
|
|
25
|
+
readonly port: MessagePort<Input, Output>;
|
|
8
26
|
}
|
|
9
|
-
|
|
10
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Platform-agnostic MessagePort.
|
|
29
|
+
*
|
|
30
|
+
* Note: There is no reliable way to detect when a port is closed or
|
|
31
|
+
* disconnected. Calling `postMessage` on a disposed port does not throw — it
|
|
32
|
+
* silently fails. To detect dead ports, use a heartbeat pattern where the other
|
|
33
|
+
* end periodically sends "alive" messages and stale ports are pruned after a
|
|
34
|
+
* timeout.
|
|
35
|
+
*
|
|
36
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
|
37
|
+
*/
|
|
38
|
+
export interface MessagePort<Input, Output = never> extends Disposable {
|
|
39
|
+
readonly postMessage: (message: Input, transfer?: ReadonlyArray<Transferable>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Handler for incoming messages. Messages are queued until this is assigned,
|
|
42
|
+
* matching native `MessagePort` behavior where setting `onmessage` implicitly
|
|
43
|
+
* calls `start()`. This enables safe async initialization — the sender can
|
|
44
|
+
* post messages immediately while the receiver sets up.
|
|
45
|
+
*/
|
|
46
|
+
onMessage: ((message: Output) => void) | null;
|
|
47
|
+
/**
|
|
48
|
+
* The native underlying port for transferring via `postMessage`.
|
|
49
|
+
*
|
|
50
|
+
* ### Example
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* sharedWorker.port.postMessage(
|
|
54
|
+
* { type: "InitConsole", port: consoleChannel.port1.native },
|
|
55
|
+
* [consoleChannel.port1.native],
|
|
56
|
+
* );
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
readonly native: NativeMessagePort<Input, Output>;
|
|
11
60
|
}
|
|
12
61
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
export declare const createInitializedWorker: <Input extends {
|
|
32
|
-
readonly type: string;
|
|
33
|
-
} & HasInit<Input>, Output extends {
|
|
34
|
-
readonly type: string;
|
|
35
|
-
} & HasWorkerErrorOutput<Output>, Deps>({ init, onMessage, }: {
|
|
36
|
-
readonly init: (initMessage: Extract<Input, {
|
|
37
|
-
type: "init";
|
|
38
|
-
}>, postMessage: (msg: Output) => void, withErrorReporting: WithErrorReporting) => Promise<Deps | null>;
|
|
39
|
-
readonly onMessage: (deps: Deps) => (message: Exclude<Input, {
|
|
40
|
-
type: "init";
|
|
41
|
-
}>) => void;
|
|
42
|
-
}) => Worker<Input, Output>;
|
|
43
|
-
/** Type helper to extract message types from a union type */
|
|
44
|
-
export type MessageHandlers<Input extends {
|
|
45
|
-
readonly type: string;
|
|
46
|
-
}, Deps> = {
|
|
47
|
-
readonly [K in Input["type"]]: (deps: Deps) => (message: Extract<Input, {
|
|
48
|
-
type: K;
|
|
49
|
-
}>) => void;
|
|
62
|
+
* Objects whose ownership can be transferred between threads via `postMessage`.
|
|
63
|
+
*
|
|
64
|
+
* Intentionally scoped to types Evolu uses. The web platform defines additional
|
|
65
|
+
* transferable types (`ImageBitmap`, `OffscreenCanvas`, `ReadableStream`, etc.)
|
|
66
|
+
* that can be added here if needed.
|
|
67
|
+
*/
|
|
68
|
+
export type Transferable = NativeMessagePort<any, any> | ArrayBuffer;
|
|
69
|
+
/**
|
|
70
|
+
* Opaque type for platform-specific native MessagePort.
|
|
71
|
+
*
|
|
72
|
+
* Exists because `postMessage` transfer requires the native object itself, not
|
|
73
|
+
* a wrapper. Ensures type-safe wiring between {@link MessagePort.native} and
|
|
74
|
+
* {@link CreateMessagePort} without exposing platform details.
|
|
75
|
+
*/
|
|
76
|
+
export type NativeMessagePort<Input = unknown, Output = never> = Brand<"NativeMessagePort"> & {
|
|
77
|
+
readonly [nativeMessagePortInput]?: Input;
|
|
78
|
+
readonly [nativeMessagePortOutput]?: Output;
|
|
50
79
|
};
|
|
80
|
+
declare const nativeMessagePortInput: unique symbol;
|
|
81
|
+
declare const nativeMessagePortOutput: unique symbol;
|
|
82
|
+
/** Factory function to create a {@link MessagePort} from a native port. */
|
|
83
|
+
export type CreateMessagePort = <Input, Output = never>(nativePort: NativeMessagePort<Input, Output>) => MessagePort<Input, Output>;
|
|
84
|
+
export interface CreateMessagePortDep {
|
|
85
|
+
readonly createMessagePort: CreateMessagePort;
|
|
86
|
+
}
|
|
87
|
+
/** Common dependencies for worker entry points. */
|
|
88
|
+
export type WorkerDeps = ConsoleDep & ConsoleStoreOutputEntryDep & CreateMessagePortDep;
|
|
89
|
+
/**
|
|
90
|
+
* Platform-agnostic MessageChannel.
|
|
91
|
+
*
|
|
92
|
+
* Creates two entangled ports: keep one and transfer the other (e.g., to a
|
|
93
|
+
* SharedWorker via `postMessage` with `transfer`). Messages sent to one port
|
|
94
|
+
* are received by the other.
|
|
95
|
+
*
|
|
96
|
+
* Messages are queued until `onMessage` is assigned, enabling safe async
|
|
97
|
+
* initialization. The sender can post messages immediately while the receiver
|
|
98
|
+
* performs async setup — no manual buffering required.
|
|
99
|
+
*
|
|
100
|
+
* For one-way communication, omit `Output` (defaults to `never`).
|
|
101
|
+
*
|
|
102
|
+
* ### Example
|
|
103
|
+
*
|
|
104
|
+
* Transfer a channel port to a SharedWorker for async initialization:
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* // Main thread: create channel, transfer port1, use port2 immediately.
|
|
108
|
+
* const channel = createMessageChannel<EvoluRequest, EvoluResponse>();
|
|
109
|
+
*
|
|
110
|
+
* sharedWorker.port.postMessage(
|
|
111
|
+
* { type: "CreateEvolu", port: channel.port1.native },
|
|
112
|
+
* [channel.port1.native],
|
|
113
|
+
* );
|
|
114
|
+
*
|
|
115
|
+
* // Safe to send immediately — messages queue until worker is ready.
|
|
116
|
+
* channel.port2.postMessage({ type: "Query", query });
|
|
117
|
+
* channel.port2.onMessage = (response) => {
|
|
118
|
+
* handleResponse(response);
|
|
119
|
+
* };
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* // Worker: receive the port, do async init, then start listening.
|
|
124
|
+
* const evoluPort = createMessagePort<EvoluResponse, EvoluRequest>(
|
|
125
|
+
* message.port,
|
|
126
|
+
* );
|
|
127
|
+
* await openDatabase(name);
|
|
128
|
+
* evoluPort.onMessage = (request) => {
|
|
129
|
+
* handleRequest(request);
|
|
130
|
+
* };
|
|
131
|
+
* // Queued messages are now delivered in order.
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
|
|
135
|
+
*/
|
|
136
|
+
export interface MessageChannel<Input, Output = never> extends Disposable {
|
|
137
|
+
/** The first port of the channel. */
|
|
138
|
+
readonly port1: MessagePort<Input, Output>;
|
|
139
|
+
/** The second port of the channel. */
|
|
140
|
+
readonly port2: MessagePort<Output, Input>;
|
|
141
|
+
}
|
|
142
|
+
/** Factory function to create a {@link MessageChannel}. */
|
|
143
|
+
export type CreateMessageChannel = <Input, Output = never>() => MessageChannel<Input, Output>;
|
|
144
|
+
export interface CreateMessageChannelDep {
|
|
145
|
+
readonly createMessageChannel: CreateMessageChannel;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Typed `self` for code running inside a dedicated worker.
|
|
149
|
+
*
|
|
150
|
+
* This is the worker-side counterpart to {@link Worker} — a typed
|
|
151
|
+
* {@link MessagePort} that wraps `self` inside the worker.
|
|
152
|
+
*/
|
|
153
|
+
export interface WorkerSelf<Input, Output = never> extends MessagePort<Output, Input> {
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Typed `self` for code running inside a shared worker.
|
|
157
|
+
*
|
|
158
|
+
* This is the worker-side counterpart to {@link SharedWorker}. It wraps `self`
|
|
159
|
+
* inside the shared worker, providing typed `onConnect` callbacks.
|
|
160
|
+
*/
|
|
161
|
+
export interface SharedWorkerSelf<Input, Output = never> extends Disposable {
|
|
162
|
+
onConnect: ((port: MessagePort<Output, Input>) => void) | null;
|
|
163
|
+
}
|
|
51
164
|
/**
|
|
52
|
-
* Creates
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
165
|
+
* Creates an in-memory {@link Worker}.
|
|
166
|
+
*
|
|
167
|
+
* This is a memory-only fallback for platforms without native worker support.
|
|
168
|
+
* Message delivery is asynchronous in-process.
|
|
169
|
+
*/
|
|
170
|
+
export declare const createWorker: <Input, Output = never>(initWorker: (self: WorkerSelf<Input, Output>) => void) => Worker<Input, Output>;
|
|
171
|
+
/**
|
|
172
|
+
* Creates an in-memory {@link SharedWorker}.
|
|
173
|
+
*
|
|
174
|
+
* This is a memory-only fallback for platforms without native SharedWorker
|
|
175
|
+
* support. Connection is synchronous while message delivery is asynchronous
|
|
176
|
+
* in-process.
|
|
177
|
+
*
|
|
178
|
+
* Intended usage is one shared worker instance per process/app runtime.
|
|
179
|
+
*/
|
|
180
|
+
export declare const createSharedWorker: <Input, Output = never>(initWorker: (self: SharedWorkerSelf<Input, Output>) => void) => SharedWorker<Input, Output>;
|
|
181
|
+
/**
|
|
182
|
+
* Creates an in-memory {@link MessageChannel}.
|
|
183
|
+
*
|
|
184
|
+
* This is a memory-only fallback for platforms without native MessageChannel
|
|
185
|
+
* support. Message delivery is asynchronous in-process.
|
|
186
|
+
*/
|
|
187
|
+
export declare const createMessageChannel: CreateMessageChannel;
|
|
188
|
+
/**
|
|
189
|
+
* Creates an in-memory {@link MessagePort} from a native token.
|
|
190
|
+
*
|
|
191
|
+
* This is a memory-only fallback for platforms without native MessagePort
|
|
192
|
+
* support. Message delivery through returned ports is asynchronous in-process.
|
|
193
|
+
*/
|
|
194
|
+
export declare const createMessagePort: CreateMessagePort;
|
|
195
|
+
/**
|
|
196
|
+
* Test {@link Worker} with access to its paired worker-side `self`.
|
|
197
|
+
*
|
|
198
|
+
* Use `self` to simulate messages and behavior from inside the worker.
|
|
199
|
+
*/
|
|
200
|
+
export interface TestWorker<Input, Output = never> extends Worker<Input, Output> {
|
|
201
|
+
/** Typed `self` counterpart for worker-side testing assertions. */
|
|
202
|
+
readonly self: WorkerSelf<Input, Output>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Test {@link SharedWorker} with direct access to `self` and `connect`.
|
|
206
|
+
*
|
|
207
|
+
* Call `connect()` to simulate a client connection and trigger
|
|
208
|
+
* `self.onConnect`.
|
|
209
|
+
*/
|
|
210
|
+
export interface TestSharedWorker<Input, Output = never> extends SharedWorker<Input, Output> {
|
|
211
|
+
readonly self: SharedWorkerSelf<Input, Output>;
|
|
212
|
+
readonly connect: () => void;
|
|
213
|
+
}
|
|
214
|
+
/** {@link MessageChannel} with disposal tracking for testing. */
|
|
215
|
+
export interface TestMessageChannel<Input, Output = never> extends MessageChannel<Input, Output> {
|
|
216
|
+
readonly isDisposed: () => boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Creates a connected {@link TestWorker} for testing.
|
|
220
|
+
*
|
|
221
|
+
* The returned worker includes its typed {@link TestWorker.self} counterpart, so
|
|
222
|
+
* tests can exercise dedicated worker communication without a real thread.
|
|
223
|
+
*/
|
|
224
|
+
export declare const testCreateWorker: <Input, Output = never>() => TestWorker<Input, Output>;
|
|
225
|
+
/**
|
|
226
|
+
* Creates a connected {@link TestSharedWorker} for testing.
|
|
227
|
+
*
|
|
228
|
+
* The returned worker includes `self` and `connect` so tests can exercise the
|
|
229
|
+
* full worker ↔ client pipeline without a real worker thread.
|
|
230
|
+
*/
|
|
231
|
+
export declare const testCreateSharedWorker: <Input, Output = never>() => TestSharedWorker<Input, Output>;
|
|
232
|
+
/**
|
|
233
|
+
* Creates an in-memory {@link MessageChannel} for testing.
|
|
234
|
+
*
|
|
235
|
+
* Messages are queued until `onMessage` is assigned, matching the browser
|
|
236
|
+
* MessagePort behavior where the port message queue starts disabled.
|
|
237
|
+
*
|
|
238
|
+
* Both ports are registered in the native port registry so
|
|
239
|
+
* {@link testCreateMessagePort} can look them up by their native token.
|
|
240
|
+
*/
|
|
241
|
+
export declare const testCreateMessageChannel: <Input, Output = never>() => TestMessageChannel<Input, Output>;
|
|
242
|
+
/** Creates an in-memory {@link CreateMessagePort} for testing. */
|
|
243
|
+
export declare const testCreateMessagePort: CreateMessagePort;
|
|
244
|
+
/**
|
|
245
|
+
* Waits long enough for multi-hop in-memory worker message delivery in tests.
|
|
246
|
+
*
|
|
247
|
+
* Some flows require several queued macrotasks across worker/message-port
|
|
248
|
+
* boundaries; this helper advances that pipeline deterministically.
|
|
249
|
+
*/
|
|
250
|
+
export declare const testWaitForWorkerMessage: () => Promise<void>;
|
|
66
251
|
export {};
|
|
67
252
|
//# sourceMappingURL=Worker.d.ts.map
|