@evolu/common 8.0.0-next.4 → 8.0.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.
Files changed (237) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +44 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +44 -32
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +5 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Error.js +16 -0
  31. package/dist/src/Function.d.ts +119 -58
  32. package/dist/src/Function.d.ts.map +1 -1
  33. package/dist/src/Function.js +86 -43
  34. package/dist/src/Http.d.ts +394 -0
  35. package/dist/src/Http.d.ts.map +1 -0
  36. package/dist/src/Http.js +102 -0
  37. package/dist/src/Identicon.d.ts +16 -7
  38. package/dist/src/Identicon.d.ts.map +1 -1
  39. package/dist/src/Identicon.js +14 -5
  40. package/dist/src/LeakDetector.d.ts +88 -0
  41. package/dist/src/LeakDetector.d.ts.map +1 -0
  42. package/dist/src/LeakDetector.js +86 -0
  43. package/dist/src/LockManager.d.ts +18 -9
  44. package/dist/src/LockManager.d.ts.map +1 -1
  45. package/dist/src/LockManager.js +7 -8
  46. package/dist/src/Lookup.d.ts +43 -13
  47. package/dist/src/Lookup.d.ts.map +1 -1
  48. package/dist/src/Lookup.js +28 -8
  49. package/dist/src/Microtask.d.ts +1 -1
  50. package/dist/src/Microtask.d.ts.map +1 -1
  51. package/dist/src/Microtask.js +3 -7
  52. package/dist/src/Number.d.ts +49 -13
  53. package/dist/src/Number.d.ts.map +1 -1
  54. package/dist/src/Number.js +34 -5
  55. package/dist/src/Object.d.ts +160 -40
  56. package/dist/src/Object.d.ts.map +1 -1
  57. package/dist/src/Object.js +92 -46
  58. package/dist/src/Option.d.ts +10 -7
  59. package/dist/src/Option.d.ts.map +1 -1
  60. package/dist/src/Option.js +9 -6
  61. package/dist/src/Order.d.ts +21 -21
  62. package/dist/src/Order.d.ts.map +1 -1
  63. package/dist/src/Order.js +18 -21
  64. package/dist/src/Platform.d.ts +31 -0
  65. package/dist/src/Platform.d.ts.map +1 -1
  66. package/dist/src/Platform.js +98 -0
  67. package/dist/src/Random.d.ts +32 -17
  68. package/dist/src/Random.d.ts.map +1 -1
  69. package/dist/src/Random.js +12 -8
  70. package/dist/src/Redacted.d.ts +43 -29
  71. package/dist/src/Redacted.d.ts.map +1 -1
  72. package/dist/src/Redacted.js +14 -3
  73. package/dist/src/Ref.d.ts +11 -3
  74. package/dist/src/Ref.d.ts.map +1 -1
  75. package/dist/src/Ref.js +2 -2
  76. package/dist/src/RefCount.d.ts +2 -6
  77. package/dist/src/RefCount.d.ts.map +1 -1
  78. package/dist/src/RefCount.js +33 -90
  79. package/dist/src/Relation.d.ts +70 -5
  80. package/dist/src/Relation.d.ts.map +1 -1
  81. package/dist/src/Relation.js +77 -2
  82. package/dist/src/Resource.d.ts +590 -155
  83. package/dist/src/Resource.d.ts.map +1 -1
  84. package/dist/src/Resource.js +592 -343
  85. package/dist/src/Result.d.ts +838 -191
  86. package/dist/src/Result.d.ts.map +1 -1
  87. package/dist/src/Result.js +163 -102
  88. package/dist/src/Schedule.d.ts +684 -231
  89. package/dist/src/Schedule.d.ts.map +1 -1
  90. package/dist/src/Schedule.js +766 -295
  91. package/dist/src/Set.d.ts +89 -40
  92. package/dist/src/Set.d.ts.map +1 -1
  93. package/dist/src/Set.js +50 -29
  94. package/dist/src/Sqlite.d.ts +29 -72
  95. package/dist/src/Sqlite.d.ts.map +1 -1
  96. package/dist/src/Sqlite.js +41 -46
  97. package/dist/src/StackTrace.d.ts +40 -0
  98. package/dist/src/StackTrace.d.ts.map +1 -0
  99. package/dist/src/StackTrace.js +140 -0
  100. package/dist/src/Store.d.ts +5 -4
  101. package/dist/src/Store.d.ts.map +1 -1
  102. package/dist/src/Store.js +5 -4
  103. package/dist/src/String.d.ts +7 -1
  104. package/dist/src/String.d.ts.map +1 -1
  105. package/dist/src/String.js +20 -7
  106. package/dist/src/Task.d.ts +3572 -1770
  107. package/dist/src/Task.d.ts.map +1 -1
  108. package/dist/src/Task.js +1879 -1274
  109. package/dist/src/Test.d.ts +17 -125
  110. package/dist/src/Test.d.ts.map +1 -1
  111. package/dist/src/Test.js +17 -68
  112. package/dist/src/Time.d.ts +108 -49
  113. package/dist/src/Time.d.ts.map +1 -1
  114. package/dist/src/Time.js +172 -73
  115. package/dist/src/Type.d.ts +3542 -1926
  116. package/dist/src/Type.d.ts.map +1 -1
  117. package/dist/src/Type.js +3070 -1784
  118. package/dist/src/Types.d.ts +200 -108
  119. package/dist/src/Types.d.ts.map +1 -1
  120. package/dist/src/Types.js +39 -8
  121. package/dist/src/WebSocket.d.ts +48 -23
  122. package/dist/src/WebSocket.d.ts.map +1 -1
  123. package/dist/src/WebSocket.js +15 -6
  124. package/dist/src/Worker.d.ts +94 -36
  125. package/dist/src/Worker.d.ts.map +1 -1
  126. package/dist/src/Worker.js +58 -24
  127. package/dist/src/index.d.ts +55 -52
  128. package/dist/src/index.d.ts.map +1 -1
  129. package/dist/src/index.js +3 -1
  130. package/dist/src/intl/cs.d.ts +94 -0
  131. package/dist/src/intl/cs.d.ts.map +1 -0
  132. package/dist/src/intl/cs.js +220 -0
  133. package/dist/src/intl/index.d.ts +7 -0
  134. package/dist/src/intl/index.d.ts.map +1 -0
  135. package/dist/src/intl/index.js +6 -0
  136. package/dist/src/local-first/Db.d.ts +12 -8
  137. package/dist/src/local-first/Db.d.ts.map +1 -1
  138. package/dist/src/local-first/Db.js +109 -132
  139. package/dist/src/local-first/Error.d.ts +4 -4
  140. package/dist/src/local-first/Evolu.d.ts +338 -92
  141. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  142. package/dist/src/local-first/Evolu.js +47 -55
  143. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  144. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.d.ts +61 -24
  146. package/dist/src/local-first/Owner.d.ts.map +1 -1
  147. package/dist/src/local-first/Owner.js +45 -8
  148. package/dist/src/local-first/Protocol.d.ts +44 -31
  149. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  150. package/dist/src/local-first/Protocol.js +36 -13
  151. package/dist/src/local-first/Query.d.ts +147 -62
  152. package/dist/src/local-first/Query.d.ts.map +1 -1
  153. package/dist/src/local-first/Query.js +112 -58
  154. package/dist/src/local-first/Relay.d.ts +44 -14
  155. package/dist/src/local-first/Relay.d.ts.map +1 -1
  156. package/dist/src/local-first/Schema.d.ts +92 -41
  157. package/dist/src/local-first/Schema.d.ts.map +1 -1
  158. package/dist/src/local-first/Schema.js +25 -8
  159. package/dist/src/local-first/Shared.d.ts +23 -21
  160. package/dist/src/local-first/Shared.d.ts.map +1 -1
  161. package/dist/src/local-first/Shared.js +100 -57
  162. package/dist/src/local-first/Storage.d.ts +53 -32
  163. package/dist/src/local-first/Storage.d.ts.map +1 -1
  164. package/dist/src/local-first/Storage.js +194 -234
  165. package/dist/src/local-first/Timestamp.d.ts +21 -21
  166. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  167. package/dist/src/local-first/Timestamp.js +2 -2
  168. package/dist/src/local-first/index.d.ts +11 -11
  169. package/package.json +14 -13
  170. package/src/Array.ts +306 -205
  171. package/src/Assert.ts +54 -69
  172. package/src/BigInt.ts +6 -3
  173. package/src/Brand.ts +27 -40
  174. package/src/Buffer.ts +28 -12
  175. package/src/Cache.ts +9 -4
  176. package/src/Callbacks.ts +30 -19
  177. package/src/Console.ts +156 -84
  178. package/src/Crypto.ts +83 -41
  179. package/src/Eq.ts +95 -22
  180. package/src/Error.ts +8 -4
  181. package/src/Function.ts +167 -59
  182. package/src/Http.ts +549 -0
  183. package/src/Identicon.ts +16 -7
  184. package/src/LeakDetector.ts +182 -0
  185. package/src/LockManager.ts +43 -39
  186. package/src/Lookup.ts +62 -28
  187. package/src/Microtask.ts +19 -23
  188. package/src/Number.ts +64 -12
  189. package/src/Object.ts +173 -49
  190. package/src/Option.ts +10 -7
  191. package/src/Order.ts +23 -21
  192. package/src/Platform.ts +136 -0
  193. package/src/Polyfills.ts +6 -12
  194. package/src/Random.ts +32 -19
  195. package/src/Redacted.ts +44 -30
  196. package/src/Ref.ts +14 -6
  197. package/src/RefCount.ts +38 -80
  198. package/src/Relation.ts +202 -10
  199. package/src/Resource.ts +1199 -591
  200. package/src/Result.ts +930 -271
  201. package/src/Schedule.ts +859 -343
  202. package/src/Set.ts +89 -40
  203. package/src/Sqlite.ts +144 -138
  204. package/src/StackTrace.ts +230 -0
  205. package/src/Store.ts +8 -7
  206. package/src/String.ts +22 -5
  207. package/src/Task.ts +5101 -2832
  208. package/src/Test.ts +20 -168
  209. package/src/Time.ts +293 -90
  210. package/src/Type.ts +10034 -4117
  211. package/src/Types.ts +227 -110
  212. package/src/WebSocket.ts +64 -36
  213. package/src/Worker.ts +180 -72
  214. package/src/index.ts +55 -52
  215. package/src/intl/cs.ts +388 -0
  216. package/src/intl/index.ts +7 -0
  217. package/src/local-first/Db.ts +152 -179
  218. package/src/local-first/Error.ts +4 -4
  219. package/src/local-first/Evolu.ts +495 -250
  220. package/src/local-first/LocalAuth.ts +6 -12
  221. package/src/local-first/Owner.ts +58 -21
  222. package/src/local-first/Protocol.ts +49 -31
  223. package/src/local-first/Query.ts +146 -61
  224. package/src/local-first/Relay.ts +51 -21
  225. package/src/local-first/Schema.ts +109 -51
  226. package/src/local-first/Shared.ts +266 -226
  227. package/src/local-first/Storage.ts +266 -264
  228. package/src/local-first/Timestamp.ts +25 -18
  229. package/src/local-first/index.ts +11 -11
  230. package/dist/src/Skiplist.d.ts +0 -28
  231. package/dist/src/Skiplist.d.ts.map +0 -1
  232. package/dist/src/Skiplist.js +0 -64
  233. package/dist/src/Tracer.d.ts +0 -48
  234. package/dist/src/Tracer.d.ts.map +0 -1
  235. package/dist/src/Tracer.js +0 -6
  236. package/src/Skiplist.ts +0 -108
  237. package/src/Tracer.ts +0 -54
