@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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/Lookup.d.ts +2 -0
  3. package/dist/src/Lookup.d.ts.map +1 -1
  4. package/dist/src/Lookup.js +8 -0
  5. package/dist/src/Polyfills.d.ts +7 -5
  6. package/dist/src/Polyfills.d.ts.map +1 -1
  7. package/dist/src/Polyfills.js +35 -5
  8. package/dist/src/Task.d.ts +11 -0
  9. package/dist/src/Task.d.ts.map +1 -1
  10. package/dist/src/Task.js +11 -0
  11. package/dist/src/Test.d.ts +28 -0
  12. package/dist/src/Test.d.ts.map +1 -1
  13. package/dist/src/Test.js +31 -0
  14. package/dist/src/Time.d.ts +4 -3
  15. package/dist/src/Time.d.ts.map +1 -1
  16. package/dist/src/Time.js +14 -7
  17. package/dist/src/Type.d.ts +6 -1
  18. package/dist/src/Type.d.ts.map +1 -1
  19. package/dist/src/Type.js +8 -0
  20. package/dist/src/WebSocket.d.ts +21 -4
  21. package/dist/src/WebSocket.d.ts.map +1 -1
  22. package/dist/src/WebSocket.js +85 -16
  23. package/dist/src/local-first/Db.d.ts +2 -2
  24. package/dist/src/local-first/Db.d.ts.map +1 -1
  25. package/dist/src/local-first/Db.js +18 -19
  26. package/dist/src/local-first/Protocol.d.ts +2 -2
  27. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  28. package/dist/src/local-first/Relay.d.ts +18 -13
  29. package/dist/src/local-first/Relay.d.ts.map +1 -1
  30. package/dist/src/local-first/Relay.js +11 -17
  31. package/dist/src/local-first/Shared.d.ts +1 -1
  32. package/dist/src/local-first/Shared.d.ts.map +1 -1
  33. package/dist/src/local-first/Storage.d.ts +6 -7
  34. package/dist/src/local-first/Storage.d.ts.map +1 -1
  35. package/dist/src/local-first/Storage.js +6 -6
  36. package/package.json +15 -12
  37. package/src/Lookup.ts +14 -0
  38. package/src/Polyfills.ts +60 -5
  39. package/src/Task.ts +11 -0
  40. package/src/Test.ts +41 -1
  41. package/src/Time.ts +15 -8
  42. package/src/Type.ts +15 -0
  43. package/src/WebSocket.ts +125 -21
  44. package/src/local-first/Db.ts +20 -24
  45. package/src/local-first/Protocol.ts +2 -5
  46. package/src/local-first/Relay.ts +46 -39
  47. package/src/local-first/Shared.ts +2 -2
  48. 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 returns a {@link Awaitable}
25
- * boolean: `true` to allow access, or `false` to deny.
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 Path (e.g.,
41
- * `ws://localhost:4000/<ownerId>`). The relay requires the URL to be in the
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) => Awaitable<boolean>;
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
- * A completely interchangeable server for syncing and backing up encrypted data
67
- * between Evolu clients.
70
+ * Sync and backup relay for Evolu clients.
68
71
  *
69
- * Unlike traditional servers, relays are blind by design—they transmit
70
- * encrypted data without understanding its shape or meaning. This enables true
71
- * decentralization and infinite horizontal scalability with minimal
72
- * infrastructure.
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;AASH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,IAAI,EAAe,MAAM,YAAY,CAAC;AAC/C,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC,OAAO,CAAC,CAAC;CACpE;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,KAAM,SAAQ,eAAe;CAAG;AAEjD,eAAO,MAAM,wBAAwB,GAClC,MAAM,iBAAiB,GAAG,kBAAkB,MAC5C,QAAQ,aAAa,KAAG,OA0LxB,CAAC;AAEJ,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,IAuB1D,CAAC"}
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, getOk, ok } from "../Result.js";
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, getOwnerUsage, getTimestampInsertStrategy, updateOwnerUsage, } from "./Storage.js";
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 messagesWithTimestampBytes = mapArray(messages, (m) => ({
59
+ const uniqueMessagesWithTimestampBytes = dedupeArray(mapArray(messages, (m) => ({
60
60
  timestamp: timestampToTimestampBytes(m.timestamp),
61
61
  change: m.change,
62
- }));
63
- const result = await run(mutexByOwnerId.withLock(ownerId, async () => {
64
- const existingTimestampsResult = sqliteStorageBase.getExistingTimestamps(ownerIdBytes, mapArray(messagesWithTimestampBytes, (m) => m.timestamp));
65
- const existingTimestampsSet = new Set(existingTimestampsResult.map((t) => t.toString()));
66
- const newMessages = filterArray(messagesWithTimestampBytes, (m) => !existingTimestampsSet.has(m.timestamp.toString()));
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 = getOk(getOwnerUsage(deps)(ownerIdBytes, firstInArray(newMessages).timestamp));
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 `
@@ -84,7 +84,7 @@ export interface DbWorkerInput {
84
84
  } | {
85
85
  readonly type: "ApplySyncMessage";
86
86
  readonly owner: Owner;
87
- readonly inputMessage: Uint8Array<ArrayBuffer>;
87
+ readonly inputMessage: Uint8Array;
88
88
  };
89
89
  };
90
90
  }
@@ -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
- /** Retrieves usage information for an owner from the evolu_usage table. */
257
- export declare const getOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, initialTimestamp: TimestampBytes) => Result<{
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":"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"}
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
- /** Retrieves usage information for an owner from the evolu_usage table. */
1131
- export const getOwnerUsage = (deps) => (ownerIdBytes, initialTimestamp) => {
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 ok({
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 ok({
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.0",
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": "evoluhq/evolu",
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": "^5.9.3",
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 resource-management polyfills (`Symbol.dispose`,
11
- * `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, and
12
- * `SuppressedError`), which are not yet supported by Safari and React Native.
10
+ * Installs:
13
11
  *
14
- * Evolu currently does not require any additional polyfills. If that changes,
15
- * this is where they will be installed.
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 via microtask (useful for tests that need monotonically increasing
96
- * values without explicit `advance()` calls).
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?: boolean;
101
+ readonly autoIncrement?: "microtask" | "sync";
101
102
  }): TestTime => {
102
103
  let now = options?.startAt ?? minMillis;
103
- const autoIncrement = options?.autoIncrement ?? false;
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
- if (autoIncrement) {
111
- queueMicrotask(() => {
112
- now = Millis.orThrow(now + 1);
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":