@evolu/common 8.7.0 → 8.9.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 (302) 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 +498 -141
  4. package/dist/src/Assert.d.ts.map +1 -1
  5. package/dist/src/Assert.js +648 -99
  6. package/dist/src/{Buffer.d.ts → Binary.d.ts} +85 -12
  7. package/dist/src/Binary.d.ts.map +1 -0
  8. package/dist/src/{Buffer.js → Binary.js} +206 -24
  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.d.ts +6 -0
  40. package/dist/src/Polyfills.d.ts.map +1 -1
  41. package/dist/src/Polyfills.js +10 -1
  42. package/dist/src/Random.d.ts +1 -1
  43. package/dist/src/Result.d.ts +49 -54
  44. package/dist/src/Result.d.ts.map +1 -1
  45. package/dist/src/Result.js +17 -22
  46. package/dist/src/Set.d.ts +6 -6
  47. package/dist/src/Set.js +2 -2
  48. package/dist/src/Sqlite.js +1 -1
  49. package/dist/src/String.js +3 -2
  50. package/dist/src/Task.d.ts +32 -32
  51. package/dist/src/Task.d.ts.map +1 -1
  52. package/dist/src/Task.js +20 -22
  53. package/dist/src/Test.d.ts +31 -3
  54. package/dist/src/Test.d.ts.map +1 -1
  55. package/dist/src/Test.js +46 -3
  56. package/dist/src/Time.d.ts +3 -11
  57. package/dist/src/Time.d.ts.map +1 -1
  58. package/dist/src/Time.js +10 -18
  59. package/dist/src/Type.d.ts +87 -85
  60. package/dist/src/Type.d.ts.map +1 -1
  61. package/dist/src/Type.js +70 -63
  62. package/dist/src/Types.d.ts +12 -12
  63. package/dist/src/Types.js +1 -1
  64. package/dist/src/WebSocket.d.ts +4 -7
  65. package/dist/src/WebSocket.d.ts.map +1 -1
  66. package/dist/src/Worker.d.ts +1 -1
  67. package/dist/src/Worker.js +7 -7
  68. package/dist/src/index.d.ts +1 -1
  69. package/dist/src/index.js +1 -1
  70. package/dist/src/intl/_en.d.ts.map +1 -1
  71. package/dist/src/intl/_en.js +1 -1
  72. package/dist/src/intl/ar.d.ts.map +1 -1
  73. package/dist/src/intl/ar.js +1 -1
  74. package/dist/src/intl/bn.d.ts.map +1 -1
  75. package/dist/src/intl/bn.js +1 -1
  76. package/dist/src/intl/ca.d.ts.map +1 -1
  77. package/dist/src/intl/ca.js +1 -1
  78. package/dist/src/intl/cs.d.ts.map +1 -1
  79. package/dist/src/intl/cs.js +1 -1
  80. package/dist/src/intl/da.d.ts.map +1 -1
  81. package/dist/src/intl/da.js +1 -1
  82. package/dist/src/intl/de.d.ts.map +1 -1
  83. package/dist/src/intl/de.js +1 -1
  84. package/dist/src/intl/el.d.ts.map +1 -1
  85. package/dist/src/intl/el.js +1 -1
  86. package/dist/src/intl/es.d.ts.map +1 -1
  87. package/dist/src/intl/es.js +1 -1
  88. package/dist/src/intl/fa.d.ts.map +1 -1
  89. package/dist/src/intl/fa.js +1 -1
  90. package/dist/src/intl/fi.d.ts.map +1 -1
  91. package/dist/src/intl/fi.js +1 -1
  92. package/dist/src/intl/fil.d.ts.map +1 -1
  93. package/dist/src/intl/fil.js +1 -1
  94. package/dist/src/intl/fr.d.ts.map +1 -1
  95. package/dist/src/intl/fr.js +1 -1
  96. package/dist/src/intl/he.d.ts.map +1 -1
  97. package/dist/src/intl/he.js +1 -1
  98. package/dist/src/intl/hi.d.ts.map +1 -1
  99. package/dist/src/intl/hi.js +1 -1
  100. package/dist/src/intl/hr.d.ts.map +1 -1
  101. package/dist/src/intl/hr.js +1 -1
  102. package/dist/src/intl/hu.d.ts.map +1 -1
  103. package/dist/src/intl/hu.js +1 -1
  104. package/dist/src/intl/id.d.ts.map +1 -1
  105. package/dist/src/intl/id.js +1 -1
  106. package/dist/src/intl/it.d.ts.map +1 -1
  107. package/dist/src/intl/it.js +1 -1
  108. package/dist/src/intl/ja.d.ts.map +1 -1
  109. package/dist/src/intl/ja.js +1 -1
  110. package/dist/src/intl/ko.d.ts.map +1 -1
  111. package/dist/src/intl/ko.js +1 -1
  112. package/dist/src/intl/ml.d.ts.map +1 -1
  113. package/dist/src/intl/ml.js +1 -1
  114. package/dist/src/intl/mr.d.ts.map +1 -1
  115. package/dist/src/intl/mr.js +1 -1
  116. package/dist/src/intl/ms.d.ts.map +1 -1
  117. package/dist/src/intl/ms.js +1 -1
  118. package/dist/src/intl/nb.d.ts.map +1 -1
  119. package/dist/src/intl/nb.js +1 -1
  120. package/dist/src/intl/nl.d.ts.map +1 -1
  121. package/dist/src/intl/nl.js +1 -1
  122. package/dist/src/intl/pa.d.ts.map +1 -1
  123. package/dist/src/intl/pa.js +1 -1
  124. package/dist/src/intl/pl.d.ts.map +1 -1
  125. package/dist/src/intl/pl.js +1 -1
  126. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  127. package/dist/src/intl/pt-BR.js +1 -1
  128. package/dist/src/intl/pt.d.ts.map +1 -1
  129. package/dist/src/intl/pt.js +1 -1
  130. package/dist/src/intl/ro.d.ts.map +1 -1
  131. package/dist/src/intl/ro.js +1 -1
  132. package/dist/src/intl/sk.d.ts.map +1 -1
  133. package/dist/src/intl/sk.js +1 -1
  134. package/dist/src/intl/sl.d.ts.map +1 -1
  135. package/dist/src/intl/sl.js +1 -1
  136. package/dist/src/intl/sv.d.ts.map +1 -1
  137. package/dist/src/intl/sv.js +1 -1
  138. package/dist/src/intl/sw.d.ts.map +1 -1
  139. package/dist/src/intl/sw.js +1 -1
  140. package/dist/src/intl/ta.d.ts.map +1 -1
  141. package/dist/src/intl/ta.js +1 -1
  142. package/dist/src/intl/te.d.ts.map +1 -1
  143. package/dist/src/intl/te.js +1 -1
  144. package/dist/src/intl/th.d.ts.map +1 -1
  145. package/dist/src/intl/th.js +1 -1
  146. package/dist/src/intl/tr.d.ts.map +1 -1
  147. package/dist/src/intl/tr.js +1 -1
  148. package/dist/src/intl/uk.d.ts.map +1 -1
  149. package/dist/src/intl/uk.js +1 -1
  150. package/dist/src/intl/ur.d.ts.map +1 -1
  151. package/dist/src/intl/ur.js +1 -1
  152. package/dist/src/intl/vi.d.ts.map +1 -1
  153. package/dist/src/intl/vi.js +1 -1
  154. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  155. package/dist/src/intl/zh-CN.js +1 -1
  156. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  157. package/dist/src/intl/zh-TW.js +1 -1
  158. package/dist/src/local-first/Evolu.d.ts +12 -12
  159. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  160. package/dist/src/local-first/Evolu.js +2 -2
  161. package/dist/src/local-first/Protocol.d.ts +14 -76
  162. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  163. package/dist/src/local-first/Protocol.js +29 -206
  164. package/dist/src/local-first/Query.d.ts +11 -11
  165. package/dist/src/local-first/Query.js +6 -6
  166. package/dist/src/local-first/Relay.d.ts +2 -2
  167. package/dist/src/local-first/Schema.d.ts +3 -5
  168. package/dist/src/local-first/Schema.d.ts.map +1 -1
  169. package/dist/src/local-first/Schema.js +2 -4
  170. package/dist/src/local-first/Shared.d.ts.map +1 -1
  171. package/dist/src/local-first/Shared.js +5 -5
  172. package/dist/src/local-first/Storage.js +4 -4
  173. package/dist/src/local-first/Timestamp.d.ts +9 -0
  174. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  175. package/dist/src/local-first/Timestamp.js +13 -10
  176. package/package.json +7 -4
  177. package/src/Array.test.ts +809 -0
  178. package/src/Array.ts +21 -21
  179. package/src/Assert.test.ts +1232 -0
  180. package/src/Assert.ts +1023 -227
  181. package/src/BigInt.test.ts +28 -0
  182. package/src/Binary.test.ts +1128 -0
  183. package/src/{Buffer.ts → Binary.ts} +273 -46
  184. package/src/Brand.test.ts +90 -0
  185. package/src/Cache.test.ts +183 -0
  186. package/src/Callbacks.test.ts +66 -0
  187. package/src/Console.test.ts +809 -0
  188. package/src/Console.ts +9 -17
  189. package/src/Crypto.test.ts +138 -0
  190. package/src/Crypto.ts +5 -5
  191. package/src/Eq.test.ts +333 -0
  192. package/src/Eq.ts +110 -176
  193. package/src/Error.test.ts +118 -0
  194. package/src/Function.test.ts +204 -0
  195. package/src/Function.ts +10 -15
  196. package/src/Http.test.ts +816 -0
  197. package/src/Http.ts +6 -7
  198. package/src/LeakDetector.test.ts +210 -0
  199. package/src/LeakDetector.ts +1 -0
  200. package/src/LockManager.ts +2 -2
  201. package/src/Lookup.test.ts +418 -0
  202. package/src/Lookup.ts +4 -3
  203. package/src/Microtask.test.ts +140 -0
  204. package/src/Number.test.ts +218 -0
  205. package/src/Number.ts +2 -2
  206. package/src/Object.test.ts +293 -0
  207. package/src/Object.ts +5 -4
  208. package/src/Option.test.ts +104 -0
  209. package/src/Option.ts +1 -2
  210. package/src/Order.test.ts +56 -0
  211. package/src/Platform.ts +2 -0
  212. package/src/Polyfills.ts +9 -4
  213. package/src/Random.test.ts +89 -0
  214. package/src/Random.ts +1 -1
  215. package/src/Ref.test.ts +139 -0
  216. package/src/RefCount.test.ts +184 -0
  217. package/src/Relation.test.ts +505 -0
  218. package/src/Resource.test.ts +4623 -0
  219. package/src/Result.test.ts +1829 -0
  220. package/src/Result.ts +49 -54
  221. package/src/Schedule.test.ts +1554 -0
  222. package/src/Set.test.ts +279 -0
  223. package/src/Set.ts +6 -6
  224. package/src/Sqlite.ts +1 -1
  225. package/src/Store.test.ts +288 -0
  226. package/src/String.test.ts +54 -0
  227. package/src/String.ts +3 -3
  228. package/src/Task.test.ts +10820 -0
  229. package/src/Task.ts +38 -43
  230. package/src/Test.test.ts +88 -0
  231. package/src/Test.ts +49 -3
  232. package/src/Time.test.ts +926 -0
  233. package/src/Time.ts +13 -21
  234. package/src/Type.base64.test.ts +88 -0
  235. package/src/Type.test.ts +20465 -0
  236. package/src/Type.ts +114 -111
  237. package/src/Types.test.ts +324 -0
  238. package/src/Types.ts +13 -13
  239. package/src/WebSocket.ts +4 -7
  240. package/src/Worker.test.ts +755 -0
  241. package/src/Worker.ts +13 -18
  242. package/src/index.ts +1 -1
  243. package/src/intl/_en.ts +1 -1
  244. package/src/intl/ar.ts +1 -1
  245. package/src/intl/bn.ts +1 -1
  246. package/src/intl/ca.ts +1 -1
  247. package/src/intl/cs.ts +1 -1
  248. package/src/intl/da.ts +1 -1
  249. package/src/intl/de.ts +1 -1
  250. package/src/intl/el.ts +1 -1
  251. package/src/intl/es.ts +1 -1
  252. package/src/intl/fa.ts +1 -1
  253. package/src/intl/fi.ts +1 -1
  254. package/src/intl/fil.ts +1 -1
  255. package/src/intl/fr.ts +1 -1
  256. package/src/intl/he.ts +1 -1
  257. package/src/intl/hi.ts +1 -1
  258. package/src/intl/hr.ts +1 -1
  259. package/src/intl/hu.ts +1 -1
  260. package/src/intl/id.ts +1 -1
  261. package/src/intl/intl.test.ts +3106 -0
  262. package/src/intl/it.ts +1 -1
  263. package/src/intl/ja.ts +1 -1
  264. package/src/intl/ko.ts +1 -1
  265. package/src/intl/ml.ts +1 -1
  266. package/src/intl/mr.ts +1 -1
  267. package/src/intl/ms.ts +1 -1
  268. package/src/intl/nb.ts +1 -1
  269. package/src/intl/nl.ts +1 -1
  270. package/src/intl/pa.ts +1 -1
  271. package/src/intl/pl.ts +1 -1
  272. package/src/intl/pt-BR.ts +1 -1
  273. package/src/intl/pt.ts +1 -1
  274. package/src/intl/ro.ts +1 -1
  275. package/src/intl/sk.ts +1 -1
  276. package/src/intl/sl.ts +1 -1
  277. package/src/intl/sv.ts +1 -1
  278. package/src/intl/sw.ts +1 -1
  279. package/src/intl/ta.ts +1 -1
  280. package/src/intl/te.ts +1 -1
  281. package/src/intl/th.ts +1 -1
  282. package/src/intl/tr.ts +1 -1
  283. package/src/intl/uk.ts +1 -1
  284. package/src/intl/ur.ts +1 -1
  285. package/src/intl/vi.ts +1 -1
  286. package/src/intl/zh-CN.ts +1 -1
  287. package/src/intl/zh-TW.ts +1 -1
  288. package/src/local-first/Evolu.test.ts +1921 -0
  289. package/src/local-first/Evolu.ts +18 -14
  290. package/src/local-first/Owner.test.ts +83 -0
  291. package/src/local-first/Protocol.test.ts +1037 -0
  292. package/src/local-first/Protocol.ts +58 -261
  293. package/src/local-first/Query.test.ts +349 -0
  294. package/src/local-first/Query.ts +11 -11
  295. package/src/local-first/Relay.ts +2 -2
  296. package/src/local-first/Schema.ts +3 -5
  297. package/src/local-first/Shared.test.ts +1828 -0
  298. package/src/local-first/Shared.ts +9 -8
  299. package/src/local-first/Storage.ts +4 -4
  300. package/src/local-first/Timestamp.test.ts +374 -0
  301. package/src/local-first/Timestamp.ts +22 -10
  302. package/dist/src/Buffer.d.ts.map +0 -1
