@evolu/common 8.6.2 → 8.8.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 (303) hide show
  1. package/dist/src/Array.d.ts +21 -21
  2. package/dist/src/Array.js +5 -5
  3. package/dist/src/Assert.d.ts +478 -141
  4. package/dist/src/Assert.d.ts.map +1 -1
  5. package/dist/src/Assert.js +644 -99
  6. package/dist/src/Binary.d.ts +254 -0
  7. package/dist/src/Binary.d.ts.map +1 -0
  8. package/dist/src/Binary.js +958 -0
  9. package/dist/src/Console.d.ts +2 -10
  10. package/dist/src/Console.d.ts.map +1 -1
  11. package/dist/src/Console.js +6 -12
  12. package/dist/src/Crypto.d.ts +5 -5
  13. package/dist/src/Crypto.js +1 -1
  14. package/dist/src/Eq.d.ts +35 -40
  15. package/dist/src/Eq.d.ts.map +1 -1
  16. package/dist/src/Eq.js +51 -87
  17. package/dist/src/Function.d.ts +9 -14
  18. package/dist/src/Function.d.ts.map +1 -1
  19. package/dist/src/Function.js +7 -9
  20. package/dist/src/Http.d.ts +6 -7
  21. package/dist/src/Http.d.ts.map +1 -1
  22. package/dist/src/LeakDetector.d.ts.map +1 -1
  23. package/dist/src/LeakDetector.js +1 -0
  24. package/dist/src/LockManager.d.ts +1 -1
  25. package/dist/src/LockManager.js +1 -1
  26. package/dist/src/Lookup.d.ts +3 -2
  27. package/dist/src/Lookup.d.ts.map +1 -1
  28. package/dist/src/Lookup.js +4 -3
  29. package/dist/src/Number.d.ts +1 -1
  30. package/dist/src/Number.d.ts.map +1 -1
  31. package/dist/src/Number.js +1 -1
  32. package/dist/src/Object.d.ts +4 -4
  33. package/dist/src/Object.d.ts.map +1 -1
  34. package/dist/src/Object.js +5 -4
  35. package/dist/src/Option.d.ts +1 -2
  36. package/dist/src/Option.d.ts.map +1 -1
  37. package/dist/src/Option.js +1 -2
  38. package/dist/src/Platform.js +2 -0
  39. package/dist/src/Polyfills.js +4 -1
  40. package/dist/src/Random.d.ts +1 -1
  41. package/dist/src/Result.d.ts +49 -54
  42. package/dist/src/Result.d.ts.map +1 -1
  43. package/dist/src/Result.js +17 -22
  44. package/dist/src/Set.d.ts +6 -6
  45. package/dist/src/Set.js +2 -2
  46. package/dist/src/Sqlite.js +1 -1
  47. package/dist/src/String.js +3 -2
  48. package/dist/src/Task.d.ts +32 -32
  49. package/dist/src/Task.d.ts.map +1 -1
  50. package/dist/src/Task.js +20 -22
  51. package/dist/src/Test.d.ts +31 -3
  52. package/dist/src/Test.d.ts.map +1 -1
  53. package/dist/src/Test.js +46 -3
  54. package/dist/src/Time.d.ts +3 -11
  55. package/dist/src/Time.d.ts.map +1 -1
  56. package/dist/src/Time.js +10 -18
  57. package/dist/src/Type.d.ts +87 -85
  58. package/dist/src/Type.d.ts.map +1 -1
  59. package/dist/src/Type.js +70 -63
  60. package/dist/src/Types.d.ts +12 -12
  61. package/dist/src/Types.js +1 -1
  62. package/dist/src/WebSocket.d.ts +4 -7
  63. package/dist/src/WebSocket.d.ts.map +1 -1
  64. package/dist/src/Worker.d.ts +1 -1
  65. package/dist/src/Worker.js +7 -7
  66. package/dist/src/index.d.ts +1 -1
  67. package/dist/src/index.js +1 -1
  68. package/dist/src/intl/_en.d.ts.map +1 -1
  69. package/dist/src/intl/_en.js +1 -1
  70. package/dist/src/intl/ar.d.ts.map +1 -1
  71. package/dist/src/intl/ar.js +1 -1
  72. package/dist/src/intl/bn.d.ts.map +1 -1
  73. package/dist/src/intl/bn.js +1 -1
  74. package/dist/src/intl/ca.d.ts.map +1 -1
  75. package/dist/src/intl/ca.js +1 -1
  76. package/dist/src/intl/cs.d.ts.map +1 -1
  77. package/dist/src/intl/cs.js +1 -1
  78. package/dist/src/intl/da.d.ts.map +1 -1
  79. package/dist/src/intl/da.js +1 -1
  80. package/dist/src/intl/de.d.ts.map +1 -1
  81. package/dist/src/intl/de.js +1 -1
  82. package/dist/src/intl/el.d.ts.map +1 -1
  83. package/dist/src/intl/el.js +1 -1
  84. package/dist/src/intl/es.d.ts.map +1 -1
  85. package/dist/src/intl/es.js +1 -1
  86. package/dist/src/intl/fa.d.ts.map +1 -1
  87. package/dist/src/intl/fa.js +1 -1
  88. package/dist/src/intl/fi.d.ts.map +1 -1
  89. package/dist/src/intl/fi.js +1 -1
  90. package/dist/src/intl/fil.d.ts.map +1 -1
  91. package/dist/src/intl/fil.js +1 -1
  92. package/dist/src/intl/fr.d.ts.map +1 -1
  93. package/dist/src/intl/fr.js +1 -1
  94. package/dist/src/intl/he.d.ts.map +1 -1
  95. package/dist/src/intl/he.js +1 -1
  96. package/dist/src/intl/hi.d.ts.map +1 -1
  97. package/dist/src/intl/hi.js +1 -1
  98. package/dist/src/intl/hr.d.ts.map +1 -1
  99. package/dist/src/intl/hr.js +1 -1
  100. package/dist/src/intl/hu.d.ts.map +1 -1
  101. package/dist/src/intl/hu.js +1 -1
  102. package/dist/src/intl/id.d.ts.map +1 -1
  103. package/dist/src/intl/id.js +1 -1
  104. package/dist/src/intl/it.d.ts.map +1 -1
  105. package/dist/src/intl/it.js +1 -1
  106. package/dist/src/intl/ja.d.ts.map +1 -1
  107. package/dist/src/intl/ja.js +1 -1
  108. package/dist/src/intl/ko.d.ts.map +1 -1
  109. package/dist/src/intl/ko.js +1 -1
  110. package/dist/src/intl/ml.d.ts.map +1 -1
  111. package/dist/src/intl/ml.js +1 -1
  112. package/dist/src/intl/mr.d.ts.map +1 -1
  113. package/dist/src/intl/mr.js +1 -1
  114. package/dist/src/intl/ms.d.ts.map +1 -1
  115. package/dist/src/intl/ms.js +1 -1
  116. package/dist/src/intl/nb.d.ts.map +1 -1
  117. package/dist/src/intl/nb.js +1 -1
  118. package/dist/src/intl/nl.d.ts.map +1 -1
  119. package/dist/src/intl/nl.js +1 -1
  120. package/dist/src/intl/pa.d.ts.map +1 -1
  121. package/dist/src/intl/pa.js +1 -1
  122. package/dist/src/intl/pl.d.ts.map +1 -1
  123. package/dist/src/intl/pl.js +1 -1
  124. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  125. package/dist/src/intl/pt-BR.js +1 -1
  126. package/dist/src/intl/pt.d.ts.map +1 -1
  127. package/dist/src/intl/pt.js +1 -1
  128. package/dist/src/intl/ro.d.ts.map +1 -1
  129. package/dist/src/intl/ro.js +1 -1
  130. package/dist/src/intl/sk.d.ts.map +1 -1
  131. package/dist/src/intl/sk.js +1 -1
  132. package/dist/src/intl/sl.d.ts.map +1 -1
  133. package/dist/src/intl/sl.js +1 -1
  134. package/dist/src/intl/sv.d.ts.map +1 -1
  135. package/dist/src/intl/sv.js +1 -1
  136. package/dist/src/intl/sw.d.ts.map +1 -1
  137. package/dist/src/intl/sw.js +1 -1
  138. package/dist/src/intl/ta.d.ts.map +1 -1
  139. package/dist/src/intl/ta.js +1 -1
  140. package/dist/src/intl/te.d.ts.map +1 -1
  141. package/dist/src/intl/te.js +1 -1
  142. package/dist/src/intl/th.d.ts.map +1 -1
  143. package/dist/src/intl/th.js +1 -1
  144. package/dist/src/intl/tr.d.ts.map +1 -1
  145. package/dist/src/intl/tr.js +1 -1
  146. package/dist/src/intl/uk.d.ts.map +1 -1
  147. package/dist/src/intl/uk.js +1 -1
  148. package/dist/src/intl/ur.d.ts.map +1 -1
  149. package/dist/src/intl/ur.js +1 -1
  150. package/dist/src/intl/vi.d.ts.map +1 -1
  151. package/dist/src/intl/vi.js +1 -1
  152. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  153. package/dist/src/intl/zh-CN.js +1 -1
  154. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  155. package/dist/src/intl/zh-TW.js +1 -1
  156. package/dist/src/local-first/Evolu.d.ts +12 -12
  157. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  158. package/dist/src/local-first/Evolu.js +2 -2
  159. package/dist/src/local-first/Protocol.d.ts +28 -76
  160. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  161. package/dist/src/local-first/Protocol.js +45 -229
  162. package/dist/src/local-first/Query.d.ts +11 -11
  163. package/dist/src/local-first/Query.js +6 -6
  164. package/dist/src/local-first/Relay.d.ts +2 -2
  165. package/dist/src/local-first/Schema.d.ts +3 -5
  166. package/dist/src/local-first/Schema.d.ts.map +1 -1
  167. package/dist/src/local-first/Schema.js +2 -4
  168. package/dist/src/local-first/Shared.d.ts.map +1 -1
  169. package/dist/src/local-first/Shared.js +5 -5
  170. package/dist/src/local-first/Storage.js +4 -4
  171. package/dist/src/local-first/Timestamp.d.ts +9 -0
  172. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  173. package/dist/src/local-first/Timestamp.js +13 -10
  174. package/package.json +7 -5
  175. package/src/Array.test.ts +809 -0
  176. package/src/Array.ts +21 -21
  177. package/src/Assert.test.ts +1181 -0
  178. package/src/Assert.ts +971 -227
  179. package/src/BigInt.test.ts +28 -0
  180. package/src/Binary.test.ts +1128 -0
  181. package/src/Binary.ts +1377 -0
  182. package/src/Brand.test.ts +90 -0
  183. package/src/Cache.test.ts +183 -0
  184. package/src/Callbacks.test.ts +66 -0
  185. package/src/Console.test.ts +809 -0
  186. package/src/Console.ts +9 -17
  187. package/src/Crypto.test.ts +138 -0
  188. package/src/Crypto.ts +5 -5
  189. package/src/Eq.test.ts +333 -0
  190. package/src/Eq.ts +110 -176
  191. package/src/Error.test.ts +118 -0
  192. package/src/Function.test.ts +204 -0
  193. package/src/Function.ts +10 -15
  194. package/src/Http.test.ts +816 -0
  195. package/src/Http.ts +6 -7
  196. package/src/LeakDetector.test.ts +210 -0
  197. package/src/LeakDetector.ts +1 -0
  198. package/src/LockManager.ts +2 -2
  199. package/src/Lookup.test.ts +418 -0
  200. package/src/Lookup.ts +4 -3
  201. package/src/Microtask.test.ts +140 -0
  202. package/src/Number.test.ts +218 -0
  203. package/src/Number.ts +2 -2
  204. package/src/Object.test.ts +293 -0
  205. package/src/Object.ts +5 -4
  206. package/src/Option.test.ts +104 -0
  207. package/src/Option.ts +1 -2
  208. package/src/Order.test.ts +56 -0
  209. package/src/Platform.ts +2 -0
  210. package/src/Polyfills.ts +3 -4
  211. package/src/Random.test.ts +89 -0
  212. package/src/Random.ts +1 -1
  213. package/src/Ref.test.ts +139 -0
  214. package/src/RefCount.test.ts +184 -0
  215. package/src/Relation.test.ts +505 -0
  216. package/src/Resource.test.ts +4623 -0
  217. package/src/Result.test.ts +1829 -0
  218. package/src/Result.ts +49 -54
  219. package/src/Schedule.test.ts +1554 -0
  220. package/src/Set.test.ts +279 -0
  221. package/src/Set.ts +6 -6
  222. package/src/Sqlite.ts +1 -1
  223. package/src/Store.test.ts +288 -0
  224. package/src/String.test.ts +54 -0
  225. package/src/String.ts +3 -3
  226. package/src/Task.test.ts +10820 -0
  227. package/src/Task.ts +38 -43
  228. package/src/Test.test.ts +88 -0
  229. package/src/Test.ts +49 -3
  230. package/src/Time.test.ts +926 -0
  231. package/src/Time.ts +13 -21
  232. package/src/Type.base64.test.ts +88 -0
  233. package/src/Type.test.ts +20465 -0
  234. package/src/Type.ts +114 -111
  235. package/src/Types.test.ts +324 -0
  236. package/src/Types.ts +13 -13
  237. package/src/WebSocket.ts +4 -7
  238. package/src/Worker.test.ts +755 -0
  239. package/src/Worker.ts +13 -18
  240. package/src/index.ts +1 -1
  241. package/src/intl/_en.ts +1 -1
  242. package/src/intl/ar.ts +1 -1
  243. package/src/intl/bn.ts +1 -1
  244. package/src/intl/ca.ts +1 -1
  245. package/src/intl/cs.ts +1 -1
  246. package/src/intl/da.ts +1 -1
  247. package/src/intl/de.ts +1 -1
  248. package/src/intl/el.ts +1 -1
  249. package/src/intl/es.ts +1 -1
  250. package/src/intl/fa.ts +1 -1
  251. package/src/intl/fi.ts +1 -1
  252. package/src/intl/fil.ts +1 -1
  253. package/src/intl/fr.ts +1 -1
  254. package/src/intl/he.ts +1 -1
  255. package/src/intl/hi.ts +1 -1
  256. package/src/intl/hr.ts +1 -1
  257. package/src/intl/hu.ts +1 -1
  258. package/src/intl/id.ts +1 -1
  259. package/src/intl/intl.test.ts +3106 -0
  260. package/src/intl/it.ts +1 -1
  261. package/src/intl/ja.ts +1 -1
  262. package/src/intl/ko.ts +1 -1
  263. package/src/intl/ml.ts +1 -1
  264. package/src/intl/mr.ts +1 -1
  265. package/src/intl/ms.ts +1 -1
  266. package/src/intl/nb.ts +1 -1
  267. package/src/intl/nl.ts +1 -1
  268. package/src/intl/pa.ts +1 -1
  269. package/src/intl/pl.ts +1 -1
  270. package/src/intl/pt-BR.ts +1 -1
  271. package/src/intl/pt.ts +1 -1
  272. package/src/intl/ro.ts +1 -1
  273. package/src/intl/sk.ts +1 -1
  274. package/src/intl/sl.ts +1 -1
  275. package/src/intl/sv.ts +1 -1
  276. package/src/intl/sw.ts +1 -1
  277. package/src/intl/ta.ts +1 -1
  278. package/src/intl/te.ts +1 -1
  279. package/src/intl/th.ts +1 -1
  280. package/src/intl/tr.ts +1 -1
  281. package/src/intl/uk.ts +1 -1
  282. package/src/intl/ur.ts +1 -1
  283. package/src/intl/vi.ts +1 -1
  284. package/src/intl/zh-CN.ts +1 -1
  285. package/src/intl/zh-TW.ts +1 -1
  286. package/src/local-first/Evolu.test.ts +1921 -0
  287. package/src/local-first/Evolu.ts +18 -14
  288. package/src/local-first/Owner.test.ts +83 -0
  289. package/src/local-first/Protocol.test.ts +1037 -0
  290. package/src/local-first/Protocol.ts +75 -290
  291. package/src/local-first/Query.test.ts +349 -0
  292. package/src/local-first/Query.ts +11 -11
  293. package/src/local-first/Relay.ts +2 -2
  294. package/src/local-first/Schema.ts +3 -5
  295. package/src/local-first/Shared.test.ts +1828 -0
  296. package/src/local-first/Shared.ts +9 -8
  297. package/src/local-first/Storage.ts +4 -4
  298. package/src/local-first/Timestamp.test.ts +374 -0
  299. package/src/local-first/Timestamp.ts +22 -10
  300. package/dist/src/Buffer.d.ts +0 -122
  301. package/dist/src/Buffer.d.ts.map +0 -1
  302. package/dist/src/Buffer.js +0 -69
  303. package/src/Buffer.ts +0 -206
package/src/Console.ts CHANGED
@@ -466,19 +466,19 @@ export const createConsole = ({
466
466
  *
467
467
  * const output = createNativeConsoleOutput();
468
468
  *
469
- * assertType<ConsoleOutput, typeof output>();
469
+ * assertType<typeof output, ConsoleOutput>();
470
470
  * assertEqual(typeof output.write, "function");
471
471
  * ```
472
472
  */
473
473
  export const createNativeConsoleOutput = (): ConsoleOutput => ({
474
474
  write: (entry, formatter) => {
475
475
  const args = formatter ? formatter(entry) : entry.args;
476
- (
477
- globalThis.console as unknown as Record<
478
- ConsoleMethod,
479
- (...args: Array<unknown>) => void
480
- >
481
- )[entry.method](...args);
476
+ // oxlint-disable-next-line evolu/no-unnecessary-global-this -- Write to the global object console even if a realm lexical binding shadows it.
477
+ const nativeConsole = globalThis.console as unknown as Record<
478
+ ConsoleMethod,
479
+ (...args: Array<unknown>) => void
480
+ >;
481
+ nativeConsole[entry.method](...args);
482
482
  },
483
483
  });
484
484
 
@@ -522,15 +522,7 @@ export const createNativeConsoleOutput = (): ConsoleOutput => ({
522
522
  * assertType(Data, nested);
523
523
  * assertEqual(nested, ["+1.500s [relay] [db]", "opened"]);
524
524
  *
525
- * const localTimestamp = new globalThis.Date(
526
- * 2026,
527
- * 0,
528
- * 28,
529
- * 14,
530
- * 30,
531
- * 15,
532
- * 123,
533
- * ).getTime();
525
+ * const localTimestamp = new Date(2026, 0, 28, 14, 30, 15, 123).getTime();
534
526
  * const formatAbsolute = createConsoleFormatter({
535
527
  * time: testCreateTime({ startAt: Millis.orThrow(localTimestamp) }),
536
528
  * })({ timestampFormat: "absolute" });
@@ -561,7 +553,7 @@ export const createConsoleFormatter =
561
553
  timestamp = formatMillisAsClockTime(now);
562
554
  break;
563
555
  case "iso":
564
- timestamp = new globalThis.Date(now).toISOString();
556
+ timestamp = new Date(now).toISOString();
565
557
  break;
566
558
  }
567
559
 
@@ -0,0 +1,138 @@
1
+ import { test } from "node:test";
2
+ import { assertEqual, assertErr, assertSame } from "./Assert.ts";
3
+ import { bytesToHex, utf8ToBytes } from "@noble/ciphers/utils.js";
4
+
5
+ import {
6
+ createPadmePaddedLength,
7
+ createPadmePadding,
8
+ createRandomBytes,
9
+ createSlip21,
10
+ decryptWithXChaCha20Poly1305,
11
+ encryptWithXChaCha20Poly1305,
12
+ XChaCha20Poly1305Ciphertext,
13
+ } from "./Crypto.ts";
14
+ import { mnemonicToOwnerSecret } from "./index.ts";
15
+ import { ok } from "./Result.ts";
16
+ import { testCreateDeps } from "./Task.ts";
17
+ import { Mnemonic, NonNegativeInt } from "./Type.ts";
18
+ import { testAppOwner } from "./local-first/Owner.ts";
19
+
20
+ test("encryptWithXChaCha20Poly1305 / decryptWithXChaCha20Poly1305", () => {
21
+ const deps = testCreateDeps();
22
+ const plaintext = utf8ToBytes("Hello, world!");
23
+ const encryptionKey = testAppOwner.encryptionKey;
24
+
25
+ const [ciphertext, nonce] = encryptWithXChaCha20Poly1305(deps)(
26
+ plaintext,
27
+ encryptionKey,
28
+ );
29
+
30
+ assertEqual(
31
+ decryptWithXChaCha20Poly1305(ciphertext, nonce, encryptionKey),
32
+ ok(plaintext),
33
+ );
34
+
35
+ const result = decryptWithXChaCha20Poly1305(
36
+ XChaCha20Poly1305Ciphertext.orThrow(new Uint8Array([1, 2, 3])),
37
+ nonce,
38
+ encryptionKey,
39
+ );
40
+ assertErr(result);
41
+ assertEqual(result.error.type, "DecryptWithXChaCha20Poly1305Error");
42
+ });
43
+
44
+ test("createPadmePaddedLength", () => {
45
+ [
46
+ [0, 0],
47
+ [1, 1],
48
+ [2, 2],
49
+ [3, 3],
50
+ [4, 4],
51
+ [8, 8],
52
+ [9, 10],
53
+ [15, 16],
54
+ [16, 16],
55
+ [17, 18],
56
+ [31, 32],
57
+ [32, 32],
58
+ [33, 36],
59
+ [64, 64],
60
+ [65, 72],
61
+ [100, 104],
62
+ [128, 128],
63
+ [129, 144],
64
+ [200, 208],
65
+ [256, 256],
66
+ [300, 304],
67
+ [512, 512],
68
+ [1000, 1024],
69
+ [1024, 1024],
70
+ [2048, 2048],
71
+ [4096, 4096],
72
+ [10000, 10240],
73
+ [65536, 65536],
74
+ [100000, 100352],
75
+ [1048576, 1048576],
76
+ ].forEach(([input, expected]) => {
77
+ assertSame(createPadmePaddedLength(input as NonNegativeInt), expected);
78
+ assertSame(
79
+ createPadmePadding(input as NonNegativeInt).length,
80
+ expected - input,
81
+ );
82
+ });
83
+ });
84
+
85
+ test("createSlip21", () => {
86
+ const mnemonic = Mnemonic.orThrow(
87
+ "all all all all all all all all all all all all",
88
+ );
89
+ const ownerSecret = mnemonicToOwnerSecret(mnemonic);
90
+
91
+ const ownerId = createSlip21(ownerSecret, ["Evolu", "Owner Id"]);
92
+ assertEqual(
93
+ bytesToHex(ownerId),
94
+ "bce9b26dad1a3364c105eb65e7aef032fdffd53816819ac4664442c4a915327f",
95
+ );
96
+
97
+ const encryptionKey = createSlip21(ownerSecret, ["Evolu", "Encryption Key"]);
98
+ assertEqual(
99
+ bytesToHex(encryptionKey),
100
+ "abf2095887bc74adda889a572e29a407a457a39bfdd4202d34ee6eac5c28effc",
101
+ );
102
+ });
103
+
104
+ test("createSlip21 normalizes numeric path elements", () => {
105
+ const mnemonic = Mnemonic.orThrow(
106
+ "all all all all all all all all all all all all",
107
+ );
108
+ const ownerSecret = mnemonicToOwnerSecret(mnemonic);
109
+
110
+ const withNumber = createSlip21(ownerSecret, ["Evolu", 0]);
111
+ const withString = createSlip21(ownerSecret, ["Evolu", "0"]);
112
+
113
+ assertSame(bytesToHex(withNumber), bytesToHex(withString));
114
+ });
115
+
116
+ /**
117
+ * This test demonstrates createRandomBytes performance, which is used for
118
+ * createId and is fast enough: ~0.0014ms per call on Apple M1.
119
+ */
120
+ test.skip("createRandomBytes generates unique values", () => {
121
+ const randomBytes = createRandomBytes();
122
+ const values = new Set<string>();
123
+ const iterations = 10_000;
124
+
125
+ const start = performance.now();
126
+ for (let i = 0; i < iterations; i++) {
127
+ values.add(bytesToHex(randomBytes.create(16)));
128
+ }
129
+ const end = performance.now();
130
+
131
+ // ~14ms on Apple M1
132
+ // oxlint-disable-next-line eslint/no-console
133
+ console.log(
134
+ `createRandomBytes: ${iterations} in ${(end - start).toFixed(2)}ms`,
135
+ );
136
+
137
+ assertSame(values.size, iterations);
138
+ });
package/src/Crypto.ts CHANGED
@@ -63,10 +63,10 @@ export interface RandomBytes {
63
63
  * const seed = randomBytes.create(64);
64
64
  * const custom = randomBytes.create(48);
65
65
  *
66
- * assertType<Entropy16, typeof nonce16>();
67
- * assertType<Entropy24, typeof nonce24>();
68
- * assertType<Entropy32, typeof key>();
69
- * assertType<Entropy64, typeof seed>();
66
+ * assertType<typeof nonce16, Entropy16>();
67
+ * assertType<typeof nonce24, Entropy24>();
68
+ * assertType<typeof key, Entropy32>();
69
+ * assertType<typeof seed, Entropy64>();
70
70
  * assertEqual(custom.length, 48);
71
71
  * ```
72
72
  */
@@ -192,7 +192,7 @@ export type XChaCha20Poly1305Ciphertext =
192
192
  * randomBytes: createRandomBytes(),
193
193
  * })(plaintext, encryptionKey);
194
194
  *
195
- * assertType<XChaCha20Poly1305Ciphertext, typeof ciphertext>();
195
+ * assertType<typeof ciphertext, XChaCha20Poly1305Ciphertext>();
196
196
  * assertEqual(nonce.length, 24);
197
197
  * ```
198
198
  *
package/src/Eq.test.ts ADDED
@@ -0,0 +1,333 @@
1
+ import { test } from "node:test";
2
+ import { assertEqual, assertFalse, assertSame, assertTrue } from "./Assert.ts";
3
+
4
+ import {
5
+ createEqArrayLike,
6
+ createEqObject,
7
+ eqArrayNumber,
8
+ eqArraySameValue,
9
+ eqData,
10
+ eqFromOrder,
11
+ eqNumber,
12
+ eqSameValue,
13
+ eqSameValueZero,
14
+ eqUint8Array,
15
+ type Eq,
16
+ } from "./Eq.ts";
17
+ import { orderNumber } from "./Order.ts";
18
+ import { assertType, type Data } from "./Type.ts";
19
+
20
+ test("eqSameValue", () => {
21
+ assertTrue(eqSameValue(1, 1));
22
+ assertTrue(eqSameValue(NaN, NaN));
23
+ assertFalse(eqSameValue(0, -0));
24
+
25
+ const object = {};
26
+ assertTrue(eqSameValue(object, object));
27
+ assertFalse(eqSameValue({}, {}));
28
+ });
29
+
30
+ test("eqSameValueZero", () => {
31
+ assertTrue(eqSameValueZero(NaN, NaN));
32
+ assertTrue(eqSameValueZero(0, -0));
33
+ assertFalse(eqSameValueZero(1, 2));
34
+
35
+ const object = {};
36
+ assertTrue(eqSameValueZero(object, object));
37
+ assertFalse(eqSameValueZero({}, {}));
38
+ });
39
+
40
+ test("eqNumber", () => {
41
+ assertTrue(eqNumber(NaN, NaN));
42
+ assertFalse(eqNumber(0, -0));
43
+ });
44
+
45
+ test("eqFromOrder", () => {
46
+ const eqNumberFromOrder = eqFromOrder(orderNumber);
47
+ assertTrue(eqNumberFromOrder(1, 1));
48
+ assertFalse(eqNumberFromOrder(1, 2));
49
+ });
50
+
51
+ test("createEqArrayLike", () => {
52
+ const eqArrayNumberFromEq = createEqArrayLike(eqNumber);
53
+ const array = [1, 2, 3];
54
+ assertTrue(eqArrayNumberFromEq(array, array));
55
+ assertFalse(eqArrayNumberFromEq([1, 2, 3], [1, 2]));
56
+ assertTrue(eqArrayNumberFromEq([1, 2, 3], [1, 2, 3]));
57
+ assertFalse(eqArrayNumberFromEq([1, 2, 3], [1, 2, 4]));
58
+ });
59
+
60
+ test("eqArraySameValue", () => {
61
+ assertTrue(eqArraySameValue([NaN], [NaN]));
62
+ assertFalse(eqArraySameValue([0], [-0]));
63
+
64
+ const object = {};
65
+ assertTrue(eqArraySameValue([object], [object]));
66
+ assertFalse(eqArraySameValue([{}], [{}]));
67
+ });
68
+
69
+ test("eqUint8Array", () => {
70
+ assertType<typeof eqUint8Array, Eq<Uint8Array>>();
71
+ assertSame(eqUint8Array, eqArrayNumber);
72
+ assertTrue(eqUint8Array(new Uint8Array([1, 2]), new Uint8Array([1, 2])));
73
+ assertFalse(eqUint8Array(new Uint8Array([1, 2]), new Uint8Array([1, 3])));
74
+ assertFalse(eqUint8Array(new Uint8Array([1]), new Uint8Array([1, 2])));
75
+ });
76
+
77
+ test("createEqObject", () => {
78
+ const eqObjectNumber = createEqObject({ a: eqNumber });
79
+ const object = { a: 1 };
80
+ assertTrue(eqObjectNumber(object, object));
81
+ assertTrue(eqObjectNumber({ a: 1 }, { a: 1 }));
82
+ assertFalse(eqObjectNumber({ a: 1 }, { a: 2 }));
83
+ });
84
+
85
+ test("eqData", () => {
86
+ const unsafeEqData = eqData as unknown as (
87
+ actual: unknown,
88
+ expected: unknown,
89
+ ) => boolean;
90
+ const emptyData: Data = {};
91
+
92
+ assertTrue(eqData(NaN, NaN));
93
+ assertFalse(eqData(0, -0));
94
+ assertFalse(eqData({ value: 0 }, { value: -0 }));
95
+ assertFalse(eqData(1, 2));
96
+ assertFalse(eqData(1, "1"));
97
+ assertFalse(eqData(null, emptyData));
98
+ assertTrue(eqData(1n, 1n));
99
+
100
+ const sameReference = { value: 1 };
101
+ assertTrue(eqData(sameReference, sameReference));
102
+ assertTrue(eqData({ a: 1, b: 2 }, { b: 2, a: 1 }));
103
+ assertFalse(eqData({ a: 1 }, { a: 2 }));
104
+ assertFalse(eqData({ a: 1 }, { b: 1 }));
105
+ assertFalse(eqData(emptyData, { value: undefined }));
106
+
107
+ const nullPrototype = Object.assign(Object.create(null) as object, {
108
+ value: 1,
109
+ });
110
+ assertTrue(eqData({ value: 1 }, nullPrototype));
111
+
112
+ assertTrue(eqData([1, 2], [1, 2]));
113
+ assertFalse(eqData([1, 2], [2, 1]));
114
+ assertFalse(eqData([1], [1, 2]));
115
+ assertFalse(eqData([1], { 0: 1 }));
116
+
117
+ assertTrue(eqData(new Date(0), new Date(0)));
118
+ assertFalse(eqData(new Date(0), new Date(1)));
119
+ assertFalse(eqData(new Date(0), emptyData));
120
+
121
+ assertTrue(eqData(new Uint8Array([1, 2]), new Uint8Array([1, 2])));
122
+ assertFalse(eqData(new Uint8Array([1, 2]), new Uint8Array([1, 3])));
123
+ assertFalse(eqData(new Uint8Array([1]), new Uint8Array([1, 2])));
124
+
125
+ assertFalse(unsafeEqData(new ArrayBuffer(1), new ArrayBuffer(1)));
126
+
127
+ assertTrue(eqData(new Set([1, 2]), new Set([2, 1])));
128
+ assertTrue(
129
+ eqData(
130
+ new Set([{ value: 1 }, { value: 2 }]),
131
+ new Set([{ value: 2 }, { value: 1 }]),
132
+ ),
133
+ );
134
+ assertFalse(eqData(new Set([{ value: 1 }]), new Set([{ value: 2 }])));
135
+ assertFalse(eqData(new Set([1]), new Set([1, 2])));
136
+
137
+ assertFalse(
138
+ unsafeEqData(
139
+ Object.assign(new Set([1]), { metadata: true }),
140
+ Object.assign(new Set([1]), { metadata: true }),
141
+ ),
142
+ );
143
+
144
+ assertTrue(
145
+ eqData(
146
+ new Map<Data, Data>([[{ id: 1 }, { name: "Ada" }]]),
147
+ new Map<Data, Data>([[{ id: 1 }, { name: "Ada" }]]),
148
+ ),
149
+ );
150
+ assertTrue(
151
+ eqData(
152
+ new Map<Data, Data>([
153
+ [1, "one"],
154
+ [2, "two"],
155
+ ]),
156
+ new Map<Data, Data>([
157
+ [2, "two"],
158
+ [1, "one"],
159
+ ]),
160
+ ),
161
+ );
162
+ assertFalse(eqData(new Map([[1, "one"]]), new Map([[1, "two"]])));
163
+ assertFalse(eqData(new Map([[1, "one"]]), new Map([[2, "one"]])));
164
+ assertFalse(eqData(new Map([[1, "one"]]), new Map<number, string>()));
165
+ assertTrue(
166
+ eqData(
167
+ new Map<Data, Data>([
168
+ [{ id: 1 }, "one"],
169
+ [{ id: 1 }, "two"],
170
+ ]),
171
+ new Map<Data, Data>([
172
+ [{ id: 1 }, "two"],
173
+ [{ id: 1 }, "one"],
174
+ ]),
175
+ ),
176
+ );
177
+
178
+ assertFalse(
179
+ unsafeEqData(
180
+ Object.assign(new Map([[1, "one"]]), { metadata: true }),
181
+ Object.assign(new Map([[1, "one"]]), { metadata: true }),
182
+ ),
183
+ );
184
+
185
+ const sparse: Array<undefined> = [];
186
+ sparse.length = 1;
187
+ assertFalse(unsafeEqData(sparse, [undefined]));
188
+
189
+ const arrayAccessor: Array<number> = [];
190
+ Object.defineProperty(arrayAccessor, 0, {
191
+ enumerable: true,
192
+ get: () => 1,
193
+ });
194
+ arrayAccessor.length = 1;
195
+ assertFalse(unsafeEqData(arrayAccessor, [1]));
196
+
197
+ let reads = 0;
198
+ const accessor = Object.defineProperty({}, "value", {
199
+ enumerable: true,
200
+ get: () => {
201
+ reads++;
202
+ return 1;
203
+ },
204
+ });
205
+ assertFalse(unsafeEqData(accessor, { value: 1 }));
206
+ assertEqual(reads, 0);
207
+ assertFalse(
208
+ unsafeEqData(
209
+ Object.defineProperty({}, "hidden", { value: 1 }),
210
+ Object.defineProperty({}, "hidden", { value: 1 }),
211
+ ),
212
+ );
213
+ const symbolKey = Symbol("value");
214
+ assertFalse(unsafeEqData({ [symbolKey]: 1 }, { [symbolKey]: 1 }));
215
+ assertFalse(unsafeEqData([1], Object.assign([1], { extra: true })));
216
+
217
+ class Model {
218
+ readonly value = 1;
219
+ }
220
+ assertFalse(unsafeEqData(new Model(), new Model()));
221
+ class NullBase extends null {}
222
+ const nullBasePrototype = NullBase.prototype;
223
+ assertFalse(
224
+ unsafeEqData(
225
+ Object.create(nullBasePrototype),
226
+ Object.create(nullBasePrototype),
227
+ ),
228
+ );
229
+ assertFalse(unsafeEqData(/value/u, /value/u));
230
+
231
+ interface CircularData {
232
+ readonly value: number;
233
+ self?: CircularData;
234
+ }
235
+ const circularOne: CircularData = { value: 1 };
236
+ circularOne.self = circularOne;
237
+ const circularTwo: CircularData = { value: 1 };
238
+ circularTwo.self = circularTwo;
239
+ assertTrue(eqData(circularOne, circularTwo));
240
+
241
+ const circularDifferent: CircularData = { value: 2 };
242
+ circularDifferent.self = circularDifferent;
243
+ assertFalse(eqData(circularOne, circularDifferent));
244
+
245
+ const circularSetOne = new Set<Data>();
246
+ circularSetOne.add(circularSetOne);
247
+ const circularSetTwo = new Set<Data>();
248
+ circularSetTwo.add(circularSetTwo);
249
+ assertTrue(eqData(circularSetOne, circularSetTwo));
250
+
251
+ const backtrackingSetOne = new Set<Data>();
252
+ backtrackingSetOne.add(backtrackingSetOne);
253
+ backtrackingSetOne.add({ value: 1 });
254
+ const backtrackingSetTwo = new Set<Data>();
255
+ backtrackingSetTwo.add({ value: 1 });
256
+ backtrackingSetTwo.add(backtrackingSetTwo);
257
+ assertTrue(eqData(backtrackingSetOne, backtrackingSetTwo));
258
+
259
+ const circularMapOne = new Map<Data, Data>();
260
+ circularMapOne.set(circularMapOne, circularSetOne);
261
+ const circularMapTwo = new Map<Data, Data>();
262
+ circularMapTwo.set(circularMapTwo, circularSetTwo);
263
+ assertTrue(eqData(circularMapOne, circularMapTwo));
264
+
265
+ const shared: { readonly value?: number } = {};
266
+ const otherEmptyData: Data = {};
267
+ assertTrue(
268
+ eqData(
269
+ { first: shared, second: shared },
270
+ { first: emptyData, second: otherEmptyData },
271
+ ),
272
+ );
273
+
274
+ interface DeepData {
275
+ next?: DeepData;
276
+ }
277
+ const deepOne: DeepData = {};
278
+ const deepTwo: DeepData = {};
279
+ let currentOne = deepOne;
280
+ let currentTwo = deepTwo;
281
+ for (let index = 0; index < 10_000; index++) {
282
+ const nextOne: DeepData = {};
283
+ const nextTwo: DeepData = {};
284
+ currentOne.next = nextOne;
285
+ currentTwo.next = nextTwo;
286
+ currentOne = nextOne;
287
+ currentTwo = nextTwo;
288
+ }
289
+ assertTrue(eqData(deepOne, deepTwo));
290
+
291
+ interface Service {
292
+ readonly run: () => void;
293
+ }
294
+ const service: Service = { run: () => undefined };
295
+ const broadObject: NonNullable<unknown> = new WeakMap();
296
+ const compileTimeAssertions = () => {
297
+ // @ts-expect-error ⛔ eqData error: Actual and expected values must consist only of Data.
298
+ eqData(service, service);
299
+ // @ts-expect-error ⛔ eqData error: Actual and expected values must consist only of Data.
300
+ eqData(broadObject, broadObject);
301
+ };
302
+ assertEqual(typeof compileTimeAssertions, "function");
303
+ });
304
+
305
+ test("eqData compares deeply nested Set and Map data", () => {
306
+ type Shape = "Set" | "MapKey" | "MapValue" | "Mixed";
307
+
308
+ const setupNestedData = (shape: Shape, leaf: Data): Data => {
309
+ let value = leaf;
310
+
311
+ for (let index = 0; index < 10_000; index++) {
312
+ const currentShape =
313
+ shape === "Mixed" ? (index % 2 === 0 ? "Set" : "MapValue") : shape;
314
+
315
+ if (currentShape === "Set") {
316
+ value = new Set([value]);
317
+ } else if (currentShape === "MapKey") {
318
+ value = new Map([[value, null]]);
319
+ } else {
320
+ value = new Map([[null, value]]);
321
+ }
322
+ }
323
+
324
+ return value;
325
+ };
326
+
327
+ for (const shape of ["Set", "MapKey", "MapValue", "Mixed"] as const) {
328
+ const actual = setupNestedData(shape, 0);
329
+
330
+ assertTrue(eqData(actual, setupNestedData(shape, 0)));
331
+ assertFalse(eqData(actual, setupNestedData(shape, 1)));
332
+ }
333
+ });