@evolu/common 8.5.0 → 8.6.1

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 (328) hide show
  1. package/dist/src/Array.d.ts +170 -97
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +67 -42
  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 +150 -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/LockManager.d.ts +12 -10
  35. package/dist/src/LockManager.d.ts.map +1 -1
  36. package/dist/src/Lookup.d.ts +4 -2
  37. package/dist/src/Lookup.d.ts.map +1 -1
  38. package/dist/src/Lookup.js +4 -2
  39. package/dist/src/Number.d.ts +28 -24
  40. package/dist/src/Number.d.ts.map +1 -1
  41. package/dist/src/Number.js +16 -17
  42. package/dist/src/Object.d.ts +89 -47
  43. package/dist/src/Object.d.ts.map +1 -1
  44. package/dist/src/Object.js +88 -41
  45. package/dist/src/Option.d.ts +14 -5
  46. package/dist/src/Option.d.ts.map +1 -1
  47. package/dist/src/Option.js +14 -5
  48. package/dist/src/Order.d.ts +12 -12
  49. package/dist/src/Order.js +12 -12
  50. package/dist/src/Platform.d.ts +2 -2
  51. package/dist/src/Random.d.ts +7 -4
  52. package/dist/src/Random.d.ts.map +1 -1
  53. package/dist/src/Redacted.d.ts +13 -6
  54. package/dist/src/Redacted.d.ts.map +1 -1
  55. package/dist/src/Redacted.js +4 -2
  56. package/dist/src/Ref.d.ts +4 -4
  57. package/dist/src/Relation.d.ts +5 -7
  58. package/dist/src/Relation.d.ts.map +1 -1
  59. package/dist/src/Resource.d.ts +27 -12
  60. package/dist/src/Resource.d.ts.map +1 -1
  61. package/dist/src/Result.d.ts +258 -114
  62. package/dist/src/Result.d.ts.map +1 -1
  63. package/dist/src/Result.js +71 -34
  64. package/dist/src/Schedule.d.ts +233 -116
  65. package/dist/src/Schedule.d.ts.map +1 -1
  66. package/dist/src/Schedule.js +204 -110
  67. package/dist/src/Set.d.ts +43 -24
  68. package/dist/src/Set.d.ts.map +1 -1
  69. package/dist/src/Set.js +25 -13
  70. package/dist/src/Sqlite.d.ts +4 -4
  71. package/dist/src/Sqlite.d.ts.map +1 -1
  72. package/dist/src/Sqlite.js +9 -8
  73. package/dist/src/Task.d.ts +441 -212
  74. package/dist/src/Task.d.ts.map +1 -1
  75. package/dist/src/Task.js +238 -115
  76. package/dist/src/Test.d.ts +10 -4
  77. package/dist/src/Test.d.ts.map +1 -1
  78. package/dist/src/Test.js +10 -4
  79. package/dist/src/Time.d.ts +27 -17
  80. package/dist/src/Time.d.ts.map +1 -1
  81. package/dist/src/Time.js +16 -6
  82. package/dist/src/Type.d.ts +1096 -387
  83. package/dist/src/Type.d.ts.map +1 -1
  84. package/dist/src/Type.js +1046 -372
  85. package/dist/src/Types.d.ts +95 -40
  86. package/dist/src/Types.d.ts.map +1 -1
  87. package/dist/src/Types.js +13 -4
  88. package/dist/src/WebSocket.d.ts +14 -6
  89. package/dist/src/WebSocket.d.ts.map +1 -1
  90. package/dist/src/Worker.d.ts +10 -6
  91. package/dist/src/Worker.d.ts.map +1 -1
  92. package/dist/src/intl/_en.d.ts +3 -1
  93. package/dist/src/intl/_en.d.ts.map +1 -1
  94. package/dist/src/intl/_en.js +17 -3
  95. package/dist/src/intl/ar.d.ts +3 -1
  96. package/dist/src/intl/ar.d.ts.map +1 -1
  97. package/dist/src/intl/ar.js +15 -2
  98. package/dist/src/intl/bn.d.ts +3 -1
  99. package/dist/src/intl/bn.d.ts.map +1 -1
  100. package/dist/src/intl/bn.js +16 -3
  101. package/dist/src/intl/ca.d.ts +3 -1
  102. package/dist/src/intl/ca.d.ts.map +1 -1
  103. package/dist/src/intl/ca.js +16 -3
  104. package/dist/src/intl/cs.d.ts +3 -1
  105. package/dist/src/intl/cs.d.ts.map +1 -1
  106. package/dist/src/intl/cs.js +16 -3
  107. package/dist/src/intl/da.d.ts +3 -1
  108. package/dist/src/intl/da.d.ts.map +1 -1
  109. package/dist/src/intl/da.js +16 -3
  110. package/dist/src/intl/de.d.ts +3 -1
  111. package/dist/src/intl/de.d.ts.map +1 -1
  112. package/dist/src/intl/de.js +16 -3
  113. package/dist/src/intl/el.d.ts +3 -1
  114. package/dist/src/intl/el.d.ts.map +1 -1
  115. package/dist/src/intl/el.js +16 -3
  116. package/dist/src/intl/es.d.ts +3 -1
  117. package/dist/src/intl/es.d.ts.map +1 -1
  118. package/dist/src/intl/es.js +16 -3
  119. package/dist/src/intl/fa.d.ts +3 -1
  120. package/dist/src/intl/fa.d.ts.map +1 -1
  121. package/dist/src/intl/fa.js +16 -3
  122. package/dist/src/intl/fi.d.ts +3 -1
  123. package/dist/src/intl/fi.d.ts.map +1 -1
  124. package/dist/src/intl/fi.js +16 -3
  125. package/dist/src/intl/fil.d.ts +3 -1
  126. package/dist/src/intl/fil.d.ts.map +1 -1
  127. package/dist/src/intl/fil.js +16 -3
  128. package/dist/src/intl/fr.d.ts +3 -1
  129. package/dist/src/intl/fr.d.ts.map +1 -1
  130. package/dist/src/intl/fr.js +16 -3
  131. package/dist/src/intl/he.d.ts +3 -1
  132. package/dist/src/intl/he.d.ts.map +1 -1
  133. package/dist/src/intl/he.js +16 -3
  134. package/dist/src/intl/hi.d.ts +3 -1
  135. package/dist/src/intl/hi.d.ts.map +1 -1
  136. package/dist/src/intl/hi.js +16 -3
  137. package/dist/src/intl/hr.d.ts +3 -1
  138. package/dist/src/intl/hr.d.ts.map +1 -1
  139. package/dist/src/intl/hr.js +16 -3
  140. package/dist/src/intl/hu.d.ts +2 -1
  141. package/dist/src/intl/hu.d.ts.map +1 -1
  142. package/dist/src/intl/hu.js +15 -3
  143. package/dist/src/intl/id.d.ts +3 -1
  144. package/dist/src/intl/id.d.ts.map +1 -1
  145. package/dist/src/intl/id.js +16 -3
  146. package/dist/src/intl/it.d.ts +3 -1
  147. package/dist/src/intl/it.d.ts.map +1 -1
  148. package/dist/src/intl/it.js +16 -3
  149. package/dist/src/intl/ja.d.ts +3 -1
  150. package/dist/src/intl/ja.d.ts.map +1 -1
  151. package/dist/src/intl/ja.js +16 -3
  152. package/dist/src/intl/ko.d.ts +3 -1
  153. package/dist/src/intl/ko.d.ts.map +1 -1
  154. package/dist/src/intl/ko.js +16 -3
  155. package/dist/src/intl/ml.d.ts +3 -1
  156. package/dist/src/intl/ml.d.ts.map +1 -1
  157. package/dist/src/intl/ml.js +16 -3
  158. package/dist/src/intl/mr.d.ts +3 -1
  159. package/dist/src/intl/mr.d.ts.map +1 -1
  160. package/dist/src/intl/mr.js +16 -3
  161. package/dist/src/intl/ms.d.ts +3 -1
  162. package/dist/src/intl/ms.d.ts.map +1 -1
  163. package/dist/src/intl/ms.js +15 -2
  164. package/dist/src/intl/nb.d.ts +2 -1
  165. package/dist/src/intl/nb.d.ts.map +1 -1
  166. package/dist/src/intl/nb.js +14 -2
  167. package/dist/src/intl/nl.d.ts +3 -1
  168. package/dist/src/intl/nl.d.ts.map +1 -1
  169. package/dist/src/intl/nl.js +16 -3
  170. package/dist/src/intl/pa.d.ts +3 -1
  171. package/dist/src/intl/pa.d.ts.map +1 -1
  172. package/dist/src/intl/pa.js +16 -3
  173. package/dist/src/intl/pl.d.ts +2 -0
  174. package/dist/src/intl/pl.d.ts.map +1 -1
  175. package/dist/src/intl/pl.js +15 -2
  176. package/dist/src/intl/pt-BR.d.ts +3 -1
  177. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  178. package/dist/src/intl/pt-BR.js +16 -3
  179. package/dist/src/intl/pt.d.ts +3 -1
  180. package/dist/src/intl/pt.d.ts.map +1 -1
  181. package/dist/src/intl/pt.js +15 -2
  182. package/dist/src/intl/ro.d.ts +3 -1
  183. package/dist/src/intl/ro.d.ts.map +1 -1
  184. package/dist/src/intl/ro.js +16 -3
  185. package/dist/src/intl/sk.d.ts +3 -1
  186. package/dist/src/intl/sk.d.ts.map +1 -1
  187. package/dist/src/intl/sk.js +15 -2
  188. package/dist/src/intl/sl.d.ts +3 -1
  189. package/dist/src/intl/sl.d.ts.map +1 -1
  190. package/dist/src/intl/sl.js +16 -3
  191. package/dist/src/intl/sv.d.ts +3 -1
  192. package/dist/src/intl/sv.d.ts.map +1 -1
  193. package/dist/src/intl/sv.js +16 -3
  194. package/dist/src/intl/sw.d.ts +1 -0
  195. package/dist/src/intl/sw.d.ts.map +1 -1
  196. package/dist/src/intl/sw.js +14 -2
  197. package/dist/src/intl/ta.d.ts +3 -1
  198. package/dist/src/intl/ta.d.ts.map +1 -1
  199. package/dist/src/intl/ta.js +16 -3
  200. package/dist/src/intl/te.d.ts +3 -1
  201. package/dist/src/intl/te.d.ts.map +1 -1
  202. package/dist/src/intl/te.js +16 -3
  203. package/dist/src/intl/th.d.ts +3 -1
  204. package/dist/src/intl/th.d.ts.map +1 -1
  205. package/dist/src/intl/th.js +16 -3
  206. package/dist/src/intl/tr.d.ts +3 -1
  207. package/dist/src/intl/tr.d.ts.map +1 -1
  208. package/dist/src/intl/tr.js +16 -3
  209. package/dist/src/intl/uk.d.ts +3 -1
  210. package/dist/src/intl/uk.d.ts.map +1 -1
  211. package/dist/src/intl/uk.js +16 -3
  212. package/dist/src/intl/ur.d.ts +3 -1
  213. package/dist/src/intl/ur.d.ts.map +1 -1
  214. package/dist/src/intl/ur.js +15 -2
  215. package/dist/src/intl/vi.d.ts +3 -1
  216. package/dist/src/intl/vi.d.ts.map +1 -1
  217. package/dist/src/intl/vi.js +16 -3
  218. package/dist/src/intl/zh-CN.d.ts +3 -1
  219. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  220. package/dist/src/intl/zh-CN.js +16 -3
  221. package/dist/src/intl/zh-TW.d.ts +3 -1
  222. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  223. package/dist/src/intl/zh-TW.js +16 -3
  224. package/dist/src/local-first/Evolu.d.ts +47 -24
  225. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  226. package/dist/src/local-first/Owner.d.ts +10 -7
  227. package/dist/src/local-first/Owner.d.ts.map +1 -1
  228. package/dist/src/local-first/Owner.js +10 -7
  229. package/dist/src/local-first/Protocol.d.ts +5 -5
  230. package/dist/src/local-first/Protocol.js +7 -7
  231. package/dist/src/local-first/Query.d.ts +34 -21
  232. package/dist/src/local-first/Query.d.ts.map +1 -1
  233. package/dist/src/local-first/Query.js +25 -13
  234. package/dist/src/local-first/Relay.d.ts +12 -5
  235. package/dist/src/local-first/Relay.d.ts.map +1 -1
  236. package/dist/src/local-first/Schema.d.ts +19 -11
  237. package/dist/src/local-first/Schema.d.ts.map +1 -1
  238. package/dist/src/local-first/Schema.js +11 -6
  239. package/dist/src/local-first/Storage.d.ts +4 -2
  240. package/dist/src/local-first/Storage.d.ts.map +1 -1
  241. package/package.json +3 -4
  242. package/src/Array.ts +170 -97
  243. package/src/Assert.ts +336 -31
  244. package/src/BigInt.ts +3 -3
  245. package/src/Brand.ts +7 -7
  246. package/src/Buffer.ts +12 -4
  247. package/src/Cache.ts +8 -3
  248. package/src/Callbacks.ts +8 -4
  249. package/src/Console.ts +45 -30
  250. package/src/Crypto.ts +13 -8
  251. package/src/Eq.ts +450 -122
  252. package/src/Function.ts +54 -24
  253. package/src/Http.ts +52 -19
  254. package/src/Identicon.ts +9 -4
  255. package/src/LockManager.ts +12 -10
  256. package/src/Lookup.ts +4 -2
  257. package/src/Number.ts +28 -24
  258. package/src/Object.ts +115 -49
  259. package/src/Option.ts +14 -5
  260. package/src/Order.ts +12 -12
  261. package/src/Platform.ts +2 -2
  262. package/src/Random.ts +7 -4
  263. package/src/Redacted.ts +13 -6
  264. package/src/Ref.ts +4 -4
  265. package/src/Relation.ts +5 -7
  266. package/src/Resource.ts +27 -12
  267. package/src/Result.ts +258 -114
  268. package/src/Schedule.ts +233 -116
  269. package/src/Set.ts +43 -24
  270. package/src/Sqlite.ts +9 -8
  271. package/src/Task.ts +441 -212
  272. package/src/Test.ts +10 -4
  273. package/src/Time.ts +27 -17
  274. package/src/Type.ts +2077 -620
  275. package/src/Types.ts +108 -40
  276. package/src/WebSocket.ts +14 -6
  277. package/src/Worker.ts +10 -6
  278. package/src/intl/_en.ts +20 -4
  279. package/src/intl/ar.ts +17 -2
  280. package/src/intl/bn.ts +19 -4
  281. package/src/intl/ca.ts +19 -4
  282. package/src/intl/cs.ts +19 -4
  283. package/src/intl/da.ts +19 -4
  284. package/src/intl/de.ts +19 -4
  285. package/src/intl/el.ts +19 -4
  286. package/src/intl/es.ts +19 -4
  287. package/src/intl/fa.ts +19 -4
  288. package/src/intl/fi.ts +19 -4
  289. package/src/intl/fil.ts +19 -4
  290. package/src/intl/fr.ts +19 -4
  291. package/src/intl/he.ts +19 -4
  292. package/src/intl/hi.ts +19 -4
  293. package/src/intl/hr.ts +19 -4
  294. package/src/intl/hu.ts +18 -4
  295. package/src/intl/id.ts +19 -4
  296. package/src/intl/it.ts +19 -4
  297. package/src/intl/ja.ts +19 -4
  298. package/src/intl/ko.ts +19 -4
  299. package/src/intl/ml.ts +19 -4
  300. package/src/intl/mr.ts +19 -4
  301. package/src/intl/ms.ts +17 -2
  302. package/src/intl/nb.ts +16 -2
  303. package/src/intl/nl.ts +19 -4
  304. package/src/intl/pa.ts +19 -4
  305. package/src/intl/pl.ts +18 -2
  306. package/src/intl/pt-BR.ts +19 -4
  307. package/src/intl/pt.ts +17 -2
  308. package/src/intl/ro.ts +19 -4
  309. package/src/intl/sk.ts +17 -2
  310. package/src/intl/sl.ts +19 -4
  311. package/src/intl/sv.ts +19 -4
  312. package/src/intl/sw.ts +15 -2
  313. package/src/intl/ta.ts +19 -4
  314. package/src/intl/te.ts +19 -4
  315. package/src/intl/th.ts +19 -4
  316. package/src/intl/tr.ts +19 -4
  317. package/src/intl/uk.ts +19 -4
  318. package/src/intl/ur.ts +17 -2
  319. package/src/intl/vi.ts +19 -3
  320. package/src/intl/zh-CN.ts +19 -4
  321. package/src/intl/zh-TW.ts +19 -4
  322. package/src/local-first/Evolu.ts +47 -24
  323. package/src/local-first/Owner.ts +10 -7
  324. package/src/local-first/Protocol.ts +7 -7
  325. package/src/local-first/Query.ts +34 -21
  326. package/src/local-first/Relay.ts +12 -5
  327. package/src/local-first/Schema.ts +19 -11
  328. package/src/local-first/Storage.ts +4 -2
@@ -5,7 +5,7 @@ import type { RandomBytesDep } from "./Crypto.ts";
5
5
  import { type Thunk } from "./Function.ts";
6
6
  import { type Result } from "./Result.ts";
7
7
  import type { TimeDep } from "./Time.ts";