package/src/Console.ts CHANGED
@@ -4,17 +4,17 @@
4
4
  * @module
5
5
  */
6
6
 
7
- import { objectFrom } from "./Object.js";
8
- import type { ReadonlyStore } from "./Store.js";
9
- import { createStore } from "./Store.js";
10
- import type { Task } from "./Task.js";
7
+ import { objectFrom } from "./Object.ts";
8
+ import type { ReadonlyStore } from "./Store.ts";
9
+ import { createStore } from "./Store.ts";
10
+ import type { Task } from "./Task.ts";
11
11
  import {
12
12
  createTime,
13
13
  formatMillisAsClockTime,
14
14
  formatMillisAsDuration,
15
15
  type Millis,
16
16
  type TimeDep,
17
- } from "./Time.js";
17
+ } from "./Time.ts";
18
18
 
19
19
  /**
20
20
  * Platform-agnostic console with structured logging.
@@ -42,31 +42,47 @@ import {
42
42
  * {@link ReadonlyStore} for subscribing
43
43
  * - {@link createMultiOutput} — fans out to multiple outputs
44
44
  *
45
+ * Wrap logged values in named objects such as `{ config }` so developer tools
46
+ * display useful, expandable labels.
47
+ *
45
48
  * ### Example
46
49
  *
47
50
  * ```ts
48
- * // Basic usage - defaults to "log"
49
- * const console = createConsole();
51
+ * import {
52
+ * createConsole,
53
+ * createConsoleArrayOutput,
54
+ * type Console,
55
+ * type ConsoleEntry,
56
+ * type ConsoleLevel,
57
+ * } from "@evolu/common";
50
58
  *
51
- * // With formatting (timestamps and path prefixes)
52
- * const console = createConsole({
53
- * level: "info",
54
- * formatter: createConsoleFormatter()({ timestampFormat: "relative" }),
59
+ * const entries: Array<ConsoleEntry> = [];
60
+ * const root = createConsole({
61
+ * output: createConsoleArrayOutput(entries),
55
62
  * });
56
- *
57
- * // Children inherit level at creation, then are independent
58
- * const console = run.deps.console.child("relay");
59
- * console.setLevel("silent");
60
- *
61
- * // Tip: Wrap logged values in objects for labeled output in DevTools
62
- * console.info("Creating instance", { config }); // Good — expandable "config:" label
63
- * console.info("Creating instance", config); // Avoid — anonymous object, no label
64
- *
65
- * // Batch update via children
66
- * const setLevelRecursive = (c: Console, level: ConsoleLevel): void => {
67
- * c.setLevel(level);
68
- * for (const child of c.children) setLevelRecursive(child, level);
63
+ * const relay = root.child("relay");
64
+ * // Silence a noisy subsystem without affecting root logs.
65
+ * relay.setLevel("silent");
66
+ * relay.info("polling");
67
+ * root.info("Creating instance", { config: { port: 4000 } });
68
+ *
69
+ * expect(root.getLevel()).toBe("log");
70
+ * expect(relay.getLevel()).toBe("silent");
71
+ * expect(entries[0]?.args).toEqual([
72
+ * "Creating instance",
73
+ * { config: { port: 4000 } },
74
+ * ]);
75
+ *
76
+ * const setLevelRecursive = (
77
+ * console: Console,
78
+ * level: ConsoleLevel,
79
+ * ): void => {
80
+ * console.setLevel(level);
81
+ * for (const child of console.children) setLevelRecursive(child, level);
69
82
  * };
83
+ * setLevelRecursive(root, "warn");
84
+ * expect(root.getLevel()).toBe("warn");
85
+ * expect(relay.getLevel()).toBe("warn");
70
86
  * ```
71
87
  *
72
88
  * Console intentionally does not use {@link Task}. Logging must be as fast as
@@ -177,13 +193,7 @@ export interface ConsoleDep {
177
193
  * - `"silent"` — Disables all logging
178
194
  */
