@evolu/common 8.4.0 → 8.6.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 (353) hide show
  1. package/dist/src/Array.d.ts +191 -94
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +91 -40
  4. package/dist/src/Assert.d.ts +233 -30
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +151 -30
  7. package/dist/src/BigInt.d.ts +3 -3
  8. package/dist/src/BigInt.js +3 -3
  9. package/dist/src/Brand.d.ts +7 -7
  10. package/dist/src/Buffer.d.ts +12 -4
  11. package/dist/src/Buffer.d.ts.map +1 -1
  12. package/dist/src/Cache.d.ts +8 -3
  13. package/dist/src/Cache.d.ts.map +1 -1
  14. package/dist/src/Cache.js +8 -3
  15. package/dist/src/Callbacks.d.ts +8 -4
  16. package/dist/src/Callbacks.d.ts.map +1 -1
  17. package/dist/src/Console.d.ts +45 -30
  18. package/dist/src/Console.d.ts.map +1 -1
  19. package/dist/src/Console.js +32 -21
  20. package/dist/src/Crypto.d.ts +13 -8
  21. package/dist/src/Crypto.d.ts.map +1 -1
  22. package/dist/src/Crypto.js +6 -3
  23. package/dist/src/Eq.d.ts +107 -58
  24. package/dist/src/Eq.d.ts.map +1 -1
  25. package/dist/src/Eq.js +308 -112
  26. package/dist/src/Function.d.ts +54 -24
  27. package/dist/src/Function.d.ts.map +1 -1
  28. package/dist/src/Function.js +34 -17
  29. package/dist/src/Http.d.ts +52 -19
  30. package/dist/src/Http.d.ts.map +1 -1
  31. package/dist/src/Identicon.d.ts +9 -4
  32. package/dist/src/Identicon.d.ts.map +1 -1
  33. package/dist/src/Identicon.js +9 -4
  34. package/dist/src/LeakDetector.d.ts.map +1 -1
  35. package/dist/src/LeakDetector.js +4 -1
  36. package/dist/src/LockManager.d.ts +12 -10
  37. package/dist/src/LockManager.d.ts.map +1 -1
  38. package/dist/src/Lookup.d.ts +4 -2
  39. package/dist/src/Lookup.d.ts.map +1 -1
  40. package/dist/src/Lookup.js +5 -2
  41. package/dist/src/Number.d.ts +28 -24
  42. package/dist/src/Number.d.ts.map +1 -1
  43. package/dist/src/Number.js +16 -17
  44. package/dist/src/Object.d.ts +89 -47
  45. package/dist/src/Object.d.ts.map +1 -1
  46. package/dist/src/Object.js +88 -41
  47. package/dist/src/Option.d.ts +14 -5
  48. package/dist/src/Option.d.ts.map +1 -1
  49. package/dist/src/Option.js +14 -5
  50. package/dist/src/Order.d.ts +12 -12
  51. package/dist/src/Order.js +12 -12
  52. package/dist/src/Platform.d.ts +2 -2
  53. package/dist/src/Platform.js +1 -0
  54. package/dist/src/Random.d.ts +7 -4
  55. package/dist/src/Random.d.ts.map +1 -1
  56. package/dist/src/Redacted.d.ts +13 -6
  57. package/dist/src/Redacted.d.ts.map +1 -1
  58. package/dist/src/Redacted.js +4 -2
  59. package/dist/src/Ref.d.ts +4 -4
  60. package/dist/src/Relation.d.ts +5 -7
  61. package/dist/src/Relation.d.ts.map +1 -1
  62. package/dist/src/Relation.js +3 -3
  63. package/dist/src/Resource.d.ts +27 -12
  64. package/dist/src/Resource.d.ts.map +1 -1
  65. package/dist/src/Resource.js +9 -3
  66. package/dist/src/Result.d.ts +258 -114
  67. package/dist/src/Result.d.ts.map +1 -1
  68. package/dist/src/Result.js +74 -39
  69. package/dist/src/Schedule.d.ts +233 -116
  70. package/dist/src/Schedule.d.ts.map +1 -1
  71. package/dist/src/Schedule.js +204 -110
  72. package/dist/src/Set.d.ts +43 -24
  73. package/dist/src/Set.d.ts.map +1 -1
  74. package/dist/src/Set.js +25 -13
  75. package/dist/src/Sqlite.d.ts +4 -4
  76. package/dist/src/Sqlite.d.ts.map +1 -1
  77. package/dist/src/Sqlite.js +18 -15
  78. package/dist/src/Store.d.ts.map +1 -1
  79. package/dist/src/Store.js +3 -1
  80. package/dist/src/String.d.ts.map +1 -1
  81. package/dist/src/String.js +1 -1
  82. package/dist/src/Task.d.ts +555 -264
  83. package/dist/src/Task.d.ts.map +1 -1
  84. package/dist/src/Task.js +278 -168
  85. package/dist/src/Test.d.ts +10 -4
  86. package/dist/src/Test.d.ts.map +1 -1
  87. package/dist/src/Test.js +11 -4
  88. package/dist/src/Time.d.ts +27 -17
  89. package/dist/src/Time.d.ts.map +1 -1
  90. package/dist/src/Time.js +25 -8
  91. package/dist/src/Type.d.ts +1218 -372
  92. package/dist/src/Type.d.ts.map +1 -1
  93. package/dist/src/Type.js +1056 -379
  94. package/dist/src/Types.d.ts +95 -40
  95. package/dist/src/Types.d.ts.map +1 -1
  96. package/dist/src/Types.js +13 -4
  97. package/dist/src/WebSocket.d.ts +14 -6
  98. package/dist/src/WebSocket.d.ts.map +1 -1
  99. package/dist/src/WebSocket.js +8 -0
  100. package/dist/src/Worker.d.ts +10 -6
  101. package/dist/src/Worker.d.ts.map +1 -1
  102. package/dist/src/Worker.js +1 -1
  103. package/dist/src/intl/_en.d.ts +3 -1
  104. package/dist/src/intl/_en.d.ts.map +1 -1
  105. package/dist/src/intl/_en.js +17 -3
  106. package/dist/src/intl/ar.d.ts +3 -1
  107. package/dist/src/intl/ar.d.ts.map +1 -1
  108. package/dist/src/intl/ar.js +15 -2
  109. package/dist/src/intl/bn.d.ts +3 -1
  110. package/dist/src/intl/bn.d.ts.map +1 -1
  111. package/dist/src/intl/bn.js +16 -3
  112. package/dist/src/intl/ca.d.ts +3 -1
  113. package/dist/src/intl/ca.d.ts.map +1 -1
  114. package/dist/src/intl/ca.js +16 -3
  115. package/dist/src/intl/cs.d.ts +3 -1
  116. package/dist/src/intl/cs.d.ts.map +1 -1
  117. package/dist/src/intl/cs.js +16 -3
  118. package/dist/src/intl/da.d.ts +3 -1
  119. package/dist/src/intl/da.d.ts.map +1 -1
  120. package/dist/src/intl/da.js +16 -3
  121. package/dist/src/intl/de.d.ts +3 -1
  122. package/dist/src/intl/de.d.ts.map +1 -1
  123. package/dist/src/intl/de.js +16 -3
  124. package/dist/src/intl/el.d.ts +3 -1
  125. package/dist/src/intl/el.d.ts.map +1 -1
  126. package/dist/src/intl/el.js +16 -3
  127. package/dist/src/intl/es.d.ts +3 -1
  128. package/dist/src/intl/es.d.ts.map +1 -1
  129. package/dist/src/intl/es.js +16 -3
  130. package/dist/src/intl/fa.d.ts +3 -1
  131. package/dist/src/intl/fa.d.ts.map +1 -1
  132. package/dist/src/intl/fa.js +16 -3
  133. package/dist/src/intl/fi.d.ts +3 -1
  134. package/dist/src/intl/fi.d.ts.map +1 -1
  135. package/dist/src/intl/fi.js +16 -3
  136. package/dist/src/intl/fil.d.ts +3 -1
  137. package/dist/src/intl/fil.d.ts.map +1 -1
  138. package/dist/src/intl/fil.js +16 -3
  139. package/dist/src/intl/fr.d.ts +3 -1
  140. package/dist/src/intl/fr.d.ts.map +1 -1
  141. package/dist/src/intl/fr.js +16 -3
  142. package/dist/src/intl/he.d.ts +3 -1
  143. package/dist/src/intl/he.d.ts.map +1 -1
  144. package/dist/src/intl/he.js +16 -3
  145. package/dist/src/intl/hi.d.ts +3 -1
  146. package/dist/src/intl/hi.d.ts.map +1 -1
  147. package/dist/src/intl/hi.js +16 -3
  148. package/dist/src/intl/hr.d.ts +3 -1
  149. package/dist/src/intl/hr.d.ts.map +1 -1
  150. package/dist/src/intl/hr.js +16 -3
  151. package/dist/src/intl/hu.d.ts +2 -1
  152. package/dist/src/intl/hu.d.ts.map +1 -1
  153. package/dist/src/intl/hu.js +15 -3
  154. package/dist/src/intl/id.d.ts +3 -1
  155. package/dist/src/intl/id.d.ts.map +1 -1
  156. package/dist/src/intl/id.js +16 -3
  157. package/dist/src/intl/it.d.ts +3 -1
  158. package/dist/src/intl/it.d.ts.map +1 -1
  159. package/dist/src/intl/it.js +16 -3
  160. package/dist/src/intl/ja.d.ts +3 -1
  161. package/dist/src/intl/ja.d.ts.map +1 -1
  162. package/dist/src/intl/ja.js +16 -3
  163. package/dist/src/intl/ko.d.ts +3 -1
  164. package/dist/src/intl/ko.d.ts.map +1 -1
  165. package/dist/src/intl/ko.js +16 -3
  166. package/dist/src/intl/ml.d.ts +3 -1
  167. package/dist/src/intl/ml.d.ts.map +1 -1
  168. package/dist/src/intl/ml.js +16 -3
  169. package/dist/src/intl/mr.d.ts +3 -1
  170. package/dist/src/intl/mr.d.ts.map +1 -1
  171. package/dist/src/intl/mr.js +16 -3
  172. package/dist/src/intl/ms.d.ts +3 -1
  173. package/dist/src/intl/ms.d.ts.map +1 -1
  174. package/dist/src/intl/ms.js +15 -2
  175. package/dist/src/intl/nb.d.ts +2 -1
  176. package/dist/src/intl/nb.d.ts.map +1 -1
  177. package/dist/src/intl/nb.js +14 -2
  178. package/dist/src/intl/nl.d.ts +3 -1
  179. package/dist/src/intl/nl.d.ts.map +1 -1
  180. package/dist/src/intl/nl.js +16 -3
  181. package/dist/src/intl/pa.d.ts +3 -1
  182. package/dist/src/intl/pa.d.ts.map +1 -1
  183. package/dist/src/intl/pa.js +16 -3
  184. package/dist/src/intl/pl.d.ts +2 -0
  185. package/dist/src/intl/pl.d.ts.map +1 -1
  186. package/dist/src/intl/pl.js +15 -2
  187. package/dist/src/intl/pt-BR.d.ts +3 -1
  188. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  189. package/dist/src/intl/pt-BR.js +16 -3
  190. package/dist/src/intl/pt.d.ts +3 -1
  191. package/dist/src/intl/pt.d.ts.map +1 -1
  192. package/dist/src/intl/pt.js +15 -2
  193. package/dist/src/intl/ro.d.ts +3 -1
  194. package/dist/src/intl/ro.d.ts.map +1 -1
  195. package/dist/src/intl/ro.js +16 -3
  196. package/dist/src/intl/sk.d.ts +3 -1
  197. package/dist/src/intl/sk.d.ts.map +1 -1
  198. package/dist/src/intl/sk.js +15 -2
  199. package/dist/src/intl/sl.d.ts +3 -1
  200. package/dist/src/intl/sl.d.ts.map +1 -1
  201. package/dist/src/intl/sl.js +16 -3
  202. package/dist/src/intl/sv.d.ts +3 -1
  203. package/dist/src/intl/sv.d.ts.map +1 -1
  204. package/dist/src/intl/sv.js +16 -3
  205. package/dist/src/intl/sw.d.ts +1 -0
  206. package/dist/src/intl/sw.d.ts.map +1 -1
  207. package/dist/src/intl/sw.js +14 -2
  208. package/dist/src/intl/ta.d.ts +3 -1
  209. package/dist/src/intl/ta.d.ts.map +1 -1
  210. package/dist/src/intl/ta.js +16 -3
  211. package/dist/src/intl/te.d.ts +3 -1
  212. package/dist/src/intl/te.d.ts.map +1 -1
  213. package/dist/src/intl/te.js +16 -3
  214. package/dist/src/intl/th.d.ts +3 -1
  215. package/dist/src/intl/th.d.ts.map +1 -1
  216. package/dist/src/intl/th.js +16 -3
  217. package/dist/src/intl/tr.d.ts +3 -1
  218. package/dist/src/intl/tr.d.ts.map +1 -1
  219. package/dist/src/intl/tr.js +16 -3
  220. package/dist/src/intl/uk.d.ts +3 -1
  221. package/dist/src/intl/uk.d.ts.map +1 -1
  222. package/dist/src/intl/uk.js +16 -3
  223. package/dist/src/intl/ur.d.ts +3 -1
  224. package/dist/src/intl/ur.d.ts.map +1 -1
  225. package/dist/src/intl/ur.js +15 -2
  226. package/dist/src/intl/vi.d.ts +3 -1
  227. package/dist/src/intl/vi.d.ts.map +1 -1
  228. package/dist/src/intl/vi.js +16 -3
  229. package/dist/src/intl/zh-CN.d.ts +3 -1
  230. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  231. package/dist/src/intl/zh-CN.js +16 -3
  232. package/dist/src/intl/zh-TW.d.ts +3 -1
  233. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  234. package/dist/src/intl/zh-TW.js +16 -3
  235. package/dist/src/local-first/Db.js +3 -2
  236. package/dist/src/local-first/Evolu.d.ts +47 -24
  237. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  238. package/dist/src/local-first/Evolu.js +1 -0
  239. package/dist/src/local-first/Owner.d.ts +18 -14
  240. package/dist/src/local-first/Owner.d.ts.map +1 -1
  241. package/dist/src/local-first/Owner.js +14 -11
  242. package/dist/src/local-first/Protocol.d.ts +5 -5
  243. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  244. package/dist/src/local-first/Protocol.js +35 -29
  245. package/dist/src/local-first/Query.d.ts +34 -21
  246. package/dist/src/local-first/Query.d.ts.map +1 -1
  247. package/dist/src/local-first/Query.js +27 -14
  248. package/dist/src/local-first/Relay.d.ts +12 -5
  249. package/dist/src/local-first/Relay.d.ts.map +1 -1
  250. package/dist/src/local-first/Relay.js +1 -1
  251. package/dist/src/local-first/Schema.d.ts +19 -11
  252. package/dist/src/local-first/Schema.d.ts.map +1 -1
  253. package/dist/src/local-first/Schema.js +11 -6
  254. package/dist/src/local-first/Shared.d.ts.map +1 -1
  255. package/dist/src/local-first/Shared.js +4 -2
  256. package/dist/src/local-first/Storage.d.ts +4 -2
  257. package/dist/src/local-first/Storage.d.ts.map +1 -1
  258. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  259. package/dist/src/local-first/Timestamp.js +1 -1
  260. package/package.json +3 -4
  261. package/src/Array.ts +195 -95
  262. package/src/Assert.ts +337 -31
  263. package/src/BigInt.ts +3 -3
  264. package/src/Brand.ts +7 -7
  265. package/src/Buffer.ts +12 -4
  266. package/src/Cache.ts +8 -3
  267. package/src/Callbacks.ts +8 -4
  268. package/src/Console.ts +45 -30
  269. package/src/Crypto.ts +13 -8
  270. package/src/Eq.ts +450 -122
  271. package/src/Function.ts +54 -24
  272. package/src/Http.ts +52 -19
  273. package/src/Identicon.ts +9 -4
  274. package/src/LeakDetector.ts +4 -3
  275. package/src/LockManager.ts +12 -10
  276. package/src/Lookup.ts +5 -2
  277. package/src/Number.ts +28 -24
  278. package/src/Object.ts +115 -49
  279. package/src/Option.ts +14 -5
  280. package/src/Order.ts +12 -12
  281. package/src/Platform.ts +3 -2
  282. package/src/Random.ts +7 -4
  283. package/src/Redacted.ts +13 -6
  284. package/src/Ref.ts +4 -4
  285. package/src/Relation.ts +8 -10
  286. package/src/Resource.ts +36 -15
  287. package/src/Result.ts +263 -119
  288. package/src/Schedule.ts +233 -116
  289. package/src/Set.ts +43 -24
  290. package/src/Sqlite.ts +18 -15
  291. package/src/Store.ts +3 -1
  292. package/src/String.ts +1 -1
  293. package/src/Task.ts +566 -286
  294. package/src/Test.ts +11 -4
  295. package/src/Time.ts +36 -19
  296. package/src/Type.ts +2191 -590
  297. package/src/Types.ts +108 -40
  298. package/src/WebSocket.ts +22 -6
  299. package/src/Worker.ts +11 -7
  300. package/src/intl/_en.ts +20 -4
  301. package/src/intl/ar.ts +17 -2
  302. package/src/intl/bn.ts +19 -4
  303. package/src/intl/ca.ts +19 -4
  304. package/src/intl/cs.ts +19 -4
  305. package/src/intl/da.ts +19 -4
  306. package/src/intl/de.ts +19 -4
  307. package/src/intl/el.ts +19 -4
  308. package/src/intl/es.ts +19 -4
  309. package/src/intl/fa.ts +19 -4
  310. package/src/intl/fi.ts +19 -4
  311. package/src/intl/fil.ts +19 -4
  312. package/src/intl/fr.ts +19 -4
  313. package/src/intl/he.ts +19 -4
  314. package/src/intl/hi.ts +19 -4
  315. package/src/intl/hr.ts +19 -4
  316. package/src/intl/hu.ts +18 -4
  317. package/src/intl/id.ts +19 -4
  318. package/src/intl/it.ts +19 -4
  319. package/src/intl/ja.ts +19 -4
  320. package/src/intl/ko.ts +19 -4
  321. package/src/intl/ml.ts +19 -4
  322. package/src/intl/mr.ts +19 -4
  323. package/src/intl/ms.ts +17 -2
  324. package/src/intl/nb.ts +16 -2
  325. package/src/intl/nl.ts +19 -4
  326. package/src/intl/pa.ts +19 -4
  327. package/src/intl/pl.ts +18 -2
  328. package/src/intl/pt-BR.ts +19 -4
  329. package/src/intl/pt.ts +17 -2
  330. package/src/intl/ro.ts +19 -4
  331. package/src/intl/sk.ts +17 -2
  332. package/src/intl/sl.ts +19 -4
  333. package/src/intl/sv.ts +19 -4
  334. package/src/intl/sw.ts +15 -2
  335. package/src/intl/ta.ts +19 -4
  336. package/src/intl/te.ts +19 -4
  337. package/src/intl/th.ts +19 -4
  338. package/src/intl/tr.ts +19 -4
  339. package/src/intl/uk.ts +19 -4
  340. package/src/intl/ur.ts +17 -2
  341. package/src/intl/vi.ts +19 -3
  342. package/src/intl/zh-CN.ts +19 -4
  343. package/src/intl/zh-TW.ts +19 -4
  344. package/src/local-first/Db.ts +2 -1
  345. package/src/local-first/Evolu.ts +48 -24
  346. package/src/local-first/Owner.ts +18 -14
  347. package/src/local-first/Protocol.ts +45 -35
  348. package/src/local-first/Query.ts +40 -24
  349. package/src/local-first/Relay.ts +13 -6
  350. package/src/local-first/Schema.ts +19 -11
  351. package/src/local-first/Shared.ts +4 -2
  352. package/src/local-first/Storage.ts +4 -2
  353. package/src/local-first/Timestamp.ts +1 -1