8
- import { type CompileTimeError, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
8
+ import { type CompileTimeError, type IsSameType, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
9
9
  /**
10
10
  * A runtime representation of a TypeScript type, including its encoded input,
11
11
  * semantic output, structured errors, and canonical encoding.
@@ -77,12 +77,12 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
77
77
  * ### Example
78
78
  *
79
79
  * ```ts
80
- * import { PositiveInt } from "@evolu/common";
80
+ * import { assertOk, PositiveInt } from "@evolu/common";
81
81
  *
82
82
  * const value: unknown = 42;
83
83
  * const result = PositiveInt.fromUnknown(value);
84
84
  *
85
- * expectOk(result, 42);
85
+ * assertOk(result, 42);
86
86
  * ```
87
87
  */
88
88
  readonly fromUnknown: (value: unknown, options?: ValidationOptions) => Result<Output, Errors>;
@@ -98,12 +98,25 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
98
98
  * ### Example
99
99
  *
100
100
  * ```ts
101
- * import { String } from "@evolu/common";
101
+ * import {
102
+ * assertEqual,
103
+ * assertType,
104
+ * Data,
105
+ * assertErr,
106
+ * String,
107
+ * } from "@evolu/common";
102
108
  *
103
109
  * const result = String.fromUnknown(42);
104
110
  *
105
- * expectErr(result, { type: "TypeOf", expected: "String", value: 42 });
106
- * expect(String.formatError(result.error)).toBe(
111
+ * assertErr(result);
112
+ * assertType(Data, result.error);
113
+ * assertEqual(result.error, {
114
+ * type: "TypeOf",
115
+ * expected: "String",
116
+ * value: 42,
117
+ * });
118
+ * assertEqual(
119
+ * String.formatError(result.error),
107
120
  * "A value 42 is not a string.",
108
121
  * );
109
122
  * ```
@@ -119,14 +132,20 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
119
132
  * ### Example
120
133
  *
121
134
  * ```ts
122
- * import { Int64FromInt64String, type Int64 } from "@evolu/common";
135
+ * import {
136
+ * assertFalse,
137
+ * assertType,
138
+ * assertTrue,
139
+ * Int64FromInt64String,
140
+ * type Int64,
141
+ * } from "@evolu/common";
123
142
  *
124
143
  * const values: ReadonlyArray<unknown> = [42n, "42", null];
125
144
  * const integers = values.filter(Int64FromInt64String.is);
126
145
  *
127
- * expectTypeOf(integers).toEqualTypeOf<globalThis.Array<Int64>>();
128
- * expect(Int64FromInt64String.is(42n)).toBe(true);
129
- * expect(Int64FromInt64String.is("42")).toBe(false);
146
+ * assertType<Array<Int64>, typeof integers>();
147
+ * assertTrue(Int64FromInt64String.is(42n));
148
+ * assertFalse(Int64FromInt64String.is("42"));
130
149
  * ```
131
150
  */
132
151
  readonly is: (value: unknown) => value is Output;
@@ -151,6 +170,8 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
151
170
  *
152
171
  * ```ts
153
172
  * import {
173
+ * assertOk,
174
+ * assertType,
154
175
  * flatMapResult,
155
176
  * NonEmptyTrimmedString100,
156
177
  * object,
@@ -183,10 +204,11 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
183
204
  * const note = TrimmedString.orThrow("Remember oat milk");
184
205
  * const result = saveTodo(title, note);
185
206
  *
186
- * expectTypeOf(result).toEqualTypeOf<
187
- * Result<typeof Todo.Output, MaxLengthError<100> | MinLengthError<1>>
207
+ * assertType<
208
+ * Result<typeof Todo.Output, MaxLengthError<100> | MinLengthError<1>>,
209
+ * typeof result
188
210
  * >();
189
- * expectOk(result, { title, note });
211
+ * assertOk(result, { title, note });
190
212
  * ```
191
213
  */
192
214
  readonly from: [CustomFrom] extends [never] ? [Parent] extends [infer P extends TypeNode] ? FromOperation<Output, Error, P> : TypeOperationFn<"from", Output, Output, never> : CustomFrom;
@@ -201,11 +223,15 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
201
223
  * ### Example
202
224
  *
203
225
  * ```ts
204
- * import { Int64, Int64FromInt64String } from "@evolu/common";
226
+ * import {
227
+ * assertEqual,
228
+ * Int64,
229
+ * Int64FromInt64String,
230
+ * } from "@evolu/common";
205
231
  *
206
232
  * const value = Int64.orThrow(42n);
207
233
  *
208
- * expect(Int64FromInt64String.to(value)).toBe("42");
234
+ * assertEqual(Int64FromInt64String.to(value), "42");
209
235
  * ```
210
236
  */
211
237
  readonly to: [Parent] extends [infer P extends TypeNode] ? ToOperation<Output, CanonicalInput, P> : (value: Output) => CanonicalInput;
@@ -228,7 +254,12 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
228
254
  * ### Example
229
255
  *
230
256
  * ```ts
231
- * import { getOrThrow, minLength, String } from "@evolu/common";
257
+ * import {
258
+ * assertEqual,
259
+ * getOrThrow,
260
+ * minLength,
261
+ * String,
262
+ * } from "@evolu/common";
232
263
  *
233
264
  * const NonEmptyString = minLength(1)(String);
234
265
  *
@@ -237,8 +268,8 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
237
268
  * // Equivalent because `from.parent` is this Type's deepest `from` operation:
238
269
  * const sameValue = getOrThrow(NonEmptyString.from.parent("Evolu"));
239
270
  *
240
- * expect(value).toBe("Evolu");
241
- * expect(sameValue).toBe(value);
271
+ * assertEqual(value, "Evolu");
272
+ * assertEqual(sameValue, value);
242
273
  * ```
243
274
  */
244
275
  readonly orThrow: TypeOperationFn<"orThrow", Input, Output, never>;
@@ -261,7 +292,12 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
261
292
  * ### Example
262
293
  *
263
294
  * ```ts
264
- * import { getOrNull, minLength, String } from "@evolu/common";
295
+ * import {
296
+ * assertEqual,
297
+ * getOrNull,
298
+ * minLength,
299
+ * String,
300
+ * } from "@evolu/common";
265
301
  *
266
302
  * const NonEmptyString = minLength(1)(String);
267
303
  *
@@ -270,9 +306,9 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
270
306
  * // Equivalent because `from.parent` is this Type's deepest `from` operation:
271
307
  * const sameValue = getOrNull(NonEmptyString.from.parent("Evolu"));
272
308
  *
273
- * expect(value).toBe("Evolu");
274
- * expect(sameValue).toBe(value);
275
- * expect(NonEmptyString.orNull("")).toBeNull();
309
+ * assertEqual(value, "Evolu");
310
+ * assertEqual(sameValue, value);
311
+ * assertEqual(NonEmptyString.orNull(""), null);
276
312
  * ```
277
313
  */
278
314
  readonly orNull: TypeOperationFn<"orNull", Input, Output, never>;
@@ -344,12 +380,18 @@ export interface TypeNode {
344
380
  readonly is: (value: unknown) => boolean;
345
381
  }
346
382
  /**
347
- * Asserts that a value belongs to a {@link Type} Output domain.
383
+ * Asserts exact compile-time type equality or that a value belongs to a
384
+ * {@link Type} Output domain.
385
+ *
386
+ * - `assertType<Expected, Actual>()` requires compiler-identical types without
387
+ * evaluating a value.
388
+ * - `assertType(type, value)` validates and narrows a runtime value to the Type's
389
+ * Output.
348
390
  *
349
- * Use this for internal invariants, not external input. Validate external input
350
- * with `Type.fromUnknown` so validation failures remain typed values. A failed
351
- * assertion uses the Type name for its message and preserves the exact Output
352
- * validation error as the thrown Error's cause.
391
+ * Use the runtime form for internal invariants, not external input. Validate
392
+ * external input with `Type.fromUnknown` so validation failures remain typed
393
+ * values. A failed runtime assertion uses the Type name for its message and
394
+ * preserves the exact Output validation error as the thrown Error's cause.
353
395
  *
354
396
  * ### Example
355
397
  *
@@ -362,14 +404,21 @@ export interface TypeNode {
362
404
  *
363
405
  * const value: unknown = "Evolu";
364
406
  * assertType(NonEmptyTrimmedString100, value);
365
- * expectTypeOf(value).toEqualTypeOf<
366
- * string & Brand<"Trimmed"> & Brand<"MinLength1"> & Brand<"MaxLength100">
407
+ * assertType<
408
+ * string &
409
+ * Brand<"Trimmed"> &
410
+ * Brand<"MinLength1"> &
411
+ * Brand<"MaxLength100">,
412
+ * typeof value
367
413
  * >();
368
414
  * ```
369
415
  *
370
416
  * @group Core
371
417
  */
372
- export declare const assertType: <T extends TypeNode>(type: T, value: unknown) => asserts value is T["Output"];
418
+ export declare function assertType<Expected, Actual>(...error: IsSameType<Expected, Actual> extends true ? [] : [
419
+ error: CompileTimeError<"assertType", "Expected and actual types must be identical">
420
+ ]): void;
421
+ export declare function assertType<T extends TypeNode>(type: T, value: unknown): asserts value is T["Output"];
373
422
  /**
374
423
  * Creates localized copies of selected {@link Type} declarations.
375
424
  *
@@ -395,13 +444,22 @@ export declare const assertType: <T extends TypeNode>(type: T, value: unknown) =
395
444
  * every locale an app supports also allows language changes without a network
396
445
  * connection.
397
446
  *
398
- * The selected Type map, locale map, and formatter maps must be plain objects
399
- * with own enumerable string-keyed data properties.
447
+ * The selected Type map, locale map, and formatter maps must satisfy the
448
+ * realm-neutral structural heuristic described by {@link isPlainObject} and have
449
+ * own enumerable string-keyed data properties.
400
450
  *
401
451
  * ### Example
402
452
  *
403
453
  * ```ts
404
- * import { String, localizeTypes, minLength } from "@evolu/common";
454
+ * import {
455
+ * assertEqual,
456
+ * Data,
457
+ * assertErr,
458
+ * assertType,
459
+ * String,
460
+ * localizeTypes,
461
+ * minLength,
462
+ * } from "@evolu/common";
405
463
  * import { cs } from "@evolu/common/intl";
406
464
  *
407
465
  * const Label = minLength(1)(String);
@@ -416,13 +474,14 @@ export declare const assertType: <T extends TypeNode>(type: T, value: unknown) =
416
474
  * },
417
475
  * );
418
476
  *
419
- * expectTypeOf<typeof typesByLocale.cs.Label>().toEqualTypeOf<
420
- * typeof Label
421
- * >();
477
+ * assertType<typeof Label, typeof typesByLocale.cs.Label>();
422
478
  *
423
479
  * const result = typesByLocale.cs.Label.fromUnknown("");
424
- * expectErr(result, { type: "MinLength1", min: 1, value: "" });
425
- * expect(typesByLocale.cs.Label.formatError(result.error)).toBe(
480
+ * assertErr(result);
481
+ * assertType(Data, result.error);
482
+ * assertEqual(result.error, { type: "MinLength1", min: 1, value: "" });
483
+ * assertEqual(
484
+ * typesByLocale.cs.Label.formatError(result.error),
426
485
  * "Text nesmí být prázdný.",
427
486
  * );
428
487
  * ```
@@ -501,7 +560,7 @@ type LocalizedErrorEntry<Error extends TypeError, Seen extends TypeError = never
501
560
  readonly type: "DiscriminatedUnion";
502
561
  readonly reason: unknown;
503
562
  } ? LocalizedDiscriminatedUnionErrorEntry<Error, Seen | Error> : Error extends {
504
- readonly type: infer Name extends "Array" | "Tuple" | "Record" | "Set";
563
+ readonly type: infer Name extends "Array" | "Map" | "Tuple" | "Record" | "Set";
505
564
  readonly reason: infer Reason;
506
565
  } ? LocalizedErrorEntryValue<Name, Error> | LocalizedIssuesErrorEntry<Reason, Seen | Error> : Error extends {
507
566
  readonly type: "Object";
@@ -549,6 +608,8 @@ declare const identityEncodingSymbol: unique symbol;
549
608
  *
550
609
  * ```ts
551
610
  * import {
611
+ * assertEqual,
612
+ * assertType,
552
613
  * NonEmptyTrimmedString100,
553
614
  * PositiveInt,
554
615
  * object,
@@ -564,8 +625,8 @@ declare const identityEncodingSymbol: unique symbol;
564
625
  *
565
626
  * const user = User.orThrow({ name: "Ada", age: 37 });
566
627
  *
567
- * expectTypeOf(user).toExtend<User>();
568
- * expect(user.name).toBe("Ada");
628
+ * assertType<typeof User.Output, typeof user>();
629
+ * assertEqual(user.name, "Ada");
569
630
  * ```
570
631
  *
571
632
  * @group Core
@@ -696,7 +757,12 @@ type ConcreteChildTypeNameError = CompileTimeError<"Type", "Name must be one con
696
757
  *
697
758
  * ```ts
698
759
  * import {
760
+ * assertEqual,
761
+ * assertErr,
762
+ * assertOk,
763
+ * assertType,
699
764
  * createType,
765
+ * Data,
700
766
  * err,
701
767
  * ok,
702
768
  * type Result,
@@ -714,8 +780,11 @@ type ConcreteChildTypeNameError = CompileTimeError<"Type", "Name must be one con
714
780
  * () => "Expected text.",
715
781
  * );
716
782
  *
717
- * expectOk(Text.fromUnknown("Evolu"), "Evolu");
718
- * expectErr(Text.fromUnknown(42), { type: "Text", value: 42 });
783
+ * assertOk(Text.fromUnknown("Evolu"), "Evolu");
784
+ * const invalid = Text.fromUnknown(42);
785
+ * assertErr(invalid);
786
+ * assertType(Data, invalid.error);
787
+ * assertEqual(invalid.error, { type: "Text", value: 42 });
719
788
  * ```
720
789
  *
721
790
  * @group Construction
@@ -756,7 +825,15 @@ export declare function createType<Name extends TypeName, ParentType extends Con
756
825
  * ### Example
757
826
  *
758
827
  * ```ts
759
- * import { Boolean, literal, ok, transform, union } from "@evolu/common";
828
+ * import {
829
+ * assertEqual,
830
+ * assertOk,
831
+ * Boolean,
832
+ * literal,
833
+ * ok,
834
+ * transform,
835
+ * union,
836
+ * } from "@evolu/common";
760
837
  *
761
838
  * const BooleanString = union(literal("false"), literal("true"));
762
839
  * const BooleanFromString = transform(
@@ -769,8 +846,8 @@ export declare function createType<Name extends TypeName, ParentType extends Con
769
846
  * },
770
847
  * );
771
848
  *
772
- * expectOk(BooleanFromString.from.parent("true"), true);
773
- * expect(BooleanFromString.to(false)).toBe("false");
849
+ * assertOk(BooleanFromString.from.parent("true"), true);
850
+ * assertEqual(BooleanFromString.to(false), "false");
774
851
  * ```
775
852
  *
776
853
  * @group Construction
@@ -786,6 +863,11 @@ export declare function transform<Name extends TypeName, ParentType extends Conc
786
863
  *
787
864
  * ```ts
788
865
  * import {
866
+ * assertEqual,
867
+ * assertType,
868
+ * Data,
869
+ * assertErr,
870
+ * assertOk,
789
871
  * Boolean,
790
872
  * String,
791
873
  * err,
@@ -815,12 +897,15 @@ export declare function transform<Name extends TypeName, ParentType extends Conc
815
897
  * () => 'Expected "true" or "false".',
816
898
  * );
817
899
  *
818
- * expectOk(BooleanFromString.fromUnknown("true"), true);
819
- * expect(BooleanFromString.to(false)).toBe("false");
900
+ * assertOk(BooleanFromString.fromUnknown("true"), true);
901
+ * assertEqual(BooleanFromString.to(false), "false");
820
902
  *
821
903
  * const invalid = BooleanFromString.fromUnknown("yes");
822
- * expectErr(invalid, { type: "BooleanFromString", value: "yes" });
823
- * expect(BooleanFromString.formatError(invalid.error)).toBe(
904
+ * assertErr(invalid);
905
+ * assertType(Data, invalid.error);
906
+ * assertEqual(invalid.error, { type: "BooleanFromString", value: "yes" });
907
+ * assertEqual(
908
+ * BooleanFromString.formatError(invalid.error),
824
909
  * 'Expected "true" or "false".',
825
910
  * );
826
911
  * ```
@@ -916,16 +1001,20 @@ export interface TypeOfError<Name extends keyof TypeOfOutputByName> extends Type
916
1001
  * and significant whitespace:
917
1002
  *
918
1003
  * ```ts
919
- * import { String, maxLength, type Brand } from "@evolu/common";
1004
+ * import {
1005
+ * assertOk,
1006
+ * assertType,
1007
+ * String,
1008
+ * maxLength,
1009
+ * type Brand,
1010
+ * } from "@evolu/common";
920
1011
  *
921
1012
  * const WireValue100 = maxLength(100)(String);
922
1013
  * type WireValue100 = typeof WireValue100.Output;
923
1014
  *
924
- * expectTypeOf<WireValue100>().toEqualTypeOf<
925
- * string & Brand<"MaxLength100">
926
- * >();
927
- * expectOk(WireValue100.fromUnknown(""), "");
928
- * expectOk(WireValue100.fromUnknown(" value "), " value ");
1015
+ * assertType<string & Brand<"MaxLength100">, WireValue100>();
1016
+ * assertOk(WireValue100.fromUnknown(""), "");
1017
+ * assertOk(WireValue100.fromUnknown(" value "), " value ");
929
1018
  * ```
930
1019
  *
931
1020
  * @group String
@@ -951,7 +1040,12 @@ export declare const String: Type<"String", string, string, TypeOfError<"String"
951
1040
  *
952
1041
  * ```ts
953
1042
  * import {
1043
+ * assertEqual,
1044
+ * assertErr,
1045
+ * assertOk,
1046
+ * assertType,
954
1047
  * Age,
1048
+ * Data,
955
1049
  * FiniteNumber,
956
1050
  * Int,
957
1051
  * NonNaNNumber,
@@ -961,36 +1055,41 @@ export declare const String: Type<"String", string, string, TypeOfError<"String"
961
1055
  * } from "@evolu/common";
962
1056
  *
963
1057
  * // Note how every additional constraint accumulates its Brand.
964
- * expectTypeOf<typeof Number.Output>().toEqualTypeOf<number>();
965
- * expectTypeOf<typeof NonNaNNumber.Output>().toEqualTypeOf<
966
- * number & Brand<"NonNaN">
967
- * >();
968
- * expectTypeOf<typeof FiniteNumber.Output>().toEqualTypeOf<
969
- * number & Brand<"NonNaN"> & Brand<"Finite">
1058
+ * assertType<number, typeof Number.Output>();
1059
+ * assertType<number & Brand<"NonNaN">, typeof NonNaNNumber.Output>();
1060
+ * assertType<
1061
+ * number & Brand<"NonNaN"> & Brand<"Finite">,
1062
+ * typeof FiniteNumber.Output
970
1063
  * >();
971
- * expectTypeOf<typeof Int.Output>().toEqualTypeOf<
972
- * number & Brand<"NonNaN"> & Brand<"Finite"> & Brand<"Int">
1064
+ * assertType<
1065
+ * number & Brand<"NonNaN"> & Brand<"Finite"> & Brand<"Int">,
1066
+ * typeof Int.Output
973
1067
  * >();
974
- * expectTypeOf<typeof NonNegativeInt.Output>().toEqualTypeOf<
1068
+ * assertType<
975
1069
  * number &
976
1070
  * Brand<"NonNaN"> &
977
1071
  * Brand<"Finite"> &
978
1072
  * Brand<"Int"> &
979
- * Brand<"NonNegative">
1073
+ * Brand<"NonNegative">,
1074
+ * typeof NonNegativeInt.Output
980
1075
  * >();
981
1076
  *
982
- * expectTypeOf<typeof Age.Output>().toEqualTypeOf<
1077
+ * assertType<
983
1078
  * number &
984
1079
  * Brand<"NonNaN"> &
985
1080
  * Brand<"Finite"> &
986
1081
  * Brand<"Int"> &
987
1082
  * Brand<"NonNegative"> &
988
1083
  * Brand<"LessThan200"> &
989
- * Brand<"Age">
1084
+ * Brand<"Age">,
1085
+ * typeof Age.Output
990
1086
  * >();
991
1087
  *
992
- * expectOk(Age.fromUnknown(122), 122);
993
- * expectErr(Age.fromUnknown(200), {
1088
+ * assertOk(Age.fromUnknown(122), 122);
1089
+ * const invalid = Age.fromUnknown(200);
1090
+ * assertErr(invalid);
1091
+ * assertType(Data, invalid.error);
1092
+ * assertEqual(invalid.error, {
994
1093
  * type: "LessThan200",
995
1094
  * value: 200,
996
1095
  * max: 200,
@@ -1107,12 +1206,12 @@ interface ObjectTagOutputByName {
1107
1206
  * ### Example
1108
1207
  *
1109
1208
  * ```ts
1110
- * import { objectTag } from "@evolu/common";
1209
+ * import { assertOk, objectTag } from "@evolu/common";
1111
1210
  *
1112
1211
  * const DateType = objectTag("Date");
1113
- * const date = new globalThis.Date("2025-01-01T00:00:00.000Z");
1212
+ * const date = new Date("2025-01-01T00:00:00.000Z");
1114
1213
  *
1115
- * expectOk(DateType.fromUnknown(date), date);
1214
+ * assertOk(DateType.fromUnknown(date), date);
1116
1215
  * ```
1117
1216
  *
1118
1217
  * @group Base
@@ -1124,10 +1223,17 @@ export declare function objectTag<Name extends keyof ObjectTagOutputByName>(name
1124
1223
  * ### Example
1125
1224
  *
1126
1225
  * ```ts
1127
- * import { instanceOf, objectTag } from "@evolu/common";
1226
+ * import {
1227
+ * assertEqual,
1228
+ * assertOk,
1229
+ * assertSame,
1230
+ * assertType,
1231
+ * instanceOf,
1232
+ * objectTag,
1233
+ * } from "@evolu/common";
1128
1234
  *
1129
1235
  * class TaggedValue {
1130
- * readonly [globalThis.Symbol.toStringTag] = "TaggedValue";
1236
+ * readonly [Symbol.toStringTag] = "TaggedValue";
1131
1237
  * }
1132
1238
  *
1133
1239
  * const TaggedValueType = objectTag(
@@ -1137,9 +1243,13 @@ export declare function objectTag<Name extends keyof ObjectTagOutputByName>(name
1137
1243
  * const value = new TaggedValue();
1138
1244
  * const result = TaggedValueType.fromUnknown(value);
1139
1245
  *
1140
- * expectOk(result, value);
1141
- * expectTypeOf(result.value).toExtend<TaggedValue>();
1142
- * expect(TaggedValueType.expected).toBe("TaggedValue");
1246
+ * assertOk(result);
1247
+ * assertSame(result.value, value);
1248
+ * assertType<
1249
+ * true,
1250
+ * typeof result.value extends TaggedValue ? true : false
1251
+ * >();
1252
+ * assertEqual(TaggedValueType.expected, "TaggedValue");
1143
1253
  * ```
1144
1254
  */
1145
1255
  export declare function objectTag<Name extends TypeName, OutputType extends ConcreteTypeNode & {
@@ -1184,7 +1294,7 @@ export declare const ArrayBuffer: Type<"ArrayBuffer", ArrayBuffer, ArrayBuffer,
1184
1294
  * ### Example
1185
1295
  *
1186
1296
  * ```ts
1187
- * import { instanceOf } from "@evolu/common";
1297
+ * import { assertFalse, assertTrue, instanceOf } from "@evolu/common";
1188
1298
  *
1189
1299
  * class User {
1190
1300
  * readonly name: string;
@@ -1196,8 +1306,8 @@ export declare const ArrayBuffer: Type<"ArrayBuffer", ArrayBuffer, ArrayBuffer,
1196
1306
  *
1197
1307
  * const UserInstance = instanceOf(User);
1198
1308
  *
1199
- * assert(UserInstance.is(new User("Ada")));
1200
- * assert(!UserInstance.is({ name: "Ada" }));
1309
+ * assertTrue(UserInstance.is(new User("Ada")));
1310
+ * assertFalse(UserInstance.is({ name: "Ada" }));
1201
1311
  * ```
1202
1312
  *
1203
1313
  * @group Base
@@ -1247,13 +1357,23 @@ type InstanceConstructorCompileTimeError = CompileTimeError<"Type", "Constructor
1247
1357
  * ### Example
1248
1358
  *
1249
1359
  * ```ts
1250
- * import { literal } from "@evolu/common";
1360
+ * import {
1361
+ * assertEqual,
1362
+ * assertErr,
1363
+ * assertOk,
1364
+ * assertType,
1365
+ * Data,
1366
+ * literal,
1367
+ * } from "@evolu/common";
1251
1368
  *
1252
1369
  * const Ready = literal("ready");
1253
1370
  *
1254
- * expectTypeOf<typeof Ready.Output>().toEqualTypeOf<"ready">();
1255
- * expectOk(Ready.fromUnknown("ready"), "ready");
1256
- * expectErr(Ready.fromUnknown("pending"), {
1371
+ * assertType<"ready", typeof Ready.Output>();
1372
+ * assertOk(Ready.fromUnknown("ready"), "ready");
1373
+ * const invalid = Ready.fromUnknown("pending");
1374
+ * assertErr(invalid);
1375
+ * assertType(Data, invalid.error);
1376
+ * assertEqual(invalid.error, {
1257
1377
  * type: "Literal",
1258
1378
  * expected: "ready",
1259
1379
  * value: "pending",
@@ -1328,17 +1448,29 @@ export declare const Null: LiteralType<null>;
1328
1448
  * ### Example
1329
1449
  *
1330
1450
  * ```ts
1331
- * import { Number, String, union } from "@evolu/common";
1451
+ * import {
1452
+ * assertEqual,
1453
+ * assertErr,
1454
+ * assertOk,
1455
+ * assertType,
1456
+ * Data,
1457
+ * Number,
1458
+ * String,
1459
+ * union,
1460
+ * } from "@evolu/common";
1332
1461
  *
1333
1462
  * const Status = union("draft", "published");
1334
1463
  * const StatusOrCode = union("draft", "published", Number);
1335
1464
  *
1336
- * expectOk(Status.fromUnknown("draft"), "draft");
1337
- * expectOk(StatusOrCode.fromUnknown(42), 42);
1465
+ * assertOk(Status.fromUnknown("draft"), "draft");
1466
+ * assertOk(StatusOrCode.fromUnknown(42), 42);
1338
1467
  *
1339
1468
  * const TextOrNumber = union(String, Number);
1340
1469
  *
1341
- * expectErr(TextOrNumber.fromUnknown(true, { errors: "all" }), {
1470
+ * const invalid = TextOrNumber.fromUnknown(true, { errors: "all" });
1471
+ * assertErr(invalid);
1472
+ * assertType(Data, invalid.error);
1473
+ * assertEqual(invalid.error, {
1342
1474
  * type: "Union",
1343
1475
  * errors: [
1344
1476
  * {
@@ -1377,11 +1509,11 @@ export declare function union<const Members extends AtLeastTwoReadonlyArray<Type
1377
1509
  * ### Example
1378
1510
  *
1379
1511
  * ```ts
1380
- * import { String, undefinedOr } from "@evolu/common";
1512
+ * import { assertOk, String, undefinedOr } from "@evolu/common";
1381
1513
  *
1382
1514
  * const StringOrUndefined = undefinedOr(String);
1383
1515
  *
1384
- * expectOk(StringOrUndefined.fromUnknown(undefined), undefined);
1516
+ * assertOk(StringOrUndefined.fromUnknown(undefined), undefined);
1385
1517
  * ```
1386
1518
  *
1387
1519
  * @group Unions
@@ -1393,11 +1525,11 @@ export declare const undefinedOr: <ValueType extends TypeNode>(type: ValidateUni
1393
1525
  * ### Example
1394
1526
  *
1395
1527
  * ```ts
1396
- * import { String, nullOr } from "@evolu/common";
1528
+ * import { assertOk, String, nullOr } from "@evolu/common";
1397
1529
  *
1398
1530
  * const NullableString = nullOr(String);
1399
1531
  *
1400
- * expectOk(NullableString.fromUnknown(null), null);
1532
+ * assertOk(NullableString.fromUnknown(null), null);
1401
1533
  * ```
1402
1534
  *
1403
1535
  * @group Unions
@@ -1409,12 +1541,12 @@ export declare const nullOr: <ValueType extends TypeNode>(type: ValidateUnionTyp
1409
1541
  * ### Example
1410
1542
  *
1411
1543
  * ```ts
1412
- * import { String, nullishOr } from "@evolu/common";
1544
+ * import { assertOk, String, nullishOr } from "@evolu/common";
1413
1545
  *
1414
1546
  * const NullishString = nullishOr(String);
1415
1547
  *
1416
- * expectOk(NullishString.fromUnknown(undefined), undefined);
1417
- * expectOk(NullishString.fromUnknown(null), null);
1548
+ * assertOk(NullishString.fromUnknown(undefined), undefined);
1549
+ * assertOk(NullishString.fromUnknown(null), null);
1418
1550
  * ```
1419
1551
  *
1420
1552
  * @group Unions
@@ -1522,7 +1654,16 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1522
1654
  * the structured data decoded from it:
1523
1655
  *
1524
1656
  * ```ts
1525
- * import { templateLiteralParser, union } from "@evolu/common";
1657
+ * import {
1658
+ * assertFalse,
1659
+ * assertEqual,
1660
+ * assertErr,
1661
+ * assertOk,
1662
+ * assertType,
1663
+ * Data,
1664
+ * templateLiteralParser,
1665
+ * union,
1666
+ * } from "@evolu/common";
1526
1667
  *
1527
1668
  * const Language = union("en", "cs");
1528
1669
  * const Region = union("US", "CZ");
@@ -1532,36 +1673,39 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1532
1673
  *
1533
1674
  * // Output is the decoded language and region.
1534
1675
  * type SupportedLocale = typeof SupportedLocale.Output;
1535
- * expectTypeOf<SupportedLocale>().toEqualTypeOf<
1536
- * readonly ["en" | "cs", "US" | "CZ"]
1537
- * >();
1676
+ * assertType<readonly ["en" | "cs", "US" | "CZ"], SupportedLocale>();
1538
1677
  *
1539
1678
  * // The parent Output is the canonical locale string.
1540
1679
  * type SupportedLocaleLiteral = typeof SupportedLocale.parent.Output;
1541
- * expectTypeOf<SupportedLocaleLiteral>().toEqualTypeOf<
1542
- * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1680
+ * assertType<
1681
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ",
1682
+ * SupportedLocaleLiteral
1543
1683
  * >();
1544
1684
  *
1545
1685
  * // Parse an unknown string into structured data.
1546
1686
  * const result = SupportedLocale.fromUnknown("cs-CZ");
1547
- * expectOk(result, ["cs", "CZ"]);
1687
+ * assertOk(result, ["cs", "CZ"]);
1548
1688
  * const locale = result.value;
1549
- * expectTypeOf(locale).toEqualTypeOf<SupportedLocale>();
1550
- * expectErr(SupportedLocale.fromUnknown("cs/CZ"), {
1689
+ * assertType<SupportedLocale, typeof locale>();
1690
+ * const invalid = SupportedLocale.fromUnknown("cs/CZ");
1691
+ * assertErr(invalid);
1692
+ * assertType(Data, invalid.error);
1693
+ * const error: Data = invalid.error;
1694
+ * assertEqual(error, {
1551
1695
  * type: "TemplateLiteral",
1552
1696
  * value: "cs/CZ",
1553
1697
  * });
1554
1698
  *
1555
1699
  * // Encode structured data into its canonical string.
1556
1700
  * const localeLiteral = SupportedLocale.to(locale);
1557
- * expectTypeOf(localeLiteral).toEqualTypeOf<SupportedLocaleLiteral>();
1558
- * expect(localeLiteral).toBe("cs-CZ");
1701
+ * assertType<SupportedLocaleLiteral, typeof localeLiteral>();
1702
+ * assertEqual(localeLiteral, "cs-CZ");
1559
1703
  *
1560
1704
  * // Validate a string configuration value.
1561
1705
  * const configValue: unknown = "cs-CZ";
1562
- * assert(SupportedLocale.parent.is(configValue));
1563
- * expectTypeOf(configValue).toEqualTypeOf<SupportedLocaleLiteral>();
1564
- * expect(SupportedLocale.parent.is("fr-CZ")).toBe(false);
1706
+ * assertType(SupportedLocale.parent, configValue);
1707
+ * assertType<SupportedLocaleLiteral, typeof configValue>();
1708
+ * assertFalse(SupportedLocale.parent.is("fr-CZ"));
1565
1709
  * ```
1566
1710
  *
1567
1711
  * `SupportedLocale` is structured data for application code.
@@ -1574,6 +1718,8 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1574
1718
  *
1575
1719
  * ```ts
1576
1720
  * import {
1721
+ * assertEqual,
1722
+ * assertOk,
1577
1723
  * NonNegativeDecimalString,
1578
1724
  * templateLiteralParser,
1579
1725
  * } from "@evolu/common";
@@ -1586,8 +1732,8 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1586
1732
  * // DecimalText.to requires a validated NonNegativeDecimalString.
1587
1733
  * const zero = NonNegativeDecimalString.orThrow("0");
1588
1734
  *
1589
- * expectOk(DecimalText.fromUnknown("decimal:0"), [zero]);
1590
- * expect(DecimalText.to([zero])).toBe("decimal:0");
1735
+ * assertOk(DecimalText.fromUnknown("decimal:0"), [zero]);
1736
+ * assertEqual(DecimalText.to([zero]), "decimal:0");
1591
1737
  * ```
1592
1738
  *
1593
1739
  * Capture Types (the Type arguments passed to `templateLiteralParser`) can use
@@ -1595,6 +1741,9 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1595
1741
  *
1596
1742
  * ```ts
1597
1743
  * import {
1744
+ * assertEqual,
1745
+ * assertOk,
1746
+ * assertType,
1598
1747
  * Int64FromInt64String,
1599
1748
  * templateLiteralParser,
1600
1749
  * } from "@evolu/common";
@@ -1605,24 +1754,29 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1605
1754
  *
1606
1755
  * // Decode the string into structured data.
1607
1756
  * const result = ItemId.fromUnknown("item-42");
1608
- * expectOk(result, [42n]);
1757
+ * assertOk(result, [42n]);
1609
1758
  * const itemId = result.value;
1610
- * expectTypeOf(itemId).toEqualTypeOf<ItemId>();
1759
+ * assertType<ItemId, typeof itemId>();
1611
1760
  *
1612
1761
  * // Encode the structured data into its canonical string.
1613
1762
  * const itemIdLiteral = ItemId.to(itemId);
1614
- * expectTypeOf(itemIdLiteral).toEqualTypeOf<ItemIdLiteral>();
1615
- * expect(itemIdLiteral).toBe("item-42");
1763
+ * assertType<ItemIdLiteral, typeof itemIdLiteral>();
1764
+ * assertEqual(itemIdLiteral, "item-42");
1616
1765
  *
1617
1766
  * // TypeScript cannot prove from the literal alone that "42" is a valid Int64 encoding.
1618
1767
  * // @ts-expect-error Validate it with ItemId.parent or create it with ItemId.to.
1619
- * const invalidItemIdLiteral: ItemIdLiteral = "item-42";
1768
+ * const _invalidItemIdLiteral: ItemIdLiteral = "item-42";
1620
1769
  * ```
1621
1770
  *
1622
1771
  * Fixed-width captures can be adjacent:
1623
1772
  *
1624
1773
  * ```ts
1625
- * import { templateLiteralParser, union } from "@evolu/common";
1774
+ * import {
1775
+ * assertEqual,
1776
+ * assertOk,
1777
+ * templateLiteralParser,
1778
+ * union,
1779
+ * } from "@evolu/common";
1626
1780
  *
1627
1781
  * const Digit = union("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
1628
1782
  * const TwoDigits = templateLiteralParser(Digit, Digit);
@@ -1632,10 +1786,10 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1632
1786
  * const twoDigits: TwoDigits = ["4", "2"];
1633
1787
  * const twoDigitsLiteral: TwoDigitsLiteral = "42";
1634
1788
  * // @ts-expect-error TwoDigitsLiteral requires exactly two digits.
1635
- * const threeDigitsLiteral: TwoDigitsLiteral = "123";
1789
+ * const _threeDigitsLiteral: TwoDigitsLiteral = "123";
1636
1790
  *
1637
- * expectOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
1638
- * expect(TwoDigits.to(twoDigits)).toBe(twoDigitsLiteral);
1791
+ * assertOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
1792
+ * assertEqual(TwoDigits.to(twoDigits), twoDigitsLiteral);
1639
1793
  * ```
1640
1794
  *
1641
1795
  * TypeScript rejects multiple variable-width captures because their encoded
@@ -1691,17 +1845,24 @@ export interface TemplateLiteralType<Parts extends TemplateLiteralParts> extends
1691
1845
  * ### Example
1692
1846
  *
1693
1847
  * ```ts
1694
- * import { templateLiteral, union } from "@evolu/common";
1848
+ * import {
1849
+ * assertFalse,
1850
+ * assertOk,
1851
+ * assertType,
1852
+ * templateLiteral,
1853
+ * union,
1854
+ * } from "@evolu/common";
1695
1855
  *
1696
1856
  * const Language = union("en", "cs");
1697
1857
  * const Region = union("US", "CZ");
1698
1858
  * const Locale = templateLiteral(Language, "-", Region);
1699
1859
  *
1700
- * expectTypeOf<typeof Locale.Output>().toEqualTypeOf<
1701
- * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1860
+ * assertType<
1861
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ",
1862
+ * typeof Locale.Output
1702
1863
  * >();
1703
- * expectOk(Locale.fromUnknown("cs-CZ"), "cs-CZ");
1704
- * expect(Locale.is("fr-CZ")).toBe(false);
1864
+ * assertOk(Locale.fromUnknown("cs-CZ"), "cs-CZ");
1865
+ * assertFalse(Locale.is("fr-CZ"));
1705
1866
  * ```
1706
1867
  *
1707
1868
  * @group Template literals
@@ -1771,8 +1932,13 @@ type TemplateLiteralStringWidth<Value extends string, Width extends ReadonlyArra
1771
1932
  *
1772
1933
  * ```ts
1773
1934
  * import {
1935
+ * assertEqual,
1936
+ * assertErr,
1937
+ * assertOk,
1938
+ * assertType,
1774
1939
  * BigInt,
1775
1940
  * brand,
1941
+ * Data,
1776
1942
  * err,
1777
1943
  * ok,
1778
1944
  * type Brand,
@@ -1791,14 +1957,17 @@ type TemplateLiteralStringWidth<Value extends string, Width extends ReadonlyArra
1791
1957
  * type Int64 = typeof Int64.Output;
1792
1958
  *
1793
1959
  * // Note the Brand.
1794
- * expectTypeOf<Int64>().toEqualTypeOf<bigint & Brand<"Int64">>();
1960
+ * assertType<bigint & Brand<"Int64">, Int64>();
1795
1961
  *
1796
1962
  * interface Int64Error extends TypeError<"Int64"> {
1797
1963
  * readonly value: bigint;
1798
1964
  * }
1799
1965
  *
1800
- * expectOk(Int64.fromUnknown(42n), 42n);
1801
- * expectErr(Int64.fromUnknown(2n ** 63n), {
1966
+ * assertOk(Int64.fromUnknown(42n), 42n);
1967
+ * const invalid = Int64.fromUnknown(2n ** 63n);
1968
+ * assertErr(invalid);
1969
+ * assertType(Data, invalid.error);
1970
+ * assertEqual(invalid.error, {
1802
1971
  * type: "Int64",
1803
1972
  * value: 2n ** 63n,
1804
1973
  * });
@@ -1833,13 +2002,21 @@ export interface BrandType<ParentType extends TypeNode, Name extends TypeName, E
1833
2002
  * ### Example
1834
2003
  *
1835
2004
  * ```ts
1836
- * import { DateIso } from "@evolu/common";
2005
+ * import {
2006
+ * assertEqual,
2007
+ * assertErr,
2008
+ * assertOk,
2009
+ * assertType,
2010
+ * Data,
2011
+ * DateIso,
2012
+ * } from "@evolu/common";
1837
2013
  *
1838
- * expectOk(
1839
- * DateIso.fromUnknown("2023-01-01T12:00:00.000Z"),
1840
- * "2023-01-01T12:00:00.000Z",
1841
- * );
1842
- * expectErr(DateIso.fromUnknown("2023-01-01"), {
2014
+ * const value = "2023-01-01T12:00:00.000Z";
2015
+ * assertOk(DateIso.fromUnknown(value), value);
2016
+ * const invalid = DateIso.fromUnknown("2023-01-01");
2017
+ * assertErr(invalid);
2018
+ * assertType(Data, invalid.error);
2019
+ * assertEqual(invalid.error, {
1843
2020
  * type: "DateIso",
1844
2021
  * value: "2023-01-01",
1845
2022
  * });
@@ -1863,13 +2040,13 @@ export interface DateIsoError extends TypeError<"DateIso"> {
1863
2040
  * ### Example
1864
2041
  *
1865
2042
  * ```ts
1866
- * import { DateIsoFromDate } from "@evolu/common";
2043
+ * import { assertEqual, assertOk, DateIsoFromDate } from "@evolu/common";
1867
2044
  *
1868
- * const date = new globalThis.Date("2025-01-01T12:00:00.000Z");
2045
+ * const date = new Date("2025-01-01T12:00:00.000Z");
1869
2046
  * const result = DateIsoFromDate.fromUnknown(date);
1870
2047
  *
1871
- * expectOk(result, "2025-01-01T12:00:00.000Z");
1872
- * expect(DateIsoFromDate.to(result.value)).toEqual(date);
2048
+ * assertOk(result, "2025-01-01T12:00:00.000Z");
2049
+ * assertEqual(DateIsoFromDate.to(result.value), date);
1873
2050
  * ```
1874
2051
  *
1875
2052
  * @group String
@@ -1925,6 +2102,11 @@ export interface UInt64Error extends TypeError<"UInt64"> {
1925
2102
  *
1926
2103
  * ```ts
1927
2104
  * import {
2105
+ * assertEqual,
2106
+ * assertErr,
2107
+ * assertOk,
2108
+ * assertType,
2109
+ * Data,
1928
2110
  * String,
1929
2111
  * brand,
1930
2112
  * err,
@@ -1950,14 +2132,17 @@ export interface UInt64Error extends TypeError<"UInt64"> {
1950
2132
  * const TrimmedString = trimmed(String);
1951
2133
  * type TrimmedString = typeof TrimmedString.Output;
1952
2134
  *
1953
- * expectTypeOf<TrimmedString>().toEqualTypeOf<string & Brand<"Trimmed">>();
2135
+ * assertType<string & Brand<"Trimmed">, TrimmedString>();
1954
2136
  *
1955
2137
  * interface TrimmedError extends TypeError<"Trimmed"> {
1956
2138
  * readonly value: string;
1957
2139
  * }
1958
2140
  *
1959
- * expectOk(TrimmedString.fromUnknown("Evolu"), "Evolu");
1960
- * expectErr(TrimmedString.fromUnknown(" Evolu"), {
2141
+ * assertOk(TrimmedString.fromUnknown("Evolu"), "Evolu");
2142
+ * const invalid = TrimmedString.fromUnknown(" Evolu");
2143
+ * assertErr(invalid);
2144
+ * assertType(Data, invalid.error);
2145
+ * assertEqual(invalid.error, {
1961
2146
  * type: "Trimmed",
1962
2147
  * value: " Evolu",
1963
2148
  * });
@@ -1984,6 +2169,7 @@ export type BrandFactory<Name extends TypeName, Value, Error extends TypeError<N
1984
2169
  *
1985
2170
  * ```ts
1986
2171
  * import {
2172
+ * assertType,
1987
2173
  * Number,
1988
2174
  * brand,
1989
2175
  * err,
@@ -2015,10 +2201,8 @@ export type BrandFactory<Name extends TypeName, Value, Error extends TypeError<N
2015
2201
  * const LessThan100 = lessThan(100)(Number);
2016
2202
  * type LessThan100 = typeof LessThan100.Output;
2017
2203
  *
2018
- * expectTypeOf(LessThan100.name).toEqualTypeOf<"LessThan100">();
2019
- * expectTypeOf<LessThan100>().toEqualTypeOf<
2020
- * number & Brand<"LessThan100">
2021
- * >();
2204
+ * assertType<"LessThan100", typeof LessThan100.name>();
2205
+ * assertType<number & Brand<"LessThan100">, LessThan100>();
2022
2206
  *
2023
2207
  * interface LessThanError<
2024
2208
  * Max extends number,
@@ -2043,17 +2227,27 @@ type BrandFactoryNumberError = CompileTimeError<"Brand Factory", "Parameter must
2043
2227
  * ### Example
2044
2228
  *
2045
2229
  * ```ts
2046
- * import { String, capitalized, type Brand } from "@evolu/common";
2230
+ * import {
2231
+ * assertEqual,
2232
+ * assertErr,
2233
+ * assertOk,
2234
+ * assertType,
2235
+ * Data,
2236
+ * String,
2237
+ * capitalized,
2238
+ * type Brand,
2239
+ * } from "@evolu/common";
2047
2240
  *
2048
2241
  * const CapitalizedString = capitalized(String);
2049
2242
  * type CapitalizedString = typeof CapitalizedString.Output;
2050
2243
  *
2051
- * expectTypeOf<CapitalizedString>().toEqualTypeOf<
2052
- * string & Brand<"Capitalized">
2053
- * >();
2244
+ * assertType<string & Brand<"Capitalized">, CapitalizedString>();
2054
2245
  *
2055
- * expectOk(CapitalizedString.fromUnknown("Evolu"), "Evolu");
2056
- * expectErr(CapitalizedString.fromUnknown("evolu"), {
2246
+ * assertOk(CapitalizedString.fromUnknown("Evolu"), "Evolu");
2247
+ * const invalid = CapitalizedString.fromUnknown("evolu");
2248
+ * assertErr(invalid);
2249
+ * assertType(Data, invalid.error);
2250
+ * assertEqual(invalid.error, {
2057
2251
  * type: "Capitalized",
2058
2252
  * value: "evolu",
2059
2253
  * });
@@ -2083,11 +2277,11 @@ export type CapitalizedString = typeof CapitalizedString.Output;
2083
2277
  * ### Example
2084
2278
  *
2085
2279
  * ```ts
2086
- * import { String, trimmed } from "@evolu/common";
2280
+ * import { assertOk, String, trimmed } from "@evolu/common";
2087
2281
  *
2088
2282
  * const Trimmed = trimmed(String);
2089
2283
  *
2090
- * expectOk(Trimmed.fromUnknown("Evolu"), "Evolu");
2284
+ * assertOk(Trimmed.fromUnknown("Evolu"), "Evolu");
2091
2285
  * ```
2092
2286
  *
2093
2287
  * @group String
@@ -2120,9 +2314,9 @@ export type TrimmedString = typeof TrimmedString.Output;
2120
2314
  * ### Example
2121
2315
  *
2122
2316
  * ```ts
2123
- * import { trim } from "@evolu/common";
2317
+ * import { assertEqual, trim } from "@evolu/common";
2124
2318
  *
2125
- * expect(trim(" Evolu ")).toBe("Evolu");
2319
+ * assertEqual(trim(" Evolu "), "Evolu");
2126
2320
  * ```
2127
2321
  *
2128
2322
  * @group String
@@ -2134,13 +2328,13 @@ export declare const trim: (value: string) => TrimmedString;
2134
2328
  * ### Example
2135
2329
  *
2136
2330
  * ```ts
2137
- * import { String, array, minLength } from "@evolu/common";
2331
+ * import { assertOk, String, array, minLength } from "@evolu/common";
2138
2332
  *
2139
2333
  * const AtLeastThreeCharacters = minLength(3)(String);
2140
2334
  * const AtLeastTwoItems = minLength(2)(array(String));
2141
2335
  *
2142
- * expectOk(AtLeastThreeCharacters.fromUnknown("abc"), "abc");
2143
- * expectOk(AtLeastTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2336
+ * assertOk(AtLeastThreeCharacters.fromUnknown("abc"), "abc");
2337
+ * assertOk(AtLeastTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2144
2338
  * ```
2145
2339
  *
2146
2340
  * @group String
@@ -2178,13 +2372,13 @@ export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Output;
2178
2372
  * ### Example
2179
2373
  *
2180
2374
  * ```ts
2181
- * import { String, array, maxLength } from "@evolu/common";
2375
+ * import { assertOk, String, array, maxLength } from "@evolu/common";
2182
2376
  *
2183
2377
  * const AtMostThreeCharacters = maxLength(3)(String);
2184
2378
  * const AtMostTwoItems = maxLength(2)(array(String));
2185
2379
  *
2186
- * expectOk(AtMostThreeCharacters.fromUnknown("abc"), "abc");
2187
- * expectOk(AtMostTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2380
+ * assertOk(AtMostThreeCharacters.fromUnknown("abc"), "abc");
2381
+ * assertOk(AtMostTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2188
2382
  * ```
2189
2383
  *
2190
2384
  * @group String
@@ -2220,13 +2414,13 @@ export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Output;
2220
2414
  * ### Example
2221
2415
  *
2222
2416
  * ```ts
2223
- * import { String, array, length } from "@evolu/common";
2417
+ * import { assertOk, String, array, length } from "@evolu/common";
2224
2418
  *
2225
2419
  * const ThreeCharacters = length(3)(String);
2226
2420
  * const TwoItems = length(2)(array(String));
2227
2421
  *
2228
- * expectOk(ThreeCharacters.fromUnknown("abc"), "abc");
2229
- * expectOk(TwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2422
+ * assertOk(ThreeCharacters.fromUnknown("abc"), "abc");
2423
+ * assertOk(TwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2230
2424
  * ```
2231
2425
  *
2232
2426
  * @group String
@@ -2250,21 +2444,34 @@ export interface LengthError<Exact extends number = number> extends TypeError<`L
2250
2444
  * A non-empty string using the URL-safe alphabet:
2251
2445
  *
2252
2446
  * ```ts
2253
- * import { String, regex, type Brand } from "@evolu/common";
2447
+ * import {
2448
+ * assertEqual,
2449
+ * assertErr,
2450
+ * assertOk,
2451
+ * assertType,
2452
+ * Data,
2453
+ * String,
2454
+ * regex,
2455
+ * type Brand,
2456
+ * } from "@evolu/common";
2254
2457
  *
2255
- * const UrlSafeString = regex("UrlSafeString", /^[A-Za-z0-9_-]+$/)(String);
2458
+ * const UrlSafeString = regex(
2459
+ * "UrlSafeString",
2460
+ * /^[A-Za-z0-9_-]+$/u,
2461
+ * )(String);
2256
2462
  * type UrlSafeString = typeof UrlSafeString.Output;
2257
2463
  *
2258
- * expectTypeOf<UrlSafeString>().toEqualTypeOf<
2259
- * string & Brand<"UrlSafeString">
2260
- * >();
2464
+ * assertType<string & Brand<"UrlSafeString">, UrlSafeString>();
2261
2465
  *
2262
- * expectOk(UrlSafeString.fromUnknown("abc-123_DEF"), "abc-123_DEF");
2263
- * expectErr(UrlSafeString.fromUnknown("not safe"), {
2466
+ * assertOk(UrlSafeString.fromUnknown("abc-123_DEF"), "abc-123_DEF");
2467
+ * const invalid = UrlSafeString.fromUnknown("not safe");
2468
+ * assertErr(invalid);
2469
+ * assertType(Data, invalid.error);
2470
+ * assertEqual(invalid.error, {
2264
2471
  * type: "UrlSafeString",
2265
2472
  * value: "not safe",
2266
2473
  * source: "^[A-Za-z0-9_-]+$",
2267
- * flags: "",
2474
+ * flags: "u",
2268
2475
  * });
2269
2476
  * ```
2270
2477
  *
@@ -2317,9 +2524,10 @@ export interface Base64UrlError extends TypeError<"Base64Url"> {
2317
2524
  * ### Example
2318
2525
  *
2319
2526
  * ```ts
2320
- * import { uint8ArrayToBase64Url } from "@evolu/common";
2527
+ * import { assertEqual, uint8ArrayToBase64Url } from "@evolu/common";
2321
2528
  *
2322
- * expect(uint8ArrayToBase64Url(new Uint8Array([0, 1, 2, 255]))).toBe(
2529
+ * assertEqual(
2530
+ * uint8ArrayToBase64Url(new Uint8Array([0, 1, 2, 255])),
2323
2531
  * "AAEC_w",
2324
2532
  * );
2325
2533
  * ```
@@ -2333,11 +2541,16 @@ export declare const uint8ArrayToBase64Url: (bytes: Uint8Array) => Base64Url;
2333
2541
  * ### Example
2334
2542
  *
2335
2543
  * ```ts
2336
- * import { Base64Url, base64UrlToUint8Array } from "@evolu/common";
2544
+ * import {
2545
+ * assertEqual,
2546
+ * Base64Url,
2547
+ * base64UrlToUint8Array,
2548
+ * } from "@evolu/common";
2337
2549
  *
2338
2550
  * const value = Base64Url.orThrow("AAEC_w");
2339
2551
  *
2340
- * expect(base64UrlToUint8Array(value)).toEqual(
2552
+ * assertEqual(
2553
+ * base64UrlToUint8Array(value),
2341
2554
  * new Uint8Array([0, 1, 2, 255]),
2342
2555
  * );
2343
2556
  * ```
@@ -2418,6 +2631,8 @@ export interface IdError extends TypeError<"Id"> {
2418
2631
  *
2419
2632
  * ```ts
2420
2633
  * import {
2634
+ * assertTrue,
2635
+ * assertType,
2421
2636
  * Id,
2422
2637
  * createId,
2423
2638
  * createRandomBytes,
@@ -2426,8 +2641,8 @@ export interface IdError extends TypeError<"Id"> {
2426
2641
  *
2427
2642
  * const userId = createId<"User">({ randomBytes: createRandomBytes() });
2428
2643
  *
2429
- * expect(Id.is(userId)).toBe(true);
2430
- * expectTypeOf(userId).toEqualTypeOf<Id & Brand<"User">>();
2644
+ * assertTrue(Id.is(userId));
2645
+ * assertType<Id & Brand<"User">, typeof userId>();
2431
2646
  * ```
2432
2647
  *
2433
2648
  * @group String
@@ -2443,14 +2658,20 @@ export declare const createId: <B extends string = never>(deps: RandomBytesDep,
2443
2658
  * ### Example
2444
2659
  *
2445
2660
  * ```ts
2446
- * import { createIdFromString, type Brand, type Id } from "@evolu/common";
2661
+ * import {
2662
+ * assertEqual,
2663
+ * assertType,
2664
+ * createIdFromString,
2665
+ * type Brand,
2666
+ * type Id,
2667
+ * } from "@evolu/common";
2447
2668
  *
2448
2669
  * const first = createIdFromString("external-user-123");
2449
2670
  * const second = createIdFromString("external-user-123");
2450
2671
  * const todoId = createIdFromString<"Todo">("external-todo-456");
2451
2672
  *
2452
- * expect(first).toBe(second);
2453
- * expectTypeOf(todoId).toEqualTypeOf<Id & Brand<"Todo">>();
2673
+ * assertEqual(first, second);
2674
+ * assertType<Id & Brand<"Todo">, typeof todoId>();
2454
2675
  * ```
2455
2676
  *
2456
2677
  * @group String
@@ -2468,6 +2689,7 @@ export declare const createIdFromString: <B extends string = never>(value: strin
2468
2689
  *
2469
2690
  * ```ts
2470
2691
  * import {
2692
+ * assertEqual,
2471
2693
  * createIdAsUuidv7,
2472
2694
  * createRandomBytes,
2473
2695
  * createTime,
@@ -2480,8 +2702,8 @@ export declare const createIdFromString: <B extends string = never>(value: strin
2480
2702
  * });
2481
2703
  * const bytes = idToIdBytes(value);
2482
2704
  *
2483
- * expect(bytes[6] >> 4).toBe(0x7);
2484
- * expect(bytes[8] & 0xc0).toBe(0x80);
2705
+ * assertEqual(bytes[6] >> 4, 0x7);
2706
+ * assertEqual(bytes[8] & 0xc0, 0x80);
2485
2707
  * ```
2486
2708
  *
2487
2709
  * @group String
@@ -2494,6 +2716,7 @@ export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBy
2494
2716
  *
2495
2717
  * ```ts
2496
2718
  * import {
2719
+ * assertType,
2497
2720
  * createIdFromString,
2498
2721
  * id,
2499
2722
  * type Brand,
@@ -2503,7 +2726,7 @@ export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBy
2503
2726
  * const TodoId = id("Todo");
2504
2727
  * const todoId = TodoId.orThrow(createIdFromString("todo"));
2505
2728
  *
2506
- * expectTypeOf(todoId).toEqualTypeOf<Id & Brand<"Todo">>();
2729
+ * assertType<Id & Brand<"Todo">, typeof todoId>();
2507
2730
  * ```
2508
2731
  *
2509
2732
  * @group String
@@ -2553,11 +2776,15 @@ export declare const idBytesTypeValueLength: NonNegativeInt;
2553
2776
  * ### Example
2554
2777
  *
2555
2778
  * ```ts
2556
- * import { createIdFromString, idToIdBytes } from "@evolu/common";
2779
+ * import {
2780
+ * assertEqual,
2781
+ * createIdFromString,
2782
+ * idToIdBytes,
2783
+ * } from "@evolu/common";
2557
2784
  *
2558
2785
  * const bytes = idToIdBytes(createIdFromString("todo"));
2559
2786
  *
2560
- * expect(bytes).toHaveLength(16);
2787
+ * assertEqual(bytes.length, 16);
2561
2788
  * ```
2562
2789
  *
2563
2790
  * @group String
@@ -2570,6 +2797,7 @@ export declare const idToIdBytes: (value: Id) => IdBytes;
2570
2797
  *
2571
2798
  * ```ts
2572
2799
  * import {
2800
+ * assertEqual,
2573
2801
  * createIdFromString,
2574
2802
  * idBytesToId,
2575
2803
  * idToIdBytes,
@@ -2577,7 +2805,7 @@ export declare const idToIdBytes: (value: Id) => IdBytes;
2577
2805
  *
2578
2806
  * const value = createIdFromString("todo");
2579
2807
  *
2580
- * expect(idBytesToId(idToIdBytes(value))).toBe(value);
2808
+ * assertEqual(idBytesToId(idToIdBytes(value)), value);
2581
2809
  * ```
2582
2810
  *
2583
2811
  * @group String
@@ -2608,12 +2836,17 @@ export interface Int64StringError extends TypeError<"Int64String"> {
2608
2836
  * ### Example
2609
2837
  *
2610
2838
  * ```ts
2611
- * import { Int64FromInt64String } from "@evolu/common";
2839
+ * import {
2840
+ * assertEqual,
2841
+ * assertOk,
2842
+ * Int64FromInt64String,
2843
+ * } from "@evolu/common";
2612
2844
  *
2613
2845
  * const result = Int64FromInt64String.fromUnknown("9223372036854775807");
2614
2846
  *
2615
- * expectOk(result, 9223372036854775807n);
2616
- * expect(Int64FromInt64String.to(result.value)).toBe(
2847
+ * assertOk(result, 9223372036854775807n);
2848
+ * assertEqual(
2849
+ * Int64FromInt64String.to(result.value),
2617
2850
  * "9223372036854775807",
2618
2851
  * );
2619
2852
  * ```
@@ -2627,11 +2860,11 @@ export declare const Int64FromInt64String: TransformType<BrandType<BrandType<Bra
2627
2860
  * ### Example
2628
2861
  *
2629
2862
  * ```ts
2630
- * import { Number, nonNegative } from "@evolu/common";
2863
+ * import { assertOk, Number, nonNegative } from "@evolu/common";
2631
2864
  *
2632
2865
  * const NonNegative = nonNegative(Number);
2633
2866
  *
2634
- * expectOk(NonNegative.fromUnknown(0), 0);
2867
+ * assertOk(NonNegative.fromUnknown(0), 0);
2635
2868
  * ```
2636
2869
  *
2637
2870
  * @group Number
@@ -2658,11 +2891,11 @@ export type NonNegativeNumber = typeof NonNegativeNumber.Output;
2658
2891
  * ### Example
2659
2892
  *
2660
2893
  * ```ts
2661
- * import { Number, positive } from "@evolu/common";
2894
+ * import { assertOk, Number, positive } from "@evolu/common";
2662
2895
  *
2663
2896
  * const Positive = positive(Number);
2664
2897
  *
2665
- * expectOk(Positive.fromUnknown(1), 1);
2898
+ * assertOk(Positive.fromUnknown(1), 1);
2666
2899
  * ```
2667
2900
  *
2668
2901
  * @group Number
@@ -2692,11 +2925,11 @@ export type PositiveNumber = typeof PositiveNumber.Output;
2692
2925
  * ### Example
2693
2926
  *
2694
2927
  * ```ts
2695
- * import { Number, nonPositive } from "@evolu/common";
2928
+ * import { assertOk, Number, nonPositive } from "@evolu/common";
2696
2929
  *
2697
2930
  * const NonPositive = nonPositive(Number);
2698
2931
  *
2699
- * expectOk(NonPositive.fromUnknown(0), 0);
2932
+ * assertOk(NonPositive.fromUnknown(0), 0);
2700
2933
  * ```
2701
2934
  *
2702
2935
  * @group Number
@@ -2723,11 +2956,11 @@ export type NonPositiveNumber = typeof NonPositiveNumber.Output;
2723
2956
  * ### Example
2724
2957
  *
2725
2958
  * ```ts
2726
- * import { Number, negative } from "@evolu/common";
2959
+ * import { assertOk, Number, negative } from "@evolu/common";
2727
2960
  *
2728
2961
  * const Negative = negative(Number);
2729
2962
  *
2730
- * expectOk(Negative.fromUnknown(-1), -1);
2963
+ * assertOk(Negative.fromUnknown(-1), -1);
2731
2964
  * ```
2732
2965
  *
2733
2966
  * @group Number
@@ -2757,11 +2990,11 @@ export type NegativeNumber = typeof NegativeNumber.Output;
2757
2990
  * ### Example
2758
2991
  *
2759
2992
  * ```ts
2760
- * import { Number, nonNaN } from "@evolu/common";
2993
+ * import { assertOk, Number, nonNaN } from "@evolu/common";
2761
2994
  *
2762
2995
  * const NonNaN = nonNaN(Number);
2763
2996
  *
2764
- * expectOk(NonNaN.fromUnknown(Infinity), Infinity);
2997
+ * assertOk(NonNaN.fromUnknown(Infinity), Infinity);
2765
2998
  * ```
2766
2999
  *
2767
3000
  * @group Number
@@ -2793,11 +3026,11 @@ export type NonNaNNumber = typeof NonNaNNumber.Output;
2793
3026
  * ### Example
2794
3027
  *
2795
3028
  * ```ts
2796
- * import { Number, finite } from "@evolu/common";
3029
+ * import { assertOk, Number, finite } from "@evolu/common";
2797
3030
  *
2798
3031
  * const Finite = finite(Number);
2799
3032
  *
2800
- * expectOk(Finite.fromUnknown(42), 42);
3033
+ * assertOk(Finite.fromUnknown(42), 42);
2801
3034
  * ```
2802
3035
  *
2803
3036
  * @group Number
@@ -2841,15 +3074,28 @@ export type PositiveFiniteNumber = typeof PositiveFiniteNumber.Output;
2841
3074
  * ### Example
2842
3075
  *
2843
3076
  * ```ts
2844
- * import { Number, int, type Brand } from "@evolu/common";
3077
+ * import {
3078
+ * assertEqual,
3079
+ * assertErr,
3080
+ * assertOk,
3081
+ * assertType,
3082
+ * Data,
3083
+ * Number,
3084
+ * int,
3085
+ * type Brand,
3086
+ * } from "@evolu/common";
2845
3087
  *
2846
3088
  * const Int = int(Number);
2847
3089
  * type Int = typeof Int.Output;
2848
3090
  *
2849
- * expectTypeOf<Int>().toEqualTypeOf<number & Brand<"Int">>();
3091
+ * assertType<number & Brand<"Int">, Int>();
2850
3092
  *
2851
- * expectOk(Int.fromUnknown(42), 42);
2852
- * expectErr(Int.fromUnknown(1.5), { type: "Int", value: 1.5 });
3093
+ * assertOk(Int.fromUnknown(42), 42);
3094
+ * const invalid = Int.fromUnknown(1.5);
3095
+ * assertErr(invalid);
3096
+ * assertType(Data, invalid.error);
3097
+ * const error: Data = invalid.error;
3098
+ * assertEqual(error, { type: "Int", value: 1.5 });
2853
3099
  * ```
2854
3100
  *
2855
3101
  * @group Number
@@ -2928,11 +3174,11 @@ export type NegativeInt = typeof NegativeInt.Output;
2928
3174
  * ### Example
2929
3175
  *
2930
3176
  * ```ts
2931
- * import { Number, greaterThan } from "@evolu/common";
3177
+ * import { assertOk, Number, greaterThan } from "@evolu/common";
2932
3178
  *
2933
3179
  * const GreaterThanTen = greaterThan(10)(Number);
2934
3180
  *
2935
- * expectOk(GreaterThanTen.fromUnknown(11), 11);
3181
+ * assertOk(GreaterThanTen.fromUnknown(11), 11);
2936
3182
  * ```
2937
3183
  *
2938
3184
  * @group Number
@@ -2953,11 +3199,11 @@ export interface GreaterThanError<Min extends number = number> extends TypeError
2953
3199
  * ### Example
2954
3200
  *
2955
3201
  * ```ts
2956
- * import { Number, greaterThanOrEqualTo } from "@evolu/common";
3202
+ * import { assertOk, Number, greaterThanOrEqualTo } from "@evolu/common";
2957
3203
  *
2958
3204
  * const AtLeastTen = greaterThanOrEqualTo(10)(Number);
2959
3205
  *
2960
- * expectOk(AtLeastTen.fromUnknown(10), 10);
3206
+ * assertOk(AtLeastTen.fromUnknown(10), 10);
2961
3207
  * ```
2962
3208
  *
2963
3209
  * @group Number
@@ -2978,11 +3224,11 @@ export interface GreaterThanOrEqualToError<Min extends number = number> extends
2978
3224
  * ### Example
2979
3225
  *
2980
3226
  * ```ts
2981
- * import { Number, lessThan } from "@evolu/common";
3227
+ * import { assertOk, Number, lessThan } from "@evolu/common";
2982
3228
  *
2983
3229
  * const LessThanTen = lessThan(10)(Number);
2984
3230
  *
2985
- * expectOk(LessThanTen.fromUnknown(9), 9);
3231
+ * assertOk(LessThanTen.fromUnknown(9), 9);
2986
3232
  * ```
2987
3233
  *
2988
3234
  * @group Number
@@ -3010,11 +3256,11 @@ export type Age = typeof Age.Output;
3010
3256
  * ### Example
3011
3257
  *
3012
3258
  * ```ts
3013
- * import { Number, lessThanOrEqualTo } from "@evolu/common";
3259
+ * import { assertOk, Number, lessThanOrEqualTo } from "@evolu/common";
3014
3260
  *
3015
3261
  * const AtMostTen = lessThanOrEqualTo(10)(Number);
3016
3262
  *
3017
- * expectOk(AtMostTen.fromUnknown(10), 10);
3263
+ * assertOk(AtMostTen.fromUnknown(10), 10);
3018
3264
  * ```
3019
3265
  *
3020
3266
  * @group Number
@@ -3065,13 +3311,23 @@ export type Ratio = typeof Ratio.Output;
3065
3311
  * ### Example
3066
3312
  *
3067
3313
  * ```ts
3068
- * import { DecimalString } from "@evolu/common";
3314
+ * import {
3315
+ * assertEqual,
3316
+ * assertErr,
3317
+ * assertOk,
3318
+ * assertType,
3319
+ * Data,
3320
+ * DecimalString,
3321
+ * } from "@evolu/common";
3069
3322
  *
3070
- * expectOk(DecimalString.fromUnknown("-10.25"), "-10.25");
3071
- * expectOk(DecimalString.fromUnknown("0"), "0");
3072
- * expectOk(DecimalString.fromUnknown("10.25"), "10.25");
3323
+ * assertOk(DecimalString.fromUnknown("-10.25"), "-10.25");
3324
+ * assertOk(DecimalString.fromUnknown("0"), "0");
3325
+ * assertOk(DecimalString.fromUnknown("10.25"), "10.25");
3073
3326
  *
3074
- * expectErr(DecimalString.fromUnknown("10.250"), {
3327
+ * const invalid = DecimalString.fromUnknown("10.250");
3328
+ * assertErr(invalid);
3329
+ * assertType(Data, invalid.error);
3330
+ * assertEqual(invalid.error, {
3075
3331
  * type: "DecimalString",
3076
3332
  * value: "10.250",
3077
3333
  * });
@@ -3095,11 +3351,15 @@ export interface DecimalStringError extends TypeError<"DecimalString"> {
3095
3351
  * ### Example
3096
3352
  *
3097
3353
  * ```ts
3098
- * import { DecimalString, nonNegativeDecimalString } from "@evolu/common";
3354
+ * import {
3355
+ * assertOk,
3356
+ * DecimalString,
3357
+ * nonNegativeDecimalString,
3358
+ * } from "@evolu/common";
3099
3359
  *
3100
3360
  * const NonNegative = nonNegativeDecimalString(DecimalString);
3101
3361
  *
3102
- * expectOk(NonNegative.fromUnknown("0.5"), "0.5");
3362
+ * assertOk(NonNegative.fromUnknown("0.5"), "0.5");
3103
3363
  * ```
3104
3364
  *
3105
3365
  * @group Number
@@ -3126,11 +3386,15 @@ export type NonNegativeDecimalString = typeof NonNegativeDecimalString.Output;
3126
3386
  * ### Example
3127
3387
  *
3128
3388
  * ```ts
3129
- * import { DecimalString, positiveDecimalString } from "@evolu/common";
3389
+ * import {
3390
+ * assertOk,
3391
+ * DecimalString,
3392
+ * positiveDecimalString,
3393
+ * } from "@evolu/common";
3130
3394
  *
3131
3395
  * const Positive = positiveDecimalString(DecimalString);
3132
3396
  *
3133
- * expectOk(Positive.fromUnknown("0.5"), "0.5");
3397
+ * assertOk(Positive.fromUnknown("0.5"), "0.5");
3134
3398
  * ```
3135
3399
  *
3136
3400
  * @group Number
@@ -3160,11 +3424,15 @@ export type PositiveDecimalString = typeof PositiveDecimalString.Output;
3160
3424
  * ### Example
3161
3425
  *
3162
3426
  * ```ts
3163
- * import { DecimalString, nonPositiveDecimalString } from "@evolu/common";
3427
+ * import {
3428
+ * assertOk,
3429
+ * DecimalString,
3430
+ * nonPositiveDecimalString,
3431
+ * } from "@evolu/common";
3164
3432
  *
3165
3433
  * const NonPositive = nonPositiveDecimalString(DecimalString);
3166
3434
  *
3167
- * expectOk(NonPositive.fromUnknown("-0.5"), "-0.5");
3435
+ * assertOk(NonPositive.fromUnknown("-0.5"), "-0.5");
3168
3436
  * ```
3169
3437
  *
3170
3438
  * @group Number
@@ -3191,11 +3459,15 @@ export type NonPositiveDecimalString = typeof NonPositiveDecimalString.Output;
3191
3459
  * ### Example
3192
3460
  *
3193
3461
  * ```ts
3194
- * import { DecimalString, negativeDecimalString } from "@evolu/common";
3462
+ * import {
3463
+ * assertOk,
3464
+ * DecimalString,
3465
+ * negativeDecimalString,
3466
+ * } from "@evolu/common";
3195
3467
  *
3196
3468
  * const Negative = negativeDecimalString(DecimalString);
3197
3469
  *
3198
- * expectOk(Negative.fromUnknown("-0.5"), "-0.5");
3470
+ * assertOk(Negative.fromUnknown("-0.5"), "-0.5");
3199
3471
  * ```
3200
3472
  *
3201
3473
  * @group Number
@@ -3233,16 +3505,29 @@ export type NegativeDecimalString = typeof NegativeDecimalString.Output;
3233
3505
  * ### Example
3234
3506
  *
3235
3507
  * ```ts
3236
- * import { FiniteNumber, multipleOf, type Brand } from "@evolu/common";
3508
+ * import {
3509
+ * assertEqual,
3510
+ * assertErr,
3511
+ * assertOk,
3512
+ * assertType,
3513
+ * Data,
3514
+ * FiniteNumber,
3515
+ * multipleOf,
3516
+ * type Brand,
3517
+ * } from "@evolu/common";
3237
3518
  *
3238
3519
  * const Tenths = multipleOf("0.1")(FiniteNumber);
3239
3520
  *
3240
- * expectTypeOf<typeof Tenths.Output>().toEqualTypeOf<
3241
- * FiniteNumber & Brand<"MultipleOf0.1">
3521
+ * assertType<
3522
+ * FiniteNumber & Brand<"MultipleOf0.1">,
3523
+ * typeof Tenths.Output
3242
3524
  * >();
3243
3525
  *
3244
- * expectOk(Tenths.fromUnknown(0.3), 0.3);
3245
- * expectErr(Tenths.fromUnknown(0.31), {
3526
+ * assertOk(Tenths.fromUnknown(0.3), 0.3);
3527
+ * const invalid = Tenths.fromUnknown(0.31);
3528
+ * assertErr(invalid);
3529
+ * assertType(Data, invalid.error);
3530
+ * assertEqual(invalid.error, {
3246
3531
  * type: "MultipleOf0.1",
3247
3532
  * value: 0.31,
3248
3533
  * divisor: "0.1",
@@ -3275,11 +3560,11 @@ type MultipleOfDivisorError = CompileTimeError<"MultipleOf", 'Divisor must be on
3275
3560
  * ### Example
3276
3561
  *
3277
3562
  * ```ts
3278
- * import { Number, between } from "@evolu/common";
3563
+ * import { assertOk, Number, between } from "@evolu/common";
3279
3564
  *
3280
3565
  * const Percentage = between(0, 100)(Number);
3281
3566
  *
3282
- * expectOk(Percentage.fromUnknown(75), 75);
3567
+ * assertOk(Percentage.fromUnknown(75), 75);
3283
3568
  * ```
3284
3569
  *
3285
3570
  * @group Number
@@ -3321,6 +3606,11 @@ export interface BetweenError<Min extends number = number, Max extends number =
3321
3606
  *
3322
3607
  * ```ts
3323
3608
  * import {
3609
+ * assertEqual,
3610
+ * assertErr,
3611
+ * assertOk,
3612
+ * assertType,
3613
+ * Data,
3324
3614
  * String,
3325
3615
  * array,
3326
3616
  * brand,
@@ -3332,12 +3622,16 @@ export interface BetweenError<Min extends number = number, Max extends number =
3332
3622
  * const UserIds = array(UserId);
3333
3623
  * const result = UserIds.from.parent(["ada", "grace"]);
3334
3624
  *
3335
- * expectTypeOf(result).toEqualTypeOf<
3336
- * Result<ReadonlyArray<string & Brand<"UserId">>, never>
3625
+ * assertType<
3626
+ * Result<ReadonlyArray<string & Brand<"UserId">>>,
3627
+ * typeof result
3337
3628
  * >();
3338
- * expectOk(result, ["ada", "grace"]);
3339
- * expectOk(UserIds.fromUnknown(["ada", "grace"]), ["ada", "grace"]);
3340
- * expectErr(UserIds.fromUnknown("ada"), {
3629
+ * assertOk(result, ["ada", "grace"]);
3630
+ * assertOk(UserIds.fromUnknown(["ada", "grace"]), ["ada", "grace"]);
3631
+ * const invalid = UserIds.fromUnknown("ada");
3632
+ * assertErr(invalid);
3633
+ * assertType(Data, invalid.error);
3634
+ * assertEqual(invalid.error, {
3341
3635
  * type: "Array",
3342
3636
  * reason: { kind: "NotArray", value: "ada" },
3343
3637
  * });
@@ -3455,20 +3749,20 @@ interface ArrayItemsErrorValue<Error extends TypeError, IncludeStructuralIssues
3455
3749
  /**
3456
3750
  * Set {@link Type} whose every element must match one Type.
3457
3751
  *
3458
- * Direct Sets from this or another realm are accepted. Set subclasses are
3459
- * rejected. A Set must have no own properties; its elements are validated in
3460
- * iteration order. Classification uses the realm-neutral object tag and
3461
- * prototype structure under Evolu Type's trusted JavaScript policy.
3752
+ * Sets from this or another realm are accepted. A Set must have no own
3753
+ * properties; its elements are validated in iteration order. Classification
3754
+ * uses the realm-neutral object tag under Evolu Type's trusted JavaScript
3755
+ * policy.
3462
3756
  *
3463
3757
  * ### Example
3464
3758
  *
3465
3759
  * ```ts
3466
- * import { String, set } from "@evolu/common";
3760
+ * import { assertOk, String, set } from "@evolu/common";
3467
3761
  *
3468
3762
  * const Tags = set(String);
3469
3763
  * const tags = new Set(["local-first", "offline"]);
3470
3764
  *
3471
- * expectOk(Tags.fromUnknown(tags), tags);
3765
+ * assertOk(Tags.fromUnknown(tags), tags);
3472
3766
  * ```
3473
3767
  *
3474
3768
  * @group Collection
@@ -3499,12 +3793,11 @@ type SetNodeError<ElementType extends TypeNode> = [
3499
3793
  ElementType["parent"]
3500
3794
  ] extends [TypeNode] ? SetElementsError<ElementType["Error"]> : SetError<ElementType["Error"]>;
3501
3795
  /**
3502
- * Error returned by {@link set} for a non-Set value, an invalid Set prototype,
3503
- * or invalid Set items.
3796
+ * Error returned by {@link set} for a non-Set value or invalid Set items.
3504
3797
  *
3505
3798
  * @group Collection
3506
3799
  */
3507
- export type SetError<Error extends TypeError = TypeError> = SetNotSetError | SetUnexpectedPrototypeError | SetItemsErrorValue<Error, true>;
3800
+ export type SetError<Error extends TypeError = TypeError> = SetNotSetError | SetItemsErrorValue<Error, true>;
3508
3801
  /**
3509
3802
  * Error returned when a {@link set} input is not a Set.
3510
3803
  *
@@ -3516,17 +3809,6 @@ export interface SetNotSetError extends TypeError<"Set"> {
3516
3809
  readonly value: unknown;
3517
3810
  };
3518
3811
  }
3519
- /**
3520
- * An error returned when a {@link set} input is a Set subclass.
3521
- *
3522
- * @group Collection
3523
- */
3524
- export interface SetUnexpectedPrototypeError extends TypeError<"Set"> {
3525
- readonly reason: {
3526
- readonly kind: "UnexpectedPrototype";
3527
- readonly value: ReadonlySet<unknown>;
3528
- };
3529
- }
3530
3812
  /**
3531
3813
  * An own property found on a Set value.
3532
3814
  *
@@ -3565,6 +3847,133 @@ interface SetItemsErrorValue<Error extends TypeError, IncludeStructuralIssues ex
3565
3847
  readonly issues: NonEmptyReadonlyArray<(true extends IncludeStructuralIssues ? SetStructuralIssue : never) | SetElementIssue<Error>>;
3566
3848
  };
3567
3849
  }
3850
+ /**
3851
+ * Map {@link Type} whose keys and values must match their respective Types.
3852
+ *
3853
+ * Maps from this or another realm are accepted. A Map must have no own
3854
+ * properties; its entries are validated in iteration order. When distinct input
3855
+ * keys decode to the same output key, validation fails instead of discarding
3856
+ * one associated value. Classification uses the realm-neutral object tag under
3857
+ * Evolu Type's trusted JavaScript policy.
3858
+ *
3859
+ * ### Example
3860
+ *
3861
+ * ```ts
3862
+ * import { assertOk, PositiveInt, String, map } from "@evolu/common";
3863
+ *
3864
+ * const Scores = map(String, PositiveInt);
3865
+ * const scores = new Map([
3866
+ * ["Ada", 10],
3867
+ * ["Grace", 20],
3868
+ * ]);
3869
+ *
3870
+ * assertOk(Scores.fromUnknown(scores), scores);
3871
+ * ```
3872
+ *
3873
+ * @group Collection
3874
+ */
3875
+ export declare const map: <KeyType extends ConcreteTypeNode, ValueType extends ConcreteTypeNode>(key: ValidateMapKeyType<KeyType>, value: ValidateMapValueType<ValueType>) => MapType<KeyType, ValueType>;
3876
+ /**
3877
+ * The readonly-map {@link Type} returned by {@link map}.
3878
+ *
3879
+ * @group Collection
3880
+ */
3881
+ export interface MapType<KeyType extends TypeNode, ValueType extends TypeNode> extends Type<"Map", ReadonlyMap<KeyType["Input"], ValueType["Input"]>, ReadonlyMap<KeyType["Output"], ValueType["Output"]>, MapNodeError<KeyType, ValueType>, MapParent<KeyType, ValueType>, MapError<InferErrors<KeyType>, InferErrors<ValueType>, MapCollisionFor<KeyType>>, never, CanonicalInputSubset<ReadonlyMap<KeyType["Input"], ValueType["Input"]>, ReadonlyMap<CanonicalInputOf<KeyType>, CanonicalInputOf<ValueType>>>, AllTypesUseIdentityEncoding<KeyType | ValueType>> {
3882
+ readonly [reflectedTypesSymbol]?: KeyType | ValueType;
3883
+ readonly key: KeyType;
3884
+ readonly value: ValueType;
3885
+ }
3886
+ type MapParent<KeyType extends TypeNode, ValueType extends TypeNode> = [
3887
+ KeyType["parent"] | ValueType["parent"]
3888
+ ] extends [null] ? null : MapType<RootType<KeyType>, RootType<ValueType>>;
3889
+ type MapNodeError<KeyType extends TypeNode, ValueType extends TypeNode> = [
3890
+ KeyType["parent"] | ValueType["parent"]
3891
+ ] extends [null] ? MapError<InferErrors<KeyType>, InferErrors<ValueType>, never> : MapEntriesError<TypeFromError<KeyType>, TypeFromError<ValueType>, MapCollisionFor<KeyType>>;
3892
+ type MapCollisionFor<KeyType extends TypeNode> = [KeyType["parent"]] extends [
3893
+ TypeNode
3894
+ ] ? MapKeyCollisionIssue : never;
3895
+ type ValidateMapKeyType<T extends ConcreteTypeNode> = IsUnion<T> extends false ? T : CompileTimeError<"Type", "Map key must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.">;
3896
+ type ValidateMapValueType<T extends ConcreteTypeNode> = IsUnion<T> extends false ? T : CompileTimeError<"Type", "Map value must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.">;
3897
+ /**
3898
+ * Error returned while validating a {@link map} and its entries.
3899
+ *
3900
+ * @group Collection
3901
+ */
3902
+ export type MapError<KeyError extends TypeError = TypeError, ValueError extends TypeError = TypeError, Collision extends MapKeyCollisionIssue = MapKeyCollisionIssue> = MapNotMapError | MapEntriesErrorValue<KeyError, ValueError, Collision | MapExcessPropertyIssue>;
3903
+ /**
3904
+ * Error returned when a {@link map} input is not a Map.
3905
+ *
3906
+ * @group Collection
3907
+ */
3908
+ export interface MapNotMapError extends TypeError<"Map"> {
3909
+ readonly reason: {
3910
+ readonly kind: "NotMap";
3911
+ readonly value: unknown;
3912
+ };
3913
+ }
3914
+ /**
3915
+ * An own property found on a Map value.
3916
+ *
3917
+ * @group Collection
3918
+ */
3919
+ export interface MapExcessPropertyIssue {
3920
+ readonly kind: "ExcessProperty";
3921
+ readonly key: string | symbol;
3922
+ }
3923
+ /**
3924
+ * An invalid key and its entry index in a {@link map}.
3925
+ *
3926
+ * @group Collection
3927
+ */
3928
+ export type MapKeyIssue<Error extends TypeError> = Error extends TypeError ? {
3929
+ readonly kind: "Key";
3930
+ readonly index: number;
3931
+ readonly key: unknown;
3932
+ readonly error: Error;
3933
+ } : never;
3934
+ /**
3935
+ * An invalid value and its entry index in a {@link map}.
3936
+ *
3937
+ * @group Collection
3938
+ */
3939
+ export type MapValueIssue<Error extends TypeError> = Error extends TypeError ? {
3940
+ readonly kind: "Value";
3941
+ readonly index: number;
3942
+ readonly key: unknown;
3943
+ readonly error: Error;
3944
+ } : never;
3945
+ /**
3946
+ * Two {@link map} keys that decode to the same output key.
3947
+ *
3948
+ * @group Collection
3949
+ */
3950
+ export interface MapKeyCollisionIssue {
3951
+ readonly kind: "Collision";
3952
+ readonly index: number;
3953
+ readonly key: unknown;
3954
+ readonly previousIndex: number;
3955
+ readonly previousKey: unknown;
3956
+ readonly outputKey: unknown;
3957
+ }
3958
+ type MapStructuralIssue = MapExcessPropertyIssue | MapKeyCollisionIssue;
3959
+ /**
3960
+ * An invalid key, value, or structure in a {@link map}.
3961
+ *
3962
+ * @group Collection
3963
+ */
3964
+ export type MapIssue<KeyError extends TypeError, ValueError extends TypeError, StructuralIssue extends MapStructuralIssue = MapKeyCollisionIssue> = MapKeyIssue<KeyError> | MapValueIssue<ValueError> | StructuralIssue;
3965
+ /**
3966
+ * Entry errors returned by a {@link map} operation.
3967
+ *
3968
+ * @group Collection
3969
+ */
3970
+ export type MapEntriesError<KeyError extends TypeError, ValueError extends TypeError, StructuralIssue extends MapStructuralIssue = MapKeyCollisionIssue> = [KeyError | ValueError | StructuralIssue] extends [never] ? never : MapEntriesErrorValue<KeyError, ValueError, StructuralIssue>;
3971
+ interface MapEntriesErrorValue<KeyError extends TypeError, ValueError extends TypeError, StructuralIssue extends MapStructuralIssue> extends TypeError<"Map"> {
3972
+ readonly reason: {
3973
+ readonly kind: "Entries";
3974
+ readonly issues: NonEmptyReadonlyArray<MapKeyIssue<KeyError> | MapValueIssue<ValueError> | StructuralIssue>;
3975
+ };
3976
+ }
3568
3977
  /**
3569
3978
  * Tuple {@link Type}.
3570
3979
  *
@@ -3582,20 +3991,24 @@ interface SetItemsErrorValue<Error extends TypeError, IncludeStructuralIssues ex
3582
3991
  *
3583
3992
  * A Tuple must be recognized by `Array.isArray`, have exactly the declared
3584
3993
  * length, be dense, and have no own properties other than `length` and the
3585
- * indexed data properties for its elements. Array subclasses, custom-prototype
3586
- * arrays, and foreign-realm arrays are accepted when their own data
3587
- * representation is valid. Sparse arrays, accessor elements, and excess
3588
- * properties are rejected.
3994
+ * indexed data properties for its elements. Foreign-realm arrays are accepted
3995
+ * when their own data representation is valid. Sparse arrays, accessor
3996
+ * elements, and excess properties are rejected.
3589
3997
  *
3590
3998
  * ### Example
3591
3999
  *
3592
4000
  * ```ts
3593
- * import { Int64FromInt64String, String, tuple } from "@evolu/common";
4001
+ * import {
4002
+ * assertOk,
4003
+ * Int64FromInt64String,
4004
+ * String,
4005
+ * tuple,
4006
+ * } from "@evolu/common";
3594
4007
  *
3595
4008
  * const Entry = tuple(String, Int64FromInt64String);
3596
4009
  *
3597
- * expectOk(Entry.fromUnknown(["count", "1"]), ["count", 1n]);
3598
- * expectOk(Entry.from.parent(["count", "1"]), ["count", 1n]);
4010
+ * assertOk(Entry.fromUnknown(["count", "1"]), ["count", 1n]);
4011
+ * assertOk(Entry.from.parent(["count", "1"]), ["count", 1n]);
3599
4012
  * ```
3600
4013
  *
3601
4014
  * @group Collection
@@ -3784,11 +4197,12 @@ type PlainObjectError = ObjectError<Readonly<Record<never, never>>, ObjectProper
3784
4197
  * A {@link Type} for readonly plain objects with unknown property values.
3785
4198
  *
3786
4199
  * `Object` is the runtime counterpart of a `Readonly<Record<string, unknown>>`
3787
- * data boundary. Its realm-neutral prototype rule accepts a `null` prototype or
3788
- * an immediate root prototype whose own prototype is `null`. Every own property
3789
- * must have a string key and be an enumerable data property. It rejects
3790
- * ordinary class instances, deeper prototype chains, accessors, non-enumerable
3791
- * properties, and symbol properties without reading their values.
4200
+ * data boundary. Its prototype rule uses the realm-neutral structural heuristic
4201
+ * described by {@link isPlainObject}. A matching custom root prototype can be
4202
+ * classified as plain; other custom prototypes and class instances are
4203
+ * rejected. Every own property must have a string key and be an enumerable data
4204
+ * property. Accessors, non-enumerable properties, and symbol properties are
4205
+ * rejected without reading their values.
3792
4206
  *
3793
4207
  * Use {@link object} when property names are fixed, {@link record} when keys and
3794
4208
  * values have their own Types, and {@link instanceOf} when an instance belongs
@@ -3814,12 +4228,13 @@ export { _Object as Object };
3814
4228
  * By default, validation returns the first issue. Pass `{ errors: "all" }` to
3815
4229
  * collect issues across the whole Record.
3816
4230
  *
3817
- * A Record must have a `null` prototype or an immediate root prototype whose
3818
- * own prototype is `null`. Ordinary class instances and deeper prototype chains
3819
- * are rejected. Every own property must be an enumerable data property whose
3820
- * key and value satisfy their Types. When decoding or encoding changes an
3821
- * entry, the constructed Record uses a `null` prototype so keys such as
3822
- * `__proto__` remain ordinary data.
4231
+ * A Record must satisfy the realm-neutral structural heuristic described by
4232
+ * {@link isPlainObject}. A matching custom root prototype can be classified as
4233
+ * plain; other custom prototypes and class instances are rejected. Every own
4234
+ * property must be an enumerable data property whose key and value satisfy
4235
+ * their Types. When decoding or encoding changes an entry, the constructed
4236
+ * Record uses a `null` prototype so keys such as `__proto__` remain ordinary
4237
+ * data.
3823
4238
  *
3824
4239
  * If transformed keys collide, validation fails instead of overwriting an
3825
4240
  * entry.
@@ -3828,6 +4243,8 @@ export { _Object as Object };
3828
4243
  *
3829
4244
  * ```ts
3830
4245
  * import {
4246
+ * assertOk,
4247
+ * assertType,
3831
4248
  * Int64FromInt64String,
3832
4249
  * String,
3833
4250
  * record,
@@ -3843,21 +4260,22 @@ export { _Object as Object };
3843
4260
  * grace: "20",
3844
4261
  * });
3845
4262
  *
3846
- * expectOk(scoresFromUnknown, { ada: 10n, grace: 20n });
4263
+ * assertOk(scoresFromUnknown, { ada: 10n, grace: 20n });
3847
4264
  *
3848
4265
  * // Validate keys and values with their root Types.
3849
4266
  * const scoresInput = ScoresByUser.parent.fromUnknown({
3850
4267
  * ada: "10",
3851
4268
  * grace: "20",
3852
4269
  * });
3853
- * expectOk(scoresInput, { ada: "10", grace: "20" });
4270
+ * assertOk(scoresInput, { ada: "10", grace: "20" });
3854
4271
  *
3855
4272
  * // Run the remaining key and value stages.
3856
4273
  * const scoresFromInput = ScoresByUser.from.parent(scoresInput.value);
3857
4274
  *
3858
- * expectOk(scoresFromInput, { ada: 10n, grace: 20n });
3859
- * expectTypeOf(scoresFromInput.value).toEqualTypeOf<
3860
- * Readonly<Partial<Record<string, Int64>>>
4275
+ * assertOk(scoresFromInput, { ada: 10n, grace: 20n });
4276
+ * assertType<
4277
+ * Readonly<Partial<Record<string, Int64>>>,
4278
+ * typeof scoresFromInput.value
3861
4279
  * >();
3862
4280
  * ```
3863
4281
  *
@@ -3866,34 +4284,64 @@ export { _Object as Object };
3866
4284
  * as a Record entry:
3867
4285
  *
3868
4286
  * ```ts
4287
+ * import {
4288
+ * assertEqual,
4289
+ * assertErr,
4290
+ * assertTrue,
4291
+ * trySync,
4292
+ * } from "@evolu/common";
4293
+ *
3869
4294
  * type Values = Partial<Record<"toString", number>>;
3870
4295
  * const values: Values = {};
3871
4296
  *
3872
4297
  * // TypeScript treats the inherited function as `number | undefined`.
3873
4298
  * const value: number | undefined = values.toString;
4299
+ * const valueType = typeof value;
3874
4300
  *
3875
- * expect(typeof value).toBe("function");
3876
- * expect(() => {
3877
- * if (value !== undefined) value.toFixed();
3878
- * }).toThrow(TypeError);
4301
+ * assertEqual(valueType, "function");
4302
+ * const called = trySync(
4303
+ * () => {
4304
+ * if (value !== undefined) value.toFixed(0);
4305
+ * },
4306
+ * (error) => error,
4307
+ * );
4308
+ * assertErr(called);
4309
+ * assertTrue(called.error instanceof TypeError);
3879
4310
  * ```
3880
4311
  *
3881
4312
  * Evolu Record Outputs use the same TypeScript Record representation.
3882
4313
  *
3883
4314
  * ```ts
3884
- * import { Number, literal, record } from "@evolu/common";
4315
+ * import {
4316
+ * assertEqual,
4317
+ * assertErr,
4318
+ * assertOk,
4319
+ * assertTrue,
4320
+ * trySync,
4321
+ * Number,
4322
+ * literal,
4323
+ * record,
4324
+ * type Data,
4325
+ * } from "@evolu/common";
3885
4326
  *
3886
4327
  * const Values = record(literal("toString"), Number);
3887
4328
  * const result = Values.fromUnknown({});
4329
+ * const emptyValues: Data = {};
3888
4330
  *
3889
- * expectOk(result, {});
4331
+ * assertOk(result, emptyValues);
3890
4332
  *
3891
4333
  * const value: number | undefined = result.value.toString;
4334
+ * const valueType = typeof value;
3892
4335
  *
3893
- * expect(typeof value).toBe("function");
3894
- * expect(() => {
3895
- * if (value !== undefined) value.toFixed();
3896
- * }).toThrow(TypeError);
4336
+ * assertEqual(valueType, "function");
4337
+ * const called = trySync(
4338
+ * () => {
4339
+ * if (value !== undefined) value.toFixed(0);
4340
+ * },
4341
+ * (error) => error,
4342
+ * );
4343
+ * assertErr(called);
4344
+ * assertTrue(called.error instanceof TypeError);
3897
4345
  * ```
3898
4346
  *
3899
4347
  * In other words, treat Record Outputs as string-keyed data rather than calling
@@ -4060,7 +4508,13 @@ export interface OptionalProperty<T extends TypeNode> {
4060
4508
  * ### Example
4061
4509
  *
4062
4510
  * ```ts
4063
- * import { String, object, optional, undefinedOr } from "@evolu/common";
4511
+ * import {
4512
+ * assertOk,
4513
+ * String,
4514
+ * object,
4515
+ * optional,
4516
+ * undefinedOr,
4517
+ * } from "@evolu/common";
4064
4518
  *
4065
4519
  * const User = object({
4066
4520
  * name: String,
@@ -4068,8 +4522,8 @@ export interface OptionalProperty<T extends TypeNode> {
4068
4522
  * preferredName: optional(undefinedOr(String)),
4069
4523
  * });
4070
4524
  *
4071
- * expectOk(User.fromUnknown({ name: "Ada" }), { name: "Ada" });
4072
- * expectOk(User.fromUnknown({ name: "Ada", preferredName: undefined }), {
4525
+ * assertOk(User.fromUnknown({ name: "Ada" }), { name: "Ada" });
4526
+ * assertOk(User.fromUnknown({ name: "Ada", preferredName: undefined }), {
4073
4527
  * name: "Ada",
4074
4528
  * preferredName: undefined,
4075
4529
  * });
@@ -4103,10 +4557,11 @@ type ObjectProperty = ObjectProps[string];
4103
4557
  * required properties nor make optional properties present. This keeps Outputs
4104
4558
  * valid after ordinary object spread restores `Object.prototype`.
4105
4559
  *
4106
- * A `null` prototype or an immediate root prototype whose own prototype is
4107
- * `null` is accepted. Ordinary class instances, deeper prototype chains,
4108
- * accessors, and non-enumerable properties are rejected. Use {@link instanceOf}
4109
- * for class Outputs or {@link transform} to decode instances into plain data.
4560
+ * The prototype must satisfy the realm-neutral structural heuristic described
4561
+ * by {@link isPlainObject}. A matching custom root prototype can be classified
4562
+ * as plain; other custom prototypes, class instances, accessors, and
4563
+ * non-enumerable properties are rejected. Use {@link instanceOf} for class
4564
+ * Outputs or {@link transform} to decode instances into plain data.
4110
4565
  *
4111
4566
  * When decoding changes no property, it preserves the input. When decoding or
4112
4567
  * encoding changes a property, it constructs an object with a `null`
@@ -4119,6 +4574,8 @@ type ObjectProperty = ObjectProps[string];
4119
4574
  *
4120
4575
  * ```ts
4121
4576
  * import {
4577
+ * assertOk,
4578
+ * assertType,
4122
4579
  * Int64FromInt64String,
4123
4580
  * String,
4124
4581
  * object,
@@ -4137,20 +4594,20 @@ type ObjectProperty = ObjectProps[string];
4137
4594
  * loginCount: "42",
4138
4595
  * });
4139
4596
  *
4140
- * expectOk(userFromUnknown, { name: "Ada", loginCount: 42n });
4597
+ * assertOk(userFromUnknown, { name: "Ada", loginCount: 42n });
4141
4598
  *
4142
4599
  * // Validate the object and root property Types.
4143
4600
  * const userInput = User.parent.fromUnknown({
4144
4601
  * name: "Ada",
4145
4602
  * loginCount: "42",
4146
4603
  * });
4147
- * expectOk(userInput, { name: "Ada", loginCount: "42" });
4604
+ * assertOk(userInput, { name: "Ada", loginCount: "42" });
4148
4605
  *
4149
4606
  * // Run the remaining property stages.
4150
4607
  * const userFromInput = User.from.parent(userInput.value);
4151
4608
  *
4152
- * expectOk(userFromInput, { name: "Ada", loginCount: 42n });
4153
- * expectTypeOf(userFromInput.value).toExtend<User>();
4609
+ * assertOk(userFromInput, { name: "Ada", loginCount: 42n });
4610
+ * assertType<typeof User.Output, typeof userFromInput.value>();
4154
4611
  * ```
4155
4612
  *
4156
4613
  * Note that TypeScript does not model an object's runtime prototype. This can
@@ -4158,38 +4615,68 @@ type ObjectProperty = ObjectProps[string];
4158
4615
  * as an object property:
4159
4616
  *
4160
4617
  * ```ts
4618
+ * import {
4619
+ * assertEqual,
4620
+ * assertErr,
4621
+ * assertTrue,
4622
+ * trySync,
4623
+ * } from "@evolu/common";
4624
+ *
4161
4625
  * interface Values {
4162
4626
  * readonly toString?: number;
4163
4627
  * }
4164
4628
  *
4165
- * const nullPrototypeValues = globalThis.Object.create(null) as Values;
4629
+ * const nullPrototypeValues = Object.create(null) as Values;
4166
4630
  * const values = { ...nullPrototypeValues };
4167
4631
  *
4168
4632
  * // TypeScript treats the inherited function as `number | undefined`.
4169
4633
  * const value: number | undefined = values.toString;
4634
+ * const valueType = typeof value;
4170
4635
  *
4171
- * expect(typeof value).toBe("function");
4172
- * expect(() => {
4173
- * if (value !== undefined) value.toFixed();
4174
- * }).toThrow(TypeError);
4636
+ * assertEqual(valueType, "function");
4637
+ * const called = trySync(
4638
+ * () => {
4639
+ * if (value !== undefined) value.toFixed(0);
4640
+ * },
4641
+ * (error) => error,
4642
+ * );
4643
+ * assertErr(called);
4644
+ * assertTrue(called.error instanceof TypeError);
4175
4645
  * ```
4176
4646
  *
4177
4647
  * Evolu Object Outputs use the same TypeScript object representation.
4178
4648
  *
4179
4649
  * ```ts
4180
- * import { Number, object, optional } from "@evolu/common";
4650
+ * import {
4651
+ * assertEqual,
4652
+ * assertErr,
4653
+ * assertOk,
4654
+ * assertTrue,
4655
+ * trySync,
4656
+ * Number,
4657
+ * object,
4658
+ * optional,
4659
+ * type Data,
4660
+ * } from "@evolu/common";
4181
4661
  *
4182
4662
  * const Values = object({ toString: optional(Number) });
4183
4663
  * const result = Values.fromUnknown({});
4664
+ * const emptyValues: Data = {};
4184
4665
  *
4185
- * expectOk(result, {});
4666
+ * assertOk(result, emptyValues);
4186
4667
  *
4187
4668
  * const value: number | undefined = result.value.toString;
4669
+ * const valueType = typeof value;
4188
4670
  *
4189
- * expect(typeof value).toBe("function");
4190
- * expect(() => {
4191
- * if (value !== undefined) value.toFixed();
4192
- * }).toThrow(TypeError);
4671
+ * assertEqual(valueType, "function");
4672
+ * const called = trySync(
4673
+ * () => {
4674
+ * if (value !== undefined) value.toFixed(0);
4675
+ * },
4676
+ * (error) => error,
4677
+ * );
4678
+ * assertErr(called);
4679
+ * assertTrue(called.error instanceof TypeError);
4193
4680
  * ```
4194
4681
  *
4195
4682
  * In other words, treat Object Outputs as data rather than calling inherited
@@ -4204,7 +4691,13 @@ export declare function object<const Props extends ObjectProps>(props: Props, ..
4204
4691
  * ### Example
4205
4692
  *
4206
4693
  * ```ts
4207
- * import { String, object, record } from "@evolu/common";
4694
+ * import {
4695
+ * assertOk,
4696
+ * assertType,
4697
+ * String,
4698
+ * object,
4699
+ * record,
4700
+ * } from "@evolu/common";
4208
4701
  *
4209
4702
  * const RequestHeaders = object(
4210
4703
  * { authorization: String },
@@ -4215,13 +4708,11 @@ export declare function object<const Props extends ObjectProps>(props: Props, ..
4215
4708
  * "x-request-id": "request-1",
4216
4709
  * });
4217
4710
  *
4218
- * expectOk(result, {
4711
+ * assertOk(result, {
4219
4712
  * authorization: "Bearer token",
4220
4713
  * "x-request-id": "request-1",
4221
4714
  * });
4222
- * expectTypeOf(result.value["x-request-id"]).toEqualTypeOf<
4223
- * string | undefined
4224
- * >();
4715
+ * assertType<string | undefined, (typeof result.value)["x-request-id"]>();
4225
4716
  * ```
4226
4717
  */
4227
4718
  export declare function object<const Props extends ObjectProps, const Rest extends RecordTypeNode & ConcreteTypeNode>(props: Props, record: Rest, ...validation: [
@@ -4365,12 +4856,10 @@ export interface ObjectNotObjectError extends TypeError<"Object"> {
4365
4856
  * An error returned when an {@link object} input falls outside its supported
4366
4857
  * plain-object prototype boundary.
4367
4858
  *
4368
- * Object Types accept a `null` prototype or a prototype whose own prototype is
4369
- * `null`. This includes ordinary and cross-realm plain objects as well as
4370
- * objects created from an immediate root prototype. Arrays, JavaScript built-in
4371
- * objects, class instances, and objects with deeper custom prototype chains
4372
- * return this error instead of having their prototype or inherited state
4373
- * discarded. `reason.value` is the rejected object.
4859
+ * Object Types use the realm-neutral structural heuristic described by
4860
+ * {@link isPlainObject}. Values that do not satisfy it return this error instead
4861
+ * of having their prototype or inherited state discarded. `reason.value` is the
4862
+ * rejected object.
4374
4863
  *
4375
4864
  * @group Objects
4376
4865
  */
@@ -4447,13 +4936,20 @@ type ObjectRestFromParentError<Rest extends ObjectRecordTypeNode | undefined> =
4447
4936
  * ### Example
4448
4937
  *
4449
4938
  * ```ts
4450
- * import { PositiveInt, String, partial } from "@evolu/common";
4939
+ * import {
4940
+ * assertFalse,
4941
+ * assertOk,
4942
+ * PositiveInt,
4943
+ * String,
4944
+ * partial,
4945
+ * } from "@evolu/common";
4451
4946
  *
4452
4947
  * const PartialUser = partial({ name: String, age: PositiveInt });
4948
+ * const emptyPartialUser: typeof PartialUser.Output = {};
4453
4949
  *
4454
- * expectOk(PartialUser.fromUnknown({}), {});
4455
- * expectOk(PartialUser.fromUnknown({ name: "Ada" }), { name: "Ada" });
4456
- * expect(PartialUser.fromUnknown({ age: -1 }).ok).toBe(false);
4950
+ * assertOk(PartialUser.fromUnknown({}), emptyPartialUser);
4951
+ * assertOk(PartialUser.fromUnknown({ name: "Ada" }), { name: "Ada" });
4952
+ * assertFalse(PartialUser.fromUnknown({ age: -1 }).ok);
4457
4953
  * ```
4458
4954
  *
4459
4955
  * @group Objects
@@ -4478,15 +4974,20 @@ export type PartialObjectProps<Props extends ObjectProps> = {
4478
4974
  * ### Example
4479
4975
  *
4480
4976
  * ```ts
4481
- * import { String, nullOr, nullableToOptional } from "@evolu/common";
4977
+ * import {
4978
+ * assertOk,
4979
+ * String,
4980
+ * nullOr,
4981
+ * nullableToOptional,
4982
+ * } from "@evolu/common";
4482
4983
  *
4483
4984
  * const User = nullableToOptional({
4484
4985
  * name: String,
4485
4986
  * nickname: nullOr(String),
4486
4987
  * });
4487
4988
  *
4488
- * expectOk(User.fromUnknown({ name: "Ada" }), { name: "Ada" });
4489
- * expectOk(User.fromUnknown({ name: "Ada", nickname: null }), {
4989
+ * assertOk(User.fromUnknown({ name: "Ada" }), { name: "Ada" });
4990
+ * assertOk(User.fromUnknown({ name: "Ada", nickname: null }), {
4490
4991
  * name: "Ada",
4491
4992
  * nickname: null,
4492
4993
  * });
@@ -4510,12 +5011,12 @@ export type NullableToOptionalProps<Props extends ObjectProps> = {
4510
5011
  * ### Example
4511
5012
  *
4512
5013
  * ```ts
4513
- * import { String, object, omit } from "@evolu/common";
5014
+ * import { assertOk, String, object, omit } from "@evolu/common";
4514
5015
  *
4515
5016
  * const User = object({ name: String, password: String });
4516
5017
  * const PublicUser = omit(User, "password");
4517
5018
  *
4518
- * expectOk(PublicUser.fromUnknown({ name: "Ada" }), { name: "Ada" });
5019
+ * assertOk(PublicUser.fromUnknown({ name: "Ada" }), { name: "Ada" });
4519
5020
  * ```
4520
5021
  *
4521
5022
  * @group Objects
@@ -4538,6 +5039,7 @@ type OmitKeyConcreteTypeError = CompileTimeError<"Type", "Each omitted key must
4538
5039
  *
4539
5040
  * ```ts
4540
5041
  * import {
5042
+ * assertOk,
4541
5043
  * NonNegativeInt,
4542
5044
  * String,
4543
5045
  * object,
@@ -4555,8 +5057,8 @@ type OmitKeyConcreteTypeError = CompileTimeError<"Type", "Each omitted key must
4555
5057
  * value: { timestamp: 42 },
4556
5058
  * });
4557
5059
  *
4558
- * expectOk(validated, { ok: true, value: { timestamp: 42 } });
4559
- * expectOk(validated.value, { timestamp: 42 });
5060
+ * assertOk(validated, { ok: true, value: { timestamp: 42 } });
5061
+ * assertOk(validated.value, { timestamp: 42 });
4560
5062
  * ```
4561
5063
  *
4562
5064
  * @group Results
@@ -4600,21 +5102,31 @@ export type UnknownResult = typeof UnknownResult.Output;
4600
5102
  * ### Example
4601
5103
  *
4602
5104
  * ```ts
4603
- * import { String, discriminatedUnion, typed } from "@evolu/common";
5105
+ * import {
5106
+ * assertFalse,
5107
+ * assertOk,
5108
+ * assertType,
5109
+ * String,
5110
+ * discriminatedUnion,
5111
+ * typed,
5112
+ * } from "@evolu/common";
4604
5113
  *
4605
5114
  * const Loading = typed("Loading");
4606
5115
  * const Loaded = typed("Loaded", { value: String });
4607
5116
  * const State = discriminatedUnion(Loading, Loaded);
4608
5117
  *
4609
- * expectOk(State.fromUnknown({ type: "Loading" }), { type: "Loading" });
4610
- * expectOk(State.fromUnknown({ type: "Loaded", value: "Evolu" }), {
5118
+ * assertOk(State.fromUnknown({ type: "Loading" }), { type: "Loading" });
5119
+ * assertOk(State.fromUnknown({ type: "Loaded", value: "Evolu" }), {
4611
5120
  * type: "Loaded",
4612
5121
  * value: "Evolu",
4613
5122
  * });
4614
- * expect(Loading.is({ type: "Loading", progress: 1 })).toBe(false);
4615
- * expectTypeOf<typeof Loading.Output>().toExtend<{
4616
- * readonly type: "Loading";
4617
- * }>();
5123
+ * assertFalse(Loading.is({ type: "Loading", progress: 1 }));
5124
+ * assertType<
5125
+ * true,
5126
+ * typeof Loading.Output extends { readonly type: "Loading" }
5127
+ * ? true
5128
+ * : false
5129
+ * >();
4618
5130
  * ```
4619
5131
  *
4620
5132
  * @group Discriminated unions
@@ -4626,13 +5138,13 @@ export declare function typed<const Tag extends TypeName>(tag: ValidateTypedTag<
4626
5138
  * ### Example
4627
5139
  *
4628
5140
  * ```ts
4629
- * import { String, typed } from "@evolu/common";
5141
+ * import { assertOk, String, typed } from "@evolu/common";
4630
5142
  *
4631
5143
  * const Pending = typed("Pending", {
4632
5144
  * label: String,
4633
5145
  * });
4634
5146
  *
4635
- * expectOk(Pending.fromUnknown({ type: "Pending", label: "Waiting" }), {
5147
+ * assertOk(Pending.fromUnknown({ type: "Pending", label: "Waiting" }), {
4636
5148
  * type: "Pending",
4637
5149
  * label: "Waiting",
4638
5150
  * });
@@ -4645,7 +5157,13 @@ export declare function typed<const Tag extends TypeName, const Props extends Ob
4645
5157
  * ### Example
4646
5158
  *
4647
5159
  * ```ts
4648
- * import { String, record, typed } from "@evolu/common";
5160
+ * import {
5161
+ * assertOk,
5162
+ * assertType,
5163
+ * String,
5164
+ * record,
5165
+ * typed,
5166
+ * } from "@evolu/common";
4649
5167
  *
4650
5168
  * const Open = typed("Open", { label: String }, record(String, String));
4651
5169
  * const result = Open.fromUnknown({
@@ -4654,12 +5172,12 @@ export declare function typed<const Tag extends TypeName, const Props extends Ob
4654
5172
  * note: "Connected",
4655
5173
  * });
4656
5174
  *
4657
- * expectOk(result, {
5175
+ * assertOk(result, {
4658
5176
  * type: "Open",
4659
5177
  * label: "Ready",
4660
5178
  * note: "Connected",
4661
5179
  * });
4662
- * expectTypeOf(result.value.note).toEqualTypeOf<string | undefined>();
5180
+ * assertType<string | undefined, typeof result.value.note>();
4663
5181
  * ```
4664
5182
  */
4665
5183
  export declare function typed<const Tag extends TypeName, const Props extends ObjectProps, const Rest extends RecordTypeNode & ConcreteTypeNode>(tag: ValidateTypedTag<Tag>, props: Props, record: Rest, ...validation: [
@@ -4686,7 +5204,14 @@ export declare function typed<const Tag extends TypeName, const Props extends Ob
4686
5204
  * ### Example
4687
5205
  *
4688
5206
  * ```ts
4689
- * import { err, ok, type Result, type Typed } from "@evolu/common";
5207
+ * import {
5208
+ * assertErr,
5209
+ * assertOk,
5210
+ * err,
5211
+ * ok,
5212
+ * type Result,
5213
+ * type Typed,
5214
+ * } from "@evolu/common";
4690
5215
  *
4691
5216
  * interface User extends Typed<"User"> {
4692
5217
  * readonly id: string;
@@ -4701,8 +5226,8 @@ export declare function typed<const Tag extends TypeName, const Props extends Ob
4701
5226
  * readonly id: string;
4702
5227
  * }
4703
5228
  *
4704
- * expectOk(getUser("user-1"), { type: "User", id: "user-1" });
4705
- * expectErr(getUser("missing"), { type: "UserNotFound", id: "missing" });
5229
+ * assertOk(getUser("user-1"), { type: "User", id: "user-1" });
5230
+ * assertErr(getUser("missing"), { type: "UserNotFound", id: "missing" });
4706
5231
  * ```
4707
5232
  *
4708
5233
  * @group Discriminated unions
@@ -4721,6 +5246,7 @@ export interface Typed<Tag extends TypeName> {
4721
5246
  *
4722
5247
  * ```ts
4723
5248
  * import {
5249
+ * assertType,
4724
5250
  * String,
4725
5251
  * discriminatedUnion,
4726
5252
  * typed,
@@ -4734,10 +5260,10 @@ export interface Typed<Tag extends TypeName> {
4734
5260
  *
4735
5261
  * type CreateMessage = ExtractTyped<Message, "Create">;
4736
5262
  *
4737
- * expectTypeOf<CreateMessage>().toEqualTypeOf<typeof Create.Output>();
5263
+ * assertType<typeof Create.Output, CreateMessage>();
4738
5264
  *
4739
5265
  * // @ts-expect-error "Cretae" is not a Message type.
4740
- * type Typo = ExtractTyped<Message, "Cretae">;
5266
+ * type _Typo = ExtractTyped<Message, "Cretae">;
4741
5267
  * ```
4742
5268
  *
4743
5269
  * @group Discriminated unions
@@ -4769,7 +5295,7 @@ type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties mu
4769
5295
  * ### Example
4770
5296
  *
4771
5297
  * ```ts
4772
- * import { String, nextResult, typed } from "@evolu/common";
5298
+ * import { assertEqual, String, nextResult, typed } from "@evolu/common";
4773
5299
  *
4774
5300
  * const StringNextResult = nextResult(
4775
5301
  * String,
@@ -4787,19 +5313,21 @@ type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties mu
4787
5313
  * return `Error: ${result.error.message}`;
4788
5314
  * };
4789
5315
  *
4790
- * expect(describeNext({ ok: true, value: "item" })).toBe("Value: item");
4791
- * expect(
5316
+ * assertEqual(describeNext({ ok: true, value: "item" }), "Value: item");
5317
+ * assertEqual(
4792
5318
  * describeNext({
4793
5319
  * ok: false,
4794
5320
  * error: { type: "Done", done: "complete" },
4795
5321
  * }),
4796
- * ).toBe("Done: complete");
4797
- * expect(
5322
+ * "Done: complete",
5323
+ * );
5324
+ * assertEqual(
4798
5325
  * describeNext({
4799
5326
  * ok: false,
4800
5327
  * error: { type: "ReadFailed", message: "Offline" },
4801
5328
  * }),
4802
- * ).toBe("Error: Offline");
5329
+ * "Error: Offline",
5330
+ * );
4803
5331
  * ```
4804
5332
  *
4805
5333
  * @group Results
@@ -4845,13 +5373,18 @@ export type UnknownNextResult = typeof UnknownNextResult.Output;
4845
5373
  * ### Example
4846
5374
  *
4847
5375
  * ```ts
4848
- * import { String, discriminatedUnion, typed } from "@evolu/common";
5376
+ * import {
5377
+ * assertOk,
5378
+ * String,
5379
+ * discriminatedUnion,
5380
+ * typed,
5381
+ * } from "@evolu/common";
4849
5382
  *
4850
5383
  * const Created = typed("Created", { id: String });
4851
5384
  * const Deleted = typed("Deleted", { id: String });
4852
5385
  * const Event = discriminatedUnion(Created, Deleted);
4853
5386
  *
4854
- * expectOk(Event.fromUnknown({ type: "Created", id: "id" }), {
5387
+ * assertOk(Event.fromUnknown({ type: "Created", id: "id" }), {
4855
5388
  * type: "Created",
4856
5389
  * id: "id",
4857
5390
  * });
@@ -4867,6 +5400,7 @@ export declare function discriminatedUnion<const Members extends DiscriminatedUn
4867
5400
  *
4868
5401
  * ```ts
4869
5402
  * import {
5403
+ * assertOk,
4870
5404
  * Number,
4871
5405
  * String,
4872
5406
  * discriminatedUnion,
@@ -4878,11 +5412,11 @@ export declare function discriminatedUnion<const Members extends DiscriminatedUn
4878
5412
  * const Removed = object({ kind: literal("removed"), id: Number });
4879
5413
  * const Event = discriminatedUnion("kind", Added, Removed);
4880
5414
  *
4881
- * expectOk(Event.fromUnknown({ kind: "added", value: "Evolu" }), {
5415
+ * assertOk(Event.fromUnknown({ kind: "added", value: "Evolu" }), {
4882
5416
  * kind: "added",
4883
5417
  * value: "Evolu",
4884
5418
  * });
4885
- * expectOk(Event.fromUnknown({ kind: "removed", id: 1 }), {
5419
+ * assertOk(Event.fromUnknown({ kind: "removed", id: 1 }), {
4886
5420
  * kind: "removed",
4887
5421
  * id: 1,
4888
5422
  * });
@@ -5024,15 +5558,18 @@ type DiscriminatedUnionDuplicateValueError<Key extends string, Expected extends
5024
5558
  *
5025
5559
  * The definition must return one concrete non-Lazy Type. Use {@link union} for
5026
5560
  * alternatives. Every recursive Lazy reference must be nested behind an
5027
- * {@link object}, {@link array}, {@link tuple}, {@link record}, or {@link set}
5028
- * structural boundary. Union does not guard recursion because it passes the
5029
- * same value to every member. Lazy defers schema construction; it does not make
5030
- * cyclic runtime object graphs or arbitrarily deep values stack-safe.
5561
+ * {@link object}, {@link array}, {@link tuple}, {@link record}, {@link set}, or
5562
+ * {@link map} structural boundary. Union does not guard recursion because it
5563
+ * passes the same value to every member. Lazy defers schema construction; it
5564
+ * does not make cyclic runtime object graphs or arbitrarily deep values
5565
+ * stack-safe.
5031
5566
  *
5032
5567
  * ### Example
5033
5568
  *
5034
5569
  * ```ts
5035
5570
  * import {
5571
+ * assertOk,
5572
+ * assertType,
5036
5573
  * String,
5037
5574
  * array,
5038
5575
  * lazy,
@@ -5062,11 +5599,11 @@ type DiscriminatedUnionDuplicateValueError<Key extends string, Expected extends
5062
5599
  * children: [{ value: "leaf", children: [] }],
5063
5600
  * });
5064
5601
  *
5065
- * expectOk(result, {
5602
+ * assertOk(result, {
5066
5603
  * value: "root",
5067
5604
  * children: [{ value: "leaf", children: [] }],
5068
5605
  * });
5069
- * expectTypeOf(result.value).toEqualTypeOf<Tree>();
5606
+ * assertType<Tree, typeof result.value>();
5070
5607
  * ```
5071
5608
  *
5072
5609
  * @group Recursive
@@ -5096,6 +5633,166 @@ export interface LazyType<in out Input, in out Output, in out FromError extends
5096
5633
  }
5097
5634
  type ValidateLazyTarget<Target extends ConcreteTypeNode> = IsUnion<Target> extends false ? Target : LazyTargetConcreteTypeError;
5098
5635
  type LazyTargetConcreteTypeError = CompileTimeError<"Type", "Lazy Type definition must return one concrete Type node. Pass a Union Type node instead of a union of Type nodes.">;
5636
+ /**
5637
+ * Evolu's recursive platform-independent structured-cloneable data domain.
5638
+ *
5639
+ * Data is intentionally limited to values supported by the structured clone
5640
+ * algorithm, so it can be sent through APIs such as worker `postMessage`. It
5641
+ * includes supported JavaScript primitives and the runtime representations
5642
+ * defined by {@link array}, {@link Object}, {@link set}, {@link map}, {@link Date},
5643
+ * and {@link Uint8Array}. Array elements, Object properties, Set elements, and
5644
+ * Map keys and values must also be Data. Cyclic and shared data graphs are
5645
+ * supported. Raw {@link ArrayBuffer} values are excluded; represent bytes with a
5646
+ * Uint8Array.
5647
+ *
5648
+ * Functions, arbitrary class instances, and other behavioral objects are not
5649
+ * Data. The structural representations follow the corresponding Evolu Type
5650
+ * rules. As with every Type operation, classification assumes trusted
5651
+ * application code or audited dependencies; it is not a security boundary for
5652
+ * adversarial Proxies or forged built-in object tags.
5653
+ *
5654
+ * TypeScript cannot express dense Arrays, exact property descriptors, object
5655
+ * prototypes, or the absence of custom properties on built-ins. Use the runtime
5656
+ * {@link Data} Type when a value crosses an unknown boundary.
5657
+ *
5658
+ * ### Example
5659
+ *
5660
+ * ```ts
5661
+ * import { assertOk, assertSame, assertType, Data } from "@evolu/common";
5662
+ *
5663
+ * const value: unknown = {
5664
+ * user: { name: "Ada" },
5665
+ * scores: new Map([["logic", 100]]),
5666
+ * roles: new Set(["admin"]),
5667
+ * };
5668
+ * const result = Data.fromUnknown(value);
5669
+ *
5670
+ * assertOk(result);
5671
+ * assertSame(result.value, value);
5672
+ * assertType<Data, typeof result.value>();
5673
+ * ```
5674
+ *
5675
+ * @group Base
5676
+ */
5677
+ export type Data = undefined | null | string | number | bigint | boolean | ReadonlyArray<Data> | {
5678
+ readonly [key: string]: Data;
5679
+ } | ReadonlySet<Data> | ReadonlyMap<Data, Data> | globalThis.Date | globalThis.Uint8Array;
5680
+ /**
5681
+ * Returns whether a TypeScript type consists only of {@link Data}.
5682
+ *
5683
+ * Unlike `Value extends Data`, this recursively checks the declared properties
5684
+ * of object types, so ordinary interfaces do not need a string index signature.
5685
+ * This is a compile-time approximation of the Data domain; representation
5686
+ * details such as prototypes and property descriptors still require the runtime
5687
+ * {@link Data} Type.
5688
+ *
5689
+ * Recursive object types are supported.
5690
+ *
5691
+ * ### Example
5692
+ *
5693
+ * ```ts
5694
+ * import { assertType, type IsData } from "@evolu/common";
5695
+ *
5696
+ * interface User {
5697
+ * readonly name: string;
5698
+ * readonly roles: ReadonlySet<string>;
5699
+ * }
5700
+ *
5701
+ * interface Service {
5702
+ * readonly run: () => void;
5703
+ * }
5704
+ *
5705
+ * assertType<true, IsData<User>>();
5706
+ * assertType<false, IsData<Service>>();
5707
+ * ```
5708
+ *
5709
+ * @group Base
5710
+ */
5711
+ export type IsData<Value> = IsDataValue<Value, readonly []>;
5712
+ type IsDataValue<Value, Seen extends ReadonlyArray<unknown>> = IsAny<Value> extends true ? false : unknown extends Value ? false : [Value] extends [never] ? true : false extends (Value extends unknown ? IsDataMember<Value, Seen> : never) ? false : true;
5713
+ type IsAny<Value> = 0 extends 1 & Value ? true : false;
5714
+ type IsDataMember<Value, Seen extends ReadonlyArray<unknown>> = IsSameType<Value, object> extends true ? false : IncludesSameType<Seen, Value> extends true ? true : Value extends void | null | string | number | bigint | boolean ? true : Value extends globalThis.Function ? false : Value extends globalThis.Date | globalThis.Uint8Array ? true : Value extends ReadonlyArray<infer Element> ? IsDataValue<Element, readonly [...Seen, Value]> : Value extends ReadonlySet<infer Element> ? IsDataValue<Element, readonly [...Seen, Value]> : Value extends ReadonlyMap<infer Key, infer MapValue> ? IsDataValue<Key | MapValue, readonly [...Seen, Value]> : Value extends object ? Extract<keyof Value, symbol> extends never ? IsDataObject<Value, readonly [...Seen, Value]> : false : false;
5715
+ type IsDataObject<Value extends object, Seen extends ReadonlyArray<unknown>> = [
5716
+ keyof Value
5717
+ ] extends [never] ? false : false extends {
5718
+ [Key in keyof Value]-?: IsDataValue<Value[Key], Seen>;
5719
+ }[keyof Value] ? false : true;
5720
+ type IncludesSameType<Values extends ReadonlyArray<unknown>, Value> = Values extends readonly [infer First, ...infer Rest] ? IsSameType<First, Value> extends true ? true : IncludesSameType<Rest, Value> : false;
5721
+ /**
5722
+ * One issue found while validating a candidate as {@link Data}.
5723
+ *
5724
+ * @group Base
5725
+ */
5726
+ export type DataIssue = {
5727
+ readonly kind: "InvalidType";
5728
+ readonly path: ReadonlyArray<PropertyKey>;
5729
+ readonly value: unknown;
5730
+ } | {
5731
+ readonly kind: "UnexpectedPrototype";
5732
+ readonly path: ReadonlyArray<PropertyKey>;
5733
+ readonly container: "Object";
5734
+ readonly value: object;
5735
+ } | {
5736
+ readonly kind: "Accessor";
5737
+ readonly path: ReadonlyArray<PropertyKey>;
5738
+ } | {
5739
+ readonly kind: "NonEnumerable";
5740
+ readonly path: ReadonlyArray<PropertyKey>;
5741
+ } | {
5742
+ readonly kind: "SymbolProperty";
5743
+ readonly path: ReadonlyArray<PropertyKey>;
5744
+ } | {
5745
+ readonly kind: "Hole";
5746
+ readonly path: ReadonlyArray<PropertyKey>;
5747
+ } | {
5748
+ readonly kind: "InvalidUint8Array";
5749
+ readonly path: ReadonlyArray<PropertyKey>;
5750
+ readonly value: globalThis.Uint8Array;
5751
+ } | {
5752
+ readonly kind: "ExcessProperty";
5753
+ readonly path: ReadonlyArray<PropertyKey>;
5754
+ readonly container: "Array" | "Set" | "Map";
5755
+ };
5756
+ /**
5757
+ * An error containing one or more issues found while validating a candidate as
5758
+ * {@link Data}.
5759
+ *
5760
+ * @group Base
5761
+ */
5762
+ export interface DataError extends TypeError<"Data"> {
5763
+ readonly reason: {
5764
+ readonly kind: "Issues";
5765
+ readonly issues: NonEmptyReadonlyArray<DataIssue>;
5766
+ };
5767
+ }
5768
+ /**
5769
+ * The root {@link Type} for Evolu {@link Data}.
5770
+ *
5771
+ * @group Base
5772
+ */
5773
+ export interface DataType extends Type<"Data", Data, Data, DataError, null, DataError, never, Data> {
5774
+ }
5775
+ /**
5776
+ * Root Type for {@link Data} values.
5777
+ *
5778
+ * Validation is iterative and preserves the input identity. Cyclic and shared
5779
+ * data graphs are valid.
5780
+ *
5781
+ * ### Example
5782
+ *
5783
+ * ```ts
5784
+ * import { assertOk, assertTrue, Data } from "@evolu/common";
5785
+ *
5786
+ * const value = new Map([["count", 1]]);
5787
+ * const result = Data.fromUnknown(value);
5788
+ *
5789
+ * assertOk(result, value);
5790
+ * assertTrue(Data.is(result.value));
5791
+ * ```
5792
+ *
5793
+ * @group Base
5794
+ */
5795
+ export declare const Data: DataType;
5099
5796
  /**
5100
5797
  * A candidate JSON value before exact runtime validation.
5101
5798
  *
@@ -5223,7 +5920,12 @@ export interface JsonError extends TypeError<"Json"> {
5223
5920
  * ### Example
5224
5921
  *
5225
5922
  * ```ts
5226
- * import { JsonValue, type JsonValueInput } from "@evolu/common";
5923
+ * import {
5924
+ * assertOk,
5925
+ * assertType,
5926
+ * JsonValue,
5927
+ * type JsonValueInput,
5928
+ * } from "@evolu/common";
5227
5929
  *
5228
5930
  * const input: JsonValueInput = {
5229
5931
  * name: "Ada",
@@ -5231,8 +5933,8 @@ export interface JsonError extends TypeError<"Json"> {
5231
5933
  * };
5232
5934
  * const result = JsonValue.fromUnknown(input);
5233
5935
  *
5234
- * expectOk(result, input);
5235
- * expectTypeOf(result.value).toEqualTypeOf<JsonValue>();
5936
+ * assertOk(result, input);
5937
+ * assertType<JsonValue, typeof result.value>();
5236
5938
  * ```
5237
5939
  *
5238
5940
  * @group JSON
@@ -5267,11 +5969,11 @@ export type Json = typeof Json.Output;
5267
5969
  * ### Example
5268
5970
  *
5269
5971
  * ```ts
5270
- * import { Json, jsonToJsonValue } from "@evolu/common";
5972
+ * import { assertEqual, Json, jsonToJsonValue } from "@evolu/common";
5271
5973
  *
5272
5974
  * const value = jsonToJsonValue(Json.orThrow('{"name":"Ada"}'));
5273
5975
  *
5274
- * expect(value).toEqual({ name: "Ada" });
5976
+ * assertEqual(value, { name: "Ada" });
5275
5977
  * ```
5276
5978
  *
5277
5979
  * @group JSON
@@ -5283,11 +5985,15 @@ export declare const jsonToJsonValue: (value: Json) => JsonValue;
5283
5985
  * ### Example
5284
5986
  *
5285
5987
  * ```ts
5286
- * import { jsonValueToJson, type JsonValue } from "@evolu/common";
5988
+ * import {
5989
+ * assertEqual,
5990
+ * jsonValueToJson,
5991
+ * type JsonValue,
5992
+ * } from "@evolu/common";
5287
5993
  *
5288
5994
  * const value: JsonValue = { name: "Ada" };
5289
5995
  *
5290
- * expect(jsonValueToJson(value)).toBe('{"name":"Ada"}');
5996
+ * assertEqual(jsonValueToJson(value), '{"name":"Ada"}');
5291
5997
  * ```
5292
5998
  *
5293
5999
  * @group JSON
@@ -5302,12 +6008,12 @@ export declare const jsonValueToJson: (value: JsonValue) => Json;
5302
6008
  * ### Example
5303
6009
  *
5304
6010
  * ```ts
5305
- * import { JsonValueFromJson } from "@evolu/common";
6011
+ * import { assertEqual, assertOk, JsonValueFromJson } from "@evolu/common";
5306
6012
  *
5307
6013
  * const result = JsonValueFromJson.fromUnknown('{ "name": "Ada" }');
5308
6014
  *
5309
- * expectOk(result, { name: "Ada" });
5310
- * expect(JsonValueFromJson.to(result.value)).toBe('{"name":"Ada"}');
6015
+ * assertOk(result, { name: "Ada" });
6016
+ * assertEqual(JsonValueFromJson.to(result.value), '{"name":"Ada"}');
5311
6017
  * ```
5312
6018
  *
5313
6019
  * @group JSON
@@ -5325,6 +6031,8 @@ export declare const JsonValueFromJson: TransformType<BrandType<Type<"String", s
5325
6031
  *
5326
6032
  * ```ts
5327
6033
  * import {
6034
+ * assertEqual,
6035
+ * assertType,
5328
6036
  * Age,
5329
6037
  * NonEmptyTrimmedString100,
5330
6038
  * json,
@@ -5345,11 +6053,13 @@ export declare const JsonValueFromJson: TransformType<BrandType<Type<"String", s
5345
6053
  * const user = User.orThrow({ name: "Ada", age: 37 });
5346
6054
  * const userJson = userToUserJson(user);
5347
6055
  *
5348
- * expectTypeOf(userJson).toEqualTypeOf<
5349
- * string & Brand<"Json"> & Brand<"UserJson">
6056
+ * assertType<
6057
+ * string & Brand<"Json"> & Brand<"UserJson">,
6058
+ * typeof userJson
5350
6059
  * >();
5351
- * expect(userJson).toBe('{"name":"Ada","age":37}');
5352
- * expect(userJsonToUser(userJson)).toEqual(user);
6060
+ * assertEqual(userJson, '{"name":"Ada","age":37}');
6061
+ * assertEqual(UserJson.orThrow(userJson), userJson);
6062
+ * assertEqual(userJsonToUser(userJson), user);
5353
6063
  * ```
5354
6064
  *
5355
6065
  * The supplied Type must have a JSON-compatible `CanonicalInput`. The branded
@@ -5366,10 +6076,9 @@ type IsJsonTypeCompatible<T extends TypeNode> = IsJsonCompatible<CanonicalInputO
5366
6076
  type IsJsonCompatible<Value, Seen extends ReadonlyArray<unknown> = readonly []> = IsExactlyJsonValue<Value> extends true ? true : false extends JsonCompatibleMember<Value, Seen> ? false : true;
5367
6077
  type IsExactlyJsonValue<Value> = IsSameType<Value, JsonValue>;
5368
6078
  type JsonCompatibleMember<Value, Seen extends ReadonlyArray<unknown>> = Value extends unknown ? IsJsonCompatibilityCycle<Value, Seen> extends true ? true : Value extends string | boolean | null ? true : Value extends number ? JsonNumberCompatible<Value> : Value extends ReadonlyArray<infer Item> ? Exclude<keyof Value, AllowedJsonArrayKeys<Value>> extends never ? IsJsonCompatible<Item, readonly [...Seen, Value]> : false : Value extends globalThis.Function ? false : Value extends object ? JsonObjectCompatible<Value, readonly [...Seen, Value]> : false : never;
5369
- type AllowedJsonArrayKeys<Value extends ReadonlyArray<unknown>> = keyof ReadonlyArray<unknown> | (Value extends globalThis.Array<unknown> ? keyof globalThis.Array<unknown> : never) | JsonTupleIndexKeys<Value>;
5370
- type JsonTupleIndexKeys<Value extends ReadonlyArray<unknown>> = Value extends readonly [...infer Elements] ? Exclude<keyof Elements, keyof globalThis.Array<unknown>> : never;
6079
+ type AllowedJsonArrayKeys<Value extends ReadonlyArray<unknown>> = keyof ReadonlyArray<unknown> | (Value extends Array<unknown> ? keyof Array<unknown> : never) | JsonTupleIndexKeys<Value>;
6080
+ type JsonTupleIndexKeys<Value extends ReadonlyArray<unknown>> = Value extends readonly [...infer Elements] ? Exclude<keyof Elements, keyof Array<unknown>> : never;
5371
6081
  type IsJsonCompatibilityCycle<Value, Seen extends ReadonlyArray<unknown>> = Seen extends readonly [infer Candidate, ...infer Rest] ? IsSameType<Value, Candidate> extends true ? true : IsJsonCompatibilityCycle<Value, Rest> : false;
5372
- type IsSameType<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => (T extends B ? 1 : 2) ? (<T>() => T extends B ? 1 : 2) extends <T>() => (T extends A ? 1 : 2) ? true : false : false;
5373
6082
  type JsonNumberCompatible<Value extends number> = [Value] extends [FiniteNumber] ? true : [Value] extends [Brand<string>] ? false : number extends Value ? false : true;
5374
6083
  type JsonObjectCompatible<Value extends object, Seen extends ReadonlyArray<unknown>> = [keyof Value] extends [never] ? false : string extends keyof Value ? Exclude<keyof Value, string | number> extends never ? Value extends Readonly<Partial<Record<string, infer Item>>> ? IsJsonCompatible<Item, Seen> : false : false : false extends {
5375
6084
  readonly [Key in keyof Value]-?: Key extends string ? IsJsonCompatible<{} extends Pick<Value, Key> ? Required<Pick<Value, Key>>[Key] : Value[Key], Seen> : false;