package/src/Eq.ts CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  import type { Order } from "./Order.ts";
8
8
  import { getObjectKind } from "./Object.ts";
9
- import type { Data, IsData, JsonValue, JsonValueInput } from "./Type.ts";
9
+ import type { Data, IsData } from "./Type.ts";
10
10
  import type { CompileTimeError } from "./Types.ts";
11
11
 
12
12
  /**
@@ -21,6 +21,13 @@ import type { CompileTimeError } from "./Types.ts";
21
21
  * - **Transitive**: if `eq(a, b)` and `eq(b, c)` are `true`, then `eq(a, c)` is
22
22
  * `true`.
23
23
  *
24
+ * Evolu does not provide `eqStrict`: JavaScript strict equality (`===`) is not
25
+ * reflexive because `NaN === NaN` is `false`, so it cannot define an
26
+ * `Eq<number>`. Use `===` directly when that behavior is required. See [MDN's
27
+ * equality comparisons and sameness
28
+ * guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness)
29
+ * for how JavaScript's equality algorithms differ.
30
+ *
24
31
  * Use {@link eqFromOrder} to derive equality from an {@link Order}.
25
32
  *
26
33
  * ### Example
@@ -50,13 +57,13 @@ export type Eq<in A> = (x: A, y: A) => boolean;
50
57
  * ### Example
51
58
  *
52
59
  * ```ts
53
- * import { assertFalse, assertTrue, eqStrict } from "@evolu/common";
60
+ * import { assertFalse, assertTrue, eqSameValue } from "@evolu/common";
54
61
  *
55
- * assertTrue(eqStrict(NaN, NaN));
56
- * assertFalse(eqStrict(0, -0));
62
+ * assertTrue(eqSameValue(NaN, NaN));
63
+ * assertFalse(eqSameValue(0, -0));
57
64
  * ```
58
65
  */
59
- export const eqStrict = <A>(x: A, y: A): boolean => Object.is(x, y);
66
+ export const eqSameValue = <A>(x: A, y: A): boolean => Object.is(x, y);
60
67
 
61
68
  /**
62
69
  * Compares two values using SameValueZero equality.
@@ -78,23 +85,23 @@ export const eqStrict = <A>(x: A, y: A): boolean => Object.is(x, y);
78
85
  export const eqSameValueZero = <A>(x: A, y: A): boolean =>
79
86
  x === y || Object.is(x, y);
80
87
 
81
- /** An {@link Eq} for strings using {@link eqStrict}. */
82
- export const eqString: Eq<string> = eqStrict;
88
+ /** An {@link Eq} for strings using {@link eqSameValue}. */
89
+ export const eqString: Eq<string> = eqSameValue;
83
90
 
84
- /** An {@link Eq} for numbers using {@link eqSameValueZero}. */
85
- export const eqNumber: Eq<number> = eqSameValueZero;
91
+ /** An {@link Eq} for numbers using {@link eqSameValue}. */
92
+ export const eqNumber: Eq<number> = eqSameValue;
86
93
 
87
- /** An {@link Eq} for bigints using {@link eqStrict}. */
88
- export const eqBigInt: Eq<bigint> = eqStrict;
94
+ /** An {@link Eq} for bigints using {@link eqSameValue}. */
95
+ export const eqBigInt: Eq<bigint> = eqSameValue;
89
96
 
90
- /** An {@link Eq} for booleans using {@link eqStrict}. */
91
- export const eqBoolean: Eq<boolean> = eqStrict;
97
+ /** An {@link Eq} for booleans using {@link eqSameValue}. */
98
+ export const eqBoolean: Eq<boolean> = eqSameValue;
92
99
 
93
100
  /** An {@link Eq} for `undefined`. */
94
- export const eqUndefined: Eq<undefined> = eqStrict;
101
+ export const eqUndefined: Eq<undefined> = eqSameValue;
95
102
 
96
103
  /** An {@link Eq} for `null`. */
97
- export const eqNull: Eq<null> = eqStrict;
104
+ export const eqNull: Eq<null> = eqSameValue;
98
105
 
99
106
  /** Derives an {@link Eq} from an {@link Order}. */
100
107
  export const eqFromOrder =
@@ -139,7 +146,8 @@ export const createEqArrayLike =
139
146
  };
140
147
 
141
148
  /**
142
- * Compares two array-like structures element by element using {@link eqStrict}.
149
+ * Compares two array-like structures element by element using
150
+ * {@link eqSameValue}.
143
151
  *
144
152
  * Useful for structural sharing checks where objects are compared by reference
145
153
  * identity. `NaN` elements are equal, while `0` and `-0` elements differ.
@@ -147,15 +155,19 @@ export const createEqArrayLike =
147
155
  * ### Example
148
156
  *
149
157
  * ```ts
150
- * import { assertFalse, assertTrue, eqArrayStrict } from "@evolu/common";
158
+ * import {
159
+ * assertFalse,
160
+ * assertTrue,
161
+ * eqArraySameValue,
162
+ * } from "@evolu/common";
151
163
  *
152
164
  * const a = { x: 1 };
153
165
  * const b = { x: 1 };
154
- * assertTrue(eqArrayStrict([a, a], [a, a]));
155
- * assertFalse(eqArrayStrict([a], [b]));
166
+ * assertTrue(eqArraySameValue([a, a], [a, a]));
167
+ * assertFalse(eqArraySameValue([a], [b]));
156
168
  * ```
157
169
  */
158
- export const eqArrayStrict = /*#__PURE__*/ createEqArrayLike(eqStrict);
170
+ export const eqArraySameValue = /*#__PURE__*/ createEqArrayLike(eqSameValue);
159
171
 
160
172
  /**
161
173
  * Compares two array-like structures of numbers for equality.
@@ -221,17 +233,17 @@ export const createEqObject =
221
233
  };
222
234
 
223
235
  /**
224
- * Compares two {@link Data} values.
236
+ * Deeply compares two {@link Data} values using platform-independent structural
237
+ * equality.
225
238
  *
226
- * Primitive values use SameValueZero, so `NaN` equals itself and `0` equals
227
- * `-0`. Arrays and Object properties are ordered and keyed respectively. Sets
228
- * and Maps compare their elements and entries without regard to insertion
229
- * order. Dates compare their time values, while Uint8Arrays compare their
230
- * bytes. Cyclic and shared data graphs are supported.
239
+ * Primitive values use `Object.is`. Arrays are ordered; plain Objects compare
240
+ * their own string-keyed properties regardless of property order or prototype;
241
+ * Sets and Maps ignore insertion order; Dates compare their time values; and
242
+ * Uint8Arrays compare their bytes. Cyclic and shared data graphs are
243
+ * supported.
231
244
  *
232
- * The compile-time validation recursively accepts ordinary interfaces whose
233
- * properties consist only of Data. Runtime representation details are trusted;
234
- * use the {@link Data} Type at an unknown boundary.
245
+ * Arguments are restricted to Data at compile time. Use the {@link Data} Type at
246
+ * an unknown boundary.
235
247
  *
236
248
  * ### Example
237
249
  *
@@ -258,115 +270,14 @@ export function eqData<Actual, Expected>(
258
270
  ...dataError: EqDataError<Actual | Expected>
259
271
  ): boolean;
260
272
  export function eqData(actual: Data, expected: Data): boolean {
261
- return eqDataInternal(actual, expected, {
262
- pairs: [],
263
- rightsByLeft: new Map(),
264
- });
265
- }
266
-
267
- /**
268
- * Compares two {@link JsonValue} values using {@link eqData}.
269
- *
270
- * ### Example
271
- *
272
- * ```ts
273
- * import { assertTrue, eqJsonValue, type JsonValue } from "@evolu/common";
274
- *
275
- * const first: JsonValue = { profile: { name: "Ada" } };
276
- * const second: JsonValue = { profile: { name: "Ada" } };
277
- *
278
- * assertTrue(eqJsonValue(first, second));
279
- * ```
280
- */
281
- export const eqJsonValue: Eq<JsonValue> = eqData;
282
-
283
- /** Compares two {@link JsonValueInput} values using {@link eqData}. */
284
- export const eqJsonValueInput: Eq<JsonValueInput> = eqData;
285
-
286
- type EqDataError<Value> =
287
- IsData<Value> extends true
288
- ? []
289
- : [
290
- error: CompileTimeError<
291
- "eqData",
292
- "Actual and expected values must consist only of Data."
293
- >,
294
- ];
295
-
296
- interface EqDataContext {
297
- readonly pairs: Array<readonly [object, object]>;
298
- readonly rightsByLeft: Map<object, Set<object>>;
299
- }
300
-
301
- interface EqDataCompareFrame {
302
- readonly kind: "Compare";
303
- readonly actual: Data;
304
- readonly expected: Data;
305
- }
306
-
307
- interface EqDataSetFrame {
308
- readonly kind: "Set";
309
- readonly actual: ReadonlyArray<Data>;
310
- readonly actualIndex: number;
311
- readonly unmatched: Array<Data>;
312
- readonly candidateIndex: number;
313
- }
314
-
315
- interface EqDataMapFrame {
316
- readonly kind: "Map";
317
- readonly actual: ReadonlyArray<readonly [Data, Data]>;
318
- readonly actualIndex: number;
319
- readonly unmatched: Array<readonly [Data, Data]>;
320
- readonly candidateIndex: number;
321
- }
322
-
323
- interface EqDataCommitSetFrame {
324
- readonly kind: "CommitSet";
325
- readonly match: EqDataSetFrame;
326
- }
327
-
328
- interface EqDataCommitMapFrame {
329
- readonly kind: "CommitMap";
330
- readonly match: EqDataMapFrame;
331
- }
332
-
333
- interface EqDataFailFrame {
334
- readonly kind: "Fail";
335
- }
336
-
337
- type EqDataFrame =
338
- | EqDataCompareFrame
339
- | EqDataSetFrame
340
- | EqDataMapFrame
341
- | EqDataCommitSetFrame
342
- | EqDataCommitMapFrame
343
- | EqDataFailFrame;
344
-
345
- type EqDataCollectionFrame = EqDataSetFrame | EqDataMapFrame;
346
-
347
- interface EqDataCandidateAttempt {
348
- readonly contextCheckpoint: number;
349
- readonly frameCheckpoint: number;
350
- readonly retry: EqDataCollectionFrame;
351
- }
352
-
353
- const eqDataInternal = (
354
- actual: Data,
355
- expected: Data,
356
- context: EqDataContext,
357
- ): boolean => {
273
+ const pairs: Array<readonly [object, object]> = [];
274
+ const rightsByLeft = new Map<object, Set<object>>();
358
275
  const frames: Array<EqDataFrame> = [{ kind: "Compare", actual, expected }];
359
- const attempts: Array<EqDataCandidateAttempt> = [];
360
-
361
- const retryFailedCandidate = (_frame: EqDataFailFrame): boolean => {
362
- const attempt = attempts.pop();
363
- if (attempt === undefined) return false;
364
-
365
- rollbackEqDataContext(context, attempt.contextCheckpoint);
366
- frames.length = attempt.frameCheckpoint;
367
- frames.push(attempt.retry);
368
- return true;
369
- };
276
+ const attempts: Array<{
277
+ readonly pairsCheckpoint: number;
278
+ readonly frameCheckpoint: number;
279
+ readonly retry: EqDataCollectionFrame;
280
+ }> = [];
370
281
 
371
282
  while (frames.length > 0) {
372
283
  const frame = frames.pop()!;
@@ -375,7 +286,7 @@ const eqDataInternal = (
375
286
  const x = frame.actual;
376
287
  const y = frame.expected;
377
288
 
378
- if (eqSameValueZero(x, y)) continue;
289
+ if (eqSameValue(x, y)) continue;
379
290
  if (typeof x !== typeof y) {
380
291
  frames.push({ kind: "Fail" });
381
292
  continue;
@@ -390,14 +301,14 @@ const eqDataInternal = (
390
301
  continue;
391
302
  }
392
303
 
393
- const rights = context.rightsByLeft.get(x);
304
+ const rights = rightsByLeft.get(x);
394
305
  if (rights?.has(y)) continue;
395
306
  if (rights === undefined) {
396
- context.rightsByLeft.set(x, new Set([y]));
307
+ rightsByLeft.set(x, new Set([y]));
397
308
  } else {
398
309
  rights.add(y);
399
310
  }
400
- context.pairs.push([x, y]);
311
+ pairs.push([x, y]);
401
312
 
402
313
  const xKind = getObjectKind(x);
403
314
  const yKind = getObjectKind(y);
@@ -546,13 +457,12 @@ const eqDataInternal = (
546
457
  continue;
547
458
  }
548
459
 
549
- const attempt: EqDataCandidateAttempt = {
550
- contextCheckpoint: context.pairs.length,
460
+ attempts.push({
461
+ pairsCheckpoint: pairs.length,
551
462
  frameCheckpoint: frames.length,
552
463
  retry: { ...frame, candidateIndex: frame.candidateIndex + 1 },
553
- };
554
- attempts.push(attempt);
555
- frames.push({ kind: "CommitSet", match: frame });
464
+ });
465
+ frames.push({ kind: "Commit", match: frame });
556
466
  frames.push({
557
467
  kind: "Compare",
558
468
  actual: frame.actual[frame.actualIndex],
@@ -571,13 +481,12 @@ const eqDataInternal = (
571
481
  const [actualKey, actualValue] = frame.actual[frame.actualIndex];
572
482
  const [expectedKey, expectedValue] =
573
483
  frame.unmatched[frame.candidateIndex];
574
- const attempt: EqDataCandidateAttempt = {
575
- contextCheckpoint: context.pairs.length,
484
+ attempts.push({
485
+ pairsCheckpoint: pairs.length,
576
486
  frameCheckpoint: frames.length,
577
487
  retry: { ...frame, candidateIndex: frame.candidateIndex + 1 },
578
- };
579
- attempts.push(attempt);
580
- frames.push({ kind: "CommitMap", match: frame });
488
+ });
489
+ frames.push({ kind: "Commit", match: frame });
581
490
  frames.push({
582
491
  kind: "Compare",
583
492
  actual: actualValue,
@@ -591,7 +500,7 @@ const eqDataInternal = (
591
500
  continue;
592
501
  }
593
502
 
594
- if (frame.kind === "CommitSet") {
503
+ if (frame.kind === "Commit") {
595
504
  attempts.pop();
596
505
  frame.match.unmatched.splice(frame.match.candidateIndex, 1);
597
506
  frames.push({
@@ -602,31 +511,56 @@ const eqDataInternal = (
602
511
  continue;
603
512
  }
604
513
 
605
- if (frame.kind === "CommitMap") {
606
- attempts.pop();
607
- frame.match.unmatched.splice(frame.match.candidateIndex, 1);
608
- frames.push({
609
- ...frame.match,
610
- actualIndex: frame.match.actualIndex + 1,
611
- candidateIndex: 0,
612
- });
613
- continue;
614
- }
514
+ const attempt = attempts.pop();
515
+ if (attempt === undefined) return false;
615
516
 
616
- if (!retryFailedCandidate(frame)) return false;
517
+ while (pairs.length > attempt.pairsCheckpoint) {
518
+ const [left, right] = pairs.pop()!;
519
+ const rights = rightsByLeft.get(left)!;
520
+ rights.delete(right);
521
+ if (rights.size === 0) rightsByLeft.delete(left);
522
+ }
523
+ frames.length = attempt.frameCheckpoint;
524
+ frames.push(attempt.retry);
617
525
  }
618
526
 
619
527
  return true;
620
- };
621
-
622
- const rollbackEqDataContext = (
623
- context: EqDataContext,
624
- checkpoint: number,
625
- ): void => {
626
- while (context.pairs.length > checkpoint) {
627
- const [left, right] = context.pairs.pop()!;
628
- const rights = context.rightsByLeft.get(left)!;
629
- rights.delete(right);
630
- if (rights.size === 0) context.rightsByLeft.delete(left);
631
- }
632
- };
528
+ }
529
+
530
+ type EqDataError<Value> =
531
+ IsData<Value> extends true
532
+ ? []
533
+ : [
534
+ error: CompileTimeError<
535
+ "eqData",
536
+ "Actual and expected values must consist only of Data."
537
+ >,
538
+ ];
539
+
540
+ interface EqDataSetFrame {
541
+ readonly kind: "Set";
542
+ readonly actual: ReadonlyArray<Data>;
543
+ readonly actualIndex: number;
544
+ readonly unmatched: Array<Data>;
545
+ readonly candidateIndex: number;
546
+ }
547
+
548
+ interface EqDataMapFrame {
549
+ readonly kind: "Map";
550
+ readonly actual: ReadonlyArray<readonly [Data, Data]>;
551
+ readonly actualIndex: number;
552
+ readonly unmatched: Array<readonly [Data, Data]>;
553
+ readonly candidateIndex: number;
554
+ }
555
+
556
+ type EqDataCollectionFrame = EqDataSetFrame | EqDataMapFrame;
557
+
558
+ type EqDataFrame =
559
+ | {
560
+ readonly kind: "Compare";
561
+ readonly actual: Data;
562
+ readonly expected: Data;
563
+ }
564
+ | EqDataCollectionFrame
565
+ | { readonly kind: "Commit"; readonly match: EqDataCollectionFrame }
566
+ | { readonly kind: "Fail" };
@@ -0,0 +1,118 @@
1
+ import { describe, it } from "node:test";
2
+ import { assertEqual, assertFalse, assertSame, assertTrue } from "./Assert.ts";
3
+
4
+ import { UnknownError, createUnknownError } from "./Error.ts";
5
+ import { assertType, Object, String } from "./Type.ts";
6
+
7
+ describe("createUnknownError", () => {
8
+ it("UnknownError validates unknown error values", () => {
9
+ const result = createUnknownError("boom");
10
+
11
+ assertTrue(UnknownError.is(result));
12
+ assertFalse(UnknownError.is({ type: "OtherError", error: "boom" }));
13
+ });
14
+
15
+ it("handles plain error", () => {
16
+ const error = new Error("Test error");
17
+ const result = createUnknownError(error);
18
+
19
+ assertEqual(result.type, "UnknownError");
20
+
21
+ assertType(Object, result.error);
22
+ const details = result.error;
23
+ assertEqual(details.message, "Test error");
24
+ assertType(String, details.stack);
25
+ });
26
+
27
+ it("handles error with cause", () => {
28
+ const innerError = new Error("Inner error");
29
+ const error = new Error("Outer error", { cause: innerError });
30
+ const result = createUnknownError(error);
31
+
32
+ assertEqual(result.type, "UnknownError");
33
+ assertType(Object, result.error);
34
+ const details = result.error;
35
+ assertEqual(details.message, "Outer error");
36
+ assertType(String, details.stack);
37
+ assertType(Object, details.cause);
38
+ const cause = details.cause;
39
+ assertEqual(cause.message, "Inner error");
40
+ assertType(String, cause.stack);
41
+ });
42
+
43
+ it("handles inherited stack getter", () => {
44
+ const prototype: object = globalThis.Object.create(Error.prototype, {
45
+ stack: { get: () => "Inherited stack" },
46
+ });
47
+ const error = globalThis.Object.create(prototype) as Error;
48
+ globalThis.Object.defineProperty(error, "message", {
49
+ value: "Test error",
50
+ });
51
+ const result = createUnknownError(error);
52
+
53
+ assertType(Object, result.error);
54
+ const details = result.error;
55
+ assertEqual(details.message, "Test error");
56
+ assertEqual(details.stack, "Inherited stack");
57
+ });
58
+
59
+ it("excludes non-clonable error properties", () => {
60
+ const error = globalThis.Object.assign(new Error("Test error"), {
61
+ nonClonable: () => undefined,
62
+ });
63
+ const result = createUnknownError(error);
64
+
65
+ assertEqual(result.type, "UnknownError");
66
+ assertType(Object, result.error);
67
+ assertFalse("nonClonable" in result.error);
68
+ });
69
+
70
+ it("handles structured cloneable objects", () => {
71
+ const error = { key: "value" };
72
+ const result = createUnknownError(error);
73
+
74
+ assertEqual(result.type, "UnknownError");
75
+ assertEqual(result.error, { key: "value" });
76
+ });
77
+
78
+ it("handles non-cloneable objects", () => {
79
+ const error = {
80
+ toString: () => {
81
+ throw new Error("Cannot stringify");
82
+ },
83
+ };
84
+ const result = createUnknownError(error);
85
+
86
+ assertEqual(result.type, "UnknownError");
87
+ assertEqual(result.error, "[Unserializable Object]");
88
+ });
89
+
90
+ it("handles primitive values", () => {
91
+ const error = "A simple string";
92
+ const result = createUnknownError(error);
93
+
94
+ assertEqual(result.type, "UnknownError");
95
+ assertEqual(result.error, "A simple string");
96
+ });
97
+
98
+ it("handles null values", () => {
99
+ const result = createUnknownError(null);
100
+
101
+ assertEqual(result.type, "UnknownError");
102
+ assertSame(result.error, null);
103
+ });
104
+
105
+ it("handles circular references", () => {
106
+ interface Circular {
107
+ self?: Circular;
108
+ }
109
+ const error: Circular = {};
110
+ // Create a circular reference
111
+ error.self = error;
112
+ const result = createUnknownError(error);
113
+
114
+ assertEqual(result.type, "UnknownError");
115
+ const actual = result.error as Circular;
116
+ assertSame(actual.self, actual);
117
+ });
118
+ });