package/src/Object.ts CHANGED
@@ -10,43 +10,88 @@
10
10
  */
11
11
  export type ReadonlyRecord<K extends keyof any, V> = Readonly<Record<K, V>>;
12
12
 
13
+ /** An Object representation recognized by {@link getObjectKind}. */
14
+ export type ObjectKind =
15
+ "Array" | "Date" | "Map" | "Object" | "Set" | "Uint8Array" | "Unsupported";
16
+
13
17
  /**
14
- * Checks if a value is a plain object (e.g., created with `{}` or `Object`).
18
+ * Classifies Object representations supported by Evolu's structural data APIs.
19
+ *
20
+ * Plain Objects are classified from their prototype without reading their
21
+ * properties, including `Symbol.toStringTag`.
22
+ *
23
+ * ### Example
24
+ *
25
+ * ```ts
26
+ * import { assertEqual, getObjectKind } from "@evolu/common";
15
27
  *
16
- * Accepts objects with `Object.prototype` and objects with a `null` prototype
17
- * created via `Object.create(null)`. Rejects class instances and other built-in
18
- * objects because their prototype chain includes an application-specific or
19
- * built-in prototype before `Object.prototype`.
28
+ * assertEqual(getObjectKind({}), "Object");
29
+ * assertEqual(getObjectKind(new Map()), "Map");
30
+ * assertEqual(getObjectKind(/value/u), "Unsupported");
31
+ * ```
32
+ */
33
+ export const getObjectKind = (value: object): ObjectKind => {
34
+ if (Array.isArray(value)) return "Array";
35
+ if (isPlainObject(value)) return "Object";
36
+
37
+ switch (Object.prototype.toString.call(value)) {
38
+ case "[object Date]":
39
+ return "Date";
40
+ case "[object Map]":
41
+ return "Map";
42
+ case "[object Set]":
43
+ return "Set";
44
+ case "[object Uint8Array]":
45
+ return "Uint8Array";
46
+ default:
47
+ return "Unsupported";
48
+ }
49
+ };
50
+
51
+ /**
52
+ * Checks if a value is a plain object (e.g., created with `{}` or `Object`).
20
53
  *
21
- * The prototype-chain check uses structure instead of `prototype ===
22
- * Object.prototype` so it also works for plain objects coming from another
23
- * JavaScript realm.
54
+ * Accepts objects with a `null` prototype. Otherwise, it uses a realm-neutral
55
+ * structural heuristic: the immediate prototype must be a root object with own
56
+ * `hasOwnProperty` and `isPrototypeOf` properties. This recognizes ordinary
57
+ * Objects from another JavaScript realm without relying on prototype identity.
24
58
  *
25
- * TODO: deprecated Use `Object.is` from Type. Define a dedicated Type when
26
- * another object domain is required.
59
+ * A custom root prototype with the same shape can therefore be classified as
60
+ * plain. This function assumes trusted JavaScript and is not a prototype
61
+ * authentication or security boundary. Other custom prototypes, class
62
+ * instances, and built-in objects are rejected.
27
63
  *
28
64
  * ### Example
29
65
  *
30
66
  * ```ts
31
- * import { isPlainObject } from "@evolu/common";
32
- *
33
- * expect(isPlainObject({})).toBe(true);
34
- * expect(isPlainObject(Object.create(null))).toBe(true);
35
- * expect(isPlainObject(new Date())).toBe(false);
36
- * expect(isPlainObject(new (class Example {})())).toBe(false);
37
- * expect(isPlainObject([])).toBe(false);
38
- * expect(isPlainObject(null)).toBe(false);
67
+ * import { assertFalse, assertTrue, isPlainObject } from "@evolu/common";
68
+ *
69
+ * class Example {
70
+ * readonly value = "example";
71
+ * }
72
+ *
73
+ * assertTrue(isPlainObject({}));
74
+ * assertTrue(isPlainObject(Object.create(null)));
75
+ * assertFalse(isPlainObject(new Date()));
76
+ * assertFalse(isPlainObject(new Example()));
77
+ * assertFalse(isPlainObject([]));
78
+ * assertFalse(isPlainObject(null));
39
79
  * ```
40
80
  */
41
81
  export const isPlainObject = (
42
82
  value: unknown,
43
83
  ): value is Record<string, unknown> => {
44
- if (Object.prototype.toString.call(value) !== "[object Object]") {
84
+ if (value === null || typeof value !== "object") {
45
85
  return false;
46
86
  }
47
87
 
48
88
  const prototype = Object.getPrototypeOf(value) as object | null;
49
- return prototype === null || Object.getPrototypeOf(prototype) === null;
89
+ if (prototype === null) return true;
90
+ return (
91
+ Object.getPrototypeOf(prototype) === null &&
92
+ Object.hasOwn(prototype, "hasOwnProperty") &&
93
+ Object.hasOwn(prototype, "isPrototypeOf")
94
+ );
50
95
  };
51
96
 
52
97
  /**
@@ -55,11 +100,11 @@ export const isPlainObject = (
55
100
  * ### Example
56
101
  *
57
102
  * ```ts
58
- * import { isFunction } from "@evolu/common";
103
+ * import { assertFalse, assertTrue, isFunction } from "@evolu/common";
59
104
  *
60
- * expect(isFunction(() => {})).toBe(true);
61
- * expect(isFunction(function () {})).toBe(true);
62
- * expect(isFunction({})).toBe(false);
105
+ * assertTrue(isFunction(() => {}));
106
+ * assertTrue(isFunction(function () {}));
107
+ * assertFalse(isFunction({}));
63
108
  * ```
64
109
  */
65
110
  export const isFunction = (value: unknown): value is globalThis.Function =>
@@ -71,11 +116,11 @@ export const isFunction = (value: unknown): value is globalThis.Function =>
71
116
  * ### Example
72
117
  *
73
118
  * ```ts
74
- * import { isIterable } from "@evolu/common";
119
+ * import { assertFalse, assertTrue, isIterable } from "@evolu/common";
75
120
  *
76
- * expect(isIterable([1, 2, 3])).toBe(true);
77
- * expect(isIterable("abc")).toBe(true);
78
- * expect(isIterable({})).toBe(false);
121
+ * assertTrue(isIterable([1, 2, 3]));
122
+ * assertTrue(isIterable("abc"));
123
+ * assertFalse(isIterable({}));
79
124
  * ```
80
125
  */
81
126
  export const isIterable = (value: unknown): value is Iterable<unknown> =>
@@ -89,6 +134,8 @@ export const isIterable = (value: unknown): value is Iterable<unknown> =>
89
134
  *
90
135
  * ```ts
91
136
  * import {
137
+ * assertEqual,
138
+ * assertType,
92
139
  * objectToEntries,
93
140
  * type Brand,
94
141
  * type ReadonlyRecord,
@@ -100,8 +147,8 @@ export const isIterable = (value: unknown): value is Iterable<unknown> =>
100
147
  * const users: ReadonlyRecord<UserId, string> = { [userId]: "Alice" };
101
148
  * const entries = objectToEntries(users);
102
149
  *
103
- * expectTypeOf(entries).toEqualTypeOf<ReadonlyArray<[UserId, string]>>();
104
- * expect(entries).toEqual([[userId, "Alice"]]);
150
+ * assertType<ReadonlyArray<[UserId, string]>, typeof entries>();
151
+ * assertEqual(entries, [[userId, "Alice"]]);
105
152
  * ```
106
153
  */
107
154
  export const objectToEntries = <T extends Record<string, any>>(
@@ -122,6 +169,8 @@ type StringKeyOf<T> = Extract<keyof T, string>;
122
169
  *
123
170
  * ```ts
124
171
  * import {
172
+ * assertEqual,
173
+ * assertType,
125
174
  * objectFromEntries,
126
175
  * type Brand,
127
176
  * type ReadonlyRecord,
@@ -133,8 +182,8 @@ type StringKeyOf<T> = Extract<keyof T, string>;
133
182
  * ];
134
183
  * const users = objectFromEntries(entries);
135
184
  *
136
- * expectTypeOf(users).toEqualTypeOf<ReadonlyRecord<UserId, string>>();
137
- * expect(users).toEqual({ u1: "Alice" });
185
+ * assertType<ReadonlyRecord<UserId, string>, typeof users>();
186
+ * assertEqual(users, { u1: "Alice" });
138
187
  * ```
139
188
  */
140
189
  export const objectFromEntries = <K extends string, V>(
@@ -150,17 +199,20 @@ export const objectFromEntries = <K extends string, V>(
150
199
  * ### Example
151
200
  *
152
201
  * ```ts
153
- * import { objectFrom, type ReadonlyRecord } from "@evolu/common";
202
+ * import {
203
+ * assertEqual,
204
+ * assertType,
205
+ * objectFrom,
206
+ * type ReadonlyRecord,
207
+ * } from "@evolu/common";
154
208
  *
155
209
  * const translations = objectFrom(
156
210
  * ["en", "fr"] as const,
157
211
  * (locale): string => `Hello in ${locale}`,
158
212
  * );
159
213
  *
160
- * expectTypeOf(translations).toEqualTypeOf<
161
- * ReadonlyRecord<"en" | "fr", string>
162
- * >();
163
- * expect(translations).toEqual({
214
+ * assertType<ReadonlyRecord<"en" | "fr", string>, typeof translations>();
215
+ * assertEqual(translations, {
164
216
  * en: "Hello in en",
165
217
  * fr: "Hello in fr",
166
218
  * });
@@ -228,7 +280,7 @@ export const excludeProp = <T extends object, K extends keyof T>(
228
280
  * ### Example
229
281
  *
230
282
  * ```ts
231
- * import { createMutableRecord } from "@evolu/common";
283
+ * import { assertEqual, createMutableRecord } from "@evolu/common";
232
284
  *
233
285
  * const createValuesByKey = (
234
286
  * entries: ReadonlyArray<readonly [string, number]>,
@@ -242,12 +294,13 @@ export const excludeProp = <T extends object, K extends keyof T>(
242
294
  * return valuesByKey;
243
295
  * };
244
296
  *
245
- * expect(
297
+ * assertEqual(
246
298
  * createValuesByKey([
247
299
  * ["a", 1],
248
300
  * ["b", 2],
249
301
  * ]),
250
- * ).toEqual({ a: 1, b: 2 });
302
+ * { a: 1, b: 2 },
303
+ * );
251
304
  * ```
252
305
  *
253
306
  * Note that TypeScript does not model an object's runtime prototype. A plain
@@ -255,21 +308,34 @@ export const excludeProp = <T extends object, K extends keyof T>(
255
308
  * object has a `null` prototype:
256
309
  *
257
310
  * ```ts
311
+ * import { assertErr, assertTrue, trySync } from "@evolu/common";
312
+ *
258
313
  * const values = Object.create(null) as Record<string, number>;
259
314
  *
260
315
  * // TypeScript accepts the call, but `toString` is undefined at runtime.
261
- * expect(() => values.toString()).toThrow(TypeError);
316
+ * // oxlint-disable-next-line typescript/no-base-to-string -- Intentionally calls a method missing from the null-prototype value.
317
+ * const result = trySync(() => values.toString());
318
+ * assertErr(result);
319
+ * assertTrue(result.error instanceof TypeError);
262
320
  * ```
263
321
  *
264
322
  * `createMutableRecord` uses the same TypeScript Record representation.
265
323
  *
266
324
  * ```ts
267
- * import { createMutableRecord } from "@evolu/common";
325
+ * import {
326
+ * assertErr,
327
+ * assertTrue,
328
+ * createMutableRecord,
329
+ * trySync,
330
+ * } from "@evolu/common";
268
331
  *
269
332
  * const values = createMutableRecord<string, number>();
270
333
  *
271
334
  * // TypeScript accepts the call, but `toString` is undefined at runtime.
272
- * expect(() => values.toString()).toThrow(TypeError);
335
+ * // oxlint-disable-next-line typescript/no-base-to-string -- Intentionally calls a method missing from the null-prototype value.
336
+ * const result = trySync(() => values.toString());
337
+ * assertErr(result);
338
+ * assertTrue(result.error instanceof TypeError);
273
339
  * ```
274
340
  *
275
341
  * In other words, treat the returned object as string-keyed data rather than
@@ -318,7 +384,7 @@ export const emptyRecord: Readonly<Record<string, never>> =
318
384
  * ### Example
319
385
  *
320
386
  * ```ts
321
- * import { getOwnProp } from "@evolu/common";
387
+ * import { assertEqual, assertType, getOwnProp } from "@evolu/common";
322
388
  *
323
389
  * interface User {
324
390
  * readonly name: string;
@@ -329,9 +395,9 @@ export const emptyRecord: Readonly<Record<string, never>> =
329
395
  * };
330
396
  * const user = getOwnProp(users, "bob");
331
397
  *
332
- * expectTypeOf(user).toEqualTypeOf<User | undefined>();
333
- * expect(user).toBeUndefined();
334
- * expect(getOwnProp(users, "toString")).toBeUndefined();
398
+ * assertType<User | undefined, typeof user>();
399
+ * assertEqual(user, undefined);
400
+ * assertEqual(getOwnProp(users, "toString"), undefined);
335
401
  * ```
336
402
  */
337
403
  export const getOwnProp = <K extends string, V>(
@@ -347,12 +413,12 @@ export const getOwnProp = <K extends string, V>(
347
413
  * ### Example
348
414
  *
349
415
  * ```ts
350
- * import { createObjectURL } from "@evolu/common";
416
+ * import { assertTrue, createObjectURL } from "@evolu/common";
351
417
  *
352
418
  * const blob = new Blob(["hello"], { type: "text/plain" });
353
419
  * using objectUrl = createObjectURL(blob);
354
420
  *
355
- * expect(objectUrl.url).toMatch(/^blob:/);
421
+ * assertTrue(objectUrl.url.startsWith("blob:"));
356
422
  * // URL.revokeObjectURL is automatically called when the scope ends.
357
423
  * ```
358
424
  *
package/src/Option.ts CHANGED
@@ -9,7 +9,16 @@
9
9
  * ### Example
10
10
  *
11
11
  * ```ts
12
- * import { isNone, isSome, none, type Option, some } from "@evolu/common";
12
+ * import {
13
+ * assert,
14
+ * assertSame,
15
+ * assertTrue,
16
+ * isNone,
17
+ * isSome,
18
+ * none,
19
+ * type Option,
20
+ * some,
21
+ * } from "@evolu/common";
13
22
  *
14
23
  * const cache = new Map<string, Option<unknown>>();
15
24
  *
@@ -19,10 +28,10 @@
19
28
  * cache.set("b", some(undefined));
20
29
  *
21
30
  * const a = get("a");
22
- * assert(isSome(a));
23
- * expect(a.value).toBeNull();
24
- * expect(isSome(get("b"))).toBe(true);
25
- * expect(isNone(get("c"))).toBe(true);
31
+ * assert(isSome(a), "Expected Some.");
32
+ * assertSame(a.value, null);
33
+ * assertTrue(isSome(get("b")));
34
+ * assertTrue(isNone(get("c")));
26
35
  * ```
27
36
  *
28
37
  * @module
package/src/Order.ts CHANGED
@@ -33,12 +33,12 @@ export type Ordering = -1 | 0 | 1;
33
33
  * ### Example
34
34
  *
35
35
  * ```ts
36
- * import { createOrder } from "@evolu/common";
36
+ * import { assertEqual, createOrder } from "@evolu/common";
37
37
  *
38
38
  * const orderNumber = createOrder<number>((x, y) => x < y);
39
- * expect(orderNumber(1, 2)).toBe(-1);
40
- * expect(orderNumber(2, 1)).toBe(1);
41
- * expect(orderNumber(1, 1)).toBe(0);
39
+ * assertEqual(orderNumber(1, 2), -1);
40
+ * assertEqual(orderNumber(2, 1), 1);
41
+ * assertEqual(orderNumber(1, 1), 0);
42
42
  * ```
43
43
  */
44
44
  export const createOrder =
@@ -52,10 +52,10 @@ export const createOrder =
52
52
  * ### Example
53
53
  *
54
54
  * ```ts
55
- * import { orderNumber, reverseOrder } from "@evolu/common";
55
+ * import { assertEqual, orderNumber, reverseOrder } from "@evolu/common";
56
56
  *
57
57
  * const descendingOrderNumber = reverseOrder(orderNumber);
58
- * expect([3, 1, 2].toSorted(descendingOrderNumber)).toEqual([3, 2, 1]);
58
+ * assertEqual([3, 1, 2].toSorted(descendingOrderNumber), [3, 2, 1]);
59
59
  * ```
60
60
  */
61
61
  export const reverseOrder =
@@ -69,9 +69,9 @@ export const reverseOrder =
69
69
  * ### Example
70
70
  *
71
71
  * ```ts
72
- * import { orderString } from "@evolu/common";
72
+ * import { assertEqual, orderString } from "@evolu/common";
73
73
  *
74
- * expect(["c", "b", "a"].toSorted(orderString)).toEqual(["a", "b", "c"]);
74
+ * assertEqual(["c", "b", "a"].toSorted(orderString), ["a", "b", "c"]);
75
75
  * ```
76
76
  */
77
77
  export const orderString: Order<string> = /*#__PURE__*/ createOrder(
@@ -84,9 +84,9 @@ export const orderString: Order<string> = /*#__PURE__*/ createOrder(
84
84
  * ### Example
85
85
  *
86
86
  * ```ts
87
- * import { orderNumber } from "@evolu/common";
87
+ * import { assertEqual, orderNumber } from "@evolu/common";
88
88
  *
89
- * expect([2, 1, 3].toSorted(orderNumber)).toEqual([1, 2, 3]);
89
+ * assertEqual([2, 1, 3].toSorted(orderNumber), [1, 2, 3]);
90
90
  * ```
91
91
  */
92
92
  export const orderNumber = /*#__PURE__*/ createOrder<number>((a, b) => a < b);
@@ -97,9 +97,9 @@ export const orderNumber = /*#__PURE__*/ createOrder<number>((a, b) => a < b);
97
97
  * ### Example
98
98
  *
99
99
  * ```ts
100
- * import { orderBigInt } from "@evolu/common";
100
+ * import { assertEqual, orderBigInt } from "@evolu/common";
101
101
  *
102
- * expect([2n, 1n, 3n].toSorted(orderBigInt)).toEqual([1n, 2n, 3n]);
102
+ * assertEqual([2n, 1n, 3n].toSorted(orderBigInt), [1n, 2n, 3n]);
103
103
  * ```
104
104
  */
105
105
  export const orderBigInt = /*#__PURE__*/ createOrder<bigint>((a, b) => a < b);
package/src/Platform.ts CHANGED
@@ -82,8 +82,8 @@ export interface TestGlobalErrors extends Disposable {
82
82
  * Platforms deliver global errors asynchronously and provide no hook for
83
83
  * "nothing was reported", so settle emits a sentinel of the same kind and
84
84
  * resolves when it arrives. Platform delivery is ordered, so all earlier
85
- * errors are recorded by then. Use it to assert absence: `expect(await
86
- * unhandledRejections.settle()).toEqual([])`.
85
+ * errors are recorded by then. Assert absence with `assertEqual(await
86
+ * unhandledRejections.settle(), [])`.
87
87
  */
88
88
  readonly settle: () => Promise<ReadonlyArray<unknown>>;
89
89
  }
@@ -176,6 +176,7 @@ const createTestGlobalErrors = (
176
176
  globalThis.removeEventListener(webEvent, listener);
177
177
  });
178
178
  } else {
179
+ // oxlint-disable-next-line unicorn/prefer-type-error -- An unsupported runtime environment is not an invalid argument type.
179
180
  throw new Error(`Unsupported platform global ${errorType} reporting.`);
180
181
  }
181
182
 
package/src/Random.ts CHANGED
@@ -23,6 +23,9 @@ export type RandomNumber = number & Brand<"RandomNumber">;
23
23
  *
24
24
  * ```ts
25
25
  * import {
26
+ * assertEqual,
27
+ * assertTrue,
28
+ * assertType,
26
29
  * createRandom,
27
30
  * testCreateRandom,
28
31
  * type RandomNumber,
@@ -31,14 +34,14 @@ export type RandomNumber = number & Brand<"RandomNumber">;
31
34
  * // For apps, use the Math.random-backed implementation.
32
35
  * const random = createRandom();
33
36
  * const value = random.next();
34
- * expectTypeOf(value).toEqualTypeOf<RandomNumber>();
35
- * expect(value).toBeGreaterThanOrEqual(0);
36
- * expect(value).toBeLessThan(1);
37
+ * assertType<RandomNumber, typeof value>();
38
+ * assertTrue(value >= 0);
39
+ * assertTrue(value < 1);
37
40
  *
38
41
  * // For tests, use a seed.
39
42
  * const firstTestRandom = testCreateRandom("test");
40
43
  * const secondTestRandom = testCreateRandom("test");
41
- * expect(firstTestRandom.next()).toBe(secondTestRandom.next());
44
+ * assertEqual(firstTestRandom.next(), secondTestRandom.next());
42
45
  * ```
43
46
  */
44
47
  export interface Random {
package/src/Redacted.ts CHANGED
@@ -30,8 +30,11 @@ import type { Eq } from "./Eq.ts";
30
30
  *
31
31
  * ```ts
32
32
  * import {
33
+ * assertErr,
34
+ * assertEqual,
33
35
  * createRedacted,
34
36
  * revealRedacted,
37
+ * trySync,
35
38
  * type Brand,
36
39
  * type Redacted,
37
40
  * } from "@evolu/common";
@@ -45,11 +48,13 @@ import type { Eq } from "./Eq.ts";
45
48
  * using redactedKey: RedactedApiKey = createRedacted(apiKey);
46
49
  * const fetchUser = (key: RedactedApiKey): ApiKey => revealRedacted(key);
47
50
  *
48
- * expect(redactedKey.toString()).toBe("<redacted>");
49
- * expect(JSON.stringify({ apiKey: redactedKey })).toBe(
51
+ * // oxlint-disable-next-line typescript/no-base-to-string -- Redacted intentionally implements a safe custom toString.
52
+ * assertEqual(redactedKey.toString(), "<redacted>");
53
+ * assertEqual(
54
+ * JSON.stringify({ apiKey: redactedKey }),
50
55
  * '{"apiKey":"<redacted>"}',
51
56
  * );
52
- * expect(fetchUser(redactedKey)).toBe(apiKey);
57
+ * assertEqual(fetchUser(redactedKey), apiKey);
53
58
  *
54
59
  * using password = createRedacted("password" as DbPassword);
55
60
  * // @ts-expect-error Redacted secrets retain their distinct branded types.
@@ -60,7 +65,7 @@ import type { Eq } from "./Eq.ts";
60
65
  * return key;
61
66
  * })();
62
67
  * // Leaving the `using` scope removes the value from memory.
63
- * expect(() => revealRedacted(disposedKey)).toThrow();
68
+ * assertErr(trySync(() => revealRedacted(disposedKey)));
64
69
  * ```
65
70
  */
66
71
  export interface Redacted<A> extends Brand<"Redacted">, Disposable {
@@ -112,6 +117,8 @@ export const isRedacted = (value: unknown): value is Redacted<unknown> =>
112
117
  *
113
118
  * ```ts
114
119
  * import {
120
+ * assertFalse,
121
+ * assertTrue,
115
122
  * createEqRedacted,
116
123
  * createRedacted,
117
124
  * eqString,
@@ -126,8 +133,8 @@ export const isRedacted = (value: unknown): value is Redacted<unknown> =>
126
133
  * using b = createRedacted("x" as ApiKey);
127
134
  * using c = createRedacted("y" as ApiKey);
128
135
  *
129
- * expect(eqRedactedApiKey(a, b)).toBe(true);
130
- * expect(eqRedactedApiKey(a, c)).toBe(false);
136
+ * assertTrue(eqRedactedApiKey(a, b));
137
+ * assertFalse(eqRedactedApiKey(a, c));
131
138
  * ```
132
139
  */
133
140
  export const createEqRedacted =
package/src/Ref.ts CHANGED
@@ -21,18 +21,18 @@ import type { Store } from "./Store.ts";
21
21
  * ### Example
22
22
  *
23
23
  * ```ts
24
- * import { createRef } from "@evolu/common";
24
+ * import { assertEqual, createRef } from "@evolu/common";
25
25
  *
26
26
  * const count = createRef(0);
27
27
  * count.set(1);
28
28
  * count.update((n) => n + 1);
29
- * expect(count.get()).toBe(2);
29
+ * assertEqual(count.get(), 2);
30
30
  * ```
31
31
  *
32
32
  * ### As a dependency
33
33
  *
34
34
  * ```ts
35
- * import { createRef, type Ref } from "@evolu/common";
35
+ * import { assertEqual, createRef, type Ref } from "@evolu/common";
36
36
  *
37
37
  * interface CounterRefDep {
38
38
  * readonly counterRef: Ref<number>;
@@ -40,7 +40,7 @@ import type { Store } from "./Store.ts";
40
40
  *
41
41
  * const deps: CounterRefDep = { counterRef: createRef(0) };
42
42
  * deps.counterRef.update((count) => count + 1);
43
- * expect(deps.counterRef.get()).toBe(1);
43
+ * assertEqual(deps.counterRef.get(), 1);
44
44
  * ```
45
45
  */
46
46
  export interface Ref<T> {
package/src/Relation.ts CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  * Use the default identity semantics.
57
57
  *
58
58
  * ```ts
59
- * import { createRelation } from "@evolu/common";
59
+ * import { assertFalse, assertTrue, createRelation } from "@evolu/common";
60
60
  *
61
61
  * interface Socket {
62
62
  * readonly id: string;
@@ -67,8 +67,8 @@ import {
67
67
  *
68
68
  * relation.add(socket, "owner-1");
69
69
  *
70
- * expect(relation.has(socket, "owner-1")).toBe(true);
71
- * expect(relation.has({ id: "socket-1" }, "owner-1")).toBe(false);
70
+ * assertTrue(relation.has(socket, "owner-1"));
71
+ * assertFalse(relation.has({ id: "socket-1" }, "owner-1"));
72
72
  * ```
73
73
  *
74
74
  * ### Lookup-derived equality
@@ -76,7 +76,7 @@ import {
76
76
  * Use lookup-derived equality.
77
77
  *
78
78
  * ```ts
79
- * import { createRelation } from "@evolu/common";
79
+ * import { assertTrue, createRelation } from "@evolu/common";
80
80
  *
81
81
  * interface Person {
82
82
  * readonly id: string;
@@ -90,9 +90,7 @@ import {
90
90
  *
91
91
  * relation.add({ id: "1", name: "Ada" }, { id: "admins" });
92
92
  *
93
- * expect(relation.has({ id: "1", name: "Grace" }, { id: "admins" })).toBe(
94
- * true,
95
- * );
93
+ * assertTrue(relation.has({ id: "1", name: "Grace" }, { id: "admins" }));
96
94
  * ```
97
95
  *
98
96
  * @see {@link createRelation}
@@ -250,14 +248,14 @@ export function createRelation<A, B, LA = A, LB = B>({
250
248
  removeByA: (a) => {
251
249
  const relatedB = bByA.get(a);
252
250
  if (!relatedB) return false;
253
- for (const b of [...relatedB.keys()]) removePair(a, b);
251
+ for (const b of relatedB.keys()) removePair(a, b);
254
252
  return true;
255
253
  },
256
254
 
257
255
  removeByB: (b) => {
258
256
  const relatedA = aByB.get(b);
259
257
  if (!relatedA) return false;
260
- for (const a of [...relatedA.keys()]) removePair(a, b);
258
+ for (const a of relatedA.keys()) removePair(a, b);
261
259
  return true;
262
260
  },
263
261
 
@@ -306,7 +304,7 @@ export function createRelation<A, B, LA = A, LB = B>({
306
304
  const assertRelationMappingConsistency: (
307
305
  condition: unknown,
308
306
  ) => asserts condition = (condition) => {
309
- assert(condition, "Relation mapping inconsistency");
307
+ assert(Boolean(condition), "Relation mapping inconsistency");
310
308
  };
311
309
 
312
310
  /**