179
195
  export type ConsoleLevel =
180
- | "trace"
181
- | "debug"
182
- | "log"
183
- | "info"
184
- | "warn"
185
- | "error"
186
- | "silent";
196
+ "trace" | "debug" | "log" | "info" | "warn" | "error" | "silent";
187
197
 
188
198
  /**
189
199
  * Structured log entry captured by {@link Console}.
@@ -294,10 +304,7 @@ export interface ConsoleFormatterConfig {
294
304
 
295
305
  /** Timestamp format for {@link ConsoleFormatterConfig}. */
296
306
  export type ConsoleEntryTimestampFormat =
297
- | "relative"
298
- | "absolute"
299
- | "iso"
300
- | "none";
307
+ "relative" | "absolute" | "iso" | "none";
301
308
 
302
309
  /**
303
310
  * A {@link ConsoleOutput} that stores the latest entry in a
@@ -308,12 +315,26 @@ export type ConsoleEntryTimestampFormat =
308
315
  * ### Example
309
316
  *
310
317
  * ```ts
318
+ * import {
319
+ * createConsole,
320
+ * createConsoleStoreOutput,
321
+ * type ConsoleEntry,
322
+ * } from "@evolu/common";
323
+ *
311
324
  * const storeOutput = createConsoleStoreOutput();
312
325
  * const console = createConsole({ output: storeOutput });
326
+ * let forwardedEntry: ConsoleEntry | null = null;
327
+ * const unsubscribe = storeOutput.entry.subscribe(() => {
328
+ * forwardedEntry = storeOutput.entry.get();
329
+ * });
330
+ *
331
+ * console.child("relay").info("connected");
332
+ * unsubscribe();
313
333
  *
314
- * storeOutput.entry.subscribe(() => {
315
- * const entry = storeOutput.entry.get();
316
- * if (entry) forwardToClient(entry);
334
+ * expect(forwardedEntry).toEqual({
335
+ * method: "info",
336
+ * path: ["relay"],
337
+ * args: ["connected"],
317
338
  * });
318
339
  * ```
319
340
  */
@@ -432,16 +453,26 @@ export const createConsole = ({
432
453
  * ### Example
433
454
  *
434
455
  * ```ts
456
+ * import {
457
+ * createNativeConsoleOutput,
458
+ * type ConsoleOutput,
459
+ * } from "@evolu/common";
460
+ *
435
461
  * const output = createNativeConsoleOutput();
462
+ *
463
+ * expectTypeOf(output).toEqualTypeOf<ConsoleOutput>();
464
+ * expect(output.write).toBeTypeOf("function");
436
465
  * ```
437
466
  */
438
467
  export const createNativeConsoleOutput = (): ConsoleOutput => ({
439
468
  write: (entry, formatter) => {
440
469
  const args = formatter ? formatter(entry) : entry.args;
441
- const fn = globalThis.console[entry.method] as (
442
- ...args: Array<unknown>
443
- ) => void;
444
- fn(...args);
470
+ (
471
+ globalThis.console as unknown as Record<
472
+ ConsoleMethod,
473
+ (...args: Array<unknown>) => void
474
+ >
475
+ )[entry.method](...args);
445
476
  },
446
477
  });
447
478
 
@@ -453,23 +484,52 @@ export const createNativeConsoleOutput = (): ConsoleOutput => ({
453
484
  * ### Example
454
485
  *
455
486
  * ```ts
456
- * const root = createConsole({
457
- * formatter: createConsoleFormatter()({
458
- * timestampFormat: "relative",
459
- * }),
487
+ * import {
488
+ * createConsoleFormatter,
489
+ * Millis,
490
+ * testCreateTime,
491
+ * type ConsoleEntry,
492
+ * } from "@evolu/common";
493
+ *
494
+ * const time = testCreateTime({ startAt: Millis.orThrow(0) });
495
+ * const formatRelative = createConsoleFormatter({ time })({
496
+ * timestampFormat: "relative",
460
497
  * });
498
+ * const connected: ConsoleEntry = {
499
+ * method: "log",
500
+ * path: ["relay"],
501
+ * args: ["connected"],
502
+ * };
461
503
  *
462
- * // Relative — elapsed since start
463
- * const relay = root.child("relay");
464
- * relay.log("connected"); // +0.000s [relay] connected
465
- * relay.log("synced"); // +1.500s [relay] synced
466
- *
467
- * // Nested children
468
- * const db = relay.child("db");
469
- * db.log("opened"); // +1.500s [relay] [db] opened
470
- *
471
- * // Absolute — local clock time (HH:MM:SS.mmm)
472
- * // relay.log("connected"); // 15:30:15.123 [relay] connected
504
+ * expect(formatRelative(connected)).toEqual([
505
+ * "+0.000s [relay]",
506
+ * "connected",
507
+ * ]);
508
+ * time.advance("1.5s");
509
+ * expect(
510
+ * formatRelative({
511
+ * ...connected,
512
+ * path: ["relay", "db"],
513
+ * args: ["opened"],
514
+ * }),
515
+ * ).toEqual(["+1.500s [relay] [db]", "opened"]);
516
+ *
517
+ * const localTimestamp = new globalThis.Date(
518
+ * 2026,
519
+ * 0,
520
+ * 28,
521
+ * 14,
522
+ * 30,
523
+ * 15,
524
+ * 123,
525
+ * ).getTime();
526
+ * const formatAbsolute = createConsoleFormatter({
527
+ * time: testCreateTime({ startAt: Millis.orThrow(localTimestamp) }),
528
+ * })({ timestampFormat: "absolute" });
529
+ * expect(formatAbsolute(connected)).toEqual([
530
+ * "14:30:15.123 [relay]",
531
+ * "connected",
532
+ * ]);
473
533
  * ```
474
534
  */
475
535
  export const createConsoleFormatter =
@@ -524,11 +584,20 @@ export const createConsoleStoreOutput = (): ConsoleStoreOutput => {
524
584
  * ### Example
525
585
  *
526
586
  * ```ts
527
- * const entries: Array<ConsoleEntry> = [];
528
- * const output = createConsoleArrayOutput(entries);
587
+ * import {
588
+ * createConsole,
589
+ * createConsoleArrayOutput,
590
+ * type ConsoleEntry,
591
+ * } from "@evolu/common";
529
592
  *
530
- * // After logging...
531
- * expect(entries).toMatchInlineSnapshot();
593
+ * const entries: Array<ConsoleEntry> = [];
594
+ * const console = createConsole({
595
+ * output: createConsoleArrayOutput(entries),
596
+ * });
597
+ * console.info("connected");
598
+ * expect(entries).toEqual([
599
+ * { method: "info", path: [], args: ["connected"] },
600
+ * ]);
532
601
  * ```
533
602
  */
534
603
  export const createConsoleArrayOutput = (
@@ -548,10 +617,25 @@ export const createConsoleArrayOutput = (
548
617
  * ### Example
549
618
  *
550
619
  * ```ts
620
+ * import {
621
+ * createConsole,
622
+ * createConsoleArrayOutput,
623
+ * createConsoleStoreOutput,
624
+ * createMultiOutput,
625
+ * type ConsoleEntry,
626
+ * } from "@evolu/common";
627
+ *
628
+ * const entries: Array<ConsoleEntry> = [];
551
629
  * const storeOutput = createConsoleStoreOutput();
552
630
  * const console = createConsole({
553
- * output: createMultiOutput([createNativeConsoleOutput(), storeOutput]),
631
+ * output: createMultiOutput([
632
+ * createConsoleArrayOutput(entries),
633
+ * storeOutput,
634
+ * ]),
554
635
  * });
636
+ * console.error("disconnected");
637
+ *
638
+ * expect(storeOutput.entry.get()).toEqual(entries[0]);
555
639
  * ```
556
640
  */
557
641
  export const createMultiOutput = (
@@ -565,33 +649,21 @@ export const createMultiOutput = (
565
649
  /**
566
650
  * Creates a {@link TestConsole} that captures all output for testing.
567
651
  *
568
- * Unlike {@link createConsole}, this doesn't require dependencies and uses a
569
- * simple incrementing counter for timestamps (starting at 0).
652
+ * Unlike {@link createConsole}, this doesn't require dependencies and captures
653
+ * entries in memory.
570
654
  *
571
655
  * ### Example
572
656
  *
573
657
  * ```ts
574
- * test("logging", () => {
575
- * const console = testCreateConsole();
576
- * console.info("Hello");
577
- *
578
- * expect(console.getEntriesSnapshot()).toMatchInlineSnapshot(`
579
- * [
580
- * {
581
- * "method": "info",
582
- * "path": [],
583
- * "args": ["Hello"]
584
- * }
585
- * ]
586
- * `);
587
- * });
658
+ * import { testCreateConsole } from "@evolu/common";
588
659
  *
589
- * test("level filtering", () => {
590
- * const console = testCreateConsole({ level: "warn" });
591
- * console.debug("ignored");
592
- * console.warn("logged");
593
- * expect(console.getEntriesSnapshot()).toHaveLength(1);
594
- * });
660
+ * const console = testCreateConsole({ level: "info" });
661
+ * console.debug("ignored");
662
+ * console.child("relay").info("connected");
663
+ *
664
+ * expect(console.getEntriesSnapshot()).toEqual([
665
+ * { method: "info", path: ["relay"], args: ["connected"] },
666
+ * ]);
595
667
  * ```
596
668
  */
597
669
  export const testCreateConsole = ({
package/src/Crypto.ts CHANGED
@@ -12,10 +12,9 @@ import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
12
12
  import { hmac } from "@noble/hashes/hmac.js";
13
13
  import { sha512 } from "@noble/hashes/sha2.js";
14
14
  import { randomBytes, utf8ToBytes } from "@noble/hashes/utils.js";
15
- import { arrayFrom } from "./Array.js";
16
- import type { RandomLibDep } from "./Random.js";
17
- import type { Result } from "./Result.js";
18
- import { trySync } from "./Result.js";
15
+ import type { RandomLibDep } from "./Random.ts";
16
+ import type { Result } from "./Result.ts";
17
+ import { trySync } from "./Result.ts";
19
18
  import {
20
19
  brand,
21
20
  length,
@@ -23,7 +22,7 @@ import {
23
22
  Uint8Array,
24
23
  type Typed,
25
24
  zeroNonNegativeInt,
26
- } from "./Type.js";
25
+ } from "./Type.ts";
27
26
 
28
27
  export interface RandomBytes {
29
28
  /**
@@ -38,20 +37,35 @@ export interface RandomBytes {
38
37
  *
39
38
  * Returns specific branded types for common sizes:
40
39
  *
41
- * - `Random16` for 16-byte values (128 bits)
42
- * - `Random24` for 24-byte values (192 bits)
43
- * - `Random32` for 32-byte values (256 bits)
44
- * - `Random64` for 64-byte values (512 bits)
45
- * - `Random` for any other size
40
+ * - `Entropy16` for 16-byte values (128 bits)
41
+ * - `Entropy24` for 24-byte values (192 bits)
42
+ * - `Entropy32` for 32-byte values (256 bits)
43
+ * - `Entropy64` for 64-byte values (512 bits)
44
+ * - `Entropy` for any other size
46
45
  *
47
46
  * ### Example
48
47
  *
49
48
  * ```ts
50
- * const nonce = randomBytes.create(16); // Type: Random16
51
- * const nonce24 = randomBytes.create(24); // Type: Random24
52
- * const key = randomBytes.create(32); // Type: Random32
53
- * const seed = randomBytes.create(64); // Type: Random64
54
- * const custom = randomBytes.create(48); // Type: Random
49
+ * import {
50
+ * createRandomBytes,
51
+ * type Entropy16,
52
+ * type Entropy24,
53
+ * type Entropy32,
54
+ * type Entropy64,
55
+ * } from "@evolu/common";
56
+ *
57
+ * const randomBytes = createRandomBytes();
58
+ * const nonce16 = randomBytes.create(16);
59
+ * const nonce24 = randomBytes.create(24);
60
+ * const key = randomBytes.create(32);
61
+ * const seed = randomBytes.create(64);
62
+ * const custom = randomBytes.create(48);
63
+ *
64
+ * expectTypeOf(nonce16).toEqualTypeOf<Entropy16>();
65
+ * expectTypeOf(nonce24).toEqualTypeOf<Entropy24>();
66
+ * expectTypeOf(key).toEqualTypeOf<Entropy32>();
67
+ * expectTypeOf(seed).toEqualTypeOf<Entropy64>();
68
+ * expect(custom).toHaveLength(48);
55
69
  * ```
56
70
  */
57
71
  create(bytesLength: 16): Entropy16;
@@ -66,19 +80,19 @@ export interface RandomBytesDep {
66
80
  }
67
81
 
68
82
  const Entropy = /*#__PURE__*/ brand("Entropy", Uint8Array);
69
- type Entropy = typeof Entropy.Type;
83
+ type Entropy = typeof Entropy.Output;
70
84
 
71
85
  export const Entropy16 = /*#__PURE__*/ length(16)(Entropy);
72
- export type Entropy16 = typeof Entropy16.Type;
86
+ export type Entropy16 = typeof Entropy16.Output;
73
87
 
74
88
  export const Entropy24 = /*#__PURE__*/ length(24)(Entropy);
75
- export type Entropy24 = typeof Entropy24.Type;
89
+ export type Entropy24 = typeof Entropy24.Output;
76
90
 
77
91
  export const Entropy32 = /*#__PURE__*/ length(32)(Entropy);
78
- export type Entropy32 = typeof Entropy32.Type;
92
+ export type Entropy32 = typeof Entropy32.Output;
79
93
 
80
94
  export const Entropy64 = /*#__PURE__*/ length(64)(Entropy);
81
- export type Entropy64 = typeof Entropy64.Type;
95
+ export type Entropy64 = typeof Entropy64.Output;
82
96
 
83
97
  export const createRandomBytes = (): RandomBytes => ({
84
98
  create: randomBytes as RandomBytes["create"],
@@ -87,10 +101,10 @@ export const createRandomBytes = (): RandomBytes => ({
87
101
  /** Creates seeded random bytes for deterministic tests. */
88
102
  export const testCreateRandomBytes = (deps: RandomLibDep): RandomBytes =>
89
103
  ({
90
- create: (bytesLength: number) => {
91
- const array = arrayFrom(bytesLength, () => deps.randomLib.int(0, 255));
92
- return new globalThis.Uint8Array(array);
93
- },
104
+ create: (bytesLength: number) =>
105
+ globalThis.Uint8Array.from({ length: bytesLength }, () =>
106
+ deps.randomLib.int(0, 255),
107
+ ),
94
108
  }) as RandomBytes;
95
109
 
96
110
  /**
@@ -134,7 +148,7 @@ export const deriveSlip21Node = (
134
148
 
135
149
  /** The encryption key for symmetric encryption. */
136
150
  export const EncryptionKey = /*#__PURE__*/ brand("EncryptionKey", Entropy32);
137
- export type EncryptionKey = typeof EncryptionKey.Type;
151
+ export type EncryptionKey = typeof EncryptionKey.Output;
138
152
 
139
153
  /** The nonce length for XChaCha20-Poly1305 encryption. */
140
154
  export const xChaCha20Poly1305NonceLength = 24;
@@ -149,7 +163,7 @@ export const XChaCha20Poly1305Ciphertext = /*#__PURE__*/ brand(
149
163
  Uint8Array,
150
164
  );
151
165
  export type XChaCha20Poly1305Ciphertext =
152
- typeof XChaCha20Poly1305Ciphertext.Type;
166
+ typeof XChaCha20Poly1305Ciphertext.Output;
153
167
 
154
168
  /**
155
169
  * Encrypts plaintext with XChaCha20-Poly1305.
@@ -160,11 +174,22 @@ export type XChaCha20Poly1305Ciphertext =
160
174
  * ### Example
161
175
  *
162
176
  * ```ts
163
- * const deps = { randomBytes: createRandomBytes() };
164
- * const [ciphertext, nonce] = encryptWithXChaCha20Poly1305(deps)(
165
- * utf8ToBytes("secret message"),
166
- * encryptionKey,
167
- * );
177
+ * import {
178
+ * createRandomBytes,
179
+ * EncryptionKey,
180
+ * encryptWithXChaCha20Poly1305,
181
+ * type XChaCha20Poly1305Ciphertext,
182
+ * utf8ToBytes,
183
+ * } from "@evolu/common";
184
+ *
185
+ * const plaintext = utf8ToBytes("secret message");
186
+ * const encryptionKey = EncryptionKey.orThrow(new Uint8Array(32).fill(7));
187
+ * const [ciphertext, nonce] = encryptWithXChaCha20Poly1305({
188
+ * randomBytes: createRandomBytes(),
189
+ * })(plaintext, encryptionKey);
190
+ *
191
+ * expectTypeOf(ciphertext).toEqualTypeOf<XChaCha20Poly1305Ciphertext>();
192
+ * expect(nonce).toHaveLength(24);
168
193
  * ```
169
194
  *
170
195
  * @see https://github.com/paulmillr/noble-ciphers
@@ -196,16 +221,33 @@ export interface DecryptWithXChaCha20Poly1305Error extends Typed<"DecryptWithXCh
196
221
  * ### Example
197
222
  *
198
223
  * ```ts
199
- * const result = decryptWithXChaCha20Poly1305(
200
- * ciphertext,
201
- * nonce,
202
- * encryptionKey,
203
- * );
204
- * if (!result.ok) {
205
- * // Handle decryption error
206
- * return result;
207
- * }
208
- * const plaintext = result.value;
224
+ * import {
225
+ * bytesToUtf8,
226
+ * createRandomBytes,
227
+ * decryptWithXChaCha20Poly1305,
228
+ * EncryptionKey,
229
+ * encryptWithXChaCha20Poly1305,
230
+ * ok,
231
+ * utf8ToBytes,
232
+ * } from "@evolu/common";
233
+ *
234
+ * const plaintext = utf8ToBytes("secret message");
235
+ * const encryptionKey = EncryptionKey.orThrow(new Uint8Array(32).fill(7));
236
+ * const [ciphertext, nonce] = encryptWithXChaCha20Poly1305({
237
+ * randomBytes: createRandomBytes(),
238
+ * })(plaintext, encryptionKey);
239
+ *
240
+ * const decryptMessage = () => {
241
+ * const result = decryptWithXChaCha20Poly1305(
242
+ * ciphertext,
243
+ * nonce,
244
+ * encryptionKey,
245
+ * );
246
+ * if (!result.ok) return result;
247
+ * return ok(bytesToUtf8(result.value));
248
+ * };
249
+ *
250
+ * expectOk(decryptMessage(), "secret message");
209
251
  * ```
210
252
  */
211
253
  export const decryptWithXChaCha20Poly1305 = (