@evolu/common 8.0.0-next.0 → 8.0.0-next.2
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/LICENSE +21 -0
- package/dist/src/Lookup.d.ts +2 -0
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +8 -0
- package/dist/src/Polyfills.d.ts +7 -5
- package/dist/src/Polyfills.d.ts.map +1 -1
- package/dist/src/Polyfills.js +35 -5
- package/dist/src/Task.d.ts +11 -0
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +11 -0
- package/dist/src/Test.d.ts +28 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +31 -0
- package/dist/src/Time.d.ts +4 -3
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +14 -7
- package/dist/src/Type.d.ts +6 -1
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +8 -0
- package/dist/src/WebSocket.d.ts +21 -4
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +85 -16
- package/dist/src/local-first/Db.d.ts +2 -2
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +18 -19
- package/dist/src/local-first/Protocol.d.ts +2 -2
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Relay.d.ts +18 -13
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +11 -17
- package/dist/src/local-first/Shared.d.ts +1 -1
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Storage.d.ts +6 -7
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +6 -6
- package/package.json +15 -12
- package/src/Lookup.ts +14 -0
- package/src/Polyfills.ts +60 -5
- package/src/Task.ts +11 -0
- package/src/Test.ts +41 -1
- package/src/Time.ts +15 -8
- package/src/Type.ts +15 -0
- package/src/WebSocket.ts +125 -21
- package/src/local-first/Db.ts +20 -24
- package/src/local-first/Protocol.ts +2 -5
- package/src/local-first/Relay.ts +46 -39
- package/src/local-first/Shared.ts +2 -2
- package/src/local-first/Storage.ts +11 -12
|
@@ -21,8 +21,9 @@ export interface RelayConfig extends StorageConfig {
|
|
|
21
21
|
* Optional callback to check if an {@link OwnerId} is allowed to access the
|
|
22
22
|
* relay. If this callback is not provided, all owners are allowed.
|
|
23
23
|
*
|
|
24
|
-
* The callback receives the {@link OwnerId} and
|
|
25
|
-
*
|
|
24
|
+
* The callback receives the {@link OwnerId} and an options object with an
|
|
25
|
+
* abort `AbortSignal`, and returns a {@link Awaitable} boolean: `true` to
|
|
26
|
+
* allow access, or `false` to deny.
|
|
26
27
|
*
|
|
27
28
|
* The callback can be synchronous (for SQLite or in-memory checks) or
|
|
28
29
|
* asynchronous (for calling remote APIs).
|
|
@@ -37,9 +38,9 @@ export interface RelayConfig extends StorageConfig {
|
|
|
37
38
|
*
|
|
38
39
|
* Owners specify which relays to connect to via `OwnerTransport`. In
|
|
39
40
|
* WebSocket-based implementations, this check occurs before accepting the
|
|
40
|
-
* connection, with the OwnerId typically extracted from the URL
|
|
41
|
-
* `ws://localhost:4000
|
|
42
|
-
* correct format for OwnerId extraction.
|
|
41
|
+
* connection, with the OwnerId typically extracted from the URL query string
|
|
42
|
+
* (e.g., `ws://localhost:4000?ownerId=...`). The relay requires the URL to be
|
|
43
|
+
* in the correct format for OwnerId extraction.
|
|
43
44
|
*
|
|
44
45
|
* ### Example
|
|
45
46
|
*
|
|
@@ -56,22 +57,26 @@ export interface RelayConfig extends StorageConfig {
|
|
|
56
57
|
*
|
|
57
58
|
*
|
|
58
59
|
* // Relay
|
|
59
|
-
* isOwnerAllowed: (ownerId) =>
|
|
60
|
+
* isOwnerAllowed: (ownerId, { signal: _signal }) =>
|
|
60
61
|
* Promise.resolve(ownerId === "6jy_2F4RT5qqeLgJ14_dnQ"),
|
|
61
62
|
* ```
|
|
62
63
|
*/
|
|
63
|
-
readonly isOwnerAllowed?: (ownerId: OwnerId
|
|
64
|
+
readonly isOwnerAllowed?: (ownerId: OwnerId, options: {
|
|
65
|
+
/** Aborted when the relay stops waiting for the access check. */
|
|
66
|
+
readonly signal: AbortSignal;
|
|
67
|
+
}) => Awaitable<boolean>;
|
|
64
68
|
}
|
|
65
69
|
/**
|
|
66
|
-
*
|
|
67
|
-
* between Evolu clients.
|
|
70
|
+
* Sync and backup relay for Evolu clients.
|
|
68
71
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
72
|
+
* A relay syncs and backs up encrypted data for Evolu apps. Evolu apps can use
|
|
73
|
+
* multiple relays at the same time, combining self-hosted and cloud relays for
|
|
74
|
+
* resilience. Relays are blind by design: they transmit and store encrypted
|
|
75
|
+
* data without understanding its shape or meaning.
|
|
73
76
|
*/
|
|
74
77
|
export interface Relay extends AsyncDisposable {
|
|
78
|
+
/** The TCP port actually bound by the relay. */
|
|
79
|
+
readonly port: number;
|
|
75
80
|
}
|
|
76
81
|
export declare const createRelaySqliteStorage: (deps: SqliteStorageDeps & TimingSafeEqualDep) => (config: StorageConfig) => Storage;
|
|
77
82
|
export declare const createRelayStorageTables: (deps: SqliteDep) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,IAAI,EAAsC,MAAM,YAAY,CAAC;AACtE,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAEV,iBAAiB,EACjB,OAAO,EACP,aAAa,EAEd,MAAM,cAAc,CAAC;AAStB,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE;QACP,iEAAiE;QACjE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;KAC9B,KACE,SAAS,CAAC,OAAO,CAAC,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,KAAM,SAAQ,eAAe;IAC5C,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,wBAAwB,GAClC,MAAM,iBAAiB,GAAG,kBAAkB,MAC5C,QAAQ,aAAa,KAAG,OAuLxB,CAAC;AAEJ,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,IAuB1D,CAAC"}
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { filterArray, firstInArray, isNonEmptyArray, mapArray, } from "../Array.js";
|
|
6
|
+
import { dedupeArray, filterArray, firstInArray, isNonEmptyArray, mapArray, } from "../Array.js";
|
|
7
7
|
import { assert } from "../Assert.js";
|
|
8
|
-
import { err,
|
|
8
|
+
import { err, ok } from "../Result.js";
|
|
9
9
|
import { sql } from "../Sqlite.js";
|
|
10
10
|
import { createMutexByKey } from "../Task.js";
|
|
11
|
-
import { Name, PositiveInt } from "../Type.js";
|
|
11
|
+
import { Name, PositiveInt, uint8ArrayToBase64Url } from "../Type.js";
|
|
12
12
|
import { isPromiseLike } from "../Types.js";
|
|
13
13
|
import { OwnerId, ownerIdBytesToOwnerId,
|
|
14
14
|
// OwnerTransport,
|
|
15
15
|
OwnerWriteKey, } from "./Owner.js";
|
|
16
|
-
import { createBaseSqliteStorage,
|
|
16
|
+
import { createBaseSqliteStorage, getTimestampInsertStrategy, readOwnerUsageOrDefault, updateOwnerUsage, } from "./Storage.js";
|
|
17
17
|
import { timestampToTimestampBytes } from "./Timestamp.js";
|
|
18
18
|
export const createRelaySqliteStorage = (deps) => (config) => {
|
|
19
19
|
const sqliteStorageBase = createBaseSqliteStorage(deps);
|
|
@@ -56,19 +56,19 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
56
56
|
},
|
|
57
57
|
writeMessages: (ownerIdBytes, messages) => async (run) => {
|
|
58
58
|
const ownerId = ownerIdBytesToOwnerId(ownerIdBytes);
|
|
59
|
-
const
|
|
59
|
+
const uniqueMessagesWithTimestampBytes = dedupeArray(mapArray(messages, (m) => ({
|
|
60
60
|
timestamp: timestampToTimestampBytes(m.timestamp),
|
|
61
61
|
change: m.change,
|
|
62
|
-
}));
|
|
63
|
-
|
|
64
|
-
const existingTimestampsResult = sqliteStorageBase.getExistingTimestamps(ownerIdBytes, mapArray(
|
|
65
|
-
const
|
|
66
|
-
const newMessages = filterArray(
|
|
62
|
+
})), (message) => uint8ArrayToBase64Url(message.timestamp));
|
|
63
|
+
return run(mutexByOwnerId.withLock(ownerId, async () => {
|
|
64
|
+
const existingTimestampsResult = sqliteStorageBase.getExistingTimestamps(ownerIdBytes, mapArray(uniqueMessagesWithTimestampBytes, (m) => m.timestamp));
|
|
65
|
+
const existingTimestampKeys = new Set(mapArray(existingTimestampsResult, uint8ArrayToBase64Url));
|
|
66
|
+
const newMessages = filterArray(uniqueMessagesWithTimestampBytes, (message) => !existingTimestampKeys.has(uint8ArrayToBase64Url(message.timestamp)));
|
|
67
67
|
// Nothing to write
|
|
68
68
|
if (!isNonEmptyArray(newMessages)) {
|
|
69
69
|
return ok();
|
|
70
70
|
}
|
|
71
|
-
const usage =
|
|
71
|
+
const usage = readOwnerUsageOrDefault(deps)(ownerIdBytes, firstInArray(newMessages).timestamp);
|
|
72
72
|
const incomingBytes = newMessages.reduce((sum, m) => sum + m.change.length, 0);
|
|
73
73
|
const newStoredBytes = PositiveInt.orThrow((usage.storedBytes ?? 0) + incomingBytes);
|
|
74
74
|
const quotaResult = config.isOwnerWithinQuota(ownerId, newStoredBytes);
|
|
@@ -99,12 +99,6 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
99
99
|
return ok();
|
|
100
100
|
});
|
|
101
101
|
}));
|
|
102
|
-
if (!result.ok) {
|
|
103
|
-
if (result.error.type === "AbortError")
|
|
104
|
-
return ok();
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
return ok();
|
|
108
102
|
},
|
|
109
103
|
readDbChange: (ownerId, timestamp) => {
|
|
110
104
|
const result = deps.sqlite.exec(sql `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Shared.d.ts","sourceRoot":"","sources":["../../../src/local-first/Shared.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Shared.d.ts","sourceRoot":"","sources":["../../../src/local-first/Shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import type { NonEmptyReadonlyArray } from "../Array.js";
|
|
7
7
|
import type { Brand } from "../Brand.js";
|
|
8
8
|
import type { RandomDep } from "../Random.js";
|
|
9
|
-
import type { Result } from "../Result.js";
|
|
10
9
|
import type { SqliteDep } from "../Sqlite.js";
|
|
11
10
|
import type { Task } from "../Task.js";
|
|
12
11
|
import type { InferType, Typed, TypeError } from "../Type.js";
|
|
@@ -253,12 +252,12 @@ export type StorageInsertTimestampStrategy = "append" | "prepend" | "insert";
|
|
|
253
252
|
export declare const getTimestampInsertStrategy: (timestamp: TimestampBytes, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => [strategy: StorageInsertTimestampStrategy, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes];
|
|
254
253
|
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
255
254
|
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => TimestampBytes;
|
|
256
|
-
/**
|
|
257
|
-
export declare const
|
|
258
|
-
storedBytes: NonNegativeInt | null;
|
|
259
|
-
firstTimestamp: TimestampBytes;
|
|
260
|
-
lastTimestamp: TimestampBytes;
|
|
261
|
-
}
|
|
255
|
+
/** Reads owner usage from SQLite and returns default bounds when absent. */
|
|
256
|
+
export declare const readOwnerUsageOrDefault: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, initialTimestamp: TimestampBytes) => {
|
|
257
|
+
readonly storedBytes: NonNegativeInt | null;
|
|
258
|
+
readonly firstTimestamp: TimestampBytes;
|
|
259
|
+
readonly lastTimestamp: TimestampBytes;
|
|
260
|
+
};
|
|
262
261
|
/**
|
|
263
262
|
* Updates timestamp bounds in evolu_usage table.
|
|
264
263
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/local-first/Storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/local-first/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1127,28 +1127,28 @@ export const getTimestampByIndex = (deps) => (ownerId, index) => {
|
|
|
1127
1127
|
`);
|
|
1128
1128
|
return result.rows[0].pt;
|
|
1129
1129
|
};
|
|
1130
|
-
/**
|
|
1131
|
-
export const
|
|
1130
|
+
/** Reads owner usage from SQLite and returns default bounds when absent. */
|
|
1131
|
+
export const readOwnerUsageOrDefault = (deps) => (ownerIdBytes, initialTimestamp) => {
|
|
1132
1132
|
const result = deps.sqlite.exec(sql `
|
|
1133
1133
|
select storedBytes, firstTimestamp, lastTimestamp
|
|
1134
1134
|
from evolu_usage
|
|
1135
1135
|
where ownerId = ${ownerIdBytes};
|
|
1136
1136
|
`);
|
|
1137
1137
|
if (!isNonEmptyArray(result.rows)) {
|
|
1138
|
-
return
|
|
1138
|
+
return {
|
|
1139
1139
|
storedBytes: null,
|
|
1140
1140
|
firstTimestamp: initialTimestamp,
|
|
1141
1141
|
lastTimestamp: initialTimestamp,
|
|
1142
|
-
}
|
|
1142
|
+
};
|
|
1143
1143
|
}
|
|
1144
1144
|
const row = firstInArray(result.rows);
|
|
1145
1145
|
assert(row.firstTimestamp, "not null");
|
|
1146
1146
|
assert(row.lastTimestamp, "not null");
|
|
1147
|
-
return
|
|
1147
|
+
return {
|
|
1148
1148
|
storedBytes: row.storedBytes,
|
|
1149
1149
|
firstTimestamp: row.firstTimestamp,
|
|
1150
1150
|
lastTimestamp: row.lastTimestamp,
|
|
1151
|
-
}
|
|
1151
|
+
};
|
|
1152
1152
|
};
|
|
1153
1153
|
/**
|
|
1154
1154
|
* Updates timestamp bounds in evolu_usage table.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/common",
|
|
3
|
-
"version": "8.0.0-next.
|
|
3
|
+
"version": "8.0.0-next.2",
|
|
4
4
|
"description": "TypeScript library and local-first platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
],
|
|
12
12
|
"author": "Daniel Steigerwald <daniel@steigerwald.cz>",
|
|
13
13
|
"license": "MIT",
|
|
14
|
-
"repository":
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/evoluhq/evolu.git"
|
|
17
|
+
},
|
|
15
18
|
"bugs": {
|
|
16
19
|
"url": "https://github.com/evoluhq/evolu/issues"
|
|
17
20
|
},
|
|
@@ -54,11 +57,6 @@
|
|
|
54
57
|
"src/**",
|
|
55
58
|
"README.md"
|
|
56
59
|
],
|
|
57
|
-
"scripts": {
|
|
58
|
-
"build": "tsc --build tsconfig.build.json",
|
|
59
|
-
"prepack": "npm run build",
|
|
60
|
-
"format": "prettier --write \"src/*.{ts,tsx,md}\""
|
|
61
|
-
},
|
|
62
60
|
"dependencies": {
|
|
63
61
|
"@noble/ciphers": "^2.1.1",
|
|
64
62
|
"@noble/hashes": "^2.0.1",
|
|
@@ -69,16 +67,17 @@
|
|
|
69
67
|
},
|
|
70
68
|
"devDependencies": {
|
|
71
69
|
"@bokuweb/zstd-wasm": "0.0.27",
|
|
72
|
-
"@evolu/typescript-config": "*",
|
|
73
70
|
"@types/better-sqlite3": "^7.6.13",
|
|
71
|
+
"@types/node": "^24.10.9",
|
|
74
72
|
"@types/ws": "^8.18.1",
|
|
75
73
|
"better-sqlite3": "^12.6.2",
|
|
76
74
|
"fast-check": "^4.5.3",
|
|
77
75
|
"playwright": "^1.58.1",
|
|
78
|
-
"typescript": "^
|
|
76
|
+
"typescript": "^6",
|
|
79
77
|
"webpack": "^5.98.0",
|
|
80
78
|
"ws": "^8.19.0",
|
|
81
|
-
"zod": "^4.3.6"
|
|
79
|
+
"zod": "^4.3.6",
|
|
80
|
+
"@evolu/typescript-config": "0.0.2"
|
|
82
81
|
},
|
|
83
82
|
"publishConfig": {
|
|
84
83
|
"access": "public"
|
|
@@ -86,5 +85,9 @@
|
|
|
86
85
|
"engines": {
|
|
87
86
|
"node": ">=24.0.0"
|
|
88
87
|
},
|
|
89
|
-
"sideEffects": false
|
|
90
|
-
|
|
88
|
+
"sideEffects": false,
|
|
89
|
+
"scripts": {
|
|
90
|
+
"build": "tsc --build tsconfig.build.json",
|
|
91
|
+
"format": "prettier --write \"src/*.{ts,tsx,md}\""
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/Lookup.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface LookupMap<K, V> extends Iterable<readonly [K, V]> {
|
|
|
46
46
|
readonly size: number;
|
|
47
47
|
readonly has: (key: K) => boolean;
|
|
48
48
|
readonly get: (key: K) => V | undefined;
|
|
49
|
+
readonly getOrInsert: (key: K, defaultValue: V) => V;
|
|
50
|
+
readonly getOrInsertComputed: (key: K, callbackfn: (key: K) => V) => V;
|
|
49
51
|
readonly getKey: (key: K) => K | undefined;
|
|
50
52
|
readonly set: (key: K, value: V) => LookupMap<K, V>;
|
|
51
53
|
readonly delete: (key: K) => boolean;
|
|
@@ -88,6 +90,18 @@ export const createLookupMap = <K, V, L>({
|
|
|
88
90
|
|
|
89
91
|
get: (key) => entriesByLookupKey.get(lookup(key))?.value,
|
|
90
92
|
|
|
93
|
+
getOrInsert: (key, defaultValue) =>
|
|
94
|
+
entriesByLookupKey.getOrInsert(lookup(key), {
|
|
95
|
+
key,
|
|
96
|
+
value: defaultValue,
|
|
97
|
+
}).value,
|
|
98
|
+
|
|
99
|
+
getOrInsertComputed: (key, callbackfn) =>
|
|
100
|
+
entriesByLookupKey.getOrInsertComputed(lookup(key), () => ({
|
|
101
|
+
key,
|
|
102
|
+
value: callbackfn(key),
|
|
103
|
+
})).value,
|
|
104
|
+
|
|
91
105
|
getKey: (key) => entriesByLookupKey.get(lookup(key))?.key,
|
|
92
106
|
|
|
93
107
|
set: (key, value) => {
|
package/src/Polyfills.ts
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Installs polyfills required by `@evolu/common`.
|
|
9
9
|
*
|
|
10
|
-
* Installs
|
|
11
|
-
* `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, and
|
|
12
|
-
* `SuppressedError`), which are not yet supported by Safari and React Native.
|
|
10
|
+
* Installs:
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* - Resource-management polyfills (`Symbol.dispose`, `Symbol.asyncDispose`,
|
|
13
|
+
* `DisposableStack`, `AsyncDisposableStack`, and `SuppressedError`) for
|
|
14
|
+
* Safari and React Native
|
|
15
|
+
* - `Map` and `WeakMap` upsert polyfills (`Map.prototype.getOrInsert`,
|
|
16
|
+
* `Map.prototype.getOrInsertComputed`, `WeakMap.prototype.getOrInsert`, and
|
|
17
|
+
* `WeakMap.prototype.getOrInsertComputed`) for Node 24 and React Native
|
|
16
18
|
*
|
|
17
19
|
* `@evolu/react-native` has its own `Polyfills` module and its
|
|
18
20
|
* `installPolyfills` calls this function first, then installs React Native
|
|
@@ -40,6 +42,7 @@ export const installPolyfills = (): void => {
|
|
|
40
42
|
* including WebKit.
|
|
41
43
|
*/
|
|
42
44
|
installDisposableStack();
|
|
45
|
+
installMapAndWeakMapUpsertPolyfills();
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
interface DisposableResource {
|
|
@@ -463,3 +466,55 @@ const createAsyncDisposableStackPolyfill = (
|
|
|
463
466
|
|
|
464
467
|
return AsyncDisposableStackPolyfill;
|
|
465
468
|
};
|
|
469
|
+
|
|
470
|
+
/** Installs `Map` and `WeakMap` upsert polyfills missing from the runtime. */
|
|
471
|
+
const installMapAndWeakMapUpsertPolyfills = (): void => {
|
|
472
|
+
const getOrInsert: typeof Map.prototype.getOrInsert = function (
|
|
473
|
+
this: Map<unknown, unknown> | WeakMap<object, unknown>,
|
|
474
|
+
key: unknown,
|
|
475
|
+
defaultValue: unknown,
|
|
476
|
+
): unknown {
|
|
477
|
+
if (!this.has(key as never)) {
|
|
478
|
+
this.set(key as never, defaultValue);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return this.get(key as never);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const getOrInsertComputed: typeof Map.prototype.getOrInsertComputed =
|
|
485
|
+
function (
|
|
486
|
+
this: Map<unknown, unknown> | WeakMap<object, unknown>,
|
|
487
|
+
key: unknown,
|
|
488
|
+
callbackFunction: (key: unknown) => unknown,
|
|
489
|
+
): unknown {
|
|
490
|
+
if (!this.has(key as never)) {
|
|
491
|
+
this.set(key as never, callbackFunction(key));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return this.get(key as never);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
if (typeof Map.prototype.getOrInsert !== "function") {
|
|
498
|
+
defineGlobalValue(Map.prototype, "getOrInsert", getOrInsert);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (typeof Map.prototype.getOrInsertComputed !== "function") {
|
|
502
|
+
defineGlobalValue(
|
|
503
|
+
Map.prototype,
|
|
504
|
+
"getOrInsertComputed",
|
|
505
|
+
getOrInsertComputed,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (typeof WeakMap.prototype.getOrInsert !== "function") {
|
|
510
|
+
defineGlobalValue(WeakMap.prototype, "getOrInsert", getOrInsert);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (typeof WeakMap.prototype.getOrInsertComputed !== "function") {
|
|
514
|
+
defineGlobalValue(
|
|
515
|
+
WeakMap.prototype,
|
|
516
|
+
"getOrInsertComputed",
|
|
517
|
+
getOrInsertComputed,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
};
|
package/src/Task.ts
CHANGED
|
@@ -1709,6 +1709,10 @@ const yieldImpl: () => Promise<void> =
|
|
|
1709
1709
|
*
|
|
1710
1710
|
* Optionally return a teardown function that runs on abort.
|
|
1711
1711
|
*
|
|
1712
|
+
* This helper can also wrap promise APIs that do not observe `AbortSignal`.
|
|
1713
|
+
* Start the promise work inside the callback, and aborting the Task stops
|
|
1714
|
+
* waiting for it immediately.
|
|
1715
|
+
*
|
|
1712
1716
|
* ### Example
|
|
1713
1717
|
*
|
|
1714
1718
|
* ```ts
|
|
@@ -1733,6 +1737,13 @@ const yieldImpl: () => Promise<void> =
|
|
|
1733
1737
|
* else ok(data);
|
|
1734
1738
|
* });
|
|
1735
1739
|
* });
|
|
1740
|
+
*
|
|
1741
|
+
* // Wrap a non-cooperating promise API. Aborting the Task stops waiting even
|
|
1742
|
+
* // if promiseApi itself ignores AbortSignal.
|
|
1743
|
+
* const fromPromiseApi = (): Task<string> =>
|
|
1744
|
+
* callback(({ ok, err }) => {
|
|
1745
|
+
* void promiseApi().then(ok, err);
|
|
1746
|
+
* });
|
|
1736
1747
|
* ```
|
|
1737
1748
|
*
|
|
1738
1749
|
* @group Composition
|
package/src/Test.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { Brand } from "./Brand.js";
|
|
7
8
|
import {
|
|
8
9
|
testCreateConsole,
|
|
9
10
|
type TestConsole,
|
|
@@ -11,9 +12,9 @@ import {
|
|
|
11
12
|
} from "./Console.js";
|
|
12
13
|
import { testCreateRandomBytes, type RandomBytes } from "./Crypto.js";
|
|
13
14
|
import {
|
|
14
|
-
type Random,
|
|
15
15
|
testCreateRandom,
|
|
16
16
|
testCreateRandomLib,
|
|
17
|
+
type Random,
|
|
17
18
|
type RandomLibDep,
|
|
18
19
|
} from "./Random.js";
|
|
19
20
|
import { createRun, type Run, type RunDeps } from "./Task.js";
|
|
@@ -25,6 +26,11 @@ import {
|
|
|
25
26
|
type TestTime,
|
|
26
27
|
type TestTimeDep,
|
|
27
28
|
} from "./Time.js";
|
|
29
|
+
import { createId, type Id } from "./Type.js";
|
|
30
|
+
|
|
31
|
+
export type TestCreateId = <B extends string = never>() => [B] extends [never]
|
|
32
|
+
? Id
|
|
33
|
+
: Id & Brand<B>;
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
36
|
* Cheap deterministic baseline deps for tests.
|
|
@@ -160,3 +166,37 @@ export function testCreateRun<D>(deps?: D): Run<TestDeps & D> {
|
|
|
160
166
|
export const testWaitForMacrotask = (
|
|
161
167
|
duration: Duration = minMillis,
|
|
162
168
|
): Promise<void> => setTimeout(duration);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Creates a deterministic `createId` helper.
|
|
172
|
+
*
|
|
173
|
+
* The returned function mirrors {@link createId}, but uses stable test entropy
|
|
174
|
+
* so each call yields the next deterministic pseudo-random id.
|
|
175
|
+
*
|
|
176
|
+
* Create one helper per test, or per reusable test setup helper such as
|
|
177
|
+
* `setupFoo`, so deterministic ids stay local to that setup.
|
|
178
|
+
*
|
|
179
|
+
* Avoid sharing one helper across a whole test file. Adding an extra `createId`
|
|
180
|
+
* call in one test would shift ids used by unrelated tests later in the file.
|
|
181
|
+
*
|
|
182
|
+
* ### Example
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* test("creates stable ids", () => {
|
|
186
|
+
* const createId = testCreateId();
|
|
187
|
+
*
|
|
188
|
+
* const callbackId = createId();
|
|
189
|
+
* const secondCallbackId = createId();
|
|
190
|
+
* const todoId = createId<"Todo">();
|
|
191
|
+
* });
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export const testCreateId = (): TestCreateId => {
|
|
195
|
+
const randomBytes = testCreateRandomBytes({
|
|
196
|
+
randomLib: testCreateRandomLib(),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return <B extends string = never>(): [B] extends [never]
|
|
200
|
+
? Id
|
|
201
|
+
: Id & Brand<B> => createId<B>({ randomBytes });
|
|
202
|
+
};
|
package/src/Time.ts
CHANGED
|
@@ -92,25 +92,32 @@ export interface TestTimeDep {
|
|
|
92
92
|
* their deadline.
|
|
93
93
|
*
|
|
94
94
|
* Set `autoIncrement` to automatically increment time by 1ms after each `now()`
|
|
95
|
-
* call
|
|
96
|
-
*
|
|
95
|
+
* call. `"microtask"` increments after the current turn, while `"sync"`
|
|
96
|
+
* increments immediately after each read. Omit it to keep time fixed until
|
|
97
|
+
* `advance()` is called.
|
|
97
98
|
*/
|
|
98
99
|
export const testCreateTime = (options?: {
|
|
99
100
|
readonly startAt?: Millis;
|
|
100
|
-
readonly autoIncrement?:
|
|
101
|
+
readonly autoIncrement?: "microtask" | "sync";
|
|
101
102
|
}): TestTime => {
|
|
102
103
|
let now = options?.startAt ?? minMillis;
|
|
103
|
-
const autoIncrement = options?.autoIncrement
|
|
104
|
+
const autoIncrement = options?.autoIncrement;
|
|
104
105
|
let nextId = 1;
|
|
105
106
|
|
|
106
107
|
const pending = new Map<number, { callback: () => void; runAt: number }>();
|
|
108
|
+
const incrementNow = (): void => {
|
|
109
|
+
now = Millis.orThrow(now + 1);
|
|
110
|
+
};
|
|
107
111
|
|
|
108
112
|
const getNowMillis = (): Millis => {
|
|
109
113
|
const result = now;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
switch (autoIncrement) {
|
|
115
|
+
case "sync":
|
|
116
|
+
incrementNow();
|
|
117
|
+
break;
|
|
118
|
+
case "microtask":
|
|
119
|
+
queueMicrotask(incrementNow);
|
|
120
|
+
break;
|
|
114
121
|
}
|
|
115
122
|
return result;
|
|
116
123
|
};
|
package/src/Type.ts
CHANGED
|
@@ -837,6 +837,18 @@ export interface Uint8ArrayError extends TypeError<"Uint8Array"> {}
|
|
|
837
837
|
export const formatUint8ArrayError =
|
|
838
838
|
/*#__PURE__*/ createBaseTypeErrorFormatter<Uint8ArrayError>();
|
|
839
839
|
|
|
840
|
+
/** @group Base Types */
|
|
841
|
+
export const ArrayBuffer = /*#__PURE__*/ base("ArrayBuffer", (value) =>
|
|
842
|
+
Object.prototype.toString.call(value as object) === "[object ArrayBuffer]"
|
|
843
|
+
? ok(value as globalThis.ArrayBuffer)
|
|
844
|
+
: err<ArrayBufferError>({ type: "ArrayBuffer", value }),
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
export interface ArrayBufferError extends TypeError<"ArrayBuffer"> {}
|
|
848
|
+
|
|
849
|
+
export const formatArrayBufferError =
|
|
850
|
+
/*#__PURE__*/ createBaseTypeErrorFormatter<ArrayBufferError>();
|
|
851
|
+
|
|
840
852
|
/**
|
|
841
853
|
* `instanceof` {@link Type}.
|
|
842
854
|
*
|
|
@@ -4504,6 +4516,7 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
|
|
|
4504
4516
|
| NullError
|
|
4505
4517
|
| FunctionError
|
|
4506
4518
|
| Uint8ArrayError
|
|
4519
|
+
| ArrayBufferError
|
|
4507
4520
|
| InstanceOfError
|
|
4508
4521
|
| EvoluTypeError
|
|
4509
4522
|
| CurrencyCodeError
|
|
@@ -4657,6 +4670,8 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
|
|
|
4657
4670
|
return formatFunctionError(error);
|
|
4658
4671
|
case "Uint8Array":
|
|
4659
4672
|
return formatUint8ArrayError(error);
|
|
4673
|
+
case "ArrayBuffer":
|
|
4674
|
+
return formatArrayBufferError(error);
|
|
4660
4675
|
case "InstanceOf":
|
|
4661
4676
|
return formatInstanceOfError(error);
|
|
4662
4677
|
case "EvoluType":
|