@evolu/common 7.4.0 → 8.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) 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 +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -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 +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -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 +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  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 +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -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 +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  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 +622 -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 +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  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 +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  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 +36 -26
  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 +415 -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 +465 -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 +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  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 +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  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 +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. package/src/local-first/Sync.ts +0 -960
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,19 @@ 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>();
758
839
 
759
840
  /**
760
841
  * `instanceof` {@link Type}.
@@ -774,7 +855,7 @@ export const formatUint8ArrayError =
774
855
  * const error = UserInstance.from({}); // err
775
856
  * ```
776
857
  *
777
- * @category Base Factories
858
+ * @group Base factories
778
859
  */
779
860
  export const instanceOf = <T extends abstract new (...args: any) => any>(
780
861
  ctor: T,
@@ -791,26 +872,28 @@ export interface InstanceOfError extends TypeError<"InstanceOf"> {
791
872
  readonly ctor: string;
792
873
  }
793
874
 
794
- export interface InstanceOfType<T extends abstract new (...args: any) => any>
795
- extends Type<
796
- "InstanceOf",
797
- InstanceType<T>,
798
- InstanceType<T>,
799
- InstanceOfError
800
- > {
875
+ export interface InstanceOfType<
876
+ T extends abstract new (...args: any) => any,
877
+ > extends Type<
878
+ "InstanceOf",
879
+ InstanceType<T>,
880
+ InstanceType<T>,
881
+ InstanceOfError
882
+ > {
801
883
  ctor: T;
802
884
  }
803
885
 
804
- export const formatInstanceOfError = createTypeErrorFormatter<InstanceOfError>(
805
- (error) => `The value ${error.value} is not an instance of ${error.ctor}.`,
806
- );
886
+ export const formatInstanceOfError =
887
+ /*#__PURE__*/ createTypeErrorFormatter<InstanceOfError>(
888
+ (error) => `The value ${error.value} is not an instance of ${error.ctor}.`,
889
+ );
807
890
 
808
891
  /**
809
892
  * JavaScript Date.
810
893
  *
811
- * @category Base Types
894
+ * @group Base Types
812
895
  */
813
- export const Date = instanceOf(globalThis.Date);
896
+ export const Date = /*#__PURE__*/ instanceOf(globalThis.Date);
814
897
 
815
898
  /**
816
899
  * Validates that an unknown value is an Evolu {@link Type} (i.e., satisfies
@@ -824,7 +907,7 @@ export const Date = instanceOf(globalThis.Date);
824
907
  * ```
825
908
  */
826
909
  // TODO: Rename to TypeInstance or something like that.
827
- export const EvoluType = base("EvoluType", (value) =>
910
+ export const EvoluType = /*#__PURE__*/ base("EvoluType", (value) =>
828
911
  isType(value)
829
912
  ? ok(value)
830
913
  : err<EvoluTypeError>({
@@ -835,19 +918,24 @@ export const EvoluType = base("EvoluType", (value) =>
835
918
 
836
919
  export interface EvoluTypeError extends TypeError<"EvoluType"> {}
837
920
 
838
- export const formatIsTypeError = createTypeErrorFormatter<EvoluTypeError>(
839
- (error) => `Value ${error.value} is not a valid Evolu Type.`,
840
- );
921
+ export const formatIsTypeError =
922
+ /*#__PURE__*/ createTypeErrorFormatter<EvoluTypeError>(
923
+ (error) => `Value ${error.value} is not a valid Evolu Type.`,
924
+ );
841
925
 
842
926
  /**
843
927
  * Branded {@link Type}.
844
928
  *
929
+ * Branding is the recommended way to define types in Evolu. Instead of using
930
+ * primitive types like `string` or `number` directly, wrap them with `brand` to
931
+ * create semantically meaningful types. See {@link Brand} for why this matters.
932
+ *
845
933
  * The `brand` Type Factory takes the name of a new {@link Brand}, a parent Type
846
934
  * to be branded, and the optional `refine` function for additional constraint.
847
935
  *
848
936
  * The `refine` function can be omitted if we only want to add a brand.
849
937
  *
850
- * ### Examples
938
+ * ### Example
851
939
  *
852
940
  * A simple `CurrencyCode` Type:
853
941
  *
@@ -966,7 +1054,7 @@ export const formatIsTypeError = createTypeErrorFormatter<EvoluTypeError>(
966
1054
  * );
967
1055
  * ```
968
1056
  *
969
- * @category Base Factories
1057
+ * @group Base factories
970
1058
  */
971
1059
  export function brand<
972
1060
  Name extends TypeName,
@@ -979,6 +1067,7 @@ export function brand<
979
1067
  refine: (value: Parent) => Result<Parent, RefineError>,
980
1068
  ): BrandType<ParentType, Name, RefineError, InferErrors<ParentType>>;
981
1069
 
1070
+ /** Without refine function. */
982
1071
  export function brand<Name extends TypeName, ParentType extends AnyType>(
983
1072
  name: Name,
984
1073
  parent: ParentType,
@@ -1002,15 +1091,15 @@ export function brand<
1002
1091
  ? (value: unknown) => {
1003
1092
  const parentResult = parent.fromUnknown(value);
1004
1093
  if (!parentResult.ok) return parentResult;
1005
- return refine(parentResult.value as IntentionalNever);
1094
+ return refine(parentResult.value as never);
1006
1095
  }
1007
1096
  : (value: unknown) => {
1008
1097
  const parentResult = parent.fromUnknown(value);
1009
1098
  if (!parentResult.ok)
1010
- return err<BrandWithoutRefineError<Name, IntentionalNever>>({
1099
+ return err<BrandWithoutRefineError<Name, never>>({
1011
1100
  type: name,
1012
1101
  value,
1013
- parentError: parentResult.error as IntentionalNever,
1102
+ parentError: parentResult.error as never,
1014
1103
  });
1015
1104
  return ok(parentResult.value);
1016
1105
  };
@@ -1018,7 +1107,7 @@ export function brand<
1018
1107
  return {
1019
1108
  ...createType("Brand", {
1020
1109
  fromUnknown,
1021
- fromParent: (refine ?? ok) as IntentionalNever,
1110
+ fromParent: (refine ?? ok) as never,
1022
1111
  }),
1023
1112
  brand: name,
1024
1113
  parentType: parent,
@@ -1031,13 +1120,13 @@ export interface BrandType<
1031
1120
  Error extends TypeError = never,
1032
1121
  ParentError extends TypeError = never,
1033
1122
  > extends Type<
1034
- "Brand",
1035
- InferType<ParentType> & Brand<Name>,
1036
- InferInput<ParentType>,
1037
- Error,
1038
- InferType<ParentType>,
1039
- ParentError
1040
- > {
1123
+ "Brand",
1124
+ InferType<ParentType> & Brand<Name>,
1125
+ InferInput<ParentType>,
1126
+ Error,
1127
+ InferType<ParentType>,
1128
+ ParentError
1129
+ > {
1041
1130
  readonly brand: Name;
1042
1131
  readonly parentType: ParentType;
1043
1132
  }
@@ -1052,12 +1141,15 @@ export interface BrandWithoutRefineError<
1052
1141
  /**
1053
1142
  * A three-letter ISO 4217 currency code (e.g., USD, EUR).
1054
1143
  *
1055
- * @category String
1144
+ * @group String
1056
1145
  */
1057
- export const CurrencyCode = brand("CurrencyCode", String, (value) =>
1058
- /^[A-Z]{3}$/.test(value)
1059
- ? ok(value)
1060
- : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
1146
+ export const CurrencyCode = /*#__PURE__*/ brand(
1147
+ "CurrencyCode",
1148
+ String,
1149
+ (value) =>
1150
+ /^[A-Z]{3}$/.test(value)
1151
+ ? ok(value)
1152
+ : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
1061
1153
  );
1062
1154
 
1063
1155
  export type CurrencyCode = typeof CurrencyCode.Type;
@@ -1065,7 +1157,7 @@ export type CurrencyCode = typeof CurrencyCode.Type;
1065
1157
  export interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
1066
1158
 
1067
1159
  export const formatCurrencyCodeError =
1068
- createTypeErrorFormatter<CurrencyCodeError>(
1160
+ /*#__PURE__*/ createTypeErrorFormatter<CurrencyCodeError>(
1069
1161
  (error) => `Invalid currency code: ${error.value}.`,
1070
1162
  );
1071
1163
 
@@ -1088,9 +1180,9 @@ export const formatCurrencyCodeError =
1088
1180
  * const error = DateIso.from("10000-01-01T00:00:00.000Z"); // err
1089
1181
  * ```
1090
1182
  *
1091
- * @category String
1183
+ * @group String
1092
1184
  */
1093
- export const DateIso = brand("DateIso", String, (value) => {
1185
+ export const DateIso = /*#__PURE__*/ brand("DateIso", String, (value) => {
1094
1186
  if (value.length !== 24) {
1095
1187
  return err<DateIsoError>({ type: "DateIso", value });
1096
1188
  }
@@ -1110,9 +1202,10 @@ export type DateIso = typeof DateIso.Type;
1110
1202
 
1111
1203
  export interface DateIsoError extends TypeError<"DateIso"> {}
1112
1204
 
1113
- export const formatDateIsoError = createTypeErrorFormatter<DateIsoError>(
1114
- (error) => `The value ${error.value} is not a valid ISO 8601 date string.`,
1115
- );
1205
+ export const formatDateIsoError =
1206
+ /*#__PURE__*/ createTypeErrorFormatter<DateIsoError>(
1207
+ (error) => `The value ${error.value} is not a valid ISO 8601 date string.`,
1208
+ );
1116
1209
 
1117
1210
  export const dateToDateIso = (value: Date): Result<DateIso, DateIsoError> =>
1118
1211
  DateIso.fromParent(value.toISOString());
@@ -1136,7 +1229,18 @@ export const dateIsoToDate = (value: DateIso): Date =>
1136
1229
  * );
1137
1230
  * ```
1138
1231
  *
1139
- * @category Utilities
1232
+ * ### Numeric literal inference
1233
+ *
1234
+ * When using factories with numeric parameters (like `lessThan`, `maxLength`,
1235
+ * `between`), use numeric literals instead of expressions. TypeScript widens
1236
+ * expressions to `number`, losing the literal type in the brand name:
1237
+ *
1238
+ * ```ts
1239
+ * lessThan(100)(Number); // Brand<"LessThan100"> ✓
1240
+ * lessThan(100 - 1)(Number); // Brand<"LessThan" + number> ✗
1241
+ * ```
1242
+ *
1243
+ * @group Utilities
1140
1244
  */
1141
1245
  export type BrandFactory<
1142
1246
  Name extends TypeName,
@@ -1172,7 +1276,7 @@ export type BrandFactory<
1172
1276
  * type TrimmedNonEmptyString = typeof TrimmedNonEmptyString.Type;
1173
1277
  * ```
1174
1278
  *
1175
- * @category String
1279
+ * @group String
1176
1280
  */
1177
1281
  export const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
1178
1282
  parent,
@@ -1185,9 +1289,10 @@ export const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
1185
1289
 
1186
1290
  export interface TrimmedError extends TypeError<"Trimmed"> {}
1187
1291
 
1188
- export const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
1189
- (error) => `The value ${error.value} must be trimmed.`,
1190
- );
1292
+ export const formatTrimmedError =
1293
+ /*#__PURE__*/ createTypeErrorFormatter<TrimmedError>(
1294
+ (error) => `The value ${error.value} must be trimmed.`,
1295
+ );
1191
1296
 
1192
1297
  /**
1193
1298
  * Trimmed string
@@ -1195,9 +1300,9 @@ export const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
1195
1300
  * - Use `TrimmedString.is` to check if an unknown value is trimmed.
1196
1301
  * - Use `TrimmedString.from` to check if a string is trimmed.
1197
1302
  *
1198
- * @category String
1303
+ * @group String
1199
1304
  */
1200
- export const TrimmedString = trimmed(String);
1305
+ export const TrimmedString = /*#__PURE__*/ trimmed(String);
1201
1306
  export type TrimmedString = typeof TrimmedString.Type;
1202
1307
 
1203
1308
  export const trim = (value: string): TrimmedString =>
@@ -1206,6 +1311,8 @@ export const trim = (value: string): TrimmedString =>
1206
1311
  /**
1207
1312
  * Minimum length.
1208
1313
  *
1314
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1315
+ *
1209
1316
  * ### Example
1210
1317
  *
1211
1318
  * ```ts
@@ -1213,8 +1320,8 @@ export const trim = (value: string): TrimmedString =>
1213
1320
  * const NonEmptyString = minLength(1)(String);
1214
1321
  * ```
1215
1322
  *
1216
- * @category String
1217
- * @category Array
1323
+ * @group String
1324
+ * @group Array
1218
1325
  */
1219
1326
  export const minLength: <Min extends number>(
1220
1327
  min: Min,
@@ -1224,19 +1331,23 @@ export const minLength: <Min extends number>(
1224
1331
  value.length >= min ? ok(value) : err({ type: "MinLength", value, min }),
1225
1332
  );
1226
1333
 
1227
- export interface MinLengthError<Min extends number = number>
1228
- extends TypeError<"MinLength"> {
1334
+ export interface MinLengthError<
1335
+ Min extends number = number,
1336
+ > extends TypeError<"MinLength"> {
1229
1337
  readonly min: Min;
1230
1338
  }
1231
1339
 
1232
- export const formatMinLengthError = createTypeErrorFormatter<MinLengthError>(
1233
- (error) =>
1234
- `The value ${error.value} does not meet the minimum length of ${error.min}.`,
1235
- );
1340
+ export const formatMinLengthError =
1341
+ /*#__PURE__*/ createTypeErrorFormatter<MinLengthError>(
1342
+ (error) =>
1343
+ `The value ${error.value} does not meet the minimum length of ${error.min}.`,
1344
+ );
1236
1345
 
1237
1346
  /**
1238
1347
  * Maximum length.
1239
1348
  *
1349
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1350
+ *
1240
1351
  * ### Example
1241
1352
  *
1242
1353
  * ```ts
@@ -1244,8 +1355,8 @@ export const formatMinLengthError = createTypeErrorFormatter<MinLengthError>(
1244
1355
  * const String100 = maxLength(100)(String);
1245
1356
  * ```
1246
1357
  *
1247
- * @category String
1248
- * @category Array
1358
+ * @group String
1359
+ * @group Array
1249
1360
  */
1250
1361
  export const maxLength: <Max extends number>(
1251
1362
  max: Max,
@@ -1255,19 +1366,23 @@ export const maxLength: <Max extends number>(
1255
1366
  value.length <= max ? ok(value) : err({ type: "MaxLength", value, max }),
1256
1367
  );
1257
1368
 
1258
- export interface MaxLengthError<Max extends number = number>
1259
- extends TypeError<"MaxLength"> {
1369
+ export interface MaxLengthError<
1370
+ Max extends number = number,
1371
+ > extends TypeError<"MaxLength"> {
1260
1372
  readonly max: Max;
1261
1373
  }
1262
1374
 
1263
- export const formatMaxLengthError = createTypeErrorFormatter<MaxLengthError>(
1264
- (error) =>
1265
- `The value ${error.value} exceeds the maximum length of ${error.max}.`,
1266
- );
1375
+ export const formatMaxLengthError =
1376
+ /*#__PURE__*/ createTypeErrorFormatter<MaxLengthError>(
1377
+ (error) =>
1378
+ `The value ${error.value} exceeds the maximum length of ${error.max}.`,
1379
+ );
1267
1380
 
1268
1381
  /**
1269
1382
  * Exact length.
1270
1383
  *
1384
+ * Use numeric literal, not expression. See {@link BrandFactory}.
1385
+ *
1271
1386
  * ### Example
1272
1387
  *
1273
1388
  * ```ts
@@ -1275,8 +1390,8 @@ export const formatMaxLengthError = createTypeErrorFormatter<MaxLengthError>(
1275
1390
  * const Length1String = length(1)(String);
1276
1391
  * ```
1277
1392
  *
1278
- * @category String
1279
- * @category Array
1393
+ * @group String
1394
+ * @group Array
1280
1395
  */
1281
1396
  export const length: <Exact extends number>(
1282
1397
  exact: Exact,
@@ -1288,54 +1403,58 @@ export const length: <Exact extends number>(
1288
1403
  : err({ type: "Length", value, exact }),
1289
1404
  );
1290
1405
 
1291
- export interface LengthError<Exact extends number = number>
1292
- extends TypeError<"Length"> {
1406
+ export interface LengthError<
1407
+ Exact extends number = number,
1408
+ > extends TypeError<"Length"> {
1293
1409
  readonly exact: Exact;
1294
1410
  }
1295
1411
 
1296
- export const formatLengthError = createTypeErrorFormatter<LengthError>(
1297
- (error) =>
1298
- `The value ${error.value} does not have the required length of ${error.exact}.`,
1299
- );
1412
+ export const formatLengthError =
1413
+ /*#__PURE__*/ createTypeErrorFormatter<LengthError>(
1414
+ (error) =>
1415
+ `The value ${error.value} does not have the required length of ${error.exact}.`,
1416
+ );
1300
1417
 
1301
- /** @category String */
1302
- export const NonEmptyString = minLength(1)(String);
1418
+ /** @group String */
1419
+ export const NonEmptyString = /*#__PURE__*/ minLength(1)(String);
1303
1420
  export type NonEmptyString = typeof NonEmptyString.Type;
1304
1421
 
1305
- /** @category String */
1306
- export const String100 = maxLength(100)(String);
1422
+ /** @group String */
1423
+ export const String100 = /*#__PURE__*/ maxLength(100)(String);
1307
1424
  export type String100 = typeof String100.Type;
1308
1425
 
1309
- /** @category String */
1310
- export const String1000 = maxLength(1000)(String);
1426
+ /** @group String */
1427
+ export const String1000 = /*#__PURE__*/ maxLength(1000)(String);
1311
1428
  export type String1000 = typeof String1000.Type;
1312
1429
 
1313
- /** @category String */
1314
- export const NonEmptyString100 = minLength(1)(String100);
1430
+ /** @group String */
1431
+ export const NonEmptyString100 = /*#__PURE__*/ minLength(1)(String100);
1315
1432
  export type NonEmptyString100 = typeof NonEmptyString100.Type;
1316
1433
 
1317
- /** @category String */
1318
- export const NonEmptyString1000 = minLength(1)(String1000);
1434
+ /** @group String */
1435
+ export const NonEmptyString1000 = /*#__PURE__*/ minLength(1)(String1000);
1319
1436
  export type NonEmptyString1000 = typeof NonEmptyString1000.Type;
1320
1437
 
1321
- /** @category String */
1322
- export const NonEmptyTrimmedString = minLength(1)(TrimmedString);
1438
+ /** @group String */
1439
+ export const NonEmptyTrimmedString = /*#__PURE__*/ minLength(1)(TrimmedString);
1323
1440
  export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Type;
1324
1441
 
1325
- /** @category String */
1326
- export const TrimmedString100 = maxLength(100)(TrimmedString);
1442
+ /** @group String */
1443
+ export const TrimmedString100 = /*#__PURE__*/ maxLength(100)(TrimmedString);
1327
1444
  export type TrimmedString100 = typeof TrimmedString100.Type;
1328
1445
 
1329
- /** @category String */
1330
- export const TrimmedString1000 = maxLength(1000)(TrimmedString);
1446
+ /** @group String */
1447
+ export const TrimmedString1000 = /*#__PURE__*/ maxLength(1000)(TrimmedString);
1331
1448
  export type TrimmedString1000 = typeof TrimmedString1000.Type;
1332
1449
 
1333
- /** @category String */
1334
- export const NonEmptyTrimmedString100 = minLength(1)(TrimmedString100);
1450
+ /** @group String */
1451
+ export const NonEmptyTrimmedString100 =
1452
+ /*#__PURE__*/ minLength(1)(TrimmedString100);
1335
1453
  export type NonEmptyTrimmedString100 = typeof NonEmptyTrimmedString100.Type;
1336
1454
 
1337
- /** @category String */
1338
- export const NonEmptyTrimmedString1000 = minLength(1)(TrimmedString1000);
1455
+ /** @group String */
1456
+ export const NonEmptyTrimmedString1000 =
1457
+ /*#__PURE__*/ minLength(1)(TrimmedString1000);
1339
1458
  export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Type;
1340
1459
 
1341
1460
  /**
@@ -1345,20 +1464,24 @@ export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Type;
1345
1464
  * safely on the user's device using cryptographically secure random number
1346
1465
  * generation, ensuring it remains private and unique.
1347
1466
  *
1348
- * @category String
1467
+ * @group String
1349
1468
  */
1350
- export const Mnemonic = brand("Mnemonic", NonEmptyTrimmedString, (value) =>
1351
- bip39.validateMnemonic(value, wordlist)
1352
- ? ok(value)
1353
- : err<MnemonicError>({ type: "Mnemonic", value }),
1469
+ export const Mnemonic = /*#__PURE__*/ brand(
1470
+ "Mnemonic",
1471
+ NonEmptyTrimmedString,
1472
+ (value) =>
1473
+ bip39.validateMnemonic(value, wordlist)
1474
+ ? ok(value)
1475
+ : err<MnemonicError>({ type: "Mnemonic", value }),
1354
1476
  );
1355
1477
  export type Mnemonic = typeof Mnemonic.Type;
1356
1478
 
1357
1479
  export interface MnemonicError extends TypeError<"Mnemonic"> {}
1358
1480
 
1359
- export const formatMnemonicError = createTypeErrorFormatter<MnemonicError>(
1360
- (error) => `Invalid BIP39 mnemonic: ${error.value}.`,
1361
- );
1481
+ export const formatMnemonicError =
1482
+ /*#__PURE__*/ createTypeErrorFormatter<MnemonicError>(
1483
+ (error) => `Invalid BIP39 mnemonic: ${error.value}.`,
1484
+ );
1362
1485
 
1363
1486
  /**
1364
1487
  * String matching a regular expression.
@@ -1369,7 +1492,7 @@ export const formatMnemonicError = createTypeErrorFormatter<MnemonicError>(
1369
1492
  * const Alphanumeric = regex("Alphanumeric", /^[a-z0-9]+$/i)(String);
1370
1493
  * ```
1371
1494
  *
1372
- * @category String
1495
+ * @group String
1373
1496
  */
1374
1497
  export const regex: <Name extends TypeName>(
1375
1498
  name: Name,
@@ -1388,16 +1511,18 @@ export const regex: <Name extends TypeName>(
1388
1511
  });
1389
1512
  };
1390
1513
 
1391
- export interface RegexError<Name extends TypeName = TypeName>
1392
- extends TypeError<"Regex"> {
1514
+ export interface RegexError<
1515
+ Name extends TypeName = TypeName,
1516
+ > extends TypeError<"Regex"> {
1393
1517
  readonly name: Name;
1394
1518
  readonly pattern: RegExp;
1395
1519
  }
1396
1520
 
1397
- export const formatRegexError = createTypeErrorFormatter<RegexError>(
1398
- (error) =>
1399
- `The value ${error.value} does not match the pattern for ${error.name}: ${error.pattern}.`,
1400
- );
1521
+ export const formatRegexError =
1522
+ /*#__PURE__*/ createTypeErrorFormatter<RegexError>(
1523
+ (error) =>
1524
+ `The value ${error.value} does not match the pattern for ${error.name}: ${error.pattern}.`,
1525
+ );
1401
1526
 
1402
1527
  /**
1403
1528
  * URL-safe string.
@@ -1424,9 +1549,12 @@ export const formatRegexError = createTypeErrorFormatter<RegexError>(
1424
1549
  * }
1425
1550
  * ```
1426
1551
  *
1427
- * @category String
1552
+ * @group String
1428
1553
  */
1429
- export const UrlSafeString = regex("UrlSafeString", /^[A-Za-z0-9_-]+$/)(String);
1554
+ export const UrlSafeString = /*#__PURE__*/ regex(
1555
+ "UrlSafeString",
1556
+ /^[A-Za-z0-9_-]+$/,
1557
+ )(String);
1430
1558
  export type UrlSafeString = typeof UrlSafeString.Type;
1431
1559
  export type UrlSafeStringError = typeof UrlSafeString.Error;
1432
1560
 
@@ -1436,9 +1564,9 @@ export type UrlSafeStringError = typeof UrlSafeString.Error;
1436
1564
  * Encode with {@link uint8ArrayToBase64Url}, decode with
1437
1565
  * {@link base64UrlToUint8Array}.
1438
1566
  *
1439
- * @category String
1567
+ * @group String
1440
1568
  */
1441
- export const Base64Url = brand(
1569
+ export const Base64Url = /*#__PURE__*/ brand(
1442
1570
  "Base64Url",
1443
1571
  String,
1444
1572
  (value: string): Result<string, Base64UrlError> => {
@@ -1461,9 +1589,10 @@ export const Base64Url = brand(
1461
1589
  export type Base64Url = typeof Base64Url.Type;
1462
1590
  export interface Base64UrlError extends TypeError<"Base64Url"> {}
1463
1591
 
1464
- export const formatBase64UrlError = createTypeErrorFormatter<Base64UrlError>(
1465
- (error) => `The value ${error.value} is not a valid Base64Url string.`,
1466
- );
1592
+ export const formatBase64UrlError =
1593
+ /*#__PURE__*/ createTypeErrorFormatter<Base64UrlError>(
1594
+ (error) => `The value ${error.value} is not a valid Base64Url string.`,
1595
+ );
1467
1596
 
1468
1597
  const base64UrlOptions = { alphabet: "base64url", omitPadding: true };
1469
1598
 
@@ -1515,33 +1644,35 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
1515
1644
  };
1516
1645
 
1517
1646
  /**
1518
- * Simple alphanumeric string for naming in file systems, URLs, and identifiers.
1647
+ * Alphanumeric string for naming in file systems, URLs, and identifiers.
1519
1648
  *
1520
1649
  * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
1521
- * `_`). See `UrlSafeString` for details.
1650
+ * `_`).
1522
1651
  *
1523
1652
  * The string must be between 1 and 64 characters.
1524
1653
  *
1525
1654
  * ### Example
1526
1655
  *
1527
1656
  * ```ts
1528
- * const result = SimpleName.from("data-report-123");
1657
+ * const result = Name.from("data-report-123");
1529
1658
  * if (result.ok) {
1530
- * console.log("Valid SimpleName string:", result.value);
1659
+ * console.log("Valid Name string:", result.value);
1531
1660
  * } else {
1532
- * console.error("Invalid SimpleName string:", result.error);
1661
+ * console.error("Invalid Name string:", result.error);
1533
1662
  * }
1534
1663
  * ```
1535
1664
  *
1536
- * @category String
1665
+ * @group String
1537
1666
  */
1538
- export const SimpleName = brand("SimpleName", UrlSafeString, (value) =>
1667
+ export const Name = /*#__PURE__*/ brand("Name", UrlSafeString, (value) =>
1539
1668
  value.length >= 1 && value.length <= 64
1540
1669
  ? ok(value)
1541
- : err<SimpleNameError>({ type: "SimpleName", value }),
1670
+ : err<NameError>({ type: "Name", value }),
1542
1671
  );
1543
- export type SimpleName = typeof SimpleName.Type;
1544
- export interface SimpleNameError extends TypeError<"SimpleName"> {}
1672
+ export type Name = typeof Name.Type;
1673
+ export interface NameError extends TypeError<"Name"> {}
1674
+
1675
+ export const testName = /*#__PURE__*/ Name.orThrow("Name");
1545
1676
 
1546
1677
  /**
1547
1678
  * Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
@@ -1565,11 +1696,11 @@ export interface SimpleNameError extends TypeError<"SimpleName"> {}
1565
1696
  * // |> brand("SimplePassword", %)
1566
1697
  * ```
1567
1698
  *
1568
- * @category String
1699
+ * @group String
1569
1700
  */
1570
- export const SimplePassword = brand(
1701
+ export const SimplePassword = /*#__PURE__*/ brand(
1571
1702
  "SimplePassword",
1572
- minLength(8)(maxLength(64)(TrimmedString)),
1703
+ /*#__PURE__*/ minLength(8)(/*#__PURE__*/ maxLength(64)(TrimmedString)),
1573
1704
  );
1574
1705
  export type SimplePassword = typeof SimplePassword.Type;
1575
1706
 
@@ -1600,16 +1731,16 @@ export const formatSimplePasswordError = (
1600
1731
  * copied (logs, URLs, exports); only use it when you explicitly want insertion
1601
1732
  * locality for very large write‑heavy tables and accept timestamp exposure.
1602
1733
  *
1603
- * ### Future
1734
+ * ## Future
1604
1735
  *
1605
1736
  * A possible hybrid masked‑time approach (`timestamp ^ H(cluster_id, timestamp
1606
1737
  *
1607
1738
  * > > N)`) could provide locality without exposing raw creation time. See
1608
1739
  * > > https://brooker.co.za/blog/2025/10/22/uuidv7.html
1609
1740
  *
1610
- * @category String
1741
+ * @group String
1611
1742
  */
1612
- export const Id = brand("Id", String, (value) =>
1743
+ export const Id = /*#__PURE__*/ brand("Id", String, (value) =>
1613
1744
  value.length === 22 && Base64Url.fromParent(value).ok
1614
1745
  ? ok(value)
1615
1746
  : err<IdError>({ type: "Id", value }),
@@ -1618,7 +1749,7 @@ export type Id = typeof Id.Type;
1618
1749
 
1619
1750
  export interface IdError extends TypeError<"Id"> {}
1620
1751
 
1621
- export const formatIdError = createTypeErrorFormatter<IdError>(
1752
+ export const formatIdError = /*#__PURE__*/ createTypeErrorFormatter<IdError>(
1622
1753
  (error) => `The value ${error.value} is not a valid Id.`,
1623
1754
  );
1624
1755
 
@@ -1673,7 +1804,7 @@ export const createId = <B extends string = never>(
1673
1804
  * external string from the generated {@link Id}. If you need to preserve the
1674
1805
  * original external ID, store it in a separate column.
1675
1806
  *
1676
- * @category String
1807
+ * @group String
1677
1808
  */
1678
1809
  export const createIdFromString = <B extends string = never>(
1679
1810
  value: string,
@@ -1718,7 +1849,9 @@ export const createIdAsUuidv7 = <B extends string = never>(
1718
1849
  id[6] = (id[6] & 0x0f) | 0x70;
1719
1850
  id[8] = (id[8] & 0x3f) | 0x80;
1720
1851
 
1721
- return id as unknown as [B] extends [never] ? Id : Id & Brand<B>;
1852
+ return uint8ArrayToBase64Url(id) as unknown as [B] extends [never]
1853
+ ? Id
1854
+ : Id & Brand<B>;
1722
1855
  };
1723
1856
 
1724
1857
  /**
@@ -1734,7 +1867,7 @@ export const createIdAsUuidv7 = <B extends string = never>(
1734
1867
  * type TodoId = typeof TodoId.Type;
1735
1868
  * ```
1736
1869
  *
1737
- * @category String
1870
+ * @group String
1738
1871
  */
1739
1872
  export const id = <Table extends TypeName>(table: Table): TableId<Table> => {
1740
1873
  const fromUnknown = (value: unknown) => {
@@ -1757,29 +1890,33 @@ export const id = <Table extends TypeName>(table: Table): TableId<Table> => {
1757
1890
  };
1758
1891
  };
1759
1892
 
1760
- export interface TableId<Table extends TypeName>
1761
- extends Type<
1762
- "Id",
1763
- string & Brand<"Id"> & Brand<Table>,
1764
- string,
1765
- TableIdError<Table>,
1766
- string,
1767
- StringError
1768
- > {
1893
+ export interface TableId<Table extends TypeName> extends Type<
1894
+ "Id",
1895
+ string & Brand<"Id"> & Brand<Table>,
1896
+ string,
1897
+ TableIdError<Table>,
1898
+ string,
1899
+ StringError
1900
+ > {
1769
1901
  table: Table;
1770
1902
  }
1771
1903
 
1772
- export interface TableIdError<Table extends TypeName = TypeName>
1773
- extends TypeError<"TableId"> {
1904
+ export interface TableIdError<
1905
+ Table extends TypeName = TypeName,
1906
+ > extends TypeError<"TableId"> {
1774
1907
  readonly table: Table;
1775
1908
  }
1776
1909
 
1777
- export const formatTableIdError = createTypeErrorFormatter<TableIdError>(
1778
- (error) => `Invalid Id for table ${error.table}: ${error.value}.`,
1779
- );
1910
+ export const formatTableIdError =
1911
+ /*#__PURE__*/ createTypeErrorFormatter<TableIdError>(
1912
+ (error) => `Invalid Id for table ${error.table}: ${error.value}.`,
1913
+ );
1780
1914
 
1781
1915
  /** Binary representation of an {@link Id}. */
1782
- export const IdBytes = brand("IdBytes", length(16)(Uint8Array));
1916
+ export const IdBytes = /*#__PURE__*/ brand(
1917
+ "IdBytes",
1918
+ /*#__PURE__*/ length(16)(Uint8Array),
1919
+ );
1783
1920
  export type IdBytes = typeof IdBytes.Type;
1784
1921
 
1785
1922
  export const idBytesTypeValueLength = 16 as NonNegativeInt;
@@ -1804,7 +1941,7 @@ export const idBytesToId = (idBytes: IdBytes): Id =>
1804
1941
  * const errorResult = PositiveNumber.from(-5); // err
1805
1942
  * ```
1806
1943
  *
1807
- * @category Number
1944
+ * @group Number
1808
1945
  */
1809
1946
  export const positive: BrandFactory<"Positive", number, PositiveError> = (
1810
1947
  parent,
@@ -1815,9 +1952,10 @@ export const positive: BrandFactory<"Positive", number, PositiveError> = (
1815
1952
 
1816
1953
  export interface PositiveError extends TypeError<"Positive"> {}
1817
1954
 
1818
- export const formatPositiveError = createTypeErrorFormatter<PositiveError>(
1819
- (error) => `The value ${error.value} must be positive (> 0).`,
1820
- );
1955
+ export const formatPositiveError =
1956
+ /*#__PURE__*/ createTypeErrorFormatter<PositiveError>(
1957
+ (error) => `The value ${error.value} must be positive (> 0).`,
1958
+ );
1821
1959
 
1822
1960
  /**
1823
1961
  * Negative number (< 0).
@@ -1828,7 +1966,7 @@ export const formatPositiveError = createTypeErrorFormatter<PositiveError>(
1828
1966
  * const NegativeNumber = negative(Number);
1829
1967
  * ```
1830
1968
  *
1831
- * @category Number
1969
+ * @group Number
1832
1970
  */
1833
1971
  export const negative: BrandFactory<"Negative", number, NegativeError> = (
1834
1972
  parent,
@@ -1839,9 +1977,10 @@ export const negative: BrandFactory<"Negative", number, NegativeError> = (
1839
1977
 
1840
1978
  export interface NegativeError extends TypeError<"Negative"> {}
1841
1979
 
1842
- export const formatNegativeError = createTypeErrorFormatter<NegativeError>(
1843
- (error) => `The value ${error.value} must be negative (< 0).`,
1844
- );
1980
+ export const formatNegativeError =
1981
+ /*#__PURE__*/ createTypeErrorFormatter<NegativeError>(
1982
+ (error) => `The value ${error.value} must be negative (< 0).`,
1983
+ );
1845
1984
 
1846
1985
  /**
1847
1986
  * Non-positive number (≤ 0).
@@ -1852,7 +1991,7 @@ export const formatNegativeError = createTypeErrorFormatter<NegativeError>(
1852
1991
  * const NonPositiveNumber = nonPositive(Number);
1853
1992
  * ```
1854
1993
  *
1855
- * @category Number
1994
+ * @group Number
1856
1995
  */
1857
1996
  export const nonPositive: BrandFactory<
1858
1997
  "NonPositive",
@@ -1868,7 +2007,7 @@ export const nonPositive: BrandFactory<
1868
2007
  export interface NonPositiveError extends TypeError<"NonPositive"> {}
1869
2008
 
1870
2009
  export const formatNonPositiveError =
1871
- createTypeErrorFormatter<NonPositiveError>(
2010
+ /*#__PURE__*/ createTypeErrorFormatter<NonPositiveError>(
1872
2011
  (error) => `The value ${error.value} must be non-positive (≤ 0).`,
1873
2012
  );
1874
2013
 
@@ -1881,7 +2020,7 @@ export const formatNonPositiveError =
1881
2020
  * const NonNegativeNumber = nonNegative(Number);
1882
2021
  * ```
1883
2022
  *
1884
- * @category Number
2023
+ * @group Number
1885
2024
  */
1886
2025
  export const nonNegative: BrandFactory<
1887
2026
  "NonNegative",
@@ -1897,40 +2036,40 @@ export const nonNegative: BrandFactory<
1897
2036
  export interface NonNegativeError extends TypeError<"NonNegative"> {}
1898
2037
 
1899
2038
  export const formatNonNegativeError =
1900
- createTypeErrorFormatter<NonNegativeError>(
2039
+ /*#__PURE__*/ createTypeErrorFormatter<NonNegativeError>(
1901
2040
  (error) => `The value ${error.value} must be non-negative (≥ 0).`,
1902
2041
  );
1903
2042
 
1904
2043
  /**
1905
2044
  * Non-negative number (≥ 0).
1906
2045
  *
1907
- * @category Number
2046
+ * @group Number
1908
2047
  */
1909
- export const NonNegativeNumber = nonNegative(Number);
2048
+ export const NonNegativeNumber = /*#__PURE__*/ nonNegative(Number);
1910
2049
  export type NonNegativeNumber = typeof NonNegativeNumber.Type;
1911
2050
 
1912
2051
  /**
1913
2052
  * Positive number (> 0).
1914
2053
  *
1915
- * @category Number
2054
+ * @group Number
1916
2055
  */
1917
- export const PositiveNumber = positive(NonNegativeNumber);
2056
+ export const PositiveNumber = /*#__PURE__*/ positive(NonNegativeNumber);
1918
2057
  export type PositiveNumber = typeof PositiveNumber.Type;
1919
2058
 
1920
2059
  /**
1921
2060
  * Non-positive number (≤ 0).
1922
2061
  *
1923
- * @category Number
2062
+ * @group Number
1924
2063
  */
1925
- export const NonPositiveNumber = nonPositive(Number);
2064
+ export const NonPositiveNumber = /*#__PURE__*/ nonPositive(Number);
1926
2065
  export type NonPositiveNumber = typeof NonPositiveNumber.Type;
1927
2066
 
1928
2067
  /**
1929
2068
  * Negative number (< 0).
1930
2069
  *
1931
- * @category Number
2070
+ * @group Number
1932
2071
  */
1933
- export const NegativeNumber = negative(NonPositiveNumber);
2072
+ export const NegativeNumber = /*#__PURE__*/ negative(NonPositiveNumber);
1934
2073
  export type NegativeNumber = typeof NegativeNumber.Type;
1935
2074
 
1936
2075
  /**
@@ -1939,12 +2078,17 @@ export type NegativeNumber = typeof NegativeNumber.Type;
1939
2078
  * ### Example
1940
2079
  *
1941
2080
  * ```ts
1942
- * const Int = int(Number);
2081
+ * const Int = safeInt(Number);
1943
2082
  * ```
1944
2083
  *
1945
- * @category Number
2084
+ * Note: This helper was previously named `int`. A Next.js regression produced
2085
+ * runtime `int is not defined`, so we keep the implementation under the
2086
+ * `safeInt` alias.
2087
+ *
2088
+ * @group Number
1946
2089
  */
1947
- export const int: BrandFactory<"Int", number, IntError> = (parent) =>
2090
+
2091
+ const safeInt: BrandFactory<"Int", number, IntError> = (parent) =>
1948
2092
  brand("Int", parent, (value) =>
1949
2093
  globalThis.Number.isSafeInteger(value)
1950
2094
  ? ok(value)
@@ -1953,59 +2097,67 @@ export const int: BrandFactory<"Int", number, IntError> = (parent) =>
1953
2097
 
1954
2098
  export interface IntError extends TypeError<"Int"> {}
1955
2099
 
1956
- export const formatIntError = createTypeErrorFormatter<IntError>(
2100
+ export const formatIntError = /*#__PURE__*/ createTypeErrorFormatter<IntError>(
1957
2101
  (error) => `The value ${error.value} must be an integer.`,
1958
2102
  );
1959
2103
 
1960
2104
  /**
1961
2105
  * Integer within the safe range of JavaScript numbers.
1962
2106
  *
1963
- * @category Number
2107
+ * @group Number
1964
2108
  */
1965
- export const Int = int(Number);
2109
+ export const Int = /*#__PURE__*/ safeInt(Number);
1966
2110
  export type Int = typeof Int.Type;
1967
2111
 
1968
2112
  /**
1969
2113
  * Non-negative integer (≥ 0).
1970
2114
  *
1971
- * @category Number
2115
+ * @group Number
1972
2116
  */
1973
- export const NonNegativeInt = nonNegative(Int);
2117
+ export const NonNegativeInt = /*#__PURE__*/ nonNegative(Int);
1974
2118
  export type NonNegativeInt = typeof NonNegativeInt.Type;
1975
2119
 
2120
+ /** Minimum {@link NonNegativeInt} value (0). */
2121
+ export const zeroNonNegativeInt = /*#__PURE__*/ NonNegativeInt.orThrow(0);
2122
+
1976
2123
  /**
1977
2124
  * Positive integer (> 0).
1978
2125
  *
1979
- * @category Number
2126
+ * @group Number
1980
2127
  */
1981
- export const PositiveInt = positive(NonNegativeInt);
2128
+ export const PositiveInt = /*#__PURE__*/ positive(NonNegativeInt);
1982
2129
  export type PositiveInt = typeof PositiveInt.Type;
1983
2130
 
1984
- /** Maximum safe positive integer value for practically infinite operations. */
1985
- export const maxPositiveInt = PositiveInt.orThrow(
2131
+ /** {@link PositiveInt} value 1. */
2132
+ export const onePositiveInt = /*#__PURE__*/ PositiveInt.orThrow(1);
2133
+
2134
+ /** Maximum {@link PositiveInt} value (MAX_SAFE_INTEGER). */
2135
+ export const maxPositiveInt = /*#__PURE__*/ PositiveInt.orThrow(
1986
2136
  globalThis.Number.MAX_SAFE_INTEGER,
1987
2137
  );
1988
2138
 
1989
2139
  /**
1990
2140
  * Non-positive integer (≤ 0).
1991
2141
  *
1992
- * @category Number
2142
+ * @group Number
1993
2143
  */
1994
- export const NonPositiveInt = nonPositive(Int);
2144
+ export const NonPositiveInt = /*#__PURE__*/ nonPositive(Int);
1995
2145
  export type NonPositiveInt = typeof NonPositiveInt.Type;
1996
2146
 
1997
2147
  /**
1998
2148
  * Negative integer (< 0).
1999
2149
  *
2000
- * @category Number
2150
+ * @group Number
2001
2151
  */
2002
- export const NegativeInt = negative(NonPositiveInt);
2152
+ export const NegativeInt = /*#__PURE__*/ negative(NonPositiveInt);
2003
2153
  export type NegativeInt = typeof NegativeInt.Type;
2004
2154
 
2005
2155
  /**
2006
2156
  * Number greater than a specified value.
2007
2157
  *
2008
- * @category Number
2158
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2159
+ *
2160
+ * @group Number
2009
2161
  */
2010
2162
  export const greaterThan: <Min extends number>(
2011
2163
  min: Min,
@@ -2015,20 +2167,23 @@ export const greaterThan: <Min extends number>(
2015
2167
  value > min ? ok(value) : err({ type: "GreaterThan", value, min }),
2016
2168
  );
2017
2169
 
2018
- export interface GreaterThanError<Min extends number = number>
2019
- extends TypeError<"GreaterThan"> {
2170
+ export interface GreaterThanError<
2171
+ Min extends number = number,
2172
+ > extends TypeError<"GreaterThan"> {
2020
2173
  readonly min: Min;
2021
2174
  }
2022
2175
 
2023
2176
  export const formatGreaterThanError =
2024
- createTypeErrorFormatter<GreaterThanError>(
2177
+ /*#__PURE__*/ createTypeErrorFormatter<GreaterThanError>(
2025
2178
  (error) => `The value ${error.value} is not > ${error.min}.`,
2026
2179
  );
2027
2180
 
2028
2181
  /**
2029
2182
  * Number less than a specified value.
2030
2183
  *
2031
- * @category Number
2184
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2185
+ *
2186
+ * @group Number
2032
2187
  */
2033
2188
  export const lessThan: <Max extends number>(
2034
2189
  max: Max,
@@ -2038,19 +2193,23 @@ export const lessThan: <Max extends number>(
2038
2193
  value < max ? ok(value) : err({ type: "LessThan", value, max }),
2039
2194
  );
2040
2195
 
2041
- export interface LessThanError<Max extends number = number>
2042
- extends TypeError<"LessThan"> {
2196
+ export interface LessThanError<
2197
+ Max extends number = number,
2198
+ > extends TypeError<"LessThan"> {
2043
2199
  readonly max: Max;
2044
2200
  }
2045
2201
 
2046
- export const formatLessThanError = createTypeErrorFormatter<LessThanError>(
2047
- (error) => `The value ${error.value} is not < ${error.max}.`,
2048
- );
2202
+ export const formatLessThanError =
2203
+ /*#__PURE__*/ createTypeErrorFormatter<LessThanError>(
2204
+ (error) => `The value ${error.value} is not < ${error.max}.`,
2205
+ );
2049
2206
 
2050
2207
  /**
2051
2208
  * Number ≥ a specified value.
2052
2209
  *
2053
- * @category Number
2210
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2211
+ *
2212
+ * @group Number
2054
2213
  */
2055
2214
  export const greaterThanOrEqualTo: <Min extends number>(
2056
2215
  min: Min,
@@ -2065,20 +2224,23 @@ export const greaterThanOrEqualTo: <Min extends number>(
2065
2224
  : err({ type: "GreaterThanOrEqualTo", value, min }),
2066
2225
  );
2067
2226
 
2068
- export interface GreaterThanOrEqualToError<Min extends number = number>
2069
- extends TypeError<"GreaterThanOrEqualTo"> {
2227
+ export interface GreaterThanOrEqualToError<
2228
+ Min extends number = number,
2229
+ > extends TypeError<"GreaterThanOrEqualTo"> {
2070
2230
  readonly min: Min;
2071
2231
  }
2072
2232
 
2073
2233
  export const formatGreaterThanOrEqualToError =
2074
- createTypeErrorFormatter<GreaterThanOrEqualToError>(
2234
+ /*#__PURE__*/ createTypeErrorFormatter<GreaterThanOrEqualToError>(
2075
2235
  (error) => `The value ${error.value} is not >= ${error.min}.`,
2076
2236
  );
2077
2237
 
2078
2238
  /**
2079
2239
  * Number ≤ a specified value.
2080
2240
  *
2081
- * @category Number
2241
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2242
+ *
2243
+ * @group Number
2082
2244
  */
2083
2245
  export const lessThanOrEqualTo: <Max extends number>(
2084
2246
  max: Max,
@@ -2091,20 +2253,21 @@ export const lessThanOrEqualTo: <Max extends number>(
2091
2253
  value <= max ? ok(value) : err({ type: "LessThanOrEqualTo", value, max }),
2092
2254
  );
2093
2255
 
2094
- export interface LessThanOrEqualToError<Max extends number = number>
2095
- extends TypeError<"LessThanOrEqualTo"> {
2256
+ export interface LessThanOrEqualToError<
2257
+ Max extends number = number,
2258
+ > extends TypeError<"LessThanOrEqualTo"> {
2096
2259
  readonly max: Max;
2097
2260
  }
2098
2261
 
2099
2262
  export const formatLessThanOrEqualToError =
2100
- createTypeErrorFormatter<LessThanOrEqualToError>(
2263
+ /*#__PURE__*/ createTypeErrorFormatter<LessThanOrEqualToError>(
2101
2264
  (error) => `The value ${error.value} is not <= ${error.max}.`,
2102
2265
  );
2103
2266
 
2104
2267
  /**
2105
2268
  * Number that is not NaN.
2106
2269
  *
2107
- * @category Number
2270
+ * @group Number
2108
2271
  */
2109
2272
  export const nonNaN: BrandFactory<"NonNaN", number, NonNaNError> = (parent) =>
2110
2273
  brand("NonNaN", parent, (value) =>
@@ -2115,18 +2278,19 @@ export const nonNaN: BrandFactory<"NonNaN", number, NonNaNError> = (parent) =>
2115
2278
 
2116
2279
  export interface NonNaNError extends TypeError<"NonNaN"> {}
2117
2280
 
2118
- export const formatNonNaNError = createTypeErrorFormatter<NonNaNError>(
2119
- () => `The value must not be NaN.`,
2120
- );
2281
+ export const formatNonNaNError =
2282
+ /*#__PURE__*/ createTypeErrorFormatter<NonNaNError>(
2283
+ () => `The value must not be NaN.`,
2284
+ );
2121
2285
 
2122
- /** @category Number */
2123
- export const NonNaNNumber = nonNaN(Number);
2286
+ /** @group Number */
2287
+ export const NonNaNNumber = /*#__PURE__*/ nonNaN(Number);
2124
2288
  export type NonNaNNumber = typeof NonNaNNumber.Type;
2125
2289
 
2126
2290
  /**
2127
2291
  * Finite number.
2128
2292
  *
2129
- * @category Number
2293
+ * @group Number
2130
2294
  */
2131
2295
  export const finite: BrandFactory<"Finite", number, FiniteError> = (parent) =>
2132
2296
  brand("Finite", parent, (value) =>
@@ -2137,9 +2301,10 @@ export const finite: BrandFactory<"Finite", number, FiniteError> = (parent) =>
2137
2301
 
2138
2302
  export interface FiniteError extends TypeError<"Finite"> {}
2139
2303
 
2140
- export const formatFiniteError = createTypeErrorFormatter<FiniteError>(
2141
- (error) => `The value ${error.value} must be finite.`,
2142
- );
2304
+ export const formatFiniteError =
2305
+ /*#__PURE__*/ createTypeErrorFormatter<FiniteError>(
2306
+ (error) => `The value ${error.value} must be finite.`,
2307
+ );
2143
2308
 
2144
2309
  /**
2145
2310
  * Finite number.
@@ -2152,15 +2317,17 @@ export const formatFiniteError = createTypeErrorFormatter<FiniteError>(
2152
2317
  * finite (e.g., `Infinity`, `-Infinity`, or `NaN`). Using `FiniteNumber` helps
2153
2318
  * prevent these unexpected behaviors when working with JSON serialization.
2154
2319
  *
2155
- * @category Number
2320
+ * @group Number
2156
2321
  */
2157
- export const FiniteNumber = finite(Number);
2322
+ export const FiniteNumber = /*#__PURE__*/ finite(Number);
2158
2323
  export type FiniteNumber = typeof FiniteNumber.Type;
2159
2324
 
2160
2325
  /**
2161
2326
  * Number that is a multiple of a divisor.
2162
2327
  *
2163
- * @category Number
2328
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2329
+ *
2330
+ * @group Number
2164
2331
  */
2165
2332
  export const multipleOf: <Divisor extends number>(
2166
2333
  divisor: Divisor,
@@ -2172,18 +2339,23 @@ export const multipleOf: <Divisor extends number>(
2172
2339
  : err({ type: "MultipleOf", value, divisor }),
2173
2340
  );
2174
2341
 
2175
- export interface MultipleOfError<Divisor extends number = number>
2176
- extends TypeError<"MultipleOf"> {
2342
+ export interface MultipleOfError<
2343
+ Divisor extends number = number,
2344
+ > extends TypeError<"MultipleOf"> {
2177
2345
  readonly divisor: Divisor;
2178
2346
  }
2179
2347
 
2180
- export const formatMultipleOfError = createTypeErrorFormatter<MultipleOfError>(
2181
- (error) => `The value ${error.value} is not a multiple of ${error.divisor}.`,
2182
- );
2348
+ export const formatMultipleOfError =
2349
+ /*#__PURE__*/ createTypeErrorFormatter<MultipleOfError>(
2350
+ (error) =>
2351
+ `The value ${error.value} is not a multiple of ${error.divisor}.`,
2352
+ );
2183
2353
 
2184
2354
  /**
2185
2355
  * Number within a range, inclusive.
2186
2356
  *
2357
+ * Use numeric literal, not expression. See {@link BrandFactory}.
2358
+ *
2187
2359
  * ### Example
2188
2360
  *
2189
2361
  * ```ts
@@ -2192,7 +2364,7 @@ export const formatMultipleOfError = createTypeErrorFormatter<MultipleOfError>(
2192
2364
  * const errorResult = Between1And10.from(11); // err
2193
2365
  * ```
2194
2366
  *
2195
- * @category Number
2367
+ * @group Number
2196
2368
  */
2197
2369
  export const between: <Min extends number, Max extends number>(
2198
2370
  min: Min,
@@ -2213,10 +2385,11 @@ export interface BetweenError<
2213
2385
  readonly max: Max;
2214
2386
  }
2215
2387
 
2216
- export const formatBetweenError = createTypeErrorFormatter<BetweenError>(
2217
- (error) =>
2218
- `The value ${error.value} is not between ${error.min} and ${error.max}, inclusive.`,
2219
- );
2388
+ export const formatBetweenError =
2389
+ /*#__PURE__*/ createTypeErrorFormatter<BetweenError>(
2390
+ (error) =>
2391
+ `The value ${error.value} is not between ${error.min} and ${error.max}, inclusive.`,
2392
+ );
2220
2393
 
2221
2394
  /**
2222
2395
  * Literal {@link Type}.
@@ -2233,7 +2406,7 @@ export const formatBetweenError = createTypeErrorFormatter<BetweenError>(
2233
2406
  *
2234
2407
  * TODO: Add JsonValue
2235
2408
  *
2236
- * @category Base Factories
2409
+ * @group Base factories
2237
2410
  */
2238
2411
  export const literal = <T extends Literal>(expected: T): LiteralType<T> => {
2239
2412
  const fromUnknown = (value: unknown): Result<T, LiteralError<T>> =>
@@ -2250,27 +2423,33 @@ export const literal = <T extends Literal>(expected: T): LiteralType<T> => {
2250
2423
  } as LiteralType<T>;
2251
2424
  };
2252
2425
 
2253
- export interface LiteralType<T extends Literal>
2254
- extends Type<"Literal", T, WidenLiteral<T>, LiteralError<T>> {
2426
+ export interface LiteralType<T extends Literal> extends Type<
2427
+ "Literal",
2428
+ T,
2429
+ WidenLiteral<T>,
2430
+ LiteralError<T>
2431
+ > {
2255
2432
  expected: T;
2256
2433
  }
2257
2434
 
2258
- export interface LiteralError<T extends Literal = Literal>
2259
- extends TypeError<"Literal"> {
2435
+ export interface LiteralError<
2436
+ T extends Literal = Literal,
2437
+ > extends TypeError<"Literal"> {
2260
2438
  readonly expected: T;
2261
2439
  }
2262
2440
 
2263
- export const formatLiteralError = createTypeErrorFormatter<LiteralError>(
2264
- (error) =>
2265
- `The value ${error.value} is not strictly equal to the expected literal: ${globalThis.String(
2266
- error.expected,
2267
- )}.`,
2268
- );
2441
+ export const formatLiteralError =
2442
+ /*#__PURE__*/ createTypeErrorFormatter<LiteralError>(
2443
+ (error) =>
2444
+ `The value ${error.value} is not strictly equal to the expected literal: ${globalThis.String(
2445
+ error.expected,
2446
+ )}.`,
2447
+ );
2269
2448
 
2270
2449
  /**
2271
2450
  * Array of a specific {@link Type}.
2272
2451
  *
2273
- * Usage:
2452
+ * ### Example
2274
2453
  *
2275
2454
  * ```ts
2276
2455
  * const NumberArray = array(Number);
@@ -2279,8 +2458,8 @@ export const formatLiteralError = createTypeErrorFormatter<LiteralError>(
2279
2458
  * const result2 = NumberArray.from(["a", "b"]); // err(...)
2280
2459
  * ```
2281
2460
  *
2282
- * @category Base Factories
2283
- * @category Array
2461
+ * @group Base factories
2462
+ * @group Array
2284
2463
  */
2285
2464
  export const array = <ElementType extends AnyType>(
2286
2465
  element: ElementType,
@@ -2351,28 +2530,28 @@ export const array = <ElementType extends AnyType>(
2351
2530
  };
2352
2531
 
2353
2532
  /** ArrayType extends Type with an additional `element` property for reflection. */
2354
- export interface ArrayType<ElementType extends AnyType>
2355
- extends Type<
2356
- "Array",
2357
- ReadonlyArray<InferType<ElementType>>,
2358
- ReadonlyArray<InferInput<ElementType>>,
2359
- ArrayError<InferError<ElementType>>,
2360
- ReadonlyArray<InferParent<ElementType>>,
2361
- ArrayError<InferParentError<ElementType>>
2362
- > {
2533
+ export interface ArrayType<ElementType extends AnyType> extends Type<
2534
+ "Array",
2535
+ ReadonlyArray<InferType<ElementType>>,
2536
+ ReadonlyArray<InferInput<ElementType>>,
2537
+ ArrayError<InferError<ElementType>>,
2538
+ ReadonlyArray<InferParent<ElementType>>,
2539
+ ArrayError<InferParentError<ElementType>>
2540
+ > {
2363
2541
  readonly element: ElementType;
2364
2542
  }
2365
2543
 
2366
- export interface ArrayError<Error extends TypeError = TypeError>
2367
- extends TypeErrorWithReason<
2368
- "Array",
2369
- | { readonly kind: "NotArray" }
2370
- | {
2371
- readonly kind: "Element";
2372
- readonly index: number;
2373
- readonly error: Error;
2374
- }
2375
- > {}
2544
+ export interface ArrayError<
2545
+ Error extends TypeError = TypeError,
2546
+ > extends TypeErrorWithReason<
2547
+ "Array",
2548
+ | { readonly kind: "NotArray" }
2549
+ | {
2550
+ readonly kind: "Element";
2551
+ readonly index: number;
2552
+ readonly error: Error;
2553
+ }
2554
+ > {}
2376
2555
 
2377
2556
  export const formatArrayError = <Error extends TypeError>(
2378
2557
  formatTypeError: TypeErrorFormatter<Error>,
@@ -2398,7 +2577,7 @@ export const formatArrayError = <Error extends TypeError>(
2398
2577
  * const result2 = NumberSet.from(new Set(["a", "b"])); // err(...)
2399
2578
  * ```
2400
2579
  *
2401
- * @category Base Factories
2580
+ * @group Base factories
2402
2581
  */
2403
2582
  export const set = <ElementType extends AnyType>(
2404
2583
  element: ElementType,
@@ -2469,28 +2648,28 @@ export const set = <ElementType extends AnyType>(
2469
2648
  };
2470
2649
 
2471
2650
  /** SetType extends Type with an additional `element` property for reflection. */
2472
- export interface SetType<ElementType extends AnyType>
2473
- extends Type<
2474
- "Set",
2475
- ReadonlySet<InferType<ElementType>>,
2476
- ReadonlySet<InferInput<ElementType>>,
2477
- SetError<InferError<ElementType>>,
2478
- ReadonlySet<InferParent<ElementType>>,
2479
- SetError<InferParentError<ElementType>>
2480
- > {
2651
+ export interface SetType<ElementType extends AnyType> extends Type<
2652
+ "Set",
2653
+ ReadonlySet<InferType<ElementType>>,
2654
+ ReadonlySet<InferInput<ElementType>>,
2655
+ SetError<InferError<ElementType>>,
2656
+ ReadonlySet<InferParent<ElementType>>,
2657
+ SetError<InferParentError<ElementType>>
2658
+ > {
2481
2659
  readonly element: ElementType;
2482
2660
  }
2483
2661
 
2484
- export interface SetError<Error extends TypeError = TypeError>
2485
- extends TypeErrorWithReason<
2486
- "Set",
2487
- | { readonly kind: "NotSet" }
2488
- | {
2489
- readonly kind: "Element";
2490
- readonly index: number;
2491
- readonly error: Error;
2492
- }
2493
- > {}
2662
+ export interface SetError<
2663
+ Error extends TypeError = TypeError,
2664
+ > extends TypeErrorWithReason<
2665
+ "Set",
2666
+ | { readonly kind: "NotSet" }
2667
+ | {
2668
+ readonly kind: "Element";
2669
+ readonly index: number;
2670
+ readonly error: Error;
2671
+ }
2672
+ > {}
2494
2673
 
2495
2674
  export const formatSetError = <Error extends TypeError>(
2496
2675
  formatTypeError: TypeErrorFormatter<Error>,
@@ -2528,8 +2707,8 @@ export const formatSetError = <Error extends TypeError>(
2528
2707
  * StringToNumberRecord.from({ a: "x", b: 2 });
2529
2708
  * ```
2530
2709
  *
2531
- * @category Base Factories
2532
- * @category Object
2710
+ * @group Base factories
2711
+ * @group Object
2533
2712
  */
2534
2713
  export const record = <
2535
2714
  KeyName extends TypeName,
@@ -2576,7 +2755,7 @@ export const record = <
2576
2755
  type: "Record",
2577
2756
  value,
2578
2757
  reason: { kind: "Key", key: rawKey, error: keyResult.error },
2579
- } as IntentionalNever);
2758
+ } as never);
2580
2759
  }
2581
2760
 
2582
2761
  const valueResult = valueType.fromUnknown(rawValue);
@@ -2587,12 +2766,12 @@ export const record = <
2587
2766
  reason: {
2588
2767
  kind: "Value",
2589
2768
  key: rawKey,
2590
- error: valueResult.error as IntentionalNever,
2769
+ error: valueResult.error as never,
2591
2770
  },
2592
2771
  });
2593
2772
  }
2594
2773
 
2595
- result[keyResult.value] = valueResult.value as IntentionalNever;
2774
+ result[keyResult.value] = valueResult.value as never;
2596
2775
  }
2597
2776
 
2598
2777
  return ok(result);
@@ -2649,7 +2828,7 @@ export const record = <
2649
2828
  }),
2650
2829
  key: keyType,
2651
2830
  value: valueType,
2652
- } as IntentionalNever;
2831
+ } as never;
2653
2832
  };
2654
2833
 
2655
2834
  /**
@@ -2665,13 +2844,13 @@ export interface RecordType<
2665
2844
  KeyParentError extends TypeError,
2666
2845
  Value extends AnyType = AnyType,
2667
2846
  > extends Type<
2668
- "Record",
2669
- Readonly<Record<KeyT, InferType<Value>>>,
2670
- Readonly<Record<KeyInput, InferInput<Value>>>,
2671
- RecordError<KeyError, InferError<Value>>,
2672
- Readonly<Record<KeyParent, InferParent<Value>>>,
2673
- RecordError<KeyParentError, InferParentError<Value>>
2674
- > {
2847
+ "Record",
2848
+ Readonly<Record<KeyT, InferType<Value>>>,
2849
+ Readonly<Record<KeyInput, InferInput<Value>>>,
2850
+ RecordError<KeyError, InferError<Value>>,
2851
+ Readonly<Record<KeyParent, InferParent<Value>>>,
2852
+ RecordError<KeyParentError, InferParentError<Value>>
2853
+ > {
2675
2854
  readonly key: Type<
2676
2855
  KeyName,
2677
2856
  KeyT,
@@ -2687,19 +2866,19 @@ export interface RecordError<
2687
2866
  KeyError extends TypeError = TypeError,
2688
2867
  ValueError extends TypeError = TypeError,
2689
2868
  > extends TypeErrorWithReason<
2690
- "Record",
2691
- | { readonly kind: "NotRecord" }
2692
- | {
2693
- readonly kind: "Key";
2694
- readonly key: unknown;
2695
- readonly error: KeyError;
2696
- }
2697
- | {
2698
- readonly kind: "Value";
2699
- readonly key: unknown;
2700
- readonly error: ValueError;
2701
- }
2702
- > {}
2869
+ "Record",
2870
+ | { readonly kind: "NotRecord" }
2871
+ | {
2872
+ readonly kind: "Key";
2873
+ readonly key: unknown;
2874
+ readonly error: KeyError;
2875
+ }
2876
+ | {
2877
+ readonly kind: "Value";
2878
+ readonly key: unknown;
2879
+ readonly error: ValueError;
2880
+ }
2881
+ > {}
2703
2882
 
2704
2883
  export const formatRecordError = <Error extends TypeError>(
2705
2884
  formatTypeError: TypeErrorFormatter<Error>,
@@ -2733,22 +2912,22 @@ export const formatRecordError = <Error extends TypeError>(
2733
2912
  *
2734
2913
  * https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures
2735
2914
  *
2736
- * ### Examples
2915
+ * ### Example
2737
2916
  *
2738
- * #### Basic Object Validation
2917
+ * ## Basic Object Validation
2739
2918
  *
2740
2919
  * ```ts
2741
2920
  * const User = object({
2742
2921
  * name: NonEmptyTrimmedString,
2743
2922
  * age: PositiveNumber,
2744
2923
  * });
2745
- * type User = typeof User.Type;
2924
+ * interface User extends InferType<typeof User> {}
2746
2925
  *
2747
2926
  * const result = User.from({ name: "John", age: 30 }); // ok({ name: "John", age: 30 })
2748
2927
  * const error = User.from({ name: "John", age: -5 }); // err
2749
2928
  * ```
2750
2929
  *
2751
- * #### Optional Properties
2930
+ * ## Optional Properties
2752
2931
  *
2753
2932
  * In this example the `age` property is marked as optional using
2754
2933
  * {@link optional}.
@@ -2758,10 +2937,10 @@ export const formatRecordError = <Error extends TypeError>(
2758
2937
  * name: NonEmptyString, // Required
2759
2938
  * age: optional(PositiveNumber), // Optional
2760
2939
  * });
2761
- * type User = typeof User.Type;
2940
+ * interface User extends InferType<typeof User> {}
2762
2941
  * ```
2763
2942
  *
2764
- * #### Allowing Additional Properties
2943
+ * ## Allowing Additional Properties
2765
2944
  *
2766
2945
  * ```ts
2767
2946
  * const UserWithAnyExtraProperties = object(
@@ -2780,7 +2959,7 @@ export const formatRecordError = <Error extends TypeError>(
2780
2959
  * });
2781
2960
  * ```
2782
2961
  *
2783
- * #### Combining Fixed and Flexible Properties
2962
+ * ## Combining Fixed and Flexible Properties
2784
2963
  *
2785
2964
  * ```ts
2786
2965
  * const NumberDictionary = object(
@@ -2817,13 +2996,14 @@ export const formatRecordError = <Error extends TypeError>(
2817
2996
  * );
2818
2997
  * ```
2819
2998
  *
2820
- * @category Base Factories
2821
- * @category Object
2999
+ * @group Base factories
3000
+ * @group Object
2822
3001
  */
2823
3002
  export function object<Props extends Record<string, AnyType>>(
2824
3003
  props: Props,
2825
3004
  ): ObjectType<Props>;
2826
3005
 
3006
+ /** With additional record properties. */
2827
3007
  export function object<
2828
3008
  Props extends Record<string, AnyType>,
2829
3009
  KeyName extends TypeName,
@@ -2886,9 +3066,9 @@ export function object(
2886
3066
 
2887
3067
  const propResult = props[key].fromUnknown(value[key]);
2888
3068
  if (!propResult.ok) {
2889
- errors[key] = propResult.error as IntentionalNever;
3069
+ errors[key] = propResult.error as never;
2890
3070
  } else {
2891
- result[key] = propResult.value as IntentionalNever;
3071
+ result[key] = propResult.value as never;
2892
3072
  }
2893
3073
  }
2894
3074
 
@@ -2906,7 +3086,7 @@ export function object(
2906
3086
  reason: {
2907
3087
  kind: "IndexKey",
2908
3088
  key,
2909
- error: keyResult.error as IntentionalNever,
3089
+ error: keyResult.error as never,
2910
3090
  },
2911
3091
  });
2912
3092
  }
@@ -2919,12 +3099,12 @@ export function object(
2919
3099
  reason: {
2920
3100
  kind: "IndexValue",
2921
3101
  key,
2922
- error: valueResult.error as IntentionalNever,
3102
+ error: valueResult.error as never,
2923
3103
  },
2924
3104
  });
2925
3105
  }
2926
3106
 
2927
- result[keyResult.value] = valueResult.value as IntentionalNever;
3107
+ result[keyResult.value] = valueResult.value as never;
2928
3108
  }
2929
3109
  } else if (extraKeys.length > 0) {
2930
3110
  return err({
@@ -3011,15 +3191,14 @@ export function object(
3011
3191
  * ObjectType extends {@link Type} with an additional `props` property for
3012
3192
  * reflection.
3013
3193
  */
3014
- export interface ObjectType<Props extends Record<string, AnyType>>
3015
- extends Type<
3016
- "Object",
3017
- Readonly<ObjectT<Props>>,
3018
- Readonly<ObjectInput<Props>>,
3019
- ObjectError<{ [K in keyof Props]: InferError<Props[K]> }>,
3020
- Readonly<ObjectParent<Props>>,
3021
- ObjectError<{ [K in keyof Props]: InferParentError<Props[K]> }>
3022
- > {
3194
+ export interface ObjectType<Props extends Record<string, AnyType>> extends Type<
3195
+ "Object",
3196
+ Readonly<ObjectT<Props>>,
3197
+ Readonly<ObjectInput<Props>>,
3198
+ ObjectError<{ [K in keyof Props]: InferError<Props[K]> }>,
3199
+ Readonly<ObjectParent<Props>>,
3200
+ ObjectError<{ [K in keyof Props]: InferParentError<Props[K]> }>
3201
+ > {
3023
3202
  readonly props: Props;
3024
3203
  }
3025
3204
 
@@ -3065,20 +3244,20 @@ type OptionalKeys<Props extends Record<string, AnyType>> = {
3065
3244
  export interface ObjectError<
3066
3245
  PropsErrors extends Record<string, TypeError> = Record<string, TypeError>,
3067
3246
  > extends TypeErrorWithReason<
3068
- "Object",
3069
- | { readonly kind: "NotObject" }
3070
- | {
3071
- readonly kind: "Props";
3072
- readonly errors: Partial<PropsErrors>;
3073
- }
3074
- | { readonly kind: "ExtraKeys"; readonly extraKeys: ReadonlyArray<string> }
3075
- > {}
3247
+ "Object",
3248
+ | { readonly kind: "NotObject" }
3249
+ | {
3250
+ readonly kind: "Props";
3251
+ readonly errors: Partial<PropsErrors>;
3252
+ }
3253
+ | { readonly kind: "ExtraKeys"; readonly extraKeys: ReadonlyArray<string> }
3254
+ > {}
3076
3255
 
3077
3256
  /**
3078
3257
  * Merge Error and ParentError into one ObjectError so tooltips and error
3079
3258
  * messages are easier to read.
3080
3259
  *
3081
- * @category Utilities
3260
+ * @group Utilities
3082
3261
  */
3083
3262
  export type MergeObjectTypeErrors<T extends ObjectType<any>> =
3084
3263
  T extends ObjectType<infer Props>
@@ -3118,23 +3297,22 @@ export interface ObjectWithRecordType<
3118
3297
  KeyParentError extends TypeError,
3119
3298
  Value extends AnyType,
3120
3299
  > extends Type<
3121
- "ObjectWithRecord",
3122
- Readonly<ObjectT<Props>> & Readonly<Record<KeyT, InferType<Value>>>,
3123
- Readonly<ObjectInput<Props>> &
3124
- Readonly<Record<KeyInput, InferInput<Value>>>,
3125
- ObjectWithRecordError<
3126
- { [K in keyof Props]: InferError<Props[K]> },
3127
- KeyError,
3128
- InferError<Value>
3129
- >,
3130
- Readonly<ObjectParent<Props>> &
3131
- Readonly<Record<KeyParent, InferParent<Value>>>,
3132
- ObjectWithRecordError<
3133
- { [K in keyof Props]: InferParentError<Props[K]> },
3134
- KeyParentError,
3135
- InferParentError<Value>
3136
- >
3137
- > {
3300
+ "ObjectWithRecord",
3301
+ Readonly<ObjectT<Props>> & Readonly<Record<KeyT, InferType<Value>>>,
3302
+ Readonly<ObjectInput<Props>> & Readonly<Record<KeyInput, InferInput<Value>>>,
3303
+ ObjectWithRecordError<
3304
+ { [K in keyof Props]: InferError<Props[K]> },
3305
+ KeyError,
3306
+ InferError<Value>
3307
+ >,
3308
+ Readonly<ObjectParent<Props>> &
3309
+ Readonly<Record<KeyParent, InferParent<Value>>>,
3310
+ ObjectWithRecordError<
3311
+ { [K in keyof Props]: InferParentError<Props[K]> },
3312
+ KeyParentError,
3313
+ InferParentError<Value>
3314
+ >
3315
+ > {
3138
3316
  readonly props: Props;
3139
3317
  readonly record: RecordType<
3140
3318
  KeyName,
@@ -3152,20 +3330,20 @@ export interface ObjectWithRecordError<
3152
3330
  KeyError extends TypeError = TypeError,
3153
3331
  ValueError extends TypeError = TypeError,
3154
3332
  > extends TypeErrorWithReason<
3155
- "ObjectWithRecord",
3156
- | { readonly kind: "NotObject" }
3157
- | { readonly kind: "Props"; readonly errors: Partial<PropsErrors> }
3158
- | {
3159
- readonly kind: "IndexKey";
3160
- readonly key: unknown;
3161
- readonly error: KeyError;
3162
- }
3163
- | {
3164
- readonly kind: "IndexValue";
3165
- readonly key: string;
3166
- readonly error: ValueError;
3167
- }
3168
- > {}
3333
+ "ObjectWithRecord",
3334
+ | { readonly kind: "NotObject" }
3335
+ | { readonly kind: "Props"; readonly errors: Partial<PropsErrors> }
3336
+ | {
3337
+ readonly kind: "IndexKey";
3338
+ readonly key: unknown;
3339
+ readonly error: KeyError;
3340
+ }
3341
+ | {
3342
+ readonly kind: "IndexValue";
3343
+ readonly key: string;
3344
+ readonly error: ValueError;
3345
+ }
3346
+ > {}
3169
3347
 
3170
3348
  export const formatObjectWithRecordError = <Error extends TypeError>(
3171
3349
  formatTypeError: TypeErrorFormatter<Error>,
@@ -3189,6 +3367,187 @@ export const formatObjectWithRecordError = <Error extends TypeError>(
3189
3367
  }
3190
3368
  });
3191
3369
 
3370
+ /**
3371
+ * Base interface for objects with a discriminant `type` property.
3372
+ *
3373
+ * This enables
3374
+ * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions | discriminated unions}
3375
+ * (also known as tagged unions) — a pattern where TypeScript uses a literal
3376
+ * `type` field to narrow union types automatically.
3377
+ *
3378
+ * ## Why Discriminated Unions?
3379
+ *
3380
+ * Discriminated unions model states that are **mutually exclusive**. Instead of
3381
+ * optional fields and boolean flags that can combine into invalid
3382
+ * configurations, each variant is a distinct type. This makes illegal states
3383
+ * unrepresentable — invalid combinations cannot exist, so bugs cannot create
3384
+ * them.
3385
+ *
3386
+ * Benefits:
3387
+ *
3388
+ * - **Self-documenting** — Union cases immediately show all possible states
3389
+ * - **Compile-time safety** — TypeScript enforces handling all cases
3390
+ * - **Refactoring-friendly** — Adding a new state breaks code that doesn't handle
3391
+ * it
3392
+ *
3393
+ * ### Example
3394
+ *
3395
+ * ```ts
3396
+ * // Bad: optional fields allow invalid states (no contact info at all)
3397
+ * interface Contact {
3398
+ * readonly email?: Email;
3399
+ * readonly phone?: Phone;
3400
+ * }
3401
+ *
3402
+ * // Good: discriminated union makes "at least one" explicit
3403
+ * interface EmailOnly extends Typed<"EmailOnly"> {
3404
+ * readonly email: Email;
3405
+ * }
3406
+ * interface PhoneOnly extends Typed<"PhoneOnly"> {
3407
+ * readonly phone: Phone;
3408
+ * }
3409
+ * interface EmailAndPhone extends Typed<"EmailAndPhone"> {
3410
+ * readonly email: Email;
3411
+ * readonly phone: Phone;
3412
+ * }
3413
+ *
3414
+ * type ContactInfo = EmailOnly | PhoneOnly | EmailAndPhone;
3415
+ * ```
3416
+ *
3417
+ * ```ts
3418
+ * interface Pending extends Typed<"Pending"> {
3419
+ * readonly createdAt: DateIso;
3420
+ * }
3421
+ * interface Shipped extends Typed<"Shipped"> {
3422
+ * readonly trackingNumber: TrackingNumber;
3423
+ * }
3424
+ * interface Delivered extends Typed<"Delivered"> {
3425
+ * readonly deliveredAt: DateIso;
3426
+ * }
3427
+ * interface Cancelled extends Typed<"Cancelled"> {
3428
+ * readonly reason: CancellationReason;
3429
+ * }
3430
+ *
3431
+ * type OrderState = Pending | Shipped | Delivered | Cancelled;
3432
+ *
3433
+ * // TypeScript enforces exhaustiveness via return type
3434
+ * const getStatusMessage = (state: OrderState): string => {
3435
+ * switch (state.type) {
3436
+ * case "Pending":
3437
+ * return "Order placed";
3438
+ * case "Shipped":
3439
+ * return `Shipped: ${state.trackingNumber}`;
3440
+ * case "Delivered":
3441
+ * return `Delivered on ${state.deliveredAt.toLocaleDateString()}`;
3442
+ * case "Cancelled":
3443
+ * return `Cancelled: ${state.reason}`;
3444
+ * }
3445
+ * };
3446
+ *
3447
+ * // For void functions, use exhaustiveCheck to ensure all cases are handled
3448
+ * const logState = (state: OrderState): void => {
3449
+ * switch (state.type) {
3450
+ * case "Pending":
3451
+ * console.log("Order placed");
3452
+ * break;
3453
+ * case "Shipped":
3454
+ * console.log(`Shipped: ${state.trackingNumber}`);
3455
+ * break;
3456
+ * case "Delivered":
3457
+ * console.log(
3458
+ * `Delivered on ${state.deliveredAt.toLocaleDateString()}`,
3459
+ * );
3460
+ * break;
3461
+ * case "Cancelled":
3462
+ * console.log(`Cancelled: ${state.reason}`);
3463
+ * break;
3464
+ * default:
3465
+ * exhaustiveCheck(state);
3466
+ * }
3467
+ * };
3468
+ * ```
3469
+ *
3470
+ * ## Why `type` (and not e.g. `_tag`)?
3471
+ *
3472
+ * Underscore-prefixing is meant to avoid clashing with domain properties, but
3473
+ * proper discriminated union design means the discriminant IS the domain
3474
+ * concept — there's no clash to avoid. The `type` prop name also aligns with
3475
+ * {@link Type}'s name. If an entity has a meaningful "type" (like product
3476
+ * category), model it as the discriminant itself:
3477
+ *
3478
+ * ```ts
3479
+ * interface Electronics extends Typed<"Electronics"> {
3480
+ * voltage: Voltage;
3481
+ * }
3482
+ * interface Clothing extends Typed<"Clothing"> {
3483
+ * size: Size;
3484
+ * }
3485
+ * type Product = Electronics | Clothing;
3486
+ * ```
3487
+ *
3488
+ * @see {@link exhaustiveCheck} to ensure all cases are handled in void functions.
3489
+ * @see {@link typed} for runtime-validated typed objects.
3490
+ */
3491
+ export interface Typed<T extends TypeName> {
3492
+ readonly type: T;
3493
+ }
3494
+
3495
+ /**
3496
+ * Creates a runtime-validated typed object with a `type` discriminant.
3497
+ *
3498
+ * ### Example
3499
+ *
3500
+ * ```ts
3501
+ * const Card = typed("Card", {
3502
+ * cardNumber: CardNumber,
3503
+ * expiry: DateIso,
3504
+ * });
3505
+ *
3506
+ * const Cash = typed("Cash", {
3507
+ * currency: NonEmptyTrimmedString,
3508
+ * });
3509
+ *
3510
+ * const Payment = union(Card, Cash);
3511
+ * type Payment = typeof Payment.Type;
3512
+ *
3513
+ * const result = Payment.fromUnknown(data);
3514
+ * if (result.ok) {
3515
+ * switch (result.value.type) {
3516
+ * case "Card":
3517
+ * console.log(result.value.cardNumber);
3518
+ * break;
3519
+ * case "Cash":
3520
+ * console.log(result.value.currency);
3521
+ * break;
3522
+ * }
3523
+ * }
3524
+ * ```
3525
+ *
3526
+ * @see {@link Typed} for type-only discrimination.
3527
+ */
3528
+ export function typed<Tag extends TypeName>(tag: Tag): TypedType<Tag>;
3529
+ /** With additional properties. */
3530
+ export function typed<
3531
+ Tag extends TypeName,
3532
+ Props extends Record<string, AnyType>,
3533
+ >(tag: Tag, props: Props): TypedType<Tag, Props>;
3534
+ export function typed<
3535
+ Tag extends TypeName,
3536
+ Props extends Record<string, AnyType>,
3537
+ >(tag: Tag, props?: Props): ObjectType<{ type: LiteralType<Tag> } & Props> {
3538
+ return object({ type: literal(tag), ...props } as {
3539
+ type: LiteralType<Tag>;
3540
+ } & Props);
3541
+ }
3542
+
3543
+ /** Return type of {@link typed}. */
3544
+ export type TypedType<
3545
+ Tag extends TypeName,
3546
+ Props extends Record<string, AnyType> = Record<never, never>,
3547
+ > = ObjectType<{ type: LiteralType<Tag> } & Props>;
3548
+
3549
+ // TODO: TypeError with "Error" suffix.
3550
+
3192
3551
  /**
3193
3552
  * Union {@link Type}.
3194
3553
  *
@@ -3211,12 +3570,13 @@ export const formatObjectWithRecordError = <Error extends TypeError>(
3211
3570
  * const result3 = StringOrNumber.from(42); // ok(42)
3212
3571
  * ```
3213
3572
  *
3214
- * @category Base Factories
3573
+ * @group Base factories
3215
3574
  */
3216
3575
  export function union<
3217
3576
  Members extends [AnyType, AnyType, ...ReadonlyArray<AnyType>],
3218
3577
  >(...members: Members): UnionType<Members>;
3219
3578
 
3579
+ /** With literal values. */
3220
3580
  export function union<
3221
3581
  Literals extends [Literal, Literal, ...ReadonlyArray<Literal>],
3222
3582
  >(
@@ -3271,20 +3631,22 @@ export function union(...args: ReadonlyArray<any>): any {
3271
3631
  * UnionType extends {@link Type} with an additional `members` property for
3272
3632
  * reflection.
3273
3633
  */
3274
- export interface UnionType<Members extends [AnyType, ...ReadonlyArray<AnyType>]>
3275
- extends Type<
3276
- "Union",
3277
- InferType<Members[number]>,
3278
- InferInput<Members[number]>,
3279
- UnionError<InferErrors<Members[number]>>,
3280
- InferInput<Members[number]>,
3281
- never
3282
- > {
3634
+ export interface UnionType<
3635
+ Members extends [AnyType, ...ReadonlyArray<AnyType>],
3636
+ > extends Type<
3637
+ "Union",
3638
+ InferType<Members[number]>,
3639
+ InferInput<Members[number]>,
3640
+ UnionError<InferErrors<Members[number]>>,
3641
+ InferInput<Members[number]>,
3642
+ never
3643
+ > {
3283
3644
  readonly members: Members;
3284
3645
  }
3285
3646
 
3286
- export interface UnionError<E extends TypeError = TypeError>
3287
- extends TypeError<"Union"> {
3647
+ export interface UnionError<
3648
+ E extends TypeError = TypeError,
3649
+ > extends TypeError<"Union"> {
3288
3650
  readonly errors: Array<E>;
3289
3651
  }
3290
3652
 
@@ -3304,6 +3666,110 @@ export const isUnionType = (
3304
3666
  ): t is UnionType<[AnyType, ...ReadonlyArray<AnyType>]> =>
3305
3667
  t.name === "Union" && Array.isArray((t as { members?: unknown }).members);
3306
3668
 
3669
+ /**
3670
+ * Creates a {@link Type} for {@link Result} values.
3671
+ *
3672
+ * Use for validating serialized Results from storage, APIs, or message passing.
3673
+ *
3674
+ * ### Example
3675
+ *
3676
+ * ```ts
3677
+ * const SyncResponse = result(
3678
+ * object({ timestamp: NonNegativeInt }),
3679
+ * typed("SyncError", { message: String }),
3680
+ * );
3681
+ *
3682
+ * // Validate response from worker or API
3683
+ * const validated = SyncResponse.from(JSON.parse(message));
3684
+ * if (!validated.ok) return validated; // validation error
3685
+ * // validated.value is Result<{ timestamp }, SyncError>
3686
+ * ```
3687
+ *
3688
+ * @group Composite factories
3689
+ */
3690
+ export const result = <OkType extends AnyType, ErrType extends AnyType>(
3691
+ okType: OkType,
3692
+ errType: ErrType,
3693
+ ): UnionType<
3694
+ [
3695
+ ObjectType<{ ok: LiteralType<true>; value: OkType }>,
3696
+ ObjectType<{ ok: LiteralType<false>; error: ErrType }>,
3697
+ ]
3698
+ > =>
3699
+ union(
3700
+ object({ ok: literal(true), value: okType }),
3701
+ object({ ok: literal(false), error: errType }),
3702
+ );
3703
+
3704
+ /**
3705
+ * A {@link result} type for `Result<unknown, unknown>`.
3706
+ *
3707
+ * Useful for serializing Results where the value and error types are unknown.
3708
+ *
3709
+ * @group Composite factories
3710
+ */
3711
+ export const UnknownResult = /*#__PURE__*/ result(Unknown, Unknown);
3712
+ export type UnknownResult = typeof UnknownResult.Type;
3713
+
3714
+ /**
3715
+ * Creates a {@link Type} for {@link NextResult} with three outcomes.
3716
+ *
3717
+ * Validates results where the producer responds with:
3718
+ *
3719
+ * - `Ok<A>` — produced a value
3720
+ * - `Err<Done<D>>` — completed normally with a done value
3721
+ * - `Err<E>` — failed with an error
3722
+ *
3723
+ * ### Example
3724
+ *
3725
+ * ```ts
3726
+ * const MyNextResult = nextResult(Item, MyError, Summary);
3727
+ *
3728
+ * const validated = MyNextResult.fromUnknown(data);
3729
+ * if (!validated.ok) return validated;
3730
+ *
3731
+ * const result = validated.value;
3732
+ * if (result.ok) {
3733
+ * console.log(result.value);
3734
+ * } else if (result.error.type === "Done") {
3735
+ * console.log("Done:", result.error.done);
3736
+ * } else {
3737
+ * console.error(result.error);
3738
+ * }
3739
+ * ```
3740
+ *
3741
+ * @group Composite factories
3742
+ */
3743
+ export const nextResult = <
3744
+ ValueType extends AnyType,
3745
+ ErrorType extends AnyType,
3746
+ DoneType extends AnyType,
3747
+ >(
3748
+ valueType: ValueType,
3749
+ errorType: ErrorType,
3750
+ doneType: DoneType,
3751
+ ): ReturnType<
3752
+ typeof result<
3753
+ ValueType,
3754
+ UnionType<[ErrorType, TypedType<"Done", { done: DoneType }>]>
3755
+ >
3756
+ > => result(valueType, union(errorType, typed("Done", { done: doneType })));
3757
+
3758
+ /**
3759
+ * A {@link nextResult} type for `NextResult<unknown, unknown, unknown>`.
3760
+ *
3761
+ * Useful for checking if a value is a {@link NextResult} via
3762
+ * `UnknownNextResult.is(value)`.
3763
+ *
3764
+ * @group Composite factories
3765
+ */
3766
+ export const UnknownNextResult = /*#__PURE__*/ nextResult(
3767
+ Unknown,
3768
+ Unknown,
3769
+ Unknown,
3770
+ );
3771
+ export type UnknownNextResult = typeof UnknownNextResult.Type;
3772
+
3307
3773
  /**
3308
3774
  * Recursive {@link Type}.
3309
3775
  *
@@ -3337,7 +3803,7 @@ export const isUnionType = (
3337
3803
  * );
3338
3804
  * ```
3339
3805
  *
3340
- * @category Base Factories
3806
+ * @group Base factories
3341
3807
  */
3342
3808
  export const recursive = <ParentType extends AnyType>(
3343
3809
  create: () => ParentType,
@@ -3374,15 +3840,14 @@ export const recursive = <ParentType extends AnyType>(
3374
3840
  } as RecursiveType<ParentType>;
3375
3841
  };
3376
3842
 
3377
- export interface RecursiveType<ParentType extends AnyType>
3378
- extends Type<
3379
- "Recursive",
3380
- InferType<ParentType>,
3381
- InferInput<ParentType>,
3382
- InferError<ParentType>,
3383
- InferParent<ParentType>,
3384
- InferParentError<ParentType>
3385
- > {
3843
+ export interface RecursiveType<ParentType extends AnyType> extends Type<
3844
+ "Recursive",
3845
+ InferType<ParentType>,
3846
+ InferInput<ParentType>,
3847
+ InferError<ParentType>,
3848
+ InferParent<ParentType>,
3849
+ InferParentError<ParentType>
3850
+ > {
3386
3851
  getParentType(): ParentType;
3387
3852
  }
3388
3853
 
@@ -3398,7 +3863,7 @@ export interface RecursiveType<ParentType extends AnyType>
3398
3863
  * NullOrString.from(42); // err(...)
3399
3864
  * ```
3400
3865
  *
3401
- * @category Base Factories
3866
+ * @group Base factories
3402
3867
  */
3403
3868
  export const nullOr = <T extends AnyType>(
3404
3869
  type: T,
@@ -3416,7 +3881,7 @@ export const nullOr = <T extends AnyType>(
3416
3881
  * UndefinedOrString.from(42); // err(...)
3417
3882
  * ```
3418
3883
  *
3419
- * @category Base Factories
3884
+ * @group Base factories
3420
3885
  */
3421
3886
  export const undefinedOr = <T extends AnyType>(
3422
3887
  type: T,
@@ -3438,7 +3903,7 @@ export const undefinedOr = <T extends AnyType>(
3438
3903
  * NullishOrString.from(42); // err(...)
3439
3904
  * ```
3440
3905
  *
3441
- * @category Base Factories
3906
+ * @group Base factories
3442
3907
  */
3443
3908
  export const nullishOr = <T extends AnyType>(
3444
3909
  type: T,
@@ -3459,7 +3924,7 @@ export const nullishOr = <T extends AnyType>(
3459
3924
  * const error = NameAndAge.from(["Alice", -10]); // err
3460
3925
  * ```
3461
3926
  *
3462
- * @category Base Factories
3927
+ * @group Base factories
3463
3928
  */
3464
3929
  export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3465
3930
  ...elements: Elements
@@ -3489,7 +3954,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3489
3954
  reason: {
3490
3955
  kind: "Element",
3491
3956
  index: i,
3492
- error: elementResult.error as IntentionalNever,
3957
+ error: elementResult.error as never,
3493
3958
  },
3494
3959
  });
3495
3960
  }
@@ -3520,7 +3985,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3520
3985
  reason: {
3521
3986
  kind: "Element",
3522
3987
  index: i,
3523
- error: elementResult.error as IntentionalNever,
3988
+ error: elementResult.error as never,
3524
3989
  },
3525
3990
  });
3526
3991
  }
@@ -3536,7 +4001,7 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3536
4001
  fromParent,
3537
4002
  }),
3538
4003
  elements,
3539
- } as IntentionalNever;
4004
+ } as never;
3540
4005
  };
3541
4006
 
3542
4007
  /**
@@ -3546,26 +4011,27 @@ export const tuple = <Elements extends [AnyType, ...ReadonlyArray<AnyType>]>(
3546
4011
  export interface TupleType<
3547
4012
  Elements extends readonly [AnyType, ...ReadonlyArray<AnyType>],
3548
4013
  > extends Type<
3549
- "Tuple",
3550
- readonly [...{ [K in keyof Elements]: InferType<Elements[K]> }],
3551
- readonly [...{ [K in keyof Elements]: InferInput<Elements[K]> }],
3552
- TupleError<{ [K in keyof Elements]: InferError<Elements[K]> }[number]>,
3553
- readonly [...{ [K in keyof Elements]: InferParent<Elements[K]> }],
3554
- TupleError<{ [K in keyof Elements]: InferParentError<Elements[K]> }[number]>
3555
- > {
4014
+ "Tuple",
4015
+ readonly [...{ [K in keyof Elements]: InferType<Elements[K]> }],
4016
+ readonly [...{ [K in keyof Elements]: InferInput<Elements[K]> }],
4017
+ TupleError<{ [K in keyof Elements]: InferError<Elements[K]> }[number]>,
4018
+ readonly [...{ [K in keyof Elements]: InferParent<Elements[K]> }],
4019
+ TupleError<{ [K in keyof Elements]: InferParentError<Elements[K]> }[number]>
4020
+ > {
3556
4021
  readonly elements: Elements;
3557
4022
  }
3558
4023
 
3559
- export interface TupleError<ElementError extends TypeError = TypeError>
3560
- extends TypeErrorWithReason<
3561
- "Tuple",
3562
- | { readonly kind: "InvalidLength"; readonly expected: number }
3563
- | {
3564
- readonly kind: "Element";
3565
- readonly index: number;
3566
- readonly error: ElementError;
3567
- }
3568
- > {}
4024
+ export interface TupleError<
4025
+ ElementError extends TypeError = TypeError,
4026
+ > extends TypeErrorWithReason<
4027
+ "Tuple",
4028
+ | { readonly kind: "InvalidLength"; readonly expected: number }
4029
+ | {
4030
+ readonly kind: "Element";
4031
+ readonly index: number;
4032
+ readonly error: ElementError;
4033
+ }
4034
+ > {}
3569
4035
 
3570
4036
  export const formatTupleError = <Error extends TypeError>(
3571
4037
  formatTypeError: TypeErrorFormatter<Error>,
@@ -3594,9 +4060,9 @@ export const formatTupleError = <Error extends TypeError>(
3594
4060
  *
3595
4061
  * https://www.sqlite.org/c3ref/int64.html
3596
4062
  *
3597
- * @category Number
4063
+ * @group Number
3598
4064
  */
3599
- export const Int64 = brand("Int64", BigInt, (value) =>
4065
+ export const Int64 = /*#__PURE__*/ brand("Int64", BigInt, (value) =>
3600
4066
  value >= -9223372036854775808n && value <= 9223372036854775807n
3601
4067
  ? ok(value)
3602
4068
  : err<Int64Error>({ type: "Int64", value }),
@@ -3604,25 +4070,29 @@ export const Int64 = brand("Int64", BigInt, (value) =>
3604
4070
  export type Int64 = typeof Int64.Type;
3605
4071
  export interface Int64Error extends TypeError<"Int64"> {}
3606
4072
 
3607
- export const formatInt64Error = createTypeErrorFormatter<Int64Error>(
3608
- (error) =>
3609
- `The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
3610
- );
4073
+ export const formatInt64Error =
4074
+ /*#__PURE__*/ createTypeErrorFormatter<Int64Error>(
4075
+ (error) =>
4076
+ `The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
4077
+ );
3611
4078
 
3612
4079
  /**
3613
4080
  * Stringified {@link Int64}.
3614
4081
  *
3615
- * @category String
4082
+ * @group String
3616
4083
  */
3617
- export const Int64String = brand("Int64", NonEmptyTrimmedString, (value) =>
3618
- trySync(
3619
- () => {
3620
- const maybeInt = globalThis.BigInt(value);
3621
- Int64.orThrow(maybeInt);
3622
- return value;
3623
- },
3624
- (): Int64StringError => ({ type: "Int64String", value }),
3625
- ),
4084
+ export const Int64String = /*#__PURE__*/ brand(
4085
+ "Int64",
4086
+ NonEmptyTrimmedString,
4087
+ (value) =>
4088
+ trySync(
4089
+ () => {
4090
+ const maybeInt = globalThis.BigInt(value);
4091
+ Int64.orThrow(maybeInt);
4092
+ return value;
4093
+ },
4094
+ (): Int64StringError => ({ type: "Int64String", value }),
4095
+ ),
3626
4096
  );
3627
4097
 
3628
4098
  export type Int64String = typeof Int64String.Type;
@@ -3630,10 +4100,20 @@ export type Int64String = typeof Int64String.Type;
3630
4100
  export interface Int64StringError extends TypeError<"Int64String"> {}
3631
4101
 
3632
4102
  export const formatInt64StringError =
3633
- createTypeErrorFormatter<Int64StringError>(
4103
+ /*#__PURE__*/ createTypeErrorFormatter<Int64StringError>(
3634
4104
  (error) => `The value ${error.value} is not a valid Int64 string.`,
3635
4105
  );
3636
4106
 
4107
+ /**
4108
+ * Validated JSON-compatible value.
4109
+ *
4110
+ * This is the output side of JSON data in Evolu. It uses {@link FiniteNumber}
4111
+ * instead of `number` because JSON numbers are expected to be finite once the
4112
+ * value has been parsed or validated.
4113
+ *
4114
+ * Compare with {@link JsonValueInput}, which represents caller-provided input
4115
+ * before validation.
4116
+ */
3637
4117
  export type JsonValue =
3638
4118
  | string
3639
4119
  | FiniteNumber
@@ -3642,6 +4122,19 @@ export type JsonValue =
3642
4122
  | JsonArray
3643
4123
  | JsonObject;
3644
4124
 
4125
+ /**
4126
+ * JSON-compatible input value before validation.
4127
+ *
4128
+ * This is broader than {@link JsonValue} because inputs arrive as ordinary
4129
+ * JavaScript values, so numbers are typed as `number` before validation can
4130
+ * narrow them to {@link FiniteNumber}.
4131
+ *
4132
+ * That means `JsonValueInput` can temporarily contain numbers that are lossy in
4133
+ * JSON serialization. For example, `JSON.stringify(NaN)` and
4134
+ * `JSON.stringify(Infinity)` produce `null`, and `JSON.stringify(-0)` produces
4135
+ * `0`. Use {@link JsonValue} when the value must already satisfy JSON numeric
4136
+ * constraints.
4137
+ */
3645
4138
  export type JsonValueInput =
3646
4139
  | string
3647
4140
  | number
@@ -3675,9 +4168,9 @@ export type JsonArrayInput = ReadonlyArray<JsonValueInput>;
3675
4168
  * JSON-compatible value: string, {@link FiniteNumber}, boolean, null,
3676
4169
  * {@link JsonArray}, or {@link JsonObject}.
3677
4170
  *
3678
- * @category Base Types
4171
+ * @group Base Types
3679
4172
  */
3680
- export const JsonValue = recursive(
4173
+ export const JsonValue = /*#__PURE__*/ recursive(
3681
4174
  (): UnionType<
3682
4175
  [
3683
4176
  typeof String,
@@ -3717,16 +4210,16 @@ export const JsonValue = recursive(
3717
4210
  /**
3718
4211
  * JSON-compatible array of {@link JsonValue} elements.
3719
4212
  *
3720
- * @category Array
4213
+ * @group Array
3721
4214
  */
3722
- export const JsonArray = array(JsonValue);
4215
+ export const JsonArray = /*#__PURE__*/ array(JsonValue);
3723
4216
 
3724
4217
  /**
3725
4218
  * JSON-compatible object with string keys and {@link JsonValue} values.
3726
4219
  *
3727
- * @category Object
4220
+ * @group Object
3728
4221
  */
3729
- export const JsonObject = record(String, JsonValue);
4222
+ export const JsonObject = /*#__PURE__*/ record(String, JsonValue);
3730
4223
 
3731
4224
  export const parseJson = (value: string): Result<JsonValue, JsonError> =>
3732
4225
  trySync(
@@ -3748,9 +4241,9 @@ export const parseJson = (value: string): Result<JsonValue, JsonError> =>
3748
4241
  * const error = Json.from("invalid json"); // err
3749
4242
  * ```
3750
4243
  *
3751
- * @category String
4244
+ * @group String
3752
4245
  */
3753
- export const Json = brand("Json", String, (value) => {
4246
+ export const Json = /*#__PURE__*/ brand("Json", String, (value) => {
3754
4247
  const result = parseJson(value);
3755
4248
  if (!result.ok) return result;
3756
4249
  return ok(value);
@@ -3762,9 +4255,10 @@ export interface JsonError extends TypeError<"Json"> {
3762
4255
  readonly message: string;
3763
4256
  }
3764
4257
 
3765
- export const formatJsonError = createTypeErrorFormatter<JsonError>(
3766
- (error) => `Invalid JSON: ${error.value}. Error: ${error.message}`,
3767
- );
4258
+ export const formatJsonError =
4259
+ /*#__PURE__*/ createTypeErrorFormatter<JsonError>(
4260
+ (error) => `Invalid JSON: ${error.value}. Error: ${error.message}`,
4261
+ );
3768
4262
 
3769
4263
  export const jsonValueToJson = (value: JsonValue): Json =>
3770
4264
  JSON.stringify(value) as Json;
@@ -3792,7 +4286,7 @@ export const jsonToJsonValue = (value: Json): JsonValue =>
3792
4286
  * name: NonEmptyString100,
3793
4287
  * age: FiniteNumber,
3794
4288
  * });
3795
- * type Person = typeof Person.Type;
4289
+ * interface Person extends InferType<typeof Person> {}
3796
4290
  *
3797
4291
  * const [PersonJson, personToPersonJson, personJsonToPerson] = json(
3798
4292
  * Person,
@@ -3833,11 +4327,7 @@ export const json = <T extends AnyType, Name extends TypeName>(
3833
4327
  return ok(value);
3834
4328
  }) as BrandType<typeof String, Name, JsonError | InferErrors<T>, StringError>;
3835
4329
 
3836
- return [
3837
- BrandedJsonType,
3838
- jsonValueToJson as IntentionalNever,
3839
- jsonToJsonValue as IntentionalNever,
3840
- ];
4330
+ return [BrandedJsonType, jsonValueToJson as never, jsonToJsonValue as never];
3841
4331
  };
3842
4332
 
3843
4333
  /**
@@ -3869,15 +4359,14 @@ export const optional = <T extends AnyType>(type: T): OptionalType<T> => ({
3869
4359
  parent: type,
3870
4360
  });
3871
4361
 
3872
- export interface OptionalType<T extends AnyType>
3873
- extends Type<
3874
- "Optional",
3875
- InferType<T>,
3876
- InferInput<T>,
3877
- InferError<T>,
3878
- InferParent<T>,
3879
- InferParentError<T>
3880
- > {
4362
+ export interface OptionalType<T extends AnyType> extends Type<
4363
+ "Optional",
4364
+ InferType<T>,
4365
+ InferInput<T>,
4366
+ InferError<T>,
4367
+ InferParent<T>,
4368
+ InferParentError<T>
4369
+ > {
3881
4370
  readonly parent: T;
3882
4371
  }
3883
4372
 
@@ -3910,7 +4399,7 @@ export const isOptionalType = (x: unknown): x is OptionalType<any> =>
3910
4399
  * PartialUser.from({ age: -5 });
3911
4400
  * ```
3912
4401
  *
3913
- * @category Object
4402
+ * @group Object
3914
4403
  */
3915
4404
  export const partial = <Props extends Record<string, AnyType>>(
3916
4405
  props: Props,
@@ -3930,7 +4419,7 @@ export const partial = <Props extends Record<string, AnyType>>(
3930
4419
  * entirely, or set it to `null`, or set it to the non-null member of the
3931
4420
  * union.
3932
4421
  *
3933
- * @category Object
4422
+ * @group Object
3934
4423
  */
3935
4424
  export const nullableToOptional = <Props extends Record<string, AnyType>>(
3936
4425
  props: Props,
@@ -3976,12 +4465,12 @@ export type NullTypeInMembers<Members extends [AnyType, ...Array<AnyType>]> =
3976
4465
  /**
3977
4466
  * Create a new `object` {@link Type} by omitting some keys.
3978
4467
  *
3979
- * @category Object
4468
+ * @group Object
3980
4469
  */
3981
- export function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(
4470
+ export const omit = <T extends ObjectType<any>, Keys extends keyof T["props"]>(
3982
4471
  objectType: T,
3983
4472
  ...keys: ReadonlyArray<Keys>
3984
- ): ObjectType<Omit<T["props"], Keys>> {
4473
+ ): ObjectType<Omit<T["props"], Keys>> => {
3985
4474
  const newProps = {} as Omit<T["props"], Keys>;
3986
4475
 
3987
4476
  for (const key in objectType.props) {
@@ -3991,41 +4480,7 @@ export function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(
3991
4480
  }
3992
4481
  }
3993
4482
  return object(newProps);
3994
- }
3995
-
3996
- export const maxMutationSize = 655360;
3997
-
3998
- /**
3999
- * Evolu has to limit the maximum mutation size. Otherwise, sync couldn't use
4000
- * the `maxProtocolMessageRangesSize`. The max size is 640KB in bytes, measured
4001
- * via MessagePack. Evolu Protocol DbChange will be smaller thanks to various
4002
- * optimizations.
4003
- */
4004
- export const validMutationSize = <T extends AnyType>(
4005
- type: T,
4006
- ): BrandType<T, "ValidMutationSize", ValidMutationSizeError, InferErrors<T>> =>
4007
- brand("ValidMutationSize", type, (value) =>
4008
- pack(value).byteLength <= maxMutationSize
4009
- ? ok(value)
4010
- : err<ValidMutationSizeError>({ type: "ValidMutationSize", value }),
4011
- );
4012
-
4013
- export interface ValidMutationSizeError
4014
- extends TypeError<"ValidMutationSize"> {}
4015
-
4016
- export const formatValidMutationSizeError =
4017
- createTypeErrorFormatter<ValidMutationSizeError>(
4018
- (error) =>
4019
- `The mutation size exceeds the maximum limit of ${maxMutationSize} bytes. The provided mutation has a size of ${pack(error.value).byteLength} bytes.`,
4020
- );
4021
-
4022
- export type ValidMutationSize<Props extends Record<string, AnyType>> =
4023
- BrandType<
4024
- ObjectType<Props>,
4025
- "ValidMutationSize",
4026
- ValidMutationSizeError,
4027
- InferErrors<ObjectType<Props>>
4028
- >;
4483
+ };
4029
4484
 
4030
4485
  /**
4031
4486
  * Union of all `TypeError`s defined in the `Type.ts` file, including base type
@@ -4038,7 +4493,7 @@ export type ValidMutationSize<Props extends Record<string, AnyType>> =
4038
4493
  * Used by {@link createFormatTypeError} to generate human-readable error
4039
4494
  * messages.
4040
4495
  *
4041
- * @category Utilities
4496
+ * @group Utilities
4042
4497
  */
4043
4498
  export type TypeErrors<ExtraErrors extends TypeError = never> =
4044
4499
  | StringError
@@ -4079,7 +4534,6 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
4079
4534
  | Int64Error
4080
4535
  | Int64StringError
4081
4536
  | JsonError
4082
- | ValidMutationSizeError
4083
4537
  | ExtraErrors
4084
4538
  // Composite errors
4085
4539
  | ArrayError<TypeErrors<ExtraErrors>>
@@ -4172,7 +4626,7 @@ export type TypeErrors<ExtraErrors extends TypeError = never> =
4172
4626
  * };
4173
4627
  * ```
4174
4628
  *
4175
- * @category Utilities
4629
+ * @group Utilities
4176
4630
  */
4177
4631
  export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4178
4632
  extraFormatter?: TypeErrorFormatter<ExtraErrors>,
@@ -4261,8 +4715,6 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4261
4715
  return formatInt64StringError(error);
4262
4716
  case "Json":
4263
4717
  return formatJsonError(error);
4264
- case "ValidMutationSize":
4265
- return formatValidMutationSizeError(error);
4266
4718
  // Composite Types
4267
4719
  case "SimplePassword":
4268
4720
  return formatSimplePasswordError(formatTypeError)(error);
@@ -4295,25 +4747,25 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
4295
4747
  * Converts an Evolu {@link TypeError} to Standard Schema V1 issues format.
4296
4748
  *
4297
4749
  * This function recursively converts Evolu's typed errors into the Standard
4298
- * Schema issue format with proper path tracking for nested structures.
4750
+ * Schema issue format with proper path tracking for nested structures. The
4751
+ * `message` field contains the JSON-serialized typed error, which users can
4752
+ * deserialize and format using appropriate {@link TypeErrorFormatter}s.
4299
4753
  *
4300
- * @category Utilities
4754
+ * @group Utilities
4301
4755
  */
4302
4756
  export const typeErrorToStandardSchemaIssues = <
4303
4757
  ExtraErrors extends TypeError = never,
4304
4758
  >(
4305
4759
  error: TypeErrors<ExtraErrors>,
4306
- formatTypeError: TypeErrorFormatter<TypeErrors<ExtraErrors>>,
4307
4760
  path: ReadonlyArray<PropertyKey> = [],
4308
4761
  ): ReadonlyArray<StandardSchemaV1.Issue> => {
4309
4762
  if (error.type === "Array") {
4310
4763
  const arrayError = error as ArrayError;
4311
4764
  if (arrayError.reason.kind === "NotArray") {
4312
- return [{ message: formatTypeError(error), path }];
4765
+ return [{ message: JSON.stringify(error), path }];
4313
4766
  }
4314
4767
  return typeErrorToStandardSchemaIssues(
4315
4768
  arrayError.reason.error as TypeErrors<ExtraErrors>,
4316
- formatTypeError,
4317
4769
  [...path, arrayError.reason.index],
4318
4770
  );
4319
4771
  }
@@ -4321,11 +4773,10 @@ export const typeErrorToStandardSchemaIssues = <
4321
4773
  if (error.type === "Set") {
4322
4774
  const setError = error as SetError;
4323
4775
  if (setError.reason.kind === "NotSet") {
4324
- return [{ message: formatTypeError(error), path }];
4776
+ return [{ message: JSON.stringify(error), path }];
4325
4777
  }
4326
4778
  return typeErrorToStandardSchemaIssues(
4327
4779
  setError.reason.error as TypeErrors<ExtraErrors>,
4328
- formatTypeError,
4329
4780
  [...path, setError.reason.index],
4330
4781
  );
4331
4782
  }
@@ -4336,14 +4787,13 @@ export const typeErrorToStandardSchemaIssues = <
4336
4787
  objectError.reason.kind === "NotObject" ||
4337
4788
  objectError.reason.kind === "ExtraKeys"
4338
4789
  ) {
4339
- return [{ message: formatTypeError(error), path }];
4790
+ return [{ message: JSON.stringify(error), path }];
4340
4791
  }
4341
4792
  const issues: Array<StandardSchemaV1.Issue> = [];
4342
4793
  for (const [key, propError] of Object.entries(objectError.reason.errors)) {
4343
4794
  issues.push(
4344
4795
  ...typeErrorToStandardSchemaIssues(
4345
4796
  propError as TypeErrors<ExtraErrors>,
4346
- formatTypeError,
4347
4797
  [...path, key],
4348
4798
  ),
4349
4799
  );
@@ -4354,7 +4804,7 @@ export const typeErrorToStandardSchemaIssues = <
4354
4804
  if (error.type === "ObjectWithRecord") {
4355
4805
  const objectWithRecordError = error as ObjectWithRecordError;
4356
4806
  if (objectWithRecordError.reason.kind === "NotObject") {
4357
- return [{ message: formatTypeError(error), path }];
4807
+ return [{ message: JSON.stringify(error), path }];
4358
4808
  }
4359
4809
  if (
4360
4810
  objectWithRecordError.reason.kind === "IndexKey" ||
@@ -4362,7 +4812,6 @@ export const typeErrorToStandardSchemaIssues = <
4362
4812
  ) {
4363
4813
  return typeErrorToStandardSchemaIssues(
4364
4814
  objectWithRecordError.reason.error as TypeErrors<ExtraErrors>,
4365
- formatTypeError,
4366
4815
  [...path, objectWithRecordError.reason.key as PropertyKey],
4367
4816
  );
4368
4817
  }
@@ -4373,7 +4822,6 @@ export const typeErrorToStandardSchemaIssues = <
4373
4822
  issues.push(
4374
4823
  ...typeErrorToStandardSchemaIssues(
4375
4824
  propError as TypeErrors<ExtraErrors>,
4376
- formatTypeError,
4377
4825
  [...path, key],
4378
4826
  ),
4379
4827
  );
@@ -4384,11 +4832,10 @@ export const typeErrorToStandardSchemaIssues = <
4384
4832
  if (error.type === "Record") {
4385
4833
  const recordError = error as RecordError;
4386
4834
  if (recordError.reason.kind === "NotRecord") {
4387
- return [{ message: formatTypeError(error), path }];
4835
+ return [{ message: JSON.stringify(error), path }];
4388
4836
  }
4389
4837
  return typeErrorToStandardSchemaIssues(
4390
4838
  recordError.reason.error as TypeErrors<ExtraErrors>,
4391
- formatTypeError,
4392
4839
  [...path, recordError.reason.key as PropertyKey],
4393
4840
  );
4394
4841
  }
@@ -4396,11 +4843,10 @@ export const typeErrorToStandardSchemaIssues = <
4396
4843
  if (error.type === "Tuple") {
4397
4844
  const tupleError = error as TupleError;
4398
4845
  if (tupleError.reason.kind === "InvalidLength") {
4399
- return [{ message: formatTypeError(error), path }];
4846
+ return [{ message: JSON.stringify(error), path }];
4400
4847
  }
4401
4848
  return typeErrorToStandardSchemaIssues(
4402
4849
  tupleError.reason.error as TypeErrors<ExtraErrors>,
4403
- formatTypeError,
4404
4850
  [...path, tupleError.reason.index],
4405
4851
  );
4406
4852
  }
@@ -4408,11 +4854,7 @@ export const typeErrorToStandardSchemaIssues = <
4408
4854
  if (error.type === "Union") {
4409
4855
  const unionError = error as UnionError;
4410
4856
  return unionError.errors.flatMap((err) =>
4411
- typeErrorToStandardSchemaIssues(
4412
- err as TypeErrors<ExtraErrors>,
4413
- formatTypeError,
4414
- path,
4415
- ),
4857
+ typeErrorToStandardSchemaIssues(err as TypeErrors<ExtraErrors>, path),
4416
4858
  );
4417
4859
  }
4418
4860
 
@@ -4421,14 +4863,13 @@ export const typeErrorToStandardSchemaIssues = <
4421
4863
  if ("parentError" in brandError) {
4422
4864
  return typeErrorToStandardSchemaIssues(
4423
4865
  brandError.parentError as TypeErrors<ExtraErrors>,
4424
- formatTypeError,
4425
4866
  path,
4426
4867
  );
4427
4868
  }
4428
- return [{ message: formatTypeError(error), path }];
4869
+ return [{ message: JSON.stringify(error), path }];
4429
4870
  }
4430
4871
 
4431
- return [{ message: formatTypeError(error), path }];
4872
+ return [{ message: JSON.stringify(error), path }];
4432
4873
  };
4433
4874
 
4434
4875
  /** The Standard Schema interface. */
@@ -4502,10 +4943,3 @@ export declare namespace StandardSchemaV1 {
4502
4943
  Schema["~standard"]["types"]
4503
4944
  >["output"];
4504
4945
  }
4505
-
4506
- /**
4507
- * Shared formatter cache for Standard Schema integration - avoids circular
4508
- * dependency by lazily creating the formatter on first use rather than during
4509
- * module initialization.
4510
- */
4511
- let cachedStandardSchemaFormatTypeError: TypeErrorFormatter<any> | undefined;