@evolu/common 7.4.1 → 8.0.0-next.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 (250) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +162 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +200 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +29 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +329 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2274 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1499 -348
  107. package/dist/src/Test.d.ts +150 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +97 -0
  110. package/dist/src/Time.d.ts +202 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +199 -88
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +587 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +370 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +130 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +274 -113
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +621 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +34 -49
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -177
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +54 -58
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +127 -179
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +37 -24
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +429 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +520 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3696 -740
  206. package/src/Test.ts +202 -0
  207. package/src/Time.ts +357 -147
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +866 -430
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +404 -167
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +867 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -824
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +441 -307
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +119 -273
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +211 -283
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/dist/src/Instances.d.ts +0 -34
  228. package/dist/src/Instances.d.ts.map +0 -1
  229. package/dist/src/Instances.js +0 -44
  230. package/dist/src/Resources.d.ts +0 -118
  231. package/dist/src/Resources.d.ts.map +0 -1
  232. package/dist/src/Resources.js +0 -197
  233. package/dist/src/local-first/Platform.d.ts +0 -25
  234. package/dist/src/local-first/Platform.d.ts.map +0 -1
  235. package/dist/src/local-first/Platform.js +0 -1
  236. package/dist/src/local-first/Public.d.ts +0 -22
  237. package/dist/src/local-first/Public.d.ts.map +0 -1
  238. package/dist/src/local-first/Public.js +0 -15
  239. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  240. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  241. package/dist/src/local-first/PublicKysely.js +0 -184
  242. package/dist/src/local-first/Sync.d.ts +0 -112
  243. package/dist/src/local-first/Sync.d.ts.map +0 -1
  244. package/dist/src/local-first/Sync.js +0 -526
  245. package/src/Instances.ts +0 -92
  246. package/src/Resources.ts +0 -367
  247. package/src/local-first/Platform.ts +0 -27
  248. package/src/local-first/Public.ts +0 -42
  249. package/src/local-first/PublicKysely.ts +0 -239
  250. package/src/local-first/Sync.ts +0 -949
package/src/Type.ts CHANGED
@@ -1,24 +1,35 @@
1
+ /**
2
+ * Runtime type validation, parsing, and branded types.
3
+ *
4
+ * @module
5
+ */
6
+
1
7
  import { utf8ToBytes } from "@noble/ciphers/utils.js";
2
8
  import { sha256 } from "@noble/hashes/sha2.js";
3
9
  import * as bip39 from "@scure/bip39";
4
10
  import { wordlist } from "@scure/bip39/wordlists/english.js";
5
- import { pack } from "msgpackr";
6
11
  import type { Brand } from "./Brand.js";
7
12
  import type { RandomBytesDep } from "./Crypto.js";
8
- import { isPlainObject } from "./Object.js";
13
+ import { exhaustiveCheck } from "./Function.js";
14
+ import { isFunction, isPlainObject } from "./Object.js";
9
15
  import { hasNodeBuffer } from "./Platform.js";
10
- import { err, getOrNull, getOrThrow, ok, Result, trySync } from "./Result.js";
16
+ import type { NextResult, Result } from "./Result.js";
17
+ import { err, getOrNull, getOrThrow, ok, trySync } from "./Result.js";
11
18
  import { safelyStringifyUnknownValue } from "./String.js";
12
19
  import type { TimeDep } from "./Time.js";
13
20
  import type { Literal, Refinement, Simplify, WidenLiteral } from "./Types.js";
14
- import { IntentionalNever } from "./Types.js";
15
21
 
16
22
  /**
17
23
  * Evolu {@link Type} is like a type guard that returns typed errors (via
18
- * {@link Result}) instead of throwing. We either get a safely typed value or a
19
- * composable typed error telling us exactly why validation failed.
24
+ * {@link Result}) instead of throwing. We either receive a safely typed value or
25
+ * a composable typed error that tells us exactly why the validation failed.
20
26
  *
21
- * Why another validation library?
27
+ * The reason why Evolu Type exists is that no other TypeScript validation
28
+ * library met Evolu's requirements. A distinctive feature of Evolu Type
29
+ * compared to other validation libraries is that it returns typed errors rather
30
+ * than string messages. This allows TypeScript to enforce that all validation
31
+ * errors are handled via {@link exhaustiveCheck}, significantly improving the
32
+ * developer experience. Those requirements are:
22
33
  *
23
34
  * - **Result-based error handling** – no exceptions for normal control flow.
24
35
  * - **Typed errors with decoupled formatters** – validation logic ≠ user
@@ -31,15 +42,10 @@ import { IntentionalNever } from "./Types.js";
31
42
  * bottom.
32
43
  * - **No user-land chaining DSL** – prepared for TC39 Hack pipes.
33
44
  *
34
- * A distinctive feature of Evolu Type compared to other validation libraries is
35
- * that it returns typed errors rather than string messages. This allows
36
- * TypeScript to enforce that all validation errors are handled by type
37
- * checking, significantly improving the developer experience.
38
- *
39
45
  * Evolu Type supports [Standard Schema](https://standardschema.dev/) for
40
46
  * interoperability with 40+ validation-compatible tools and frameworks.
41
47
  *
42
- * ### Base Types Quick Start
48
+ * ## Base Types
43
49
  *
44
50
  * ```ts
45
51
  * // Validate unknown values
@@ -47,7 +53,7 @@ import { IntentionalNever } from "./Types.js";
47
53
  * const stringResult = String.fromUnknown(value);
48
54
  * if (!stringResult.ok) {
49
55
  * // console.error(formatStringError(stringResult.error));
50
- * return stringResult; // inside a function returning Result<string, _>
56
+ * return stringResult;
51
57
  * }
52
58
  * // Safe branch: value is now string
53
59
  * const upper = stringResult.value.toUpperCase();
@@ -66,10 +72,12 @@ import { IntentionalNever } from "./Types.js";
66
72
  * Point.from({ x: 1, y: "2" }); // err -> nested Number error
67
73
  * ```
68
74
  *
69
- * ### Branding Basics
75
+ * ## Branded types
70
76
  *
71
- * Branding adds semantic meaning & constraints while preserving the runtime
72
- * shape:
77
+ * Branding is the recommended way to define types in Evolu. Instead of using
78
+ * primitive types like `string` or `number` directly, wrap them with
79
+ * {@link brand} to create semantically meaningful types. See {@link Brand} for
80
+ * why this matters.
73
81
  *
74
82
  * ```ts
75
83
  * const CurrencyCode = brand("CurrencyCode", String, (value) =>
@@ -93,20 +101,25 @@ import { IntentionalNever } from "./Types.js";
93
101
  * See also reusable brand factories like `minLength`, `maxLength`, `trimmed`,
94
102
  * `positive`, `between`, etc.
95
103
  *
96
- * ### Objects & Optional Fields
104
+ * ## Object types
97
105
  *
98
106
  * ```ts
99
107
  * const User = object({
100
108
  * name: NonEmptyTrimmedString100,
101
109
  * age: optional(PositiveInt),
102
110
  * });
103
- * type User = typeof User.Type;
111
+ *
112
+ * // Use interface for objects. TypeScript displays the interface name
113
+ * // instead of expanding all properties.
114
+ * interface User extends InferType<typeof User> {}
104
115
  *
105
116
  * User.from({ name: "Alice" }); // ok
106
117
  * User.from({ name: "Alice", age: -1 }); // err(PositiveInt)
118
+ *
119
+ * // TODO: Add `record`
107
120
  * ```
108
121
  *
109
- * ### Deriving JSON String Types
122
+ * ## JSON type
110
123
  *
111
124
  * ```ts
112
125
  * const Person = object({
@@ -115,7 +128,7 @@ import { IntentionalNever } from "./Types.js";
115
128
  * // To prevent this, use FiniteNumber.
116
129
  * age: FiniteNumber,
117
130
  * });
118
- * type Person = typeof Person.Type;
131
+ * interface Person extends InferType<typeof Person> {}
119
132
  *
120
133
  * const [PersonJson, personToPersonJson, personJsonToPerson] = json(
121
134
  * Person,
@@ -133,7 +146,7 @@ import { IntentionalNever } from "./Types.js";
133
146
  * expect(personJsonToPerson(personJson)).toEqual(person);
134
147
  * ```
135
148
  *
136
- * ### Error Formatting
149
+ * ## Error Formatting
137
150
  *
138
151
  * Evolu separates validation logic from human-readable messages. There are two
139
152
  * layers:
@@ -144,14 +157,14 @@ import { IntentionalNever } from "./Types.js";
144
157
  * custom errors (including nested composite types) and lets us override
145
158
  * selected messages.
146
159
  *
147
- * #### 1. Per-Type Formatter (recap)
160
+ * ### 1. Per-Type formatter
148
161
  *
149
162
  * ```ts
150
163
  * const r = String.fromUnknown(42);
151
164
  * if (!r.ok) console.error(formatStringError(r.error));
152
165
  * ```
153
166
  *
154
- * #### 2. Unified Formatter with Overrides
167
+ * ### 2. Unified formatter with overrides
155
168
  *
156
169
  * ```ts
157
170
  * // Override only what we care about; fall back to built-ins for the rest.
@@ -174,12 +187,14 @@ import { IntentionalNever } from "./Types.js";
174
187
  * tuple / union) and applies overrides only where specified, greatly reducing
175
188
  * boilerplate when formatting complex validation errors.
176
189
  *
177
- * ### Tip
190
+ * ## Naming
178
191
  *
179
- * If necessary, write `globalThis.String` instead of `String` to avoid naming
180
- * clashes with native types.
192
+ * Evolu Types intentionally use the same names as native JavaScript types
193
+ * (String, Number, Boolean, etc.). When you need to distinguish between an
194
+ * Evolu Type and the native type, use `globalThis` to reference the native one
195
+ * (e.g., `globalThis.String`, `globalThis.Number`).
181
196
  *
182
- * ### Design Decision: No Bidirectional Transformations
197
+ * ## Design decision
183
198
  *
184
199
  * Evolu Type intentionally does not support bidirectional transformations. It
185
200
  * previously did, but supporting that while keeping typed error fidelity added
@@ -188,28 +203,62 @@ import { IntentionalNever } from "./Types.js";
188
203
  * reverse transforms would not buy much. We may revisit this if we can design a
189
204
  * minimal, 100% safe API that preserves simplicity.
190
205
  *
191
- * ### Prepared for TC39 Hack Pipes
206
+ * ## Composition without pipe
192
207
  *
193
208
  * Take a look how `SimplePassword` is defined:
194
209
  *
195
210
  * ```ts
196
- * export const SimplePassword = brand(
211
+ * const SimplePassword = brand(
197
212
  * "SimplePassword",
198
213
  * minLength(8)(maxLength(64)(TrimmedString)),
199
214
  * );
200
215
  * ```
201
216
  *
202
- * Nested functions are often OK (if not, make a helper) and read well, but with
203
- * TC39 Hack pipes it would be clearer:
217
+ * Shallow nesting often fits one line. If it doesn't, split into named parts:
204
218
  *
205
219
  * ```ts
206
- * // TrimmedString
207
- * // |> minLength(8)(%)
208
- * // |> maxLength(64)(%)
209
- * // |> brand("SimplePassword", %)
220
+ * const Min8TrimmedString64 = minLength(8)(maxLength(64)(TrimmedString));
221
+ * const SimplePassword = brand("SimplePassword", Min8TrimmedString64);
222
+ * ```
223
+ *
224
+ * ## FAQ
225
+ *
226
+ * ### How do I create a generic interface like `FooState<T>`?
227
+ *
228
+ * TypeScript's {@link InferType} extracts a concrete type, not a generic one. We
229
+ * cannot write `interface FooState<T> extends InferType<typeof fooState<T>>`
230
+ * because `InferType` needs a concrete Type instance.
231
+ *
232
+ * The recommended approach is to define the generic interface manually, then
233
+ * create a Type factory that produces structurally compatible Types:
234
+ *
235
+ * ```ts
236
+ * // Define the generic interface manually
237
+ * interface FooState<T> {
238
+ * readonly value: T;
239
+ * readonly loading: boolean;
240
+ * }
241
+ *
242
+ * // Create a Type factory that produces Types matching the interface
243
+ * const fooState = <T extends AnyType>(valueType: T) =>
244
+ * object({
245
+ * value: valueType,
246
+ * loading: Boolean,
247
+ * });
248
+ *
249
+ * // Usage
250
+ * const StringFooState = fooState(String);
251
+ * type StringFooState = InferType<typeof StringFooState>;
252
+ *
253
+ * // The interface and inferred type are structurally compatible
254
+ * const state: FooState<string> = StringFooState.orThrow({
255
+ * value: "hi",
256
+ * loading: false,
257
+ * });
210
258
  * ```
211
259
  *
212
- * Note `minLength` and `maxLength` are curried because they are factories.
260
+ * This keeps the interface generic while having type-safe runtime validation
261
+ * for each concrete use.
213
262
  */
214
263
  export interface Type<
215
264
  Name extends TypeName,
@@ -238,6 +287,9 @@ export interface Type<
238
287
  /**
239
288
  * Creates `T` from an `Input` value, throwing an error if validation fails.
240
289
  *
290
+ * Use this where failure should crash the current flow instead of being
291
+ * handled locally.
292
+ *
241
293
  * Throws an Error with the Type validation error in its `cause` property,
242
294
  * making it debuggable while avoiding the need for custom error messages.
243
295
  *
@@ -245,36 +297,41 @@ export interface Type<
245
297
  *
246
298
  * **When to use:**
247
299
  *
248
- * - Configuration values that are guaranteed to be valid (e.g., hardcoded
249
- * constants)
250
- * - Application startup where failure should crash the program
300
+ * - Application startup or composition-root setup where errors must stop the
301
+ * program immediately. In Evolu apps, errors are handled by
302
+ * platform-specific `createRun` adapters at the app boundary.
303
+ * - Module-level constants
304
+ * - Test setup with values that are expected to be valid
251
305
  * - As an alternative to assertions when the Type error in the thrown Error's
252
306
  * `cause` provides sufficient debugging information
253
- * - Test code with known valid inputs (when error message clarity is not
254
- * critical; for better test error messages, use Vitest `schemaMatching` +
255
- * `assert` with `.is()`)
307
+ *
308
+ * Prefer `from` in ordinary application logic where the caller can recover,
309
+ * show validation errors, or choose a different flow.
310
+ *
311
+ * For clearer test failure messages on invalid input, use Vitest
312
+ * `schemaMatching` + `assert` with `.is()`.
256
313
  *
257
314
  * ### Example
258
315
  *
259
316
  * ```ts
260
- * // Good: Known valid constant
317
+ * // Good: Known valid constant
261
318
  * const maxRetries = PositiveInt.orThrow(3);
262
319
  *
263
- * // Good: App configuration that should crash on invalid values
264
- * const appName = SimpleName.orThrow("MyApp");
320
+ * // Good: App configuration that should crash on invalid values
321
+ * const appName = Name.orThrow("MyApp");
265
322
  *
266
- * // Good: Instead of assert when Type error is clear enough
323
+ * // Good: Instead of assert when Type error is clear enough
267
324
  * // Context makes it obvious: count increments from non-negative value
268
325
  * const currentCount = counts.get(id) ?? 0;
269
326
  * const newCount = PositiveInt.orThrow(currentCount + 1);
270
327
  *
271
- * // Good: Test setup with known valid values
328
+ * // Good: Test setup with known valid values
272
329
  * const testUser = User.orThrow({ name: "Alice", age: 30 });
273
330
  *
274
- * // Avoid: User input (use `from` instead)
331
+ * // Avoid: User input (use `from` instead)
275
332
  * const userAge = PositiveInt.orThrow(userInput); // Could crash!
276
333
  *
277
- * // Better: Handle user input gracefully
334
+ * // Better: Handle user input gracefully
278
335
  * const ageResult = PositiveInt.from(userInput);
279
336
  * if (!ageResult.ok) {
280
337
  * // Handle validation error
@@ -296,16 +353,16 @@ export interface Type<
296
353
  * ### Example
297
354
  *
298
355
  * ```ts
299
- * // Good: Optional user input
356
+ * // Good: Optional user input
300
357
  * const age = PositiveInt.orNull(userInput);
301
358
  * if (age != null) {
302
359
  * console.log("Valid age:", age);
303
360
  * }
304
361
  *
305
- * // Good: Default fallback
362
+ * // Good: Default fallback
306
363
  * const maxRetries = PositiveInt.orNull(config.retries) ?? 3;
307
364
  *
308
- * // Avoid: When you need to know why validation failed (use `from` instead)
365
+ * // Avoid: When you need to know why validation failed (use `from` instead)
309
366
  * const result = PositiveInt.from(userInput);
310
367
  * if (!result.ok) {
311
368
  * console.error(formatPositiveError(result.error));
@@ -420,7 +477,7 @@ export interface Type<
420
477
  /**
421
478
  * Unique identifier for a {@link Type}.
422
479
  *
423
- * @category Utilities
480
+ * @group Utilities
424
481
  */
425
482
  export type TypeName = Capitalize<string>;
426
483
 
@@ -444,12 +501,17 @@ export interface TypeErrorWithReason<
444
501
  readonly reason: Reason;
445
502
  }
446
503
 
504
+ /**
505
+ * A {@link Type} with all type parameters set to `any`.
506
+ *
507
+ * @group Utilities
508
+ */
447
509
  export type AnyType = Type<any, any, any, any, any, any>;
448
510
 
449
511
  /**
450
512
  * Extracts the name from a {@link Type}.
451
513
  *
452
- * @category Utilities
514
+ * @group Utilities
453
515
  */
454
516
  export type InferName<A extends AnyType> =
455
517
  A extends Type<infer Name, any, any, any, any, any> ? Name : never;
@@ -457,7 +519,20 @@ export type InferName<A extends AnyType> =
457
519
  /**
458
520
  * Extracts the type from a {@link Type}.
459
521
  *
460
- * @category Utilities
522
+ * ### Example
523
+ *
524
+ * ```ts
525
+ * const User = object({
526
+ * name: NonEmptyTrimmedString100,
527
+ * age: optional(PositiveInt),
528
+ * });
529
+ *
530
+ * // Use interface for objects. TypeScript displays the interface name
531
+ * // instead of expanding all properties.
532
+ * interface User extends InferType<typeof User> {}
533
+ * ```
534
+ *
535
+ * @group Utilities
461
536
  */
462
537
  export type InferType<A extends AnyType> =
463
538
  A extends Type<any, infer T, any, any, any, any> ? T : never;
@@ -465,7 +540,7 @@ export type InferType<A extends AnyType> =
465
540
  /**
466
541
  * Extracts the input type from a {@link Type}.
467
542
  *
468
- * @category Utilities
543
+ * @group Utilities
469
544
  */
470
545
  export type InferInput<A extends AnyType> =
471
546
  A extends Type<any, any, infer Input, any, any, any> ? Input : never;
@@ -473,7 +548,7 @@ export type InferInput<A extends AnyType> =
473
548
  /**
474
549
  * Extracts the specific error type from a {@link Type}.
475
550
  *
476
- * @category Utilities
551
+ * @group Utilities
477
552
  */
478
553
  export type InferError<A extends AnyType> =
479
554
  A extends Type<any, any, any, infer Error, any, any> ? Error : never;
@@ -481,7 +556,7 @@ export type InferError<A extends AnyType> =
481
556
  /**
482
557
  * Extracts the parent type from a {@link Type}.
483
558
  *
484
- * @category Utilities
559
+ * @group Utilities
485
560
  */
486
561
  export type InferParent<A extends AnyType> =
487
562
  A extends Type<any, any, any, any, infer Parent, any> ? Parent : never;
@@ -489,7 +564,7 @@ export type InferParent<A extends AnyType> =
489
564
  /**
490
565
  * Extracts the parent error type from a {@link Type}.
491
566
  *
492
- * @category Utilities
567
+ * @group Utilities
493
568
  */
494
569
  export type InferParentError<A extends AnyType> =
495
570
  A extends Type<any, any, any, any, any, infer ParentError>
@@ -499,7 +574,7 @@ export type InferParentError<A extends AnyType> =
499
574
  /**
500
575
  * Extracts all error types from a {@link Type}.
501
576
  *
502
- * @category Utilities
577
+ * @group Utilities
503
578
  */
504
579
  export type InferErrors<T extends AnyType> =
505
580
  T extends Type<any, any, any, infer Error, any, infer ParentError>
@@ -511,7 +586,7 @@ const EvoluTypeSymbol = Symbol("evolu.Type");
511
586
  /**
512
587
  * Checks if the given value is an {@link Type}.
513
588
  *
514
- * @category Utilities
589
+ * @group Utilities
515
590
  */
516
591
  export const isType = (value: unknown): value is AnyType =>
517
592
  typeof value === "object" && value !== null && EvoluTypeSymbol in value;
@@ -563,11 +638,9 @@ const createType = <
563
638
  if (result.ok) {
564
639
  return { value: result.value };
565
640
  }
566
- cachedStandardSchemaFormatTypeError ??= createFormatTypeError();
567
641
  return {
568
642
  issues: typeErrorToStandardSchemaIssues(
569
643
  result.error as TypeErrors<Error>,
570
- cachedStandardSchemaFormatTypeError,
571
644
  ),
572
645
  };
573
646
  },
@@ -592,7 +665,7 @@ const createType = <
592
665
  * );
593
666
  * ```
594
667
  *
595
- * @category Utilities
668
+ * @group Utilities
596
669
  */
597
670
  export const createTypeErrorFormatter =
598
671
  <Error extends TypeError>(
@@ -628,7 +701,7 @@ export type TypeErrorFormatter<Error extends TypeError> = (
628
701
  * );
629
702
  * ```
630
703
  *
631
- * @category Base Factories
704
+ * @group Base factories
632
705
  */
633
706
  export const base = <Name extends TypeName, T, Error extends TypeError>(
634
707
  name: Name,
@@ -652,7 +725,7 @@ export const base = <Name extends TypeName, T, Error extends TypeError>(
652
725
  * createBaseTypeErrorFormatter<StringError>();
653
726
  * ```
654
727
  *
655
- * @category Utilities
728
+ * @group Utilities
656
729
  */
657
730
  export const createBaseTypeErrorFormatter = <
658
731
  Error extends TypeError,
@@ -661,14 +734,17 @@ export const createBaseTypeErrorFormatter = <
661
734
  (error) => `A value ${error.value} is not a ${error.type.toLowerCase()}.`,
662
735
  );
663
736
 
664
- /** @category Base Types */
665
- export const Unknown = base<"Unknown", unknown, never>("Unknown", ok);
737
+ /** @group Base Types */
738
+ export const Unknown = /*#__PURE__*/ base<"Unknown", unknown, never>(
739
+ "Unknown",
740
+ ok,
741
+ );
666
742
 
667
743
  /**
668
- * @category Base Types
669
- * @category String
744
+ * @group Base Types
745
+ * @group String
670
746
  */
671
- export const String = base("String", (value) =>
747
+ export const String = /*#__PURE__*/ base("String", (value) =>
672
748
  typeof value === "string"
673
749
  ? ok(value)
674
750
  : err<StringError>({ type: "String", value }),
@@ -676,10 +752,11 @@ export const String = base("String", (value) =>
676
752
 
677
753
  export interface StringError extends TypeError<"String"> {}
678
754
 
679
- export const formatStringError = createBaseTypeErrorFormatter<StringError>();
755
+ export const formatStringError =
756
+ /*#__PURE__*/ createBaseTypeErrorFormatter<StringError>();
680
757
 
681
- /** @category Base Types */
682
- export const Number = base("Number", (value) =>
758
+ /** @group Base Types */
759
+ export const Number = /*#__PURE__*/ base("Number", (value) =>
683
760
  typeof value === "number"
684
761
  ? ok(value)
685
762
  : err<NumberError>({ type: "Number", value }),
@@ -687,10 +764,11 @@ export const Number = base("Number", (value) =>
687
764
 
688
765
  export interface NumberError extends TypeError<"Number"> {}
689
766
 
690
- export const formatNumberError = createBaseTypeErrorFormatter<NumberError>();
767
+ export const formatNumberError =
768
+ /*#__PURE__*/ createBaseTypeErrorFormatter<NumberError>();
691
769
 
692
- /** @category Base Types */
693
- export const BigInt = base("BigInt", (value) =>
770
+ /** @group Base Types */
771
+ export const BigInt = /*#__PURE__*/ base("BigInt", (value) =>
694
772
  typeof value === "bigint"
695
773
  ? ok(value)
696
774
  : err<BigIntError>({ type: "BigInt", value }),
@@ -698,10 +776,11 @@ export const BigInt = base("BigInt", (value) =>
698
776
 
699
777
  export interface BigIntError extends TypeError<"BigInt"> {}
700
778
 
701
- export const formatBigIntError = createBaseTypeErrorFormatter<BigIntError>();
779
+ export const formatBigIntError =
780
+ /*#__PURE__*/ createBaseTypeErrorFormatter<BigIntError>();
702
781
 
703
- /** @category Base Types */
704
- export const Boolean = base("Boolean", (value) =>
782
+ /** @group Base Types */
783
+ export const Boolean = /*#__PURE__*/ base("Boolean", (value) =>
705
784
  typeof value === "boolean"
706
785
  ? ok(value)
707
786
  : err<BooleanError>({ type: "Boolean", value }),
@@ -709,10 +788,11 @@ export const Boolean = base("Boolean", (value) =>
709
788
 
710
789
  export interface BooleanError extends TypeError<"Boolean"> {}
711
790
 
712
- export const formatBooleanError = createBaseTypeErrorFormatter<BooleanError>();
791
+ export const formatBooleanError =
792
+ /*#__PURE__*/ createBaseTypeErrorFormatter<BooleanError>();
713
793
 
714
- /** @category Base Types */
715
- export const Undefined = base("Undefined", (value) =>
794
+ /** @group Base Types */
795
+ export const Undefined = /*#__PURE__*/ base("Undefined", (value) =>
716
796
  value === undefined
717
797
  ? ok(value)
718
798
  : err<UndefinedError>({ type: "Undefined", value }),
@@ -721,20 +801,21 @@ export const Undefined = base("Undefined", (value) =>
721
801
  export interface UndefinedError extends TypeError<"Undefined"> {}
722
802
 
723
803
  export const formatUndefinedError =
724
- createBaseTypeErrorFormatter<UndefinedError>();
804
+ /*#__PURE__*/ createBaseTypeErrorFormatter<UndefinedError>();
725
805
 
726
- /** @category Base Types */
727
- export const Null = base("Null", (value) =>
806
+ /** @group Base Types */
807
+ export const Null = /*#__PURE__*/ base("Null", (value) =>
728
808
  value === null ? ok(value) : err<NullError>({ type: "Null", value }),
729
809
  );
730
810
 
731
811
  export interface NullError extends TypeError<"Null"> {}
732
812
 
733
- export const formatNullError = createBaseTypeErrorFormatter<NullError>();
813
+ export const formatNullError =
814
+ /*#__PURE__*/ createBaseTypeErrorFormatter<NullError>();
734
815
 
735
- /** @category Base Types */
736
- export const Function = base("Function", (value) =>
737
- typeof value === "function"
816
+ /** @group Base Types */
817
+ export const Function = /*#__PURE__*/ base("Function", (value) =>
818
+ isFunction(value)
738
819
  ? ok(value)
739
820
  : err<FunctionError>({ type: "Function", value }),
740
821
  );
@@ -742,19 +823,31 @@ export const Function = base("Function", (value) =>
742
823
  export interface FunctionError extends TypeError<"Function"> {}
743
824
 
744
825
  export const formatFunctionError =
745
- createBaseTypeErrorFormatter<FunctionError>();
826
+ /*#__PURE__*/ createBaseTypeErrorFormatter<FunctionError>();
746
827
 
747
- /** @category Base Types */
748
- export const Uint8Array = base("Uint8Array", (value) =>
749
- value instanceof globalThis.Uint8Array
750
- ? ok(value)
828
+ /** @group Base Types */
829
+ export const Uint8Array = /*#__PURE__*/ base("Uint8Array", (value) =>
830
+ Object.prototype.toString.call(value as object) === "[object Uint8Array]"
831
+ ? ok(value as globalThis.Uint8Array)
751
832
  : err<Uint8ArrayError>({ type: "Uint8Array", value }),
752
833
  );
753
834
 
754
835
  export interface Uint8ArrayError extends TypeError<"Uint8Array"> {}
755
836
 
756
837
  export const formatUint8ArrayError =
757
- createBaseTypeErrorFormatter<Uint8ArrayError>();
838
+ /*#__PURE__*/ createBaseTypeErrorFormatter<Uint8ArrayError>();
839
+
840
+ /** @group Base Types */
841
+ export const ArrayBuffer = /*#__PURE__*/ base("ArrayBuffer", (value) =>
842
+ Object.prototype.toString.call(value as object) === "[object ArrayBuffer]"
843
+ ? ok(value as globalThis.ArrayBuffer)
844
+ : err<ArrayBufferError>({ type: "ArrayBuffer", value }),
845
+ );
846
+
847
+ export interface ArrayBufferError extends TypeError<"ArrayBuffer"> {}
848
+
849
+ export const formatArrayBufferError =
850
+ /*#__PURE__*/ createBaseTypeErrorFormatter<ArrayBufferError>();
758
851
 
759
852
  /**
760
853
  * `instanceof` {@link Type}.
@@ -774,7 +867,7 @@ export const formatUint8ArrayError =
774
867
  * const error = UserInstance.from({}); // err
775
868
  * ```
776
869
  *
777
- * @category Base Factories
870
+ * @group Base factories
778
871
  */
779
872
  export const instanceOf = <T extends abstract new (...args: any) => any>(
780
873
  ctor: T,
@@ -802,16 +895,17 @@ export interface InstanceOfType<
802
895
  ctor: T;
803
896
  }
804
897
 
805
- export const formatInstanceOfError = createTypeErrorFormatter<InstanceOfError>(
806
- (error) => `The value ${error.value} is not an instance of ${error.ctor}.`,
807
- );
898
+ export const formatInstanceOfError =
899
+ /*#__PURE__*/ createTypeErrorFormatter<InstanceOfError>(
900
+ (error) => `The value ${error.value} is not an instance of ${error.ctor}.`,
901
+ );
808
902
 
809
903
  /**
810
904
  * JavaScript Date.
811
905
  *
812
- * @category Base Types
906
+ * @group Base Types
813
907
  */
814
- export const Date = instanceOf(globalThis.Date);
908
+ export const Date = /*#__PURE__*/ instanceOf(globalThis.Date);
815
909
 
816
910
  /**
817
911
  * Validates that an unknown value is an Evolu {@link Type} (i.e., satisfies
@@ -825,7 +919,7 @@ export const Date = instanceOf(globalThis.Date);
825
919
  * ```
826
920
  */
827
921
  // TODO: Rename to TypeInstance or something like that.
828
- export const EvoluType = base("EvoluType", (value) =>
922
+ export const EvoluType = /*#__PURE__*/ base("EvoluType", (value) =>
829
923
  isType(value)
830
924
  ? ok(value)
831
925
  : err<EvoluTypeError>({
@@ -836,19 +930,24 @@ export const EvoluType = base("EvoluType", (value) =>
836
930
 
837
931
  export interface EvoluTypeError extends TypeError<"EvoluType"> {}
838
932
 
839
- export const formatIsTypeError = createTypeErrorFormatter<EvoluTypeError>(
840
- (error) => `Value ${error.value} is not a valid Evolu Type.`,
841
- );
933
+ export const formatIsTypeError =
934
+ /*#__PURE__*/ createTypeErrorFormatter<EvoluTypeError>(
935
+ (error) => `Value ${error.value} is not a valid Evolu Type.`,
936
+ );
842
937
 
843
938
  /**
844
939
  * Branded {@link Type}.
845
940
  *
941
+ * Branding is the recommended way to define types in Evolu. Instead of using
942
+ * primitive types like `string` or `number` directly, wrap them with `brand` to
943
+ * create semantically meaningful types. See {@link Brand} for why this matters.
944
+ *
846
945
  * The `brand` Type Factory takes the name of a new {@link Brand}, a parent Type
847
946
  * to be branded, and the optional `refine` function for additional constraint.
848
947
  *
849
948
  * The `refine` function can be omitted if we only want to add a brand.
850
949
  *
851
- * ### Examples
950
+ * ### Example
852
951
  *
853
952
  * A simple `CurrencyCode` Type:
854
953
  *
@@ -967,7 +1066,7 @@ export const formatIsTypeError = createTypeErrorFormatter<EvoluTypeError>(
967
1066
  * );
968
1067
  * ```
969
1068
  *
970
- * @category Base Factories
1069
+ * @group Base factories
971
1070
  */
972
1071
  export function brand<
973
1072
  Name extends TypeName,
@@ -980,6 +1079,7 @@ export function brand<
980
1079
  refine: (value: Parent) => Result<Parent, RefineError>,
981
1080
  ): BrandType<ParentType, Name, RefineError, InferErrors<ParentType>>;
982
1081
 
1082
+ /** Without refine function. */
983
1083
  export function brand<Name extends TypeName, ParentType extends AnyType>(
984
1084
  name: Name,
985
1085
  parent: ParentType,
@@ -1003,15 +1103,15 @@ export function brand<
1003
1103
  ? (value: unknown) => {
1004
1104
  const parentResult = parent.fromUnknown(value);
1005
1105
  if (!parentResult.ok) return parentResult;
1006
- return refine(parentResult.value as IntentionalNever);
1106
+ return refine(parentResult.value as never);
1007
1107
  }
1008
1108
  : (value: unknown) => {
1009
1109
  const parentResult = parent.fromUnknown(value);
1010
1110
  if (!parentResult.ok)
1011
- return err<BrandWithoutRefineError<Name, IntentionalNever>>({
1111
+ return err<BrandWithoutRefineError<Name, never>>({
1012
1112
  type: name,
1013
1113
  value,
1014
- parentError: parentResult.error as IntentionalNever,
1114
+ parentError: parentResult.error as never,
1015
1115
  });
1016
1116
  return ok(parentResult.value);
1017
1117
  };
@@ -1019,7 +1119,7 @@ export function brand<
1019
1119
  return {
1020
1120
  ...createType("Brand", {
1021
1121
  fromUnknown,
1022
- fromParent: (refine ?? ok) as IntentionalNever,
1122
+ fromParent: (refine ?? ok) as never,
1023
1123
  }),
1024
1124
  brand: name,
1025
1125
  parentType: parent,
@@ -1053,12 +1153,15 @@ export interface BrandWithoutRefineError<
1053
1153
  /**
1054
1154
  * A three-letter ISO 4217 currency code (e.g., USD, EUR).
1055
1155
  *
1056
- * @category String
1156
+ * @group String
1057
1157
  */
1058
- export const CurrencyCode = brand("CurrencyCode", String, (value) =>
1059
- /^[A-Z]{3}$/.test(value)
1060
- ? ok(value)
1061
- : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
1158
+ export const CurrencyCode = /*#__PURE__*/ brand(
1159
+ "CurrencyCode",
1160
+ String,
1161
+ (value) =>
1162
+ /^[A-Z]{3}$/.test(value)
1163
+ ? ok(value)
1164
+ : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
1062
1165
  );
1063
1166
 
1064
1167
  export type CurrencyCode = typeof CurrencyCode.Type;
@@ -1066,7 +1169,7 @@ export type CurrencyCode = typeof CurrencyCode.Type;
1066
1169
  export interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
1067
1170
 
1068
1171
  export const formatCurrencyCodeError =
1069
- createTypeErrorFormatter<CurrencyCodeError>(
1172
+ /*#__PURE__*/ createTypeErrorFormatter<CurrencyCodeError>(
1070
1173
  (error) => `Invalid currency code: ${error.value}.`,
1071
1174
  );
1072
1175
 
@@ -1089,9 +1192,9 @@ export const formatCurrencyCodeError =
1089
1192
  * const error = DateIso.from("10000-01-01T00:00:00.000Z"); // err
1090
1193
  * ```
1091
1194
  *
1092
- * @category String
1195
+ * @group String
1093
1196
  */
1094
- export const DateIso = brand("DateIso", String, (value) => {
1197
+ export const DateIso = /*#__PURE__*/ brand("DateIso", String, (value) => {
1095
1198
  if (value.length !== 24) {
1096
1199
  return err<DateIsoError>({ type: "DateIso", value });
1097
1200
  }
@@ -1111,9 +1214,10 @@ export type DateIso = typeof DateIso.Type;
1111
1214
 
1112
1215
  export interface DateIsoError extends TypeError<"DateIso"> {}
1113
1216
 
1114
- export const formatDateIsoError = createTypeErrorFormatter<DateIsoError>(
1115
- (error) => `The value ${error.value} is not a valid ISO 8601 date string.`,
1116
- );
1217
+ export const formatDateIsoError =
1218
+ /*#__PURE__*/ createTypeErrorFormatter<DateIsoError>(
1219
+ (error) => `The value ${error.value} is not a valid ISO 8601 date string.`,
1220
+ );
1117
1221
 
1118
1222
  export const dateToDateIso = (value: Date): Result<DateIso, DateIsoError> =>
1119
1223
  DateIso.fromParent(value.toISOString());
@@ -1137,7 +1241,18 @@ export const dateIsoToDate = (value: DateIso): Date =>
1137
1241
  * );
1138
1242
  * ```
1139
1243
  *
1140
- * @category Utilities
1244
+ * ### Numeric literal inference
1245
+ *
1246
+ * When using factories with numeric parameters (like `lessThan`, `maxLength`,
1247
+ * `between`), use numeric literals instead of expressions. TypeScript widens
1248
+ * expressions to `number`, losing the literal type in the brand name:
1249
+ *
1250
+ * ```ts
1251
+ * lessThan(100)(Number); // Brand<"LessThan100"> ✓
1252
+ * lessThan(100 - 1)(Number); // Brand<"LessThan" + number> ✗
1253
+ * ```
1254
+ *
1255
+ * @group Utilities
1141
1256
  */
1142
1257
  export type BrandFactory<
1143
1258
  Name extends TypeName,
@@ -1173,7 +1288,7 @@ export type BrandFactory<
1173
1288
  * type TrimmedNonEmptyString = typeof TrimmedNonEmptyString.Type;
1174
1289
  * ```
1175
1290
  *
1176
- * @category String
1291
+ * @group String
1177
1292
  */
1178
1293
  export const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
1179
1294
  parent,
@@ -1186,9 +1301,10 @@ export const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
1186
1301
 
1187
1302
  export interface TrimmedError extends TypeError<"Trimmed"> {}
1188
1303
 
1189
- export const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
1190
- (error) => `The value ${error.value} must be trimmed.`,
1191
- );
1304
+ export const formatTrimmedError =
1305
+ /*#__PURE__*/ createTypeErrorFormatter<TrimmedError>(
1306
+ (error) => `The value ${error.value} must be trimmed.`,
1307
+ );
1192
1308
 
1193
1309
  /**
1194
1310
  * Trimmed string
@@ -1196,9 +1312,9 @@ export const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
1196
1312
  * - Use `TrimmedString.is` to check if an unknown value is trimmed.
1197
1313
  * - Use `TrimmedString.from` to check if a string is trimmed.
1198
1314
  *
1199
- * @category String
1315
+ * @group String
1200
1316
  */
1201
- export const TrimmedString = trimmed(String);
1317
+ export const TrimmedString = /*#__PURE__*/ trimmed(String);
1202
1318
  export type TrimmedString = typeof TrimmedString.Type;
1203
1319
 
1204
1320
  export const trim = (value: string): TrimmedString =>
@@ -1207,6 +1323,8 @@ export const trim = (value: string): TrimmedString =>
1207
1323
  /**
1208
1324
  * Minimum length.
1209
1325
  *
1326
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1327
+ *
1210
1328
  * ### Example
1211
1329
  *
1212
1330
  * ```ts
@@ -1214,8 +1332,8 @@ export const trim = (value: string): TrimmedString =>
1214
1332
  * const NonEmptyString = minLength(1)(String);
1215
1333
  * ```
1216
1334
  *
1217
- * @category String
1218
- * @category Array
1335
+ * @group String
1336
+ * @group Array
1219
1337
  */
1220
1338
  export const minLength: <Min extends number>(
1221
1339
  min: Min,
@@ -1231,14 +1349,17 @@ export interface MinLengthError<
1231
1349
  readonly min: Min;
1232
1350
  }
1233
1351
 
1234
- export const formatMinLengthError = createTypeErrorFormatter<MinLengthError>(
1235
- (error) =>
1236
- `The value ${error.value} does not meet the minimum length of ${error.min}.`,
1237
- );
1352
+ export const formatMinLengthError =
1353
+ /*#__PURE__*/ createTypeErrorFormatter<MinLengthError>(
1354
+ (error) =>
1355
+ `The value ${error.value} does not meet the minimum length of ${error.min}.`,
1356
+ );
1238
1357
 
1239
1358
  /**
1240
1359
  * Maximum length.
1241
1360
  *
1361
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1362
+ *
1242
1363
  * ### Example
1243
1364
  *
1244
1365
  * ```ts
@@ -1246,8 +1367,8 @@ export const formatMinLengthError = createTypeErrorFormatter<MinLengthError>(
1246
1367
  * const String100 = maxLength(100)(String);
1247
1368
  * ```
1248
1369
  *
1249
- * @category String
1250
- * @category Array
1370
+ * @group String
1371
+ * @group Array
1251
1372
  */
1252
1373
  export const maxLength: <Max extends number>(
1253
1374
  max: Max,
@@ -1263,14 +1384,17 @@ export interface MaxLengthError<
1263
1384
  readonly max: Max;
1264
1385
  }
1265
1386
 
1266
- export const formatMaxLengthError = createTypeErrorFormatter<MaxLengthError>(
1267
- (error) =>
1268
- `The value ${error.value} exceeds the maximum length of ${error.max}.`,
1269
- );
1387
+ export const formatMaxLengthError =
1388
+ /*#__PURE__*/ createTypeErrorFormatter<MaxLengthError>(
1389
+ (error) =>
1390
+ `The value ${error.value} exceeds the maximum length of ${error.max}.`,
1391
+ );
1270
1392
 
1271
1393
  /**
1272
1394
  * Exact length.
1273
1395
  *
1396
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1397
+ *
1274
1398
  * ### Example
1275
1399
  *
1276
1400
  * ```ts
@@ -1278,8 +1402,8 @@ export const formatMaxLengthError = createTypeErrorFormatter<MaxLengthError>(
1278
1402
  * const Length1String = length(1)(String);
1279
1403
  * ```
1280
1404
  *
1281
- * @category String
1282
- * @category Array
1405
+ * @group String
1406
+ * @group Array
1283
1407
  */
1284
1408
  export const length: <Exact extends number>(
1285
1409
  exact: Exact,
@@ -1297,49 +1421,52 @@ export interface LengthError<
1297
1421
  readonly exact: Exact;
1298
1422
  }
1299
1423
 
1300
- export const formatLengthError = createTypeErrorFormatter<LengthError>(
1301
- (error) =>
1302
- `The value ${error.value} does not have the required length of ${error.exact}.`,
1303
- );
1424
+ export const formatLengthError =
1425
+ /*#__PURE__*/ createTypeErrorFormatter<LengthError>(
1426
+ (error) =>
1427
+ `The value ${error.value} does not have the required length of ${error.exact}.`,
1428
+ );
1304
1429
 
1305
- /** @category String */
1306
- export const NonEmptyString = minLength(1)(String);
1430
+ /** @group String */
1431
+ export const NonEmptyString = /*#__PURE__*/ minLength(1)(String);
1307
1432
  export type NonEmptyString = typeof NonEmptyString.Type;
1308
1433
 
1309
- /** @category String */
1310
- export const String100 = maxLength(100)(String);
1434
+ /** @group String */
1435
+ export const String100 = /*#__PURE__*/ maxLength(100)(String);
1311
1436
  export type String100 = typeof String100.Type;
1312
1437
 
1313
- /** @category String */
1314
- export const String1000 = maxLength(1000)(String);
1438
+ /** @group String */
1439
+ export const String1000 = /*#__PURE__*/ maxLength(1000)(String);
1315
1440
  export type String1000 = typeof String1000.Type;
1316
1441
 
1317
- /** @category String */
1318
- export const NonEmptyString100 = minLength(1)(String100);
1442
+ /** @group String */
1443
+ export const NonEmptyString100 = /*#__PURE__*/ minLength(1)(String100);
1319
1444
  export type NonEmptyString100 = typeof NonEmptyString100.Type;
1320
1445
 
1321
- /** @category String */
1322
- export const NonEmptyString1000 = minLength(1)(String1000);
1446
+ /** @group String */
1447
+ export const NonEmptyString1000 = /*#__PURE__*/ minLength(1)(String1000);
1323
1448
  export type NonEmptyString1000 = typeof NonEmptyString1000.Type;
1324
1449
 
1325
- /** @category String */
1326
- export const NonEmptyTrimmedString = minLength(1)(TrimmedString);
1450
+ /** @group String */
1451
+ export const NonEmptyTrimmedString = /*#__PURE__*/ minLength(1)(TrimmedString);
1327
1452
  export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Type;
1328
1453
 
1329
- /** @category String */
1330
- export const TrimmedString100 = maxLength(100)(TrimmedString);
1454
+ /** @group String */
1455
+ export const TrimmedString100 = /*#__PURE__*/ maxLength(100)(TrimmedString);
1331
1456
  export type TrimmedString100 = typeof TrimmedString100.Type;
1332
1457
 
1333
- /** @category String */
1334
- export const TrimmedString1000 = maxLength(1000)(TrimmedString);
1458
+ /** @group String */
1459
+ export const TrimmedString1000 = /*#__PURE__*/ maxLength(1000)(TrimmedString);
1335
1460
  export type TrimmedString1000 = typeof TrimmedString1000.Type;
1336
1461
 
1337
- /** @category String */
1338
- export const NonEmptyTrimmedString100 = minLength(1)(TrimmedString100);
1462
+ /** @group String */
1463
+ export const NonEmptyTrimmedString100 =
1464
+ /*#__PURE__*/ minLength(1)(TrimmedString100);
1339
1465
  export type NonEmptyTrimmedString100 = typeof NonEmptyTrimmedString100.Type;
1340
1466
 
1341
- /** @category String */
1342
- export const NonEmptyTrimmedString1000 = minLength(1)(TrimmedString1000);
1467
+ /** @group String */
1468
+ export const NonEmptyTrimmedString1000 =
1469
+ /*#__PURE__*/ minLength(1)(TrimmedString1000);
1343
1470
  export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Type;
1344
1471
 
1345
1472
  /**
@@ -1349,20 +1476,24 @@ export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Type;
1349
1476
  * safely on the user's device using cryptographically secure random number
1350
1477
  * generation, ensuring it remains private and unique.
1351
1478
  *
1352
- * @category String
1479
+ * @group String
1353
1480
  */
1354
- export const Mnemonic = brand("Mnemonic", NonEmptyTrimmedString, (value) =>
1355
- bip39.validateMnemonic(value, wordlist)
1356
- ? ok(value)
1357
- : err<MnemonicError>({ type: "Mnemonic", value }),
1481
+ export const Mnemonic = /*#__PURE__*/ brand(
1482
+ "Mnemonic",
1483
+ NonEmptyTrimmedString,
1484
+ (value) =>
1485
+ bip39.validateMnemonic(value, wordlist)
1486
+ ? ok(value)
1487
+ : err<MnemonicError>({ type: "Mnemonic", value }),
1358
1488
  );
1359
1489
  export type Mnemonic = typeof Mnemonic.Type;
1360
1490
 
1361
1491
  export interface MnemonicError extends TypeError<"Mnemonic"> {}
1362
1492
 
1363
- export const formatMnemonicError = createTypeErrorFormatter<MnemonicError>(
1364
- (error) => `Invalid BIP39 mnemonic: ${error.value}.`,
1365
- );
1493
+ export const formatMnemonicError =
1494
+ /*#__PURE__*/ createTypeErrorFormatter<MnemonicError>(
1495
+ (error) => `Invalid BIP39 mnemonic: ${error.value}.`,
1496
+ );
1366
1497
 
1367
1498
  /**
1368
1499
  * String matching a regular expression.
@@ -1373,7 +1504,7 @@ export const formatMnemonicError = createTypeErrorFormatter<MnemonicError>(
1373
1504
  * const Alphanumeric = regex("Alphanumeric", /^[a-z0-9]+$/i)(String);
1374
1505
  * ```
1375
1506
  *
1376
- * @category String
1507
+ * @group String
1377
1508
  */
1378
1509
  export const regex: <Name extends TypeName>(
1379
1510
  name: Name,
@@ -1399,10 +1530,11 @@ export interface RegexError<
1399
1530
  readonly pattern: RegExp;
1400
1531
  }
1401
1532
 
1402
- export const formatRegexError = createTypeErrorFormatter<RegexError>(
1403
- (error) =>
1404
- `The value ${error.value} does not match the pattern for ${error.name}: ${error.pattern}.`,
1405
- );
1533
+ export const formatRegexError =
1534
+ /*#__PURE__*/ createTypeErrorFormatter<RegexError>(
1535
+ (error) =>
1536
+ `The value ${error.value} does not match the pattern for ${error.name}: ${error.pattern}.`,
1537
+ );
1406
1538
 
1407
1539
  /**
1408
1540
  * URL-safe string.
@@ -1429,9 +1561,12 @@ export const formatRegexError = createTypeErrorFormatter<RegexError>(
1429
1561
  * }
1430
1562
  * ```
1431
1563
  *
1432
- * @category String
1564
+ * @group String
1433
1565
  */
1434
- export const UrlSafeString = regex("UrlSafeString", /^[A-Za-z0-9_-]+$/)(String);
1566
+ export const UrlSafeString = /*#__PURE__*/ regex(
1567
+ "UrlSafeString",
1568
+ /^[A-Za-z0-9_-]+$/,
1569
+ )(String);
1435
1570
  export type UrlSafeString = typeof UrlSafeString.Type;
1436
1571
  export type UrlSafeStringError = typeof UrlSafeString.Error;
1437
1572
 
@@ -1441,9 +1576,9 @@ export type UrlSafeStringError = typeof UrlSafeString.Error;
1441
1576
  * Encode with {@link uint8ArrayToBase64Url}, decode with
1442
1577
  * {@link base64UrlToUint8Array}.
1443
1578
  *
1444
- * @category String
1579
+ * @group String
1445
1580
  */
1446
- export const Base64Url = brand(
1581
+ export const Base64Url = /*#__PURE__*/ brand(
1447
1582
  "Base64Url",
1448
1583
  String,
1449
1584
  (value: string): Result<string, Base64UrlError> => {
@@ -1466,9 +1601,10 @@ export const Base64Url = brand(
1466
1601
  export type Base64Url = typeof Base64Url.Type;
1467
1602
  export interface Base64UrlError extends TypeError<"Base64Url"> {}
1468
1603
 
1469
- export const formatBase64UrlError = createTypeErrorFormatter<Base64UrlError>(
1470
- (error) => `The value ${error.value} is not a valid Base64Url string.`,
1471
- );
1604
+ export const formatBase64UrlError =
1605
+ /*#__PURE__*/ createTypeErrorFormatter<Base64UrlError>(
1606
+ (error) => `The value ${error.value} is not a valid Base64Url string.`,
1607
+ );
1472
1608
 
1473
1609
  const base64UrlOptions = { alphabet: "base64url", omitPadding: true };
1474
1610
 
@@ -1520,33 +1656,35 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
1520
1656
  };
1521
1657
 
1522
1658
  /**
1523
- * Simple alphanumeric string for naming in file systems, URLs, and identifiers.
1659
+ * Alphanumeric string for naming in file systems, URLs, and identifiers.
1524
1660
  *
1525
1661
  * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
1526
- * `_`). See `UrlSafeString` for details.
1662
+ * `_`).
1527
1663
  *
1528
1664
  * The string must be between 1 and 64 characters.
1529
1665
  *
1530
1666
  * ### Example
1531
1667
  *
1532
1668
  * ```ts
1533
- * const result = SimpleName.from("data-report-123");
1669
+ * const result = Name.from("data-report-123");
1534
1670
  * if (result.ok) {
1535
- * console.log("Valid SimpleName string:", result.value);
1671
+ * console.log("Valid Name string:", result.value);
1536
1672
  * } else {
1537
- * console.error("Invalid SimpleName string:", result.error);
1673
+ * console.error("Invalid Name string:", result.error);
1538
1674
  * }
1539
1675
  * ```
1540
1676
  *
1541
- * @category String
1677
+ * @group String
1542
1678
  */
1543
- export const SimpleName = brand("SimpleName", UrlSafeString, (value) =>
1679
+ export const Name = /*#__PURE__*/ brand("Name", UrlSafeString, (value) =>
1544
1680
  value.length >= 1 && value.length <= 64
1545
1681
  ? ok(value)
1546
- : err<SimpleNameError>({ type: "SimpleName", value }),
1682
+ : err<NameError>({ type: "Name", value }),
1547
1683
  );
1548
- export type SimpleName = typeof SimpleName.Type;
1549
- export interface SimpleNameError extends TypeError<"SimpleName"> {}
1684
+ export type Name = typeof Name.Type;
1685
+ export interface NameError extends TypeError<"Name"> {}
1686
+
1687
+ export const testName = /*#__PURE__*/ Name.orThrow("Name");
1550
1688
 
1551
1689
  /**
1552
1690
  * Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
@@ -1570,11 +1708,11 @@ export interface SimpleNameError extends TypeError<"SimpleName"> {}
1570
1708
  * // |> brand("SimplePassword", %)
1571
1709
  * ```
1572
1710
  *
1573
- * @category String
1711
+ * @group String
1574
1712
  */
1575
- export const SimplePassword = brand(
1713
+ export const SimplePassword = /*#__PURE__*/ brand(
1576
1714
  "SimplePassword",
1577
- minLength(8)(maxLength(64)(TrimmedString)),
1715
+ /*#__PURE__*/ minLength(8)(/*#__PURE__*/ maxLength(64)(TrimmedString)),
1578
1716
  );
1579
1717
  export type SimplePassword = typeof SimplePassword.Type;
1580
1718
 
@@ -1605,16 +1743,16 @@ export const formatSimplePasswordError = (
1605
1743
  * copied (logs, URLs, exports); only use it when you explicitly want insertion
1606
1744
  * locality for very large write‑heavy tables and accept timestamp exposure.
1607
1745
  *
1608
- * ### Future
1746
+ * ## Future
1609
1747
  *
1610
1748
  * A possible hybrid masked‑time approach (`timestamp ^ H(cluster_id, timestamp
1611
1749
  *
1612
1750
  * > > N)`) could provide locality without exposing raw creation time. See
1613
1751
  * > > https://brooker.co.za/blog/2025/10/22/uuidv7.html
1614
1752
  *
1615
- * @category String
1753
+ * @group String
1616
1754
  */
1617
- export const Id = brand("Id", String, (value) =>
1755
+ export const Id = /*#__PURE__*/ brand("Id", String, (value) =>
1618
1756
  value.length === 22 && Base64Url.fromParent(value).ok
1619
1757
  ? ok(value)
1620
1758
  : err<IdError>({ type: "Id", value }),
@@ -1623,7 +1761,7 @@ export type Id = typeof Id.Type;
1623
1761
 
1624
1762
  export interface IdError extends TypeError<"Id"> {}
1625
1763
 
1626
- export const formatIdError = createTypeErrorFormatter<IdError>(
1764
+ export const formatIdError = /*#__PURE__*/ createTypeErrorFormatter<IdError>(
1627
1765
  (error) => `The value ${error.value} is not a valid Id.`,
1628
1766
  );
1629
1767
 
@@ -1678,7 +1816,7 @@ export const createId = <B extends string = never>(
1678
1816
  * external string from the generated {@link Id}. If you need to preserve the
1679
1817
  * original external ID, store it in a separate column.
1680
1818
  *
1681
- * @category String
1819
+ * @group String
1682
1820
  */
1683
1821
  export const createIdFromString = <B extends string = never>(
1684
1822
  value: string,
@@ -1723,7 +1861,9 @@ export const createIdAsUuidv7 = <B extends string = never>(
1723
1861
  id[6] = (id[6] & 0x0f) | 0x70;
1724
1862
  id[8] = (id[8] & 0x3f) | 0x80;
1725
1863
 
1726
- return id as unknown as [B] extends [never] ? Id : Id & Brand<B>;
1864
+ return uint8ArrayToBase64Url(id) as unknown as [B] extends [never]
1865
+ ? Id
1866
+ : Id & Brand<B>;
1727
1867
  };
1728
1868
 
1729
1869
  /**
@@ -1739,7 +1879,7 @@ export const createIdAsUuidv7 = <B extends string = never>(
1739
1879
  * type TodoId = typeof TodoId.Type;
1740
1880
  * ```
1741
1881
  *
1742
- * @category String
1882
+ * @group String
1743
1883
  */
1744
1884
  export const id = <Table extends TypeName>(table: Table): TableId<Table> => {
1745
1885
  const fromUnknown = (value: unknown) => {
@@ -1779,12 +1919,16 @@ export interface TableIdError<
1779
1919
  readonly table: Table;
1780
1920
  }
1781
1921
 
1782
- export const formatTableIdError = createTypeErrorFormatter<TableIdError>(
1783
- (error) => `Invalid Id for table ${error.table}: ${error.value}.`,
1784
- );
1922
+ export const formatTableIdError =
1923
+ /*#__PURE__*/ createTypeErrorFormatter<TableIdError>(
1924
+ (error) => `Invalid Id for table ${error.table}: ${error.value}.`,
1925
+ );
1785
1926
 
1786
1927
  /** Binary representation of an {@link Id}. */
1787
- export const IdBytes = brand("IdBytes", length(16)(Uint8Array));
1928
+ export const IdBytes = /*#__PURE__*/ brand(
1929
+ "IdBytes",
1930
+ /*#__PURE__*/ length(16)(Uint8Array),
1931
+ );
1788
1932
  export type IdBytes = typeof IdBytes.Type;
1789
1933
 
1790
1934
  export const idBytesTypeValueLength = 16 as NonNegativeInt;
@@ -1809,7 +1953,7 @@ export const idBytesToId = (idBytes: IdBytes): Id =>
1809
1953
  * const errorResult = PositiveNumber.from(-5); // err
1810
1954
  * ```
1811
1955
  *
1812
- * @category Number
1956
+ * @group Number
1813
1957
  */
1814
1958
  export const positive: BrandFactory<"Positive", number, PositiveError> = (
1815
1959
  parent,
@@ -1820,9 +1964,10 @@ export const positive: BrandFactory<"Positive", number, PositiveError> = (
1820
1964
 
1821
1965
  export interface PositiveError extends TypeError<"Positive"> {}
1822
1966
 
1823
- export const formatPositiveError = createTypeErrorFormatter<PositiveError>(
1824
- (error) => `The value ${error.value} must be positive (> 0).`,
1825
- );
1967
+ export const formatPositiveError =
1968
+ /*#__PURE__*/ createTypeErrorFormatter<PositiveError>(
1969
+ (error) => `The value ${error.value} must be positive (> 0).`,
1970
+ );
1826
1971
 
1827
1972
  /**
1828
1973
  * Negative number (< 0).
@@ -1833,7 +1978,7 @@ export const formatPositiveError = createTypeErrorFormatter<PositiveError>(
1833
1978
  * const NegativeNumber = negative(Number);
1834
1979
  * ```
1835
1980
  *
1836
- * @category Number
1981
+ * @group Number
1837
1982
  */
1838
1983
  export const negative: BrandFactory<"Negative", number, NegativeError> = (
1839
1984
  parent,
@@ -1844,9 +1989,10 @@ export const negative: BrandFactory<"Negative", number, NegativeError> = (
1844
1989
 
1845
1990
  export interface NegativeError extends TypeError<"Negative"> {}
1846
1991
 
1847
- export const formatNegativeError = createTypeErrorFormatter<NegativeError>(
1848
- (error) => `The value ${error.value} must be negative (< 0).`,
1849
- );
1992
+ export const formatNegativeError =
1993
+ /*#__PURE__*/ createTypeErrorFormatter<NegativeError>(
1994
+ (error) => `The value ${error.value} must be negative (< 0).`,
1995
+ );
1850
1996
 
1851
1997
  /**
1852
1998
  * Non-positive number (≤ 0).
@@ -1857,7 +2003,7 @@ export const formatNegativeError = createTypeErrorFormatter<NegativeError>(
1857
2003
  * const NonPositiveNumber = nonPositive(Number);
1858
2004
  * ```
1859
2005
  *
1860
- * @category Number
2006
+ * @group Number
1861
2007
  */
1862
2008
  export const nonPositive: BrandFactory<
1863
2009
  "NonPositive",
@@ -1873,7 +2019,7 @@ export const nonPositive: BrandFactory<
1873
2019
  export interface NonPositiveError extends TypeError<"NonPositive"> {}
1874
2020
 
1875
2021
  export const formatNonPositiveError =
1876
- createTypeErrorFormatter<NonPositiveError>(
2022
+ /*#__PURE__*/ createTypeErrorFormatter<NonPositiveError>(
1877
2023
  (error) => `The value ${error.value} must be non-positive (≤ 0).`,
1878
2024
  );
1879
2025
 
@@ -1886,7 +2032,7 @@ export const formatNonPositiveError =
1886
2032
  * const NonNegativeNumber = nonNegative(Number);
1887
2033
  * ```
1888
2034
  *
1889
- * @category Number
2035
+ * @group Number
1890
2036
  */
1891
2037
  export const nonNegative: BrandFactory<
1892
2038
  "NonNegative",
@@ -1902,40 +2048,40 @@ export const nonNegative: BrandFactory<
1902
2048
  export interface NonNegativeError extends TypeError<"NonNegative"> {}
1903
2049
 
1904
2050
  export const formatNonNegativeError =
1905
- createTypeErrorFormatter<NonNegativeError>(
2051
+ /*#__PURE__*/ createTypeErrorFormatter<NonNegativeError>(
1906
2052
  (error) => `The value ${error.value} must be non-negative (≥ 0).`,
1907
2053
  );
1908
2054
 
1909
2055
  /**
1910
2056
  * Non-negative number (≥ 0).
1911
2057
  *
1912
- * @category Number
2058
+ * @group Number
1913
2059
  */
1914
- export const NonNegativeNumber = nonNegative(Number);
2060
+ export const NonNegativeNumber = /*#__PURE__*/ nonNegative(Number);
1915
2061
  export type NonNegativeNumber = typeof NonNegativeNumber.Type;
1916
2062
 
1917
2063
  /**
1918
2064
  * Positive number (> 0).
1919
2065
  *
1920
- * @category Number
2066
+ * @group Number
1921
2067
  */
1922
- export const PositiveNumber = positive(NonNegativeNumber);
2068
+ export const PositiveNumber = /*#__PURE__*/ positive(NonNegativeNumber);
1923
2069
  export type PositiveNumber = typeof PositiveNumber.Type;
1924
2070
 
1925
2071
  /**
1926
2072
  * Non-positive number (≤ 0).
1927
2073
  *
1928
- * @category Number
2074
+ * @group Number
1929
2075
  */
1930
- export const NonPositiveNumber = nonPositive(Number);
2076
+ export const NonPositiveNumber = /*#__PURE__*/ nonPositive(Number);
1931
2077
  export type NonPositiveNumber = typeof NonPositiveNumber.Type;
1932
2078
 
1933
2079
  /**
1934
2080
  * Negative number (< 0).
1935
2081
  *
1936
- * @category Number
2082
+ * @group Number
1937
2083
  */
1938
- export const NegativeNumber = negative(NonPositiveNumber);
2084
+ export const NegativeNumber = /*#__PURE__*/ negative(NonPositiveNumber);
1939
2085
  export type NegativeNumber = typeof NegativeNumber.Type;
1940
2086
 
1941
2087
  /**
@@ -1944,12 +2090,17 @@ export type NegativeNumber = typeof NegativeNumber.Type;
1944
2090
  * ### Example
1945
2091
  *
1946
2092
  * ```ts
1947
- * const Int = int(Number);
2093
+ * const Int = safeInt(Number);
1948
2094
  * ```
1949
2095
  *
1950
- * @category Number
2096
+ * Note: This helper was previously named `int`. A Next.js regression produced
2097
+ * runtime `int is not defined`, so we keep the implementation under the
2098
+ * `safeInt` alias.
2099
+ *
2100
+ * @group Number
1951
2101
  */
1952
- export const int: BrandFactory<"Int", number, IntError> = (parent) =>
2102
+
2103
+ const safeInt: BrandFactory<"Int", number, IntError> = (parent) =>
1953
2104
  brand("Int", parent, (value) =>
1954
2105
  globalThis.Number.isSafeInteger(value)
1955
2106
  ? ok(value)
@@ -1958,59 +2109,67 @@ export const int: BrandFactory<"Int", number, IntError> = (parent) =>
1958
2109
 
1959
2110
  export interface IntError extends TypeError<"Int"> {}
1960
2111
 
1961
- export const formatIntError = createTypeErrorFormatter<IntError>(
2112
+ export const formatIntError = /*#__PURE__*/ createTypeErrorFormatter<IntError>(
1962
2113
  (error) => `The value ${error.value} must be an integer.`,
1963
2114
  );
1964
2115
 
1965
2116
  /**
1966
2117
  * Integer within the safe range of JavaScript numbers.
1967
2118
  *
1968
- * @category Number
2119
+ * @group Number
1969
2120
  */
1970
- export const Int = int(Number);
2121
+ export const Int = /*#__PURE__*/ safeInt(Number);
1971
2122
  export type Int = typeof Int.Type;
1972
2123
 
1973
2124
  /**
1974
2125
  * Non-negative integer (≥ 0).
1975
2126
  *
1976
- * @category Number
2127
+ * @group Number
1977
2128
  */
1978
- export const NonNegativeInt = nonNegative(Int);
2129
+ export const NonNegativeInt = /*#__PURE__*/ nonNegative(Int);
1979
2130
  export type NonNegativeInt = typeof NonNegativeInt.Type;
1980
2131
 
2132
+ /** Minimum {@link NonNegativeInt} value (0). */
2133
+ export const zeroNonNegativeInt = /*#__PURE__*/ NonNegativeInt.orThrow(0);
2134
+
1981
2135
  /**
1982
2136
  * Positive integer (> 0).
1983
2137
  *
1984
- * @category Number
2138
+ * @group Number
1985
2139
  */
1986
- export const PositiveInt = positive(NonNegativeInt);
2140
+ export const PositiveInt = /*#__PURE__*/ positive(NonNegativeInt);
1987
2141
  export type PositiveInt = typeof PositiveInt.Type;
1988
2142
 
1989
- /** Maximum safe positive integer value for practically infinite operations. */
1990
- export const maxPositiveInt = PositiveInt.orThrow(
2143
+ /** {@link PositiveInt} value 1. */
2144
+ export const onePositiveInt = /*#__PURE__*/ PositiveInt.orThrow(1);
2145
+
2146
+ /** Maximum {@link PositiveInt} value (MAX_SAFE_INTEGER). */
2147
+ export const maxPositiveInt = /*#__PURE__*/ PositiveInt.orThrow(
1991
2148
  globalThis.Number.MAX_SAFE_INTEGER,
1992
2149
  );
1993
2150
 
1994
2151
  /**
1995
2152
  * Non-positive integer (≤ 0).
1996
2153
  *
1997
- * @category Number
2154
+ * @group Number
1998
2155
  */
1999
- export const NonPositiveInt = nonPositive(Int);
2156
+ export const NonPositiveInt = /*#__PURE__*/ nonPositive(Int);
2000
2157
  export type NonPositiveInt = typeof NonPositiveInt.Type;
2001
2158
 
2002
2159
  /**
2003
2160
  * Negative integer (< 0).
2004
2161
  *
2005
- * @category Number
2162
+ * @group Number
2006
2163
  */
2007
- export const NegativeInt = negative(NonPositiveInt);
2164
+ export const NegativeInt = /*#__PURE__*/ negative(NonPositiveInt);
2008
2165
  export type NegativeInt = typeof NegativeInt.Type;
2009
2166
 
2010
2167
  /**
2011
2168
  * Number greater than a specified value.
2012
2169
  *
2013
- * @category Number
2170
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2171
+ *
2172
+ * @group Number
2014
2173
  */
2015
2174
  export const greaterThan: <Min extends number>(
2016
2175
  min: Min,
@@ -2027,14 +2186,16 @@ export interface GreaterThanError<
2027
2186
  }
2028
2187
 
2029
2188
  export const formatGreaterThanError =
2030
- createTypeErrorFormatter<GreaterThanError>(
2189
+ /*#__PURE__*/ createTypeErrorFormatter<GreaterThanError>(
2031
2190
  (error) => `The value ${error.value} is not > ${error.min}.`,
2032
2191
  );
2033
2192
 
2034
2193
  /**
2035
2194
  * Number less than a specified value.
2036
2195
  *
2037
- * @category Number
2196
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2197
+ *
2198
+ * @group Number
2038
2199
  */
2039
2200
  export const lessThan: <Max extends number>(
2040
2201
  max: Max,
@@ -2050,14 +2211,17 @@ export interface LessThanError<
2050
2211
  readonly max: Max;
2051
2212
  }
2052
2213
 
2053
- export const formatLessThanError = createTypeErrorFormatter<LessThanError>(
2054
- (error) => `The value ${error.value} is not < ${error.max}.`,
2055
- );
2214
+ export const formatLessThanError =
2215
+ /*#__PURE__*/ createTypeErrorFormatter<LessThanError>(
2216
+ (error) => `The value ${error.value} is not < ${error.max}.`,
2217
+ );
2056
2218
 
2057
2219
  /**
2058
2220
  * Number ≥ a specified value.
2059
2221
  *
2060
- * @category Number
2222
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2223
+ *
2224
+ * @group Number
2061
2225
  */
2062
2226
  export const greaterThanOrEqualTo: <Min extends number>(
2063
2227
  min: Min,
@@ -2079,14 +2243,16 @@ export interface GreaterThanOrEqualToError<
2079
2243
  }
2080
2244
 
2081
2245
  export const formatGreaterThanOrEqualToError =
2082
- createTypeErrorFormatter<GreaterThanOrEqualToError>(
2246
+ /*#__PURE__*/ createTypeErrorFormatter<GreaterThanOrEqualToError>(
2083
2247
  (error) => `The value ${error.value} is not >= ${error.min}.`,
2084
2248
  );
2085
2249
 
2086
2250
  /**
2087
2251
  * Number ≤ a specified value.
2088
2252
  *
2089
- * @category Number
2253
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2254
+ *
2255
+ * @group Number
2090
2256
  */
2091
2257
  export const lessThanOrEqualTo: <Max extends number>(
2092
2258
  max: Max,
@@ -2106,14 +2272,14 @@ export interface LessThanOrEqualToError<
2106
2272
  }
2107
2273
 
2108
2274
  export const formatLessThanOrEqualToError =
2109
- createTypeErrorFormatter<LessThanOrEqualToError>(
2275
+ /*#__PURE__*/ createTypeErrorFormatter<LessThanOrEqualToError>(
2110
2276
  (error) => `The value ${error.value} is not <= ${error.max}.`,
2111
2277
  );
2112
2278
 
2113
2279
  /**
2114
2280
  * Number that is not NaN.
2115
2281
  *
2116
- * @category Number
2282
+ * @group Number
2117
2283
  */
2118
2284
  export const nonNaN: BrandFactory<"NonNaN", number, NonNaNError> = (parent) =>
2119
2285
  brand("NonNaN", parent, (value) =>
@@ -2124,18 +2290,19 @@ export const nonNaN: BrandFactory<"NonNaN", number, NonNaNError> = (parent) =>
2124
2290
 
2125
2291
  export interface NonNaNError extends TypeError<"NonNaN"> {}
2126
2292
 
2127
- export const formatNonNaNError = createTypeErrorFormatter<NonNaNError>(
2128
- () => `The value must not be NaN.`,
2129
- );
2293
+ export const formatNonNaNError =
2294
+ /*#__PURE__*/ createTypeErrorFormatter<NonNaNError>(
2295
+ () => `The value must not be NaN.`,
2296
+ );
2130
2297
 
2131
- /** @category Number */
2132
- export const NonNaNNumber = nonNaN(Number);
2298
+ /** @group Number */
2299
+ export const NonNaNNumber = /*#__PURE__*/ nonNaN(Number);
2133
2300
  export type NonNaNNumber = typeof NonNaNNumber.Type;
2134
2301
 
2135
2302
  /**
2136
2303
  * Finite number.
2137
2304
  *
2138
- * @category Number
2305
+ * @group Number
2139
2306
  */
2140
2307
  export const finite: BrandFactory<"Finite", number, FiniteError> = (parent) =>
2141
2308
  brand("Finite", parent, (value) =>
@@ -2146,9 +2313,10 @@ export const finite: BrandFactory<"Finite", number, FiniteError> = (parent) =>
2146
2313
 
2147
2314
  export interface FiniteError extends TypeError<"Finite"> {}
2148
2315
 
2149
- export const formatFiniteError = createTypeErrorFormatter<FiniteError>(
2150
- (error) => `The value ${error.value} must be finite.`,
2151
- );
2316
+ export const formatFiniteError =
2317
+ /*#__PURE__*/ createTypeErrorFormatter<FiniteError>(
2318
+ (error) => `The value ${error.value} must be finite.`,
2319
+ );
2152
2320
 
2153
2321
  /**
2154
2322
  * Finite number.
@@ -2161,15 +2329,17 @@ export const formatFiniteError = createTypeErrorFormatter<FiniteError>(
2161
2329
  * finite (e.g., `Infinity`, `-Infinity`, or `NaN`). Using `FiniteNumber` helps
2162
2330
  * prevent these unexpected behaviors when working with JSON serialization.
2163
2331
  *
2164
- * @category Number
2332
+ * @group Number
2165
2333
  */
2166
- export const FiniteNumber = finite(Number);
2334
+ export const FiniteNumber = /*#__PURE__*/ finite(Number);
2167
2335
  export type FiniteNumber = typeof FiniteNumber.Type;
2168
2336
 
2169
2337
  /**
2170
2338
  * Number that is a multiple of a divisor.
2171
2339
  *
2172
- * @category Number
2340
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2341
+ *
2342
+ * @group Number
2173
2343
  */
2174
2344
  export const multipleOf: <Divisor extends number>(
2175
2345
  divisor: Divisor,
@@ -2187,13 +2357,17 @@ export interface MultipleOfError<
2187
2357
  readonly divisor: Divisor;
2188
2358
  }
2189
2359
 
2190
- export const formatMultipleOfError = createTypeErrorFormatter<MultipleOfError>(
2191
- (error) => `The value ${error.value} is not a multiple of ${error.divisor}.`,
2192
- );
2360
+ export const formatMultipleOfError =
2361
+ /*#__PURE__*/ createTypeErrorFormatter<MultipleOfError>(
2362
+ (error) =>
2363
+ `The value ${error.value} is not a multiple of ${error.divisor}.`,
2364
+ );
2193
2365
 
2194
2366
  /**
2195
2367
  * Number within a range, inclusive.
2196
2368
  *
2369
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2370
+ *
2197
2371
  * ### Example
2198
2372
  *
2199
2373
  * ```ts
@@ -2202,7 +2376,7 @@ export const formatMultipleOfError = createTypeErrorFormatter<MultipleOfError>(
2202
2376
  * const errorResult = Between1And10.from(11); // err
2203
2377
  * ```
2204
2378
  *
2205
- * @category Number
2379
+ * @group Number
2206
2380
  */
2207
2381
  export const between: <Min extends number, Max extends number>(
2208
2382
  min: Min,
@@ -2223,10 +2397,11 @@ export interface BetweenError<
2223
2397
  readonly max: Max;
2224
2398
  }
2225
2399
 
2226
- export const formatBetweenError = createTypeErrorFormatter<BetweenError>(
2227
- (error) =>
2228
- `The value ${error.value} is not between ${error.min} and ${error.max}, inclusive.`,
2229
- );
2400
+ export const formatBetweenError =
2401
+ /*#__PURE__*/ createTypeErrorFormatter<BetweenError>(
2402
+ (error) =>
2403
+ `The value ${error.value} is not between ${error.min} and ${error.max}, inclusive.`,
2404
+ );
2230
2405
 
2231
2406
  /**
2232
2407
  * Literal {@link Type}.
@@ -2243,7 +2418,7 @@ export const formatBetweenError = createTypeErrorFormatter<BetweenError>(
2243
2418
  *
2244
2419
  * TODO: Add JsonValue
2245
2420
  *
2246
- * @category Base Factories
2421
+ * @group Base factories
2247
2422
  */
2248
2423
  export const literal = <T extends Literal>(expected: T): LiteralType<T> => {
2249
2424
  const fromUnknown = (value: unknown): Result<T, LiteralError<T>> =>
@@ -2275,17 +2450,18 @@ export interface LiteralError<
2275
2450
  readonly expected: T;
2276
2451
  }
2277
2452
 
2278
- export const formatLiteralError = createTypeErrorFormatter<LiteralError>(
2279
- (error) =>
2280
- `The value ${error.value} is not strictly equal to the expected literal: ${globalThis.String(
2281
- error.expected,
2282
- )}.`,
2283
- );
2453
+ export const formatLiteralError =
2454
+ /*#__PURE__*/ createTypeErrorFormatter<LiteralError>(
2455
+ (error) =>
2456
+ `The value ${error.value} is not strictly equal to the expected literal: ${globalThis.String(
2457
+ error.expected,
2458
+ )}.`,
2459
+ );
2284
2460
 
2285
2461
  /**
2286
2462
  * Array of a specific {@link Type}.
2287
2463
  *
2288
- * Usage:
2464
+ * ### Example
2289
2465
  *
2290
2466
  * ```ts
2291
2467
  * const NumberArray = array(Number);
@@ -2294,8 +2470,8 @@ export const formatLiteralError = createTypeErrorFormatter<LiteralError>(
2294
2470
  * const result2 = NumberArray.from(["a", "b"]); // err(...)
2295
2471
  * ```
2296
2472
  *
2297
- * @category Base Factories
2298
- * @category Array
2473
+ * @group Base factories
2474
+ * @group Array
2299
2475
  */
2300
2476
  export const array = <ElementType extends AnyType>(
2301
2477
  element: ElementType,
@@ -2413,7 +2589,7 @@ export const formatArrayError = <Error extends TypeError>(
2413
2589
  * const result2 = NumberSet.from(new Set(["a", "b"])); // err(...)
2414
2590
  * ```
2415
2591
  *
2416
- * @category Base Factories
2592
+ * @group Base factories
2417
2593
  */
2418
2594
  export const set = <ElementType extends AnyType>(
2419
2595
  element: ElementType,
@@ -2543,8 +2719,8 @@ export const formatSetError = <Error extends TypeError>(
2543
2719
  * StringToNumberRecord.from({ a: "x", b: 2 });
2544
2720
  * ```
2545
2721
  *
2546
- * @category Base Factories
2547
- * @category Object
2722
+ * @group Base factories
2723
+ * @group Object
2548
2724
  */
2549
2725
  export const record = <
2550
2726
  KeyName extends TypeName,
@@ -2591,7 +2767,7 @@ export const record = <
2591
2767
  type: "Record",
2592
2768
  value,
2593
2769
  reason: { kind: "Key", key: rawKey, error: keyResult.error },
2594
- } as IntentionalNever);
2770
+ } as never);
2595
2771
  }
2596
2772
 
2597
2773
  const valueResult = valueType.fromUnknown(rawValue);
@@ -2602,12 +2778,12 @@ export const record = <
2602
2778
  reason: {
2603
2779
  kind: "Value",
2604
2780
  key: rawKey,
2605
- error: valueResult.error as IntentionalNever,
2781
+ error: valueResult.error as never,
2606
2782
  },
2607
2783
  });
2608
2784
  }
2609
2785
 
2610
- result[keyResult.value] = valueResult.value as IntentionalNever;
2786
+ result[keyResult.value] = valueResult.value as never;
2611
2787
  }
2612
2788
 
2613
2789
  return ok(result);
@@ -2664,7 +2840,7 @@ export const record = <
2664
2840
  }),
2665
2841
  key: keyType,
2666
2842
  value: valueType,
2667
- } as IntentionalNever;
2843
+ } as never;
2668
2844
  };
2669
2845
 
2670
2846
  /**
@@ -2748,22 +2924,22 @@ export const formatRecordError = <Error extends TypeError>(
2748
2924
  *
2749
2925
  * https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures
2750
2926
  *
2751
- * ### Examples
2927
+ * ### Example
2752
2928
  *
2753
- * #### Basic Object Validation
2929
+ * ## Basic Object Validation
2754
2930
  *
2755
2931
  * ```ts
2756
2932
  * const User = object({
2757
2933
  * name: NonEmptyTrimmedString,
2758
2934
  * age: PositiveNumber,
2759
2935
  * });
2760
- * type User = typeof User.Type;
2936
+ * interface User extends InferType<typeof User> {}
2761
2937
  *
2762
2938
  * const result = User.from({ name: "John", age: 30 }); // ok({ name: "John", age: 30 })
2763
2939
  * const error = User.from({ name: "John", age: -5 }); // err
2764
2940
  * ```
2765
2941
  *
2766
- * #### Optional Properties
2942
+ * ## Optional Properties
2767
2943
  *
2768
2944
  * In this example the `age` property is marked as optional using
2769
2945
  * {@link optional}.
@@ -2773,10 +2949,10 @@ export const formatRecordError = <Error extends TypeError>(
2773
2949
  * name: NonEmptyString, // Required
2774
2950
  * age: optional(PositiveNumber), // Optional
2775
2951
  * });
2776
- * type User = typeof User.Type;
2952
+ * interface User extends InferType<typeof User> {}
2777
2953
  * ```
2778
2954
  *
2779
- * #### Allowing Additional Properties
2955
+ * ## Allowing Additional Properties
2780
2956
  *
2781
2957
  * ```ts
2782
2958
  * const UserWithAnyExtraProperties = object(
@@ -2795,7 +2971,7 @@ export const formatRecordError = <Error extends TypeError>(
2795
2971
  * });
2796
2972
  * ```
2797
2973
  *
2798
- * #### Combining Fixed and Flexible Properties
2974
+ * ## Combining Fixed and Flexible Properties
2799
2975
  *
2800
2976
  * ```ts
2801
2977
  * const NumberDictionary = object(
@@ -2832,13 +3008,14 @@ export const formatRecordError = <Error extends TypeError>(
2832
3008
  * );
2833
3009
  * ```
2834
3010
  *
2835
- * @category Base Factories
2836
- * @category Object
3011
+ * @group Base factories
3012
+ * @group Object
2837
3013
  */
2838
3014
  export function object<Props extends Record<string, AnyType>>(
2839
3015
  props: Props,
2840
3016
  ): ObjectType<Props>;
2841
3017
 
3018
+ /** With additional record properties. */
2842
3019
  export function object<
2843
3020
  Props extends Record<string, AnyType>,
2844
3021
  KeyName extends TypeName,
@@ -2901,9 +3078,9 @@ export function object(
2901
3078
 
2902
3079
  const propResult = props[key].fromUnknown(value[key]);
2903
3080
  if (!propResult.ok) {
2904
- errors[key] = propResult.error as IntentionalNever;
3081
+ errors[key] = propResult.error as never;
2905
3082
  } else {
2906
- result[key] = propResult.value as IntentionalNever;
3083
+ result[key] = propResult.value as never;
2907
3084
  }
2908
3085
  }
2909
3086
 
@@ -2921,7 +3098,7 @@ export function object(
2921
3098
  reason: {
2922
3099
  kind: "IndexKey",
2923
3100
  key,
2924
- error: keyResult.error as IntentionalNever,
3101
+ error: keyResult.error as never,
2925
3102
  },
2926
3103
  });
2927
3104
  }
@@ -2934,12 +3111,12 @@ export function object(
2934
3111
  reason: {
2935
3112
  kind: "IndexValue",
2936
3113
  key,
2937
- error: valueResult.error as IntentionalNever,
3114
+ error: valueResult.error as never,
2938
3115
  },
2939
3116
  });
2940
3117
  }
2941
3118
 
2942
- result[keyResult.value] = valueResult.value as IntentionalNever;
3119
+ result[keyResult.value] = valueResult.value as never;
2943
3120
  }
2944
3121
  } else if (extraKeys.length > 0) {
2945
3122
  return err({
@@ -3092,7 +3269,7 @@ export interface ObjectError<
3092
3269
  * Merge Error and ParentError into one ObjectError so tooltips and error
3093
3270
  * messages are easier to read.
3094
3271
  *
3095
- * @category Utilities
3272
+ * @group Utilities
3096
3273
  */
3097
3274
  export type MergeObjectTypeErrors<T extends ObjectType<any>> =
3098
3275
  T extends ObjectType<infer Props>
@@ -3202,6 +3379,187 @@ export const formatObjectWithRecordError = <Error extends TypeError>(
3202
3379
  }
3203
3380
  });
3204
3381
 
3382
+ /**
3383
+ * Base interface for objects with a discriminant `type` property.
3384
+ *
3385
+ * This enables
3386
+ * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions | discriminated unions}
3387
+ * (also known as tagged unions) — a pattern where TypeScript uses a literal
3388
+ * `type` field to narrow union types automatically.
3389
+ *
3390
+ * ## Why Discriminated Unions?
3391
+ *
3392
+ * Discriminated unions model states that are **mutually exclusive**. Instead of
3393
+ * optional fields and boolean flags that can combine into invalid
3394
+ * configurations, each variant is a distinct type. This makes illegal states
3395
+ * unrepresentable — invalid combinations cannot exist, so bugs cannot create
3396
+ * them.
3397
+ *
3398
+ * Benefits:
3399
+ *
3400
+ * - **Self-documenting** — Union cases immediately show all possible states
3401
+ * - **Compile-time safety** — TypeScript enforces handling all cases
3402
+ * - **Refactoring-friendly** — Adding a new state breaks code that doesn't handle
3403
+ * it
3404
+ *
3405
+ * ### Example
3406
+ *
3407
+ * ```ts
3408
+ * // Bad: optional fields allow invalid states (no contact info at all)
3409
+ * interface Contact {
3410
+ * readonly email?: Email;
3411
+ * readonly phone?: Phone;
3412
+ * }
3413
+ *
3414
+ * // Good: discriminated union makes "at least one" explicit
3415
+ * interface EmailOnly extends Typed<"EmailOnly"> {
3416
+ * readonly email: Email;
3417
+ * }
3418
+ * interface PhoneOnly extends Typed<"PhoneOnly"> {
3419
+ * readonly phone: Phone;
3420
+ * }
3421
+ * interface EmailAndPhone extends Typed<"EmailAndPhone"> {
3422
+ * readonly email: Email;
3423
+ * readonly phone: Phone;
3424
+ * }
3425
+ *
3426
+ * type ContactInfo = EmailOnly | PhoneOnly | EmailAndPhone;
3427
+ * ```
3428
+ *
3429
+ * ```ts
3430
+ * interface Pending extends Typed<"Pending"> {
3431
+ * readonly createdAt: DateIso;
3432
+ * }
3433
+ * interface Shipped extends Typed<"Shipped"> {
3434
+ * readonly trackingNumber: TrackingNumber;
3435
+ * }
3436
+ * interface Delivered extends Typed<"Delivered"> {
3437
+ * readonly deliveredAt: DateIso;
3438
+ * }
3439
+ * interface Cancelled extends Typed<"Cancelled"> {
3440
+ * readonly reason: CancellationReason;
3441
+ * }
3442
+ *
3443
+ * type OrderState = Pending | Shipped | Delivered | Cancelled;
3444
+ *
3445
+ * // TypeScript enforces exhaustiveness via return type
3446
+ * const getStatusMessage = (state: OrderState): string => {
3447
+ * switch (state.type) {
3448
+ * case "Pending":
3449
+ * return "Order placed";
3450
+ * case "Shipped":
3451
+ * return `Shipped: ${state.trackingNumber}`;
3452
+ * case "Delivered":
3453
+ * return `Delivered on ${state.deliveredAt.toLocaleDateString()}`;
3454
+ * case "Cancelled":
3455
+ * return `Cancelled: ${state.reason}`;
3456
+ * }
3457
+ * };
3458
+ *
3459
+ * // For void functions, use exhaustiveCheck to ensure all cases are handled
3460
+ * const logState = (state: OrderState): void => {
3461
+ * switch (state.type) {
3462
+ * case "Pending":
3463
+ * console.log("Order placed");
3464
+ * break;
3465
+ * case "Shipped":
3466
+ * console.log(`Shipped: ${state.trackingNumber}`);
3467
+ * break;
3468
+ * case "Delivered":
3469
+ * console.log(
3470
+ * `Delivered on ${state.deliveredAt.toLocaleDateString()}`,
3471
+ * );
3472
+ * break;
3473
+ * case "Cancelled":
3474
+ * console.log(`Cancelled: ${state.reason}`);
3475
+ * break;
3476
+ * default:
3477
+ * exhaustiveCheck(state);
3478
+ * }
3479
+ * };
3480
+ * ```
3481
+ *
3482
+ * ## Why `type` (and not e.g. `_tag`)?
3483
+ *
3484
+ * Underscore-prefixing is meant to avoid clashing with domain properties, but
3485
+ * proper discriminated union design means the discriminant IS the domain
3486
+ * concept — there's no clash to avoid. The `type` prop name also aligns with
3487
+ * {@link Type}'s name. If an entity has a meaningful "type" (like product
3488
+ * category), model it as the discriminant itself:
3489
+ *
3490
+ * ```ts
3491
+ * interface Electronics extends Typed<"Electronics"> {
3492
+ * voltage: Voltage;
3493
+ * }
3494
+ * interface Clothing extends Typed<"Clothing"> {
3495
+ * size: Size;
3496
+ * }
3497
+ * type Product = Electronics | Clothing;
3498
+ * ```
3499
+ *
3500
+ * @see {@link exhaustiveCheck} to ensure all cases are handled in void functions.
3501
+ * @see {@link typed} for runtime-validated typed objects.
3502
+ */
3503
+ export interface Typed<T extends TypeName> {
3504
+ readonly type: T;
3505
+ }
3506
+
3507
+ /**
3508
+ * Creates a runtime-validated typed object with a `type` discriminant.
3509
+ *
3510
+ * ### Example
3511
+ *
3512
+ * ```ts
3513
+ * const Card = typed("Card", {
3514
+ * cardNumber: CardNumber,
3515
+ * expiry: DateIso,
3516
+ * });
3517
+ *
3518
+ * const Cash = typed("Cash", {
3519
+ * currency: NonEmptyTrimmedString,
3520
+ * });
3521
+ *
3522
+ * const Payment = union(Card, Cash);
3523
+ * type Payment = typeof Payment.Type;
3524
+ *
3525
+ * const result = Payment.fromUnknown(data);
3526
+ * if (result.ok) {
3527
+ * switch (result.value.type) {
3528
+ * case "Card":
3529
+ * console.log(result.value.cardNumber);
3530
+ * break;
3531
+ * case "Cash":
3532
+ * console.log(result.value.currency);
3533
+ * break;
3534
+ * }
3535
+ * }
3536
+ * ```
3537
+ *
3538
+ * @see {@link Typed} for type-only discrimination.
3539
+ */
3540
+ export function typed<Tag extends TypeName>(tag: Tag): TypedType<Tag>;
3541
+ /** With additional properties. */
3542
+ export function typed<
3543
+ Tag extends TypeName,
3544
+ Props extends Record<string, AnyType>,
3545
+ >(tag: Tag, props: Props): TypedType<Tag, Props>;
3546
+ export function typed<
3547
+ Tag extends TypeName,
3548
+ Props extends Record<string, AnyType>,
3549
+ >(tag: Tag, props?: Props): ObjectType<{ type: LiteralType<Tag> } & Props> {
3550
+ return object({ type: literal(tag), ...props } as {
3551
+ type: LiteralType<Tag>;
3552
+ } & Props);
3553
+ }
3554
+
3555
+ /** Return type of {@link typed}. */
3556
+ export type TypedType<
3557
+ Tag extends TypeName,
3558
+ Props extends Record<string, AnyType> = Record<never, never>,
3559
+ > = ObjectType<{ type: LiteralType<Tag> } & Props>;
3560
+
3561
+ // TODO: TypeError with "Error" suffix.
3562
+
3205
3563
  /**
3206
3564
  * Union {@link Type}.
3207
3565
  *
@@ -3224,12 +3582,13 @@ export const formatObjectWithRecordError = <Error extends TypeError>(
3224
3582
  * const result3 = StringOrNumber.from(42); // ok(42)
3225
3583
  * ```
3226
3584
  *
3227
- * @category Base Factories
3585
+ * @group Base factories
3228
3586
  */
3229
3587
  export function union<
3230
3588
  Members extends [AnyType, AnyType, ...ReadonlyArray<AnyType>],
3231
3589
  >(...members: Members): UnionType<Members>;
3232
3590
 
3591
+ /** With literal values. */
3233
3592
  export function union<
3234
3593
  Literals extends [Literal, Literal, ...ReadonlyArray<Literal>],
3235
3594
  >(
@@ -3319,6 +3678,110 @@ export const isUnionType = (
3319
3678
  ): t is UnionType<[AnyType, ...ReadonlyArray<AnyType>]> =>
3320
3679
  t.name === "Union" && Array.isArray((t as { members?: unknown }).members);
3321
3680
 
3681
+ /**
3682
+ * Creates a {@link Type} for {@link Result} values.
3683
+ *
3684
+ * Use for validating serialized Results from storage, APIs, or message passing.
3685
+ *
3686
+ * ### Example
3687
+ *
3688
+ * ```ts
3689
+ * const SyncResponse = result(
3690
+ * object({ timestamp: NonNegativeInt }),
3691
+ * typed("SyncError", { message: String }),
3692
+ * );
3693
+ *
3694
+ * // Validate response from worker or API
3695
+ * const validated = SyncResponse.from(JSON.parse(message));
3696
+ * if (!validated.ok) return validated; // validation error
3697
+ * // validated.value is Result<{ timestamp }, SyncError>
3698
+ * ```
3699
+ *
3700
+ * @group Composite factories
3701
+ */
3702
+ export const result = <OkType extends AnyType, ErrType extends AnyType>(
3703
+ okType: OkType,
3704
+ errType: ErrType,
3705
+ ): UnionType<
3706
+ [
3707
+ ObjectType<{ ok: LiteralType<true>; value: OkType }>,
3708
+ ObjectType<{ ok: LiteralType<false>; error: ErrType }>,
3709
+ ]
3710
+ > =>
3711
+ union(
3712
+ object({ ok: literal(true), value: okType }),
3713
+ object({ ok: literal(false), error: errType }),
3714
+ );
3715
+
3716
+ /**
3717
+ * A {@link result} type for `Result<unknown, unknown>`.
3718
+ *
3719
+ * Useful for serializing Results where the value and error types are unknown.
3720
+ *
3721
+ * @group Composite factories
3722
+ */
3723
+ export const UnknownResult = /*#__PURE__*/ result(Unknown, Unknown);
3724
+ export type UnknownResult = typeof UnknownResult.Type;
3725
+
3726
+ /**
3727
+ * Creates a {@link Type} for {@link NextResult} with three outcomes.
3728
+ *
3729
+ * Validates results where the producer responds with:
3730
+ *
3731
+ * - `Ok<A>` — produced a value
3732
+ * - `Err<Done<D>>` — completed normally with a done value
3733
+ * - `Err<E>` — failed with an error
3734
+ *
3735
+ * ### Example
3736
+ *
3737
+ * ```ts
3738
+ * const MyNextResult = nextResult(Item, MyError, Summary);
3739
+ *
3740
+ * const validated = MyNextResult.fromUnknown(data);
3741
+ * if (!validated.ok) return validated;
3742
+ *
3743
+ * const result = validated.value;
3744
+ * if (result.ok) {
3745
+ * console.log(result.value);
3746
+ * } else if (result.error.type === "Done") {
3747
+ * console.log("Done:", result.error.done);
3748
+ * } else {
3749
+ * console.error(result.error);
3750
+ * }
3751
+ * ```
3752
+ *
3753
+ * @group Composite factories
3754
+ */
3755
+ export const nextResult = <
3756
+ ValueType extends AnyType,
3757
+ ErrorType extends AnyType,
3758
+ DoneType extends AnyType,
3759
+ >(
3760
+ valueType: ValueType,
3761
+ errorType: ErrorType,
3762
+ doneType: DoneType,
3763
+ ): ReturnType<
3764
+ typeof result<
3765
+ ValueType,
3766
+ UnionType<[ErrorType, TypedType<"Done", { done: DoneType }>]>
3767
+ >
3768
+ > => result(valueType, union(errorType, typed("Done", { done: doneType })));
3769
+
3770
+ /**
3771
+ * A {@link nextResult} type for `NextResult<unknown, unknown, unknown>`.
3772
+ *
3773
+ * Useful for checking if a value is a {@link NextResult} via
3774
+ * `UnknownNextResult.is(value)`.
3775
+ *
3776
+ * @group Composite factories
3777
+ */
3778
+ export const UnknownNextResult = /*#__PURE__*/ nextResult(
3779
+ Unknown,
3780
+ Unknown,
3781
+ Unknown,
3782
+ );
3783
+ export type UnknownNextResult = typeof UnknownNextResult.Type;
3784
+
3322
3785
  /**
3323
3786
  * Recursive {@link Type}.
3324
3787
  *
@@ -3352,7 +3815,7 @@ export const isUnionType = (
3352
3815
  * );
3353
3816
  * ```
3354
3817
  *
3355
- * @category Base Factories
3818
+ * @group Base factories
3356
3819
  */
3357
3820
  export const recursive = <ParentType extends AnyType>(
3358
3821
  create: () => ParentType,
@@ -3412,7 +3875,7 @@ export interface RecursiveType<ParentType extends AnyType> extends Type<
3412
3875
  * NullOrString.from(42); // err(...)
3413
3876
  * ```
3414
3877
  *
3415
- * @category Base Factories
3878
+ * @group Base factories
3416
3879
  */
3417
3880
  export const nullOr = <T extends AnyType>(
3418
3881
  type: T,
@@ -3430,7 +3893,7 @@ export const nullOr = <T extends AnyType>(
3430
3893
  * UndefinedOrString.from(42); // err(...)
3431
3894
  * ```
3432
3895
  *
3433
- * @category Base Factories
3896
+ * @group Base factories
3434
3897
  */
3435
3898
  export const undefinedOr = <T extends AnyType>(
3436
3899
  type: T,
@@ -3452,7 +3915,7 @@ export const undefinedOr = <T extends AnyType>(
3452
3915
  * NullishOrString.from(42); // err(...)
3453
3916
  * ```
3454
3917
  *
3455
- * @category Base Factories
3918
+ * @group Base factories
3456
3919
  */
3457
3920
  export const nullishOr = <T extends AnyType>(
3458
3921
  type: T,
@@ -3473,7 +3936,7 @@ export const nullishOr = <T extends AnyType>(
3473
3936
  * const error = NameAndAge.from(["Alice", -10]); // err
3474
3937
  * ```
3475
3938
  *
3476
- * @category Base Factories
3939
+ * @group Base factories
3477
3940
  */
3478
3941
  export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3479
3942
  ...elements: Elements
@@ -3503,7 +3966,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3503
3966
  reason: {
3504
3967
  kind: "Element",
3505
3968
  index: i,
3506
- error: elementResult.error as IntentionalNever,
3969
+ error: elementResult.error as never,
3507
3970
  },
3508
3971
  });
3509
3972
  }
@@ -3534,7 +3997,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3534
3997
  reason: {
3535
3998
  kind: "Element",
3536
3999
  index: i,
3537
- error: elementResult.error as IntentionalNever,
4000
+ error: elementResult.error as never,
3538
4001
  },
3539
4002
  });
3540
4003
  }
@@ -3550,7 +4013,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3550
4013
  fromParent,
3551
4014
  }),
3552
4015
  elements,
3553
- } as IntentionalNever;
4016
+ } as never;
3554
4017
  };
3555
4018
 
3556
4019
  /**
@@ -3609,9 +4072,9 @@ export const formatTupleError = <Error extends TypeError>(
3609
4072
  *
3610
4073
  * https://www.sqlite.org/c3ref/int64.html
3611
4074
  *
3612
- * @category Number
4075
+ * @group Number
3613
4076
  */
3614
- export const Int64 = brand("Int64", BigInt, (value) =>
4077
+ export const Int64 = /*#__PURE__*/ brand("Int64", BigInt, (value) =>
3615
4078
  value >= -9223372036854775808n && value <= 9223372036854775807n
3616
4079
  ? ok(value)
3617
4080
  : err<Int64Error>({ type: "Int64", value }),
@@ -3619,25 +4082,29 @@ export const Int64 = brand("Int64", BigInt, (value) =>
3619
4082
  export type Int64 = typeof Int64.Type;
3620
4083
  export interface Int64Error extends TypeError<"Int64"> {}
3621
4084
 
3622
- export const formatInt64Error = createTypeErrorFormatter<Int64Error>(
3623
- (error) =>
3624
- `The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
3625
- );
4085
+ export const formatInt64Error =
4086
+ /*#__PURE__*/ createTypeErrorFormatter<Int64Error>(
4087
+ (error) =>
4088
+ `The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
4089
+ );
3626
4090
 
3627
4091
  /**
3628
4092
  * Stringified {@link Int64}.
3629
4093
  *
3630
- * @category String
4094
+ * @group String
3631
4095
  */
3632
- export const Int64String = brand("Int64", NonEmptyTrimmedString, (value) =>
3633
- trySync(
3634
- () => {
3635
- const maybeInt = globalThis.BigInt(value);
3636
- Int64.orThrow(maybeInt);
3637
- return value;
3638
- },
3639
- (): Int64StringError => ({ type: "Int64String", value }),
3640
- ),
4096
+ export const Int64String = /*#__PURE__*/ brand(
4097
+ "Int64",
4098
+ NonEmptyTrimmedString,
4099
+ (value) =>
4100
+ trySync(
4101
+ () => {
4102
+ const maybeInt = globalThis.BigInt(value);
4103
+ Int64.orThrow(maybeInt);
4104
+ return value;
4105
+ },
4106
+ (): Int64StringError => ({ type: "Int64String", value }),
4107
+ ),
3641
4108
  );
3642
4109
 
3643
4110
  export type Int64String = typeof Int64String.Type;
@@ -3645,10 +4112,20 @@ export type Int64String = typeof Int64String.Type;
3645
4112
  export interface Int64StringError extends TypeError<"Int64String"> {}
3646
4113
 
3647
4114
  export const formatInt64StringError =
3648
- createTypeErrorFormatter<Int64StringError>(
4115
+ /*#__PURE__*/ createTypeErrorFormatter<Int64StringError>(
3649
4116
  (error) => `The value ${error.value} is not a valid Int64 string.`,
3650
4117
  );
3651
4118
 
4119
+ /**
4120
+ * Validated JSON-compatible value.
4121
+ *
4122
+ * This is the output side of JSON data in Evolu. It uses {@link FiniteNumber}
4123
+ * instead of `number` because JSON numbers are expected to be finite once the
4124
+ * value has been parsed or validated.
4125
+ *
4126
+ * Compare with {@link JsonValueInput}, which represents caller-provided input
4127
+ * before validation.
4128
+ */
3652
4129
  export type JsonValue =
3653
4130
  | string
3654
4131
  | FiniteNumber
@@ -3657,6 +4134,19 @@ export type JsonValue =
3657
4134
  | JsonArray
3658
4135
  | JsonObject;
3659
4136
 
4137
+ /**
4138
+ * JSON-compatible input value before validation.
4139
+ *
4140
+ * This is broader than {@link JsonValue} because inputs arrive as ordinary
4141
+ * JavaScript values, so numbers are typed as `number` before validation can
4142
+ * narrow them to {@link FiniteNumber}.
4143
+ *
4144
+ * That means `JsonValueInput` can temporarily contain numbers that are lossy in
4145
+ * JSON serialization. For example, `JSON.stringify(NaN)` and
4146
+ * `JSON.stringify(Infinity)` produce `null`, and `JSON.stringify(-0)` produces
4147
+ * `0`. Use {@link JsonValue} when the value must already satisfy JSON numeric
4148
+ * constraints.
4149
+ */
3660
4150
  export type JsonValueInput =
3661
4151
  | string
3662
4152
  | number
@@ -3690,9 +4180,9 @@ export type JsonArrayInput = ReadonlyArray<JsonValueInput>;
3690
4180
  * JSON-compatible value: string, {@link FiniteNumber}, boolean, null,
3691
4181
  * {@link JsonArray}, or {@link JsonObject}.
3692
4182
  *
3693
- * @category Base Types
4183
+ * @group Base Types
3694
4184
  */
3695
- export const JsonValue = recursive(
4185
+ export const JsonValue = /*#__PURE__*/ recursive(
3696
4186
  (): UnionType<
3697
4187
  [
3698
4188
  typeof String,
@@ -3732,16 +4222,16 @@ export const JsonValue = recursive(
3732
4222
  /**
3733
4223
  * JSON-compatible array of {@link JsonValue} elements.
3734
4224
  *
3735
- * @category Array
4225
+ * @group Array
3736
4226
  */
3737
- export const JsonArray = array(JsonValue);
4227
+ export const JsonArray = /*#__PURE__*/ array(JsonValue);
3738
4228
 
3739
4229
  /**
3740
4230
  * JSON-compatible object with string keys and {@link JsonValue} values.
3741
4231
  *
3742
- * @category Object
4232
+ * @group Object
3743
4233
  */
3744
- export const JsonObject = record(String, JsonValue);
4234
+ export const JsonObject = /*#__PURE__*/ record(String, JsonValue);
3745
4235
 
3746
4236
  export const parseJson = (value: string): Result<JsonValue, JsonError> =>
3747
4237
  trySync(
@@ -3763,9 +4253,9 @@ export const parseJson = (value: string): Result<JsonValue, JsonError> =>
3763
4253
  * const error = Json.from("invalid json"); // err
3764
4254
  * ```
3765
4255
  *
3766
- * @category String
4256
+ * @group String
3767
4257
  */
3768
- export const Json = brand("Json", String, (value) => {
4258
+ export const Json = /*#__PURE__*/ brand("Json", String, (value) => {
3769
4259
  const result = parseJson(value);
3770
4260
  if (!result.ok) return result;
3771
4261
  return ok(value);
@@ -3777,9 +4267,10 @@ export interface JsonError extends TypeError<"Json"> {
3777
4267
  readonly message: string;
3778
4268
  }
3779
4269
 
3780
- export const formatJsonError = createTypeErrorFormatter<JsonError>(
3781
- (error) => `Invalid JSON: ${error.value}. Error: ${error.message}`,
3782
- );
4270
+ export const formatJsonError =
4271
+ /*#__PURE__*/ createTypeErrorFormatter<JsonError>(
4272
+ (error) => `Invalid JSON: ${error.value}. Error: ${error.message}`,
4273
+ );
3783
4274
 
3784
4275
  export const jsonValueToJson = (value: JsonValue): Json =>
3785
4276
  JSON.stringify(value) as Json;
@@ -3807,7 +4298,7 @@ export const jsonToJsonValue = (value: Json): JsonValue =>
3807
4298
  * name: NonEmptyString100,
3808
4299
  * age: FiniteNumber,
3809
4300
  * });
3810
- * type Person = typeof Person.Type;
4301
+ * interface Person extends InferType<typeof Person> {}
3811
4302
  *
3812
4303
  * const [PersonJson, personToPersonJson, personJsonToPerson] = json(
3813
4304
  * Person,
@@ -3848,11 +4339,7 @@ export const json = <T extends AnyType, Name extends TypeName>(
3848
4339
  return ok(value);
3849
4340
  }) as BrandType<typeof String, Name, JsonError | InferErrors<T>, StringError>;
3850
4341
 
3851
- return [
3852
- BrandedJsonType,
3853
- jsonValueToJson as IntentionalNever,
3854
- jsonToJsonValue as IntentionalNever,
3855
- ];
4342
+ return [BrandedJsonType, jsonValueToJson as never, jsonToJsonValue as never];
3856
4343
  };
3857
4344
 
3858
4345
  /**
@@ -3924,7 +4411,7 @@ export const isOptionalType = (x: unknown): x is OptionalType<any> =>
3924
4411
  * PartialUser.from({ age: -5 });
3925
4412
  * ```
3926
4413
  *
3927
- * @category Object
4414
+ * @group Object
3928
4415
  */
3929
4416
  export const partial = <Props extends Record<string, AnyType>>(
3930
4417
  props: Props,
@@ -3944,7 +4431,7 @@ export const partial = <Props extends Record<string, AnyType>>(
3944
4431
  * entirely, or set it to `null`, or set it to the non-null member of the
3945
4432
  * union.
3946
4433
  *
3947
- * @category Object
4434
+ * @group Object
3948
4435
  */
3949
4436
  export const nullableToOptional = <Props extends Record<string, AnyType>>(
3950
4437
  props: Props,
@@ -3990,12 +4477,12 @@ export type NullTypeInMembers<Members extends [AnyType, ...Array<AnyType>]> =
3990
4477
  /**
3991
4478
  * Create a new `object` {@link Type} by omitting some keys.
3992
4479
  *
3993
- * @category Object
4480
+ * @group Object
3994
4481
  */
3995
- export function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(
4482
+ export const omit = <T extends ObjectType<any>, Keys extends keyof T["props"]>(
3996
4483
  objectType: T,
3997
4484
  ...keys: ReadonlyArray<Keys>
3998
- ): ObjectType<Omit<T["props"], Keys>> {
4485
+ ): ObjectType<Omit<T["props"], Keys>> => {
3999
4486
  const newProps = {} as Omit<T["props"], Keys>;
4000
4487
 
4001
4488
  for (const key in objectType.props) {
@@ -4005,40 +4492,7 @@ export function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(
4005
4492
  }
4006
4493
  }
4007
4494
  return object(newProps);
4008
- }
4009
-
4010
- export const maxMutationSize = 655360;
4011
-
4012
- /**
4013
- * Evolu has to limit the maximum mutation size. Otherwise, sync couldn't use
4014
- * the `maxProtocolMessageRangesSize`. The max size is 640KB in bytes, measured
4015
- * via MessagePack. Evolu Protocol DbChange will be smaller thanks to various
4016
- * optimizations.
4017
- */
4018
- export const validMutationSize = <T extends AnyType>(
4019
- type: T,
4020
- ): BrandType<T, "ValidMutationSize", ValidMutationSizeError, InferErrors<T>> =>
4021
- brand("ValidMutationSize", type, (value) =>
4022
- pack(value).byteLength <= maxMutationSize
4023
- ? ok(value)
4024
- : err<ValidMutationSizeError>({ type: "ValidMutationSize", value }),
4025
- );
4026
-
4027
- export interface ValidMutationSizeError extends TypeError<"ValidMutationSize"> {}
4028
-
4029
- export const formatValidMutationSizeError =
4030
- createTypeErrorFormatter<ValidMutationSizeError>(
4031
- (error) =>
4032
- `The mutation size exceeds the maximum limit of ${maxMutationSize} bytes. The provided mutation has a size of ${pack(error.value).byteLength} bytes.`,
4033
- );
4034
-
4035
- export type ValidMutationSize<Props extends Record<string, AnyType>> =
4036
- BrandType<
4037
- ObjectType<Props>,
4038
- "ValidMutationSize",
4039
- ValidMutationSizeError,
4040
- InferErrors<ObjectType<Props>>
4041
- >;
4495
+ };
4042
4496
 
4043
4497
  /**
4044
4498
  * Union of all `TypeError`s defined in the `Type.ts` file, including base type
@@ -4051,7 +4505,7 @@ export type ValidMutationSize<Props extends Record<string, AnyType>> =
4051
4505
  * Used by {@link createFormatTypeError} to generate human-readable error
4052
4506
  * messages.
4053
4507
  *
4054
- * @category Utilities
4508
+ * @group Utilities
4055
4509
  */
4056
4510
  export type TypeErrors<ExtraErrors extends TypeError = never> =
4057
4511
  | StringError
@@ -4062,6 +4516,7 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
4062
4516
  | NullError
4063
4517
  | FunctionError
4064
4518
  | Uint8ArrayError
4519
+ | ArrayBufferError
4065
4520
  | InstanceOfError
4066
4521
  | EvoluTypeError
4067
4522
  | CurrencyCodeError
@@ -4092,7 +4547,6 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
4092
4547
  | Int64Error
4093
4548
  | Int64StringError
4094
4549
  | JsonError
4095
- | ValidMutationSizeError
4096
4550
  | ExtraErrors
4097
4551
  // Composite errors
4098
4552
  | ArrayError<TypeErrors<ExtraErrors>>
@@ -4185,7 +4639,7 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
4185
4639
  * };
4186
4640
  * ```
4187
4641
  *
4188
- * @category Utilities
4642
+ * @group Utilities
4189
4643
  */
4190
4644
  export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4191
4645
  extraFormatter?: TypeErrorFormatter<ExtraErrors>,
@@ -4216,6 +4670,8 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4216
4670
  return formatFunctionError(error);
4217
4671
  case "Uint8Array":
4218
4672
  return formatUint8ArrayError(error);
4673
+ case "ArrayBuffer":
4674
+ return formatArrayBufferError(error);
4219
4675
  case "InstanceOf":
4220
4676
  return formatInstanceOfError(error);
4221
4677
  case "EvoluType":
@@ -4274,8 +4730,6 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4274
4730
  return formatInt64StringError(error);
4275
4731
  case "Json":
4276
4732
  return formatJsonError(error);
4277
- case "ValidMutationSize":
4278
- return formatValidMutationSizeError(error);
4279
4733
  // Composite Types
4280
4734
  case "SimplePassword":
4281
4735
  return formatSimplePasswordError(formatTypeError)(error);
@@ -4308,25 +4762,25 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4308
4762
  * Converts an Evolu {@link TypeError} to Standard Schema V1 issues format.
4309
4763
  *
4310
4764
  * This function recursively converts Evolu's typed errors into the Standard
4311
- * Schema issue format with proper path tracking for nested structures.
4765
+ * Schema issue format with proper path tracking for nested structures. The
4766
+ * `message` field contains the JSON-serialized typed error, which users can
4767
+ * deserialize and format using appropriate {@link TypeErrorFormatter}s.
4312
4768
  *
4313
- * @category Utilities
4769
+ * @group Utilities
4314
4770
  */
4315
4771
  export const typeErrorToStandardSchemaIssues = <
4316
4772
  ExtraErrors extends TypeError = never,
4317
4773
  >(
4318
4774
  error: TypeErrors<ExtraErrors>,
4319
- formatTypeError: TypeErrorFormatter<TypeErrors<ExtraErrors>>,
4320
4775
  path: ReadonlyArray<PropertyKey> = [],
4321
4776
  ): ReadonlyArray<StandardSchemaV1.Issue> => {
4322
4777
  if (error.type === "Array") {
4323
4778
  const arrayError = error as ArrayError;
4324
4779
  if (arrayError.reason.kind === "NotArray") {
4325
- return [{ message: formatTypeError(error), path }];
4780
+ return [{ message: JSON.stringify(error), path }];
4326
4781
  }
4327
4782
  return typeErrorToStandardSchemaIssues(
4328
4783
  arrayError.reason.error as TypeErrors<ExtraErrors>,
4329
- formatTypeError,
4330
4784
  [...path, arrayError.reason.index],
4331
4785
  );
4332
4786
  }
@@ -4334,11 +4788,10 @@ export const typeErrorToStandardSchemaIssues = <
4334
4788
  if (error.type === "Set") {
4335
4789
  const setError = error as SetError;
4336
4790
  if (setError.reason.kind === "NotSet") {
4337
- return [{ message: formatTypeError(error), path }];
4791
+ return [{ message: JSON.stringify(error), path }];
4338
4792
  }
4339
4793
  return typeErrorToStandardSchemaIssues(
4340
4794
  setError.reason.error as TypeErrors<ExtraErrors>,
4341
- formatTypeError,
4342
4795
  [...path, setError.reason.index],
4343
4796
  );
4344
4797
  }
@@ -4349,14 +4802,13 @@ export const typeErrorToStandardSchemaIssues = <
4349
4802
  objectError.reason.kind === "NotObject" ||
4350
4803
  objectError.reason.kind === "ExtraKeys"
4351
4804
  ) {
4352
- return [{ message: formatTypeError(error), path }];
4805
+ return [{ message: JSON.stringify(error), path }];
4353
4806
  }
4354
4807
  const issues: Array<StandardSchemaV1.Issue> = [];
4355
4808
  for (const [key, propError] of Object.entries(objectError.reason.errors)) {
4356
4809
  issues.push(
4357
4810
  ...typeErrorToStandardSchemaIssues(
4358
4811
  propError as TypeErrors<ExtraErrors>,
4359
- formatTypeError,
4360
4812
  [...path, key],
4361
4813
  ),
4362
4814
  );
@@ -4367,7 +4819,7 @@ export const typeErrorToStandardSchemaIssues = <
4367
4819
  if (error.type === "ObjectWithRecord") {
4368
4820
  const objectWithRecordError = error as ObjectWithRecordError;
4369
4821
  if (objectWithRecordError.reason.kind === "NotObject") {
4370
- return [{ message: formatTypeError(error), path }];
4822
+ return [{ message: JSON.stringify(error), path }];
4371
4823
  }
4372
4824
  if (
4373
4825
  objectWithRecordError.reason.kind === "IndexKey" ||
@@ -4375,7 +4827,6 @@ export const typeErrorToStandardSchemaIssues = <
4375
4827
  ) {
4376
4828
  return typeErrorToStandardSchemaIssues(
4377
4829
  objectWithRecordError.reason.error as TypeErrors<ExtraErrors>,
4378
- formatTypeError,
4379
4830
  [...path, objectWithRecordError.reason.key as PropertyKey],
4380
4831
  );
4381
4832
  }
@@ -4386,7 +4837,6 @@ export const typeErrorToStandardSchemaIssues = <
4386
4837
  issues.push(
4387
4838
  ...typeErrorToStandardSchemaIssues(
4388
4839
  propError as TypeErrors<ExtraErrors>,
4389
- formatTypeError,
4390
4840
  [...path, key],
4391
4841
  ),
4392
4842
  );
@@ -4397,11 +4847,10 @@ export const typeErrorToStandardSchemaIssues = <
4397
4847
  if (error.type === "Record") {
4398
4848
  const recordError = error as RecordError;
4399
4849
  if (recordError.reason.kind === "NotRecord") {
4400
- return [{ message: formatTypeError(error), path }];
4850
+ return [{ message: JSON.stringify(error), path }];
4401
4851
  }
4402
4852
  return typeErrorToStandardSchemaIssues(
4403
4853
  recordError.reason.error as TypeErrors<ExtraErrors>,
4404
- formatTypeError,
4405
4854
  [...path, recordError.reason.key as PropertyKey],
4406
4855
  );
4407
4856
  }
@@ -4409,11 +4858,10 @@ export const typeErrorToStandardSchemaIssues = <
4409
4858
  if (error.type === "Tuple") {
4410
4859
  const tupleError = error as TupleError;
4411
4860
  if (tupleError.reason.kind === "InvalidLength") {
4412
- return [{ message: formatTypeError(error), path }];
4861
+ return [{ message: JSON.stringify(error), path }];
4413
4862
  }
4414
4863
  return typeErrorToStandardSchemaIssues(
4415
4864
  tupleError.reason.error as TypeErrors<ExtraErrors>,
4416
- formatTypeError,
4417
4865
  [...path, tupleError.reason.index],
4418
4866
  );
4419
4867
  }
@@ -4421,11 +4869,7 @@ export const typeErrorToStandardSchemaIssues = <
4421
4869
  if (error.type === "Union") {
4422
4870
  const unionError = error as UnionError;
4423
4871
  return unionError.errors.flatMap((err) =>
4424
- typeErrorToStandardSchemaIssues(
4425
- err as TypeErrors<ExtraErrors>,
4426
- formatTypeError,
4427
- path,
4428
- ),
4872
+ typeErrorToStandardSchemaIssues(err as TypeErrors<ExtraErrors>, path),
4429
4873
  );
4430
4874
  }
4431
4875
 
@@ -4434,14 +4878,13 @@ export const typeErrorToStandardSchemaIssues = <
4434
4878
  if ("parentError" in brandError) {
4435
4879
  return typeErrorToStandardSchemaIssues(
4436
4880
  brandError.parentError as TypeErrors<ExtraErrors>,
4437
- formatTypeError,
4438
4881
  path,
4439
4882
  );
4440
4883
  }
4441
- return [{ message: formatTypeError(error), path }];
4884
+ return [{ message: JSON.stringify(error), path }];
4442
4885
  }
4443
4886
 
4444
- return [{ message: formatTypeError(error), path }];
4887
+ return [{ message: JSON.stringify(error), path }];
4445
4888
  };
4446
4889
 
4447
4890
  /** The Standard Schema interface. */
@@ -4515,10 +4958,3 @@ export declare namespace StandardSchemaV1 {
4515
4958
  Schema["~standard"]["types"]
4516
4959
  >["output"];
4517
4960
  }
4518
-
4519
- /**
4520
- * Shared formatter cache for Standard Schema integration - avoids circular
4521
- * dependency by lazily creating the formatter on first use rather than during
4522
- * module initialization.
4523
- */
4524
- let cachedStandardSchemaFormatTypeError: TypeErrorFormatter<any> | undefined;