@evolu/common 8.1.0 → 8.3.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 (232) hide show
  1. package/dist/src/Function.d.ts +1 -0
  2. package/dist/src/Function.d.ts.map +1 -1
  3. package/dist/src/Http.d.ts +9 -0
  4. package/dist/src/Http.d.ts.map +1 -1
  5. package/dist/src/Number.d.ts +66 -6
  6. package/dist/src/Number.d.ts.map +1 -1
  7. package/dist/src/Number.js +11 -3
  8. package/dist/src/Object.d.ts +1 -0
  9. package/dist/src/Object.d.ts.map +1 -1
  10. package/dist/src/RefCount.d.ts +1 -0
  11. package/dist/src/RefCount.d.ts.map +1 -1
  12. package/dist/src/Relation.d.ts +2 -0
  13. package/dist/src/Relation.d.ts.map +1 -1
  14. package/dist/src/Resource.d.ts +1 -0
  15. package/dist/src/Resource.d.ts.map +1 -1
  16. package/dist/src/Result.d.ts +383 -548
  17. package/dist/src/Result.d.ts.map +1 -1
  18. package/dist/src/Result.js +296 -46
  19. package/dist/src/Schedule.d.ts +2 -2
  20. package/dist/src/Schedule.d.ts.map +1 -1
  21. package/dist/src/StackTrace.d.ts +1 -0
  22. package/dist/src/StackTrace.d.ts.map +1 -1
  23. package/dist/src/Task.d.ts +978 -1061
  24. package/dist/src/Task.d.ts.map +1 -1
  25. package/dist/src/Task.js +1010 -159
  26. package/dist/src/Time.d.ts +71 -34
  27. package/dist/src/Time.d.ts.map +1 -1
  28. package/dist/src/Time.js +77 -1
  29. package/dist/src/Type.d.ts +1740 -795
  30. package/dist/src/Type.d.ts.map +1 -1
  31. package/dist/src/Type.js +1634 -211
  32. package/dist/src/Types.d.ts +0 -24
  33. package/dist/src/Types.d.ts.map +1 -1
  34. package/dist/src/intl/_en.d.ts +126 -0
  35. package/dist/src/intl/_en.d.ts.map +1 -0
  36. package/dist/src/intl/_en.js +267 -0
  37. package/dist/src/intl/ar.d.ts +117 -0
  38. package/dist/src/intl/ar.d.ts.map +1 -0
  39. package/dist/src/intl/ar.js +245 -0
  40. package/dist/src/intl/bn.d.ts +117 -0
  41. package/dist/src/intl/bn.d.ts.map +1 -0
  42. package/dist/src/intl/bn.js +258 -0
  43. package/dist/src/intl/ca.d.ts +117 -0
  44. package/dist/src/intl/ca.d.ts.map +1 -0
  45. package/dist/src/intl/ca.js +258 -0
  46. package/dist/src/intl/cs.d.ts +30 -7
  47. package/dist/src/intl/cs.d.ts.map +1 -1
  48. package/dist/src/intl/cs.js +65 -28
  49. package/dist/src/intl/da.d.ts +117 -0
  50. package/dist/src/intl/da.d.ts.map +1 -0
  51. package/dist/src/intl/da.js +258 -0
  52. package/dist/src/intl/de.d.ts +117 -0
  53. package/dist/src/intl/de.d.ts.map +1 -0
  54. package/dist/src/intl/de.js +258 -0
  55. package/dist/src/intl/el.d.ts +117 -0
  56. package/dist/src/intl/el.d.ts.map +1 -0
  57. package/dist/src/intl/el.js +265 -0
  58. package/dist/src/intl/es.d.ts +117 -0
  59. package/dist/src/intl/es.d.ts.map +1 -0
  60. package/dist/src/intl/es.js +258 -0
  61. package/dist/src/intl/fa.d.ts +117 -0
  62. package/dist/src/intl/fa.d.ts.map +1 -0
  63. package/dist/src/intl/fa.js +258 -0
  64. package/dist/src/intl/fi.d.ts +117 -0
  65. package/dist/src/intl/fi.d.ts.map +1 -0
  66. package/dist/src/intl/fi.js +258 -0
  67. package/dist/src/intl/fil.d.ts +117 -0
  68. package/dist/src/intl/fil.d.ts.map +1 -0
  69. package/dist/src/intl/fil.js +258 -0
  70. package/dist/src/intl/fr.d.ts +117 -0
  71. package/dist/src/intl/fr.d.ts.map +1 -0
  72. package/dist/src/intl/fr.js +258 -0
  73. package/dist/src/intl/he.d.ts +117 -0
  74. package/dist/src/intl/he.d.ts.map +1 -0
  75. package/dist/src/intl/he.js +258 -0
  76. package/dist/src/intl/hi.d.ts +117 -0
  77. package/dist/src/intl/hi.d.ts.map +1 -0
  78. package/dist/src/intl/hi.js +258 -0
  79. package/dist/src/intl/hr.d.ts +117 -0
  80. package/dist/src/intl/hr.d.ts.map +1 -0
  81. package/dist/src/intl/hr.js +265 -0
  82. package/dist/src/intl/hu.d.ts +63 -0
  83. package/dist/src/intl/hu.d.ts.map +1 -0
  84. package/dist/src/intl/hu.js +204 -0
  85. package/dist/src/intl/id.d.ts +117 -0
  86. package/dist/src/intl/id.d.ts.map +1 -0
  87. package/dist/src/intl/id.js +258 -0
  88. package/dist/src/intl/index.d.ts +42 -0
  89. package/dist/src/intl/index.d.ts.map +1 -1
  90. package/dist/src/intl/index.js +42 -0
  91. package/dist/src/intl/it.d.ts +117 -0
  92. package/dist/src/intl/it.d.ts.map +1 -0
  93. package/dist/src/intl/it.js +258 -0
  94. package/dist/src/intl/ja.d.ts +117 -0
  95. package/dist/src/intl/ja.d.ts.map +1 -0
  96. package/dist/src/intl/ja.js +258 -0
  97. package/dist/src/intl/ko.d.ts +117 -0
  98. package/dist/src/intl/ko.d.ts.map +1 -0
  99. package/dist/src/intl/ko.js +258 -0
  100. package/dist/src/intl/ml.d.ts +117 -0
  101. package/dist/src/intl/ml.d.ts.map +1 -0
  102. package/dist/src/intl/ml.js +258 -0
  103. package/dist/src/intl/mr.d.ts +117 -0
  104. package/dist/src/intl/mr.d.ts.map +1 -0
  105. package/dist/src/intl/mr.js +258 -0
  106. package/dist/src/intl/ms.d.ts +117 -0
  107. package/dist/src/intl/ms.d.ts.map +1 -0
  108. package/dist/src/intl/ms.js +245 -0
  109. package/dist/src/intl/nb.d.ts +63 -0
  110. package/dist/src/intl/nb.d.ts.map +1 -0
  111. package/dist/src/intl/nb.js +191 -0
  112. package/dist/src/intl/nl.d.ts +117 -0
  113. package/dist/src/intl/nl.d.ts.map +1 -0
  114. package/dist/src/intl/nl.js +258 -0
  115. package/dist/src/intl/pa.d.ts +117 -0
  116. package/dist/src/intl/pa.d.ts.map +1 -0
  117. package/dist/src/intl/pa.js +258 -0
  118. package/dist/src/intl/pl.d.ts +117 -0
  119. package/dist/src/intl/pl.d.ts.map +1 -0
  120. package/dist/src/intl/pl.js +242 -0
  121. package/dist/src/intl/pt-BR.d.ts +117 -0
  122. package/dist/src/intl/pt-BR.d.ts.map +1 -0
  123. package/dist/src/intl/pt-BR.js +258 -0
  124. package/dist/src/intl/pt.d.ts +117 -0
  125. package/dist/src/intl/pt.d.ts.map +1 -0
  126. package/dist/src/intl/pt.js +246 -0
  127. package/dist/src/intl/ro.d.ts +117 -0
  128. package/dist/src/intl/ro.d.ts.map +1 -0
  129. package/dist/src/intl/ro.js +258 -0
  130. package/dist/src/intl/sk.d.ts +117 -0
  131. package/dist/src/intl/sk.d.ts.map +1 -0
  132. package/dist/src/intl/sk.js +244 -0
  133. package/dist/src/intl/sl.d.ts +117 -0
  134. package/dist/src/intl/sl.d.ts.map +1 -0
  135. package/dist/src/intl/sl.js +258 -0
  136. package/dist/src/intl/sv.d.ts +117 -0
  137. package/dist/src/intl/sv.d.ts.map +1 -0
  138. package/dist/src/intl/sv.js +258 -0
  139. package/dist/src/intl/sw.d.ts +63 -0
  140. package/dist/src/intl/sw.d.ts.map +1 -0
  141. package/dist/src/intl/sw.js +191 -0
  142. package/dist/src/intl/ta.d.ts +117 -0
  143. package/dist/src/intl/ta.d.ts.map +1 -0
  144. package/dist/src/intl/ta.js +258 -0
  145. package/dist/src/intl/te.d.ts +117 -0
  146. package/dist/src/intl/te.d.ts.map +1 -0
  147. package/dist/src/intl/te.js +258 -0
  148. package/dist/src/intl/th.d.ts +117 -0
  149. package/dist/src/intl/th.d.ts.map +1 -0
  150. package/dist/src/intl/th.js +258 -0
  151. package/dist/src/intl/tr.d.ts +117 -0
  152. package/dist/src/intl/tr.d.ts.map +1 -0
  153. package/dist/src/intl/tr.js +255 -0
  154. package/dist/src/intl/uk.d.ts +117 -0
  155. package/dist/src/intl/uk.d.ts.map +1 -0
  156. package/dist/src/intl/uk.js +258 -0
  157. package/dist/src/intl/ur.d.ts +117 -0
  158. package/dist/src/intl/ur.d.ts.map +1 -0
  159. package/dist/src/intl/ur.js +245 -0
  160. package/dist/src/intl/vi.d.ts +117 -0
  161. package/dist/src/intl/vi.d.ts.map +1 -0
  162. package/dist/src/intl/vi.js +255 -0
  163. package/dist/src/intl/zh-CN.d.ts +117 -0
  164. package/dist/src/intl/zh-CN.d.ts.map +1 -0
  165. package/dist/src/intl/zh-CN.js +258 -0
  166. package/dist/src/intl/zh-TW.d.ts +117 -0
  167. package/dist/src/intl/zh-TW.d.ts.map +1 -0
  168. package/dist/src/intl/zh-TW.js +258 -0
  169. package/dist/src/local-first/Owner.d.ts +156 -62
  170. package/dist/src/local-first/Owner.d.ts.map +1 -1
  171. package/dist/src/local-first/Owner.js +119 -20
  172. package/package.json +1 -1
  173. package/src/Function.ts +2 -0
  174. package/src/Http.ts +9 -0
  175. package/src/Number.ts +85 -14
  176. package/src/Object.ts +2 -0
  177. package/src/RefCount.ts +2 -0
  178. package/src/Relation.ts +4 -0
  179. package/src/Resource.ts +2 -0
  180. package/src/Result.ts +397 -560
  181. package/src/Schedule.ts +1 -1
  182. package/src/StackTrace.ts +2 -0
  183. package/src/Task.ts +1145 -1225
  184. package/src/Time.ts +110 -62
  185. package/src/Type.ts +2842 -835
  186. package/src/Types.ts +0 -49
  187. package/src/intl/_en.ts +497 -0
  188. package/src/intl/ar.ts +417 -0
  189. package/src/intl/bn.ts +486 -0
  190. package/src/intl/ca.ts +488 -0
  191. package/src/intl/cs.ts +131 -32
  192. package/src/intl/da.ts +488 -0
  193. package/src/intl/de.ts +488 -0
  194. package/src/intl/el.ts +494 -0
  195. package/src/intl/es.ts +488 -0
  196. package/src/intl/fa.ts +487 -0
  197. package/src/intl/fi.ts +487 -0
  198. package/src/intl/fil.ts +488 -0
  199. package/src/intl/fr.ts +488 -0
  200. package/src/intl/he.ts +486 -0
  201. package/src/intl/hi.ts +487 -0
  202. package/src/intl/hr.ts +494 -0
  203. package/src/intl/hu.ts +387 -0
  204. package/src/intl/id.ts +487 -0
  205. package/src/intl/index.ts +42 -0
  206. package/src/intl/it.ts +488 -0
  207. package/src/intl/ja.ts +488 -0
  208. package/src/intl/ko.ts +487 -0
  209. package/src/intl/ml.ts +487 -0
  210. package/src/intl/mr.ts +487 -0
  211. package/src/intl/ms.ts +410 -0
  212. package/src/intl/nb.ts +364 -0
  213. package/src/intl/nl.ts +488 -0
  214. package/src/intl/pa.ts +487 -0
  215. package/src/intl/pl.ts +450 -0
  216. package/src/intl/pt-BR.ts +487 -0
  217. package/src/intl/pt.ts +424 -0
  218. package/src/intl/ro.ts +488 -0
  219. package/src/intl/sk.ts +412 -0
  220. package/src/intl/sl.ts +487 -0
  221. package/src/intl/sv.ts +488 -0
  222. package/src/intl/sw.ts +324 -0
  223. package/src/intl/ta.ts +487 -0
  224. package/src/intl/te.ts +487 -0
  225. package/src/intl/th.ts +484 -0
  226. package/src/intl/tr.ts +487 -0
  227. package/src/intl/uk.ts +488 -0
  228. package/src/intl/ur.ts +410 -0
  229. package/src/intl/vi.ts +435 -0
  230. package/src/intl/zh-CN.ts +482 -0
  231. package/src/intl/zh-TW.ts +482 -0
  232. package/src/local-first/Owner.ts +156 -62
@@ -1,8 +1,3 @@
1
- /**
2
- * Runtime types.
3
- *
4
- * @module
5
- */
6
1
  import type { StandardSchemaV1 } from "@standard-schema/spec";
7
2
  import type { AtLeastTwoReadonlyArray, NonEmptyReadonlyArray } from "./Array.ts";
8
3
  import type { Brand } from "./Brand.ts";
@@ -10,361 +5,14 @@ import type { RandomBytesDep } from "./Crypto.ts";
10
5
  import { type Thunk } from "./Function.ts";
11
6
  import { type Result } from "./Result.ts";
12
7
  import type { TimeDep } from "./Time.ts";
13
- import { type CompileTimeError, type Digit, type Digit1To9, type Int1To100, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
14
- /**
15
- * A runtime representation of a TypeScript type with typed structured errors.
16
- *
17
- * Evolu Type reports expected decoding failures through {@link Result} rather
18
- * than exceptions. It represents both an encoded `Input` and its semantic
19
- * `Output`, supporting validation, transformation, and canonical encoding.
20
- *
21
- * Evolu Type is designed for correctness and developer experience. Correctness
22
- * is especially important for local-first data: application authors cannot
23
- * inspect or repair a user's data on a server because they do not have access
24
- * to it. Type declarations must reject invalid data at system boundaries, then
25
- * preserve those guarantees wherever the data travels.
26
- *
27
- * To make correct code the easiest code to write, Evolu Type preserves as much
28
- * information as TypeScript can express. Invalid declarations produce readable
29
- * {@link CompileTimeError} types when the compiler can detect them, while
30
- * runtime assertions enforce construction contracts it cannot prove. Together,
31
- * these choices create a pit of success.
32
- *
33
- * The implementation is optimized for minimal bundle size. Less descriptive
34
- * assertion messages could make it even smaller, but Evolu keeps actionable
35
- * messages as a deliberate tradeoff for developer experience.
36
- *
37
- * The main properties of Evolu Type are:
38
- *
39
- * - **Result-based error handling** – expected failures are explicit values.
40
- * - **Typed errors with decoupled formatters** – validation logic stays
41
- * independent of user-facing messages, and errors can be handled
42
- * exhaustively.
43
- * - **Type-safe, tree-shakeable localization** – formatter requirements are
44
- * inferred from selected Types, while apps bundle exactly the locales they
45
- * support so users can change language offline.
46
- * - **Consistent constraints through {@link Brand}** – every refinement constraint
47
- * is represented in the TypeScript type, so an unconstrained parent value
48
- * cannot be used where the constrained value is required.
49
- * - **Typed inputs** – prefer `from` and its `.parent` entry points to connect
50
- * precise producer and consumer contracts while preserving typed remaining
51
- * errors; reserve `fromUnknown` for genuinely unknown values.
52
- * - **Lawful codecs** – Types partially decode `Input` to `Output` and totally
53
- * encode every legitimate `Output` to `CanonicalInput`, the statically known
54
- * subtype of `Input` returned by complete encoding.
55
- * - **A top-down implementation** – the source is intended to be read from
56
- * beginning to end.
57
- *
58
- * Evolu Type supports [Standard Schema](https://standardschema.dev/) for
59
- * interoperability with compatible tools and frameworks while preserving each
60
- * Type's exact Input and Output.
61
- *
62
- * Evolu Type assumes that all executing code, including third-party
63
- * dependencies, has been audited and is trusted. It validates data contracts
64
- * under that assumption. Trusting code does not require trusting every value it
65
- * returns, so uncertain values from legacy code or another realm can still be
66
- * decoded at an explicit boundary. It does not protect against hostile
67
- * executable behavior such as sabotaged Proxies or throwing traps; Type
68
- * validation is not a security boundary for untrusted JavaScript.
69
- *
70
- * Type declarations and their callbacks are trusted construction code. Evolu
71
- * Type leverages that trust for better developer experience and does not try to
72
- * recover from code that defeats the type system with `any` or casts, including
73
- * fabricating an `Err` for `Result<_, never>`. Runtime assertions still enforce
74
- * contracts TypeScript cannot express, and every Type declaration must be
75
- * tested for its expected successes and failures.
76
- *
77
- * `fromUnknown` validates untyped input through the complete pipeline. `from`
78
- * and its `.parent` operations use their declared boundary to determine which
79
- * remaining stages can return validation errors, but assert that boundary at
80
- * runtime. `orThrow` and `orNull` reuse the deepest `from` operation accepting
81
- * `Input`, while `to` asserts its Output boundary. A failed assertion means
82
- * application code violated its static contract; it is a developer error, not
83
- * an expected validation failure. Its message identifies the expected Type and
84
- * its cause preserves the exact structured Output validation error.
85
- *
86
- * Prefer the most precise typed boundary available. A value is not unknown
87
- * merely because it originated outside the application: forms, components, and
88
- * other producers often expose a `string` or a branded value that can connect
89
- * directly to a matching `from` boundary. Reserve `fromUnknown` for values
90
- * whose TypeScript type is genuinely `unknown`. `is` means exact membership in
91
- * the output domain, not merely that output-side parsing could succeed. A
92
- * successful `fromUnknown` result always satisfies `is`.
93
- *
94
- * Decoding accepts a representation outside the Output domain only when the
95
- * Type explicitly declares that representation, such as a transformation Input.
96
- * Structural Types do not implicitly repair another JavaScript representation.
97
- * In particular, {@link array} and {@link tuple} require dense own data elements,
98
- * while the predefined {@link Object}, {@link object}, and {@link record} require
99
- * plain objects with own enumerable data properties. They do not invoke
100
- * accessors or materialize inherited and non-enumerable properties.
101
- *
102
- * TypeScript object types are structural and do not encode JavaScript realm
103
- * identity. Structural Types therefore accept legitimate representations from
104
- * other realms. Prototype checks remain when the prototype defines the semantic
105
- * domain. Plain-object Types accept a `null` prototype or an immediate root
106
- * prototype whose own prototype is `null`; ordinary class instances and deeper
107
- * prototype chains are rejected. When Record decoding must construct a
108
- * normalized value, it uses a `null` prototype so every string key remains
109
- * ordinary data.
110
- *
111
- * Evolu Type expects TypeScript's `exactOptionalPropertyTypes` compiler option
112
- * to be enabled.
113
- *
114
- * Predefined Types intentionally use the names of corresponding JavaScript
115
- * built-ins because they represent those familiar value categories. If an
116
- * imported Type shadows a built-in in the same scope, access the built-in
117
- * through `globalThis`, JavaScript's standard cross-environment global object,
118
- * such as `globalThis.String` or `globalThis.Date`.
119
- *
120
- * ### Example
121
- *
122
- * ```ts
123
- * import { String, type Result } from "@evolu/common";
124
- *
125
- * const value: unknown = "hello";
126
- * const result = String.fromUnknown(value);
127
- *
128
- * expectTypeOf(result).toEqualTypeOf<
129
- * Result<
130
- * string,
131
- * {
132
- * readonly type: "TypeOf";
133
- * readonly expected: "String";
134
- * readonly value: unknown;
135
- * }
136
- * >
137
- * >();
138
- * expectOk(result, "hello");
139
- * ```
140
- *
141
- * ## FAQ
142
- *
143
- * ### What does a Type represent?
144
- *
145
- * A Type is a lawful, pure codec for an exact semantic domain:
146
- *
147
- * ```text
148
- * Input ── partial decode ──▶ Output
149
- * CanonicalInput ◀─── total encode ─── Output
150
- *
151
- * CanonicalInput ⊆ Input
152
- * ```
153
- *
154
- * Read each line in the direction of its arrowhead. `Input` is the complete
155
- * typed decoding boundary, including candidates that validation can reject and
156
- * noncanonical representations that decoding can normalize. `Output` is the
157
- * validated semantic value. `CanonicalInput` is the statically known subtype of
158
- * `Input` returned by the complete `to` operation. It can be wider than the
159
- * values actually emitted when a refinement follows an arbitrary transformation
160
- * because TypeScript cannot determine which values its encoder returns for the
161
- * narrowed Output. `fromUnknown` and the `from` operations decode; `to`
162
- * encodes.
163
- *
164
- * A lawful Type round-trips every Output:
165
- *
166
- * ```text
167
- * fromUnknown(to(output)) ≈ ok(output)
168
- * ```
169
- *
170
- * Encoding can canonicalize a valid Input:
171
- *
172
- * ```text
173
- * "0042" ──decode──▶ 42 ──encode──▶ "42"
174
- * ```
175
- *
176
- * Once canonicalized, repeating the decode-encode cycle must preserve that
177
- * representation:
178
- *
179
- * ```text
180
- * "42" ──decode──▶ 42 ──encode──▶ "42"
181
- * ```
182
- *
183
- * Here `decode` means running the complete decoding pipeline, as `fromUnknown`
184
- * does, and `≈` means equality appropriate for the semantic domain. Validation
185
- * refinements, {@link ArrayType | Array Types}, and
186
- * {@link ObjectType | Object Types} preserve these laws when their contained
187
- * Types do. A {@link union} additionally requires compatible dispatch: it
188
- * encodes through the first member matching the Output and decodes through the
189
- * first member accepting the Input. Member ordering is lawful only when those
190
- * choices agree semantically. Encoded representations can overlap even when
191
- * member Output types are disjoint.
192
- *
193
- * When encoding returns a refined value unchanged, the refinement can narrow
194
- * `CanonicalInput` without changing its JavaScript representation. For example,
195
- * {@link FiniteNumber} has `number` as its Input, while its Output and
196
- * CanonicalInput are `FiniteNumber`: decoding can reject non-finite number
197
- * candidates, and encoding only receives validated finite Outputs. A
198
- * transformation can change the representation entirely. For
199
- * {@link Int64FromInt64String}, Input is `string`, Output is `Int64`, and
200
- * CanonicalInput is `Int64String`. Structural Type factories derive their
201
- * CanonicalInput recursively from their contained Types.
202
- *
203
- * ### Why is to total?
204
- *
205
- * Suppose a Type accepts only strings containing decimal digits and decodes
206
- * them to JavaScript numbers. Parsing `"42"` is possible, but the Type cannot
207
- * lawfully declare its Output as `number`:
208
- *
209
- * ```text
210
- * digits-only string ──partial decode──▶ number
211
- * digits-only string ◀─── total encode ── number // impossible
212
- * ```
213
- *
214
- * `number` also contains negative and fractional numbers, `NaN`, positive and
215
- * negative infinity, and `-0`. None of those values has a digits-only
216
- * representation, so `to` could not encode every valid Output.
217
- *
218
- * One lawful design narrows the Output to the exact representable domain:
219
- *
220
- * ```text
221
- * digits-only string ──partial decode──▶ NonNegativeSafeInteger
222
- * digits-only string ◀─── total encode ── NonNegativeSafeInteger
223
- *
224
- * "0042" ──decode──▶ 42 ──encode──▶ "42"
225
- * ```
226
- *
227
- * Another lawful design keeps `number` as the Output but expands the Input
228
- * representation to include a canonical string for every number, including
229
- * `"NaN"`, `"Infinity"`, `"-Infinity"`, and `"-0"`, as well as negative and
230
- * fractional numbers.
231
- *
232
- * The same principle applies when converting between two representations. Give
233
- * each representation its own Type with the same exact Output. For example, a
234
- * string representation and a number representation can both decode to the
235
- * shared `SafeInteger` domain:
236
- *
237
- * ```text
238
- * string ──partial decode──▶ SafeInteger
239
- * string ◀─── total encode ── SafeInteger
8
+ import { type CompileTimeError, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
9
+ /**
10
+ * A runtime representation of a TypeScript type, including its encoded input,
11
+ * semantic output, structured errors, and canonical encoding.
240
12
  *
241
- * number ──partial decode──▶ SafeInteger
242
- * number ◀─── total encode ── SafeInteger
243
- * ```
13
+ * See the {@link @evolu/common!Type | Type overview}.
244
14
  *
245
- * Conversion decodes the source representation, then total-encodes the shared
246
- * Output into the target representation. If no lossless shared domain exists,
247
- * the operation is a partial conversion, migration, or policy decision and
248
- * should be an explicit function returning Result, not a Type transformation.
249
- *
250
- * ### Why can a typed operation throw?
251
- *
252
- * TypeScript proves structural assignability, but it cannot describe every
253
- * runtime invariant. For example, it cannot express whether an object property
254
- * is own, enumerable, or a data property. It also permits a wider object with
255
- * excess properties where a narrower object type is expected.
256
- *
257
- * `fromUnknown` treats such invalid external values as expected data and
258
- * returns a typed error. Typed boundaries instead assert the domain promised by
259
- * their parameter type. If application code claims an accessor-backed object or
260
- * an object with excess properties is an Object Output, the assertion throws
261
- * because the application contract is broken. `orThrow` and `orNull` preserve
262
- * the assertion at their typed `Input` boundary, then apply {@link getOrThrow}
263
- * or {@link getOrNull} only to validation failures returned by the remaining
264
- * pipeline.
265
- *
266
- * Consequently, structural representation errors such as sparse Arrays,
267
- * accessors, and excess properties normally do not enter user-facing validation
268
- * in typed application flows. They violate the producer's declared contract and
269
- * throw as developer errors. At a genuinely unknown boundary, such as a
270
- * schema-authoring tool, import, or external protocol, the same issues are
271
- * legitimate typed validation errors and their formatter messages are useful.
272
- *
273
- * This distinction applies to data failures. Any Type operation, including
274
- * `fromUnknown`, can throw when trusted Type-declaration code, such as a
275
- * successful transformation callback, violates its declared contract.
276
- *
277
- * Materialize accessor values into plain data, remove properties the Type does
278
- * not represent, or use a different Type. Silently discarding excess data would
279
- * make the code constructing it dead while appearing to encode it successfully.
280
- * One exact Object policy also keeps Output membership independent of parsing
281
- * configuration. Exact structural policies also keep Output membership
282
- * independent of whether a transformation happens to allocate a new value.
283
- * Evolu Type therefore does not invoke accessors, discard excess properties, or
284
- * make `to` fallible. This keeps `to` total for every legitimate Output and
285
- * lets transformations compose without an encoding-error channel.
286
- *
287
- * ### How should values from another realm be handled?
288
- *
289
- * Code trust and data validation are separate decisions. Values returned by
290
- * trusted legacy code or another realm can still be uncertain and should be
291
- * validated. Realm-neutral Types accept an otherwise legitimate representation
292
- * without requiring conversion merely because its built-ins belong to another
293
- * realm.
294
- *
295
- * When an application trusts both the producer and its return contract, it can
296
- * cast the boundary API's `unknown` because validation is redundant. Use a
297
- * specialized Type or explicit transformation when the producer actually uses a
298
- * different representation that needs adaptation or normalization.
299
- *
300
- * All executing JavaScript remains trusted. Deliberately forged built-ins,
301
- * hostile Proxies, throwing traps, or sabotaged executable behavior can throw;
302
- * Evolu Type does not selectively contain them or claim to be a security
303
- * boundary for untrusted code.
304
- *
305
- * ### Why doesn't Evolu Type extract data from rich objects?
306
- *
307
- * Some validation libraries parse an object's data projection. An imaginary
308
- * validation library can enumerate own enumerable string properties and decode
309
- * them into a fresh plain object. That lets a class instance decode as plain
310
- * data while its prototype and methods are ignored. The same general policy can
311
- * treat a `Date` or `Map` as an empty Record and can invoke enumerable getters.
312
- * This is a coherent but intentionally forgiving normalization model.
313
- *
314
- * Evolu Type validates exactly the runtime representation defined by each Type;
315
- * it does not implicitly project one representation into another. The
316
- * predefined {@link Object} defines an open plain-object representation with
317
- * unknown values, {@link object} defines a closed plain-object representation,
318
- * and {@link record} defines a plain-dictionary representation whose complete
319
- * set of own properties are its entries. Their realm-neutral plain-object rule
320
- * accepts a `null` prototype or an immediate root prototype whose own prototype
321
- * is `null`; ordinary class instances and deeper prototype chains are rejected.
322
- * Every property must be an enumerable data property; inherited members are not
323
- * entries, while accessors and hidden properties are invalid instead of being
324
- * invoked or ignored. {@link array} similarly defines a dense sequence whose
325
- * only own properties are `length` and its indexed data properties;
326
- * {@link tuple} applies the same representation rules with a fixed length and a
327
- * distinct Type for each position. Only an explicit {@link transform} changes
328
- * the representation. Consequently, `is` tests exact Output membership and `to`
329
- * stays total for valid Outputs.
330
- *
331
- * ### Why is JsonValue stricter than JSON.stringify?
332
- *
333
- * `JSON.stringify` is a forgiving data projection. It can invoke `toJSON` and
334
- * accessors, discard object properties, replace unsupported array elements and
335
- * non-finite numbers with `null`, and normalize `-0` to `0`. Those rules are
336
- * useful for ordinary serialization, but they do not preserve an exact value.
337
- *
338
- * {@link JsonValue} instead defines data that is already represented as data.
339
- * Invalid runtime behavior and values are rejected rather than interpreted or
340
- * silently discarded. Its encoder is total and stack-safe for every valid
341
- * Output, and {@link JsonValueFromJson} preserves the semantic value when it is
342
- * encoded and decoded, including JavaScript's distinction between `-0` and `0`.
343
- * Use an explicit transformation before this boundary when a projection or
344
- * other normalization is desired.
345
- *
346
- * ### Why are Types pure and synchronous?
347
- *
348
- * A Type describes data meaning, not work. Time, I/O, dependencies, external
349
- * state, authorization, and other contextual decisions belong in a {@link Task}.
350
- * Use a Type to decode the data required by that work, then pass the decoded
351
- * value to a Task. A pure synchronous conversion that can fail can be an
352
- * ordinary function returning Result.
353
- *
354
- * Keeping those responsibilities separate prevents an Evolu Type from becoming
355
- * a hidden application workflow. It also keeps validation deterministic,
356
- * dependency-free, immediately composable, and straightforward to test.
357
- *
358
- * ### What if only decoding is needed?
359
- *
360
- * Use `fromUnknown` for unknown data. For typed application data, call `from`
361
- * at the boundary its input type proves, or use `orThrow` or `orNull` for a
362
- * flat conversion from `Input`. The canonical `to` encoder still keeps the Type
363
- * lawful and composable with transformations and structural Types. A genuinely
364
- * irreversible operation is a separate function or Task, not a Type
365
- * transformation.
366
- *
367
- * @group Core Types
15
+ * @group Core
368
16
  */
369
17
  export interface Type<Name extends TypeName, in out Input, in out Output, Error extends TypeError, in out Parent extends TypeNode | null = null, in out Errors extends TypeError = Error | ([Parent] extends [infer P extends TypeNode] ? InferErrors<P> : never), in out CustomFrom extends CustomFromOperation = never, in out CanonicalInput extends Input = Input, in out IdentityEncoding extends boolean = true> extends TypeNode {
370
18
  /** The name identifying this Type node. */
@@ -423,7 +71,20 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
423
71
  readonly [identityEncodingSymbol]: IdentityEncoding;
424
72
  /** The one preceding Type node, or `null` for a root Type. */
425
73
  readonly parent: Parent;
426
- /** Decodes an unknown value through the complete Type pipeline. */
74
+ /**
75
+ * Decodes an unknown value through the complete Type pipeline.
76
+ *
77
+ * ### Example
78
+ *
79
+ * ```ts
80
+ * import { PositiveInt } from "@evolu/common";
81
+ *
82
+ * const value: unknown = 42;
83
+ * const result = PositiveInt.fromUnknown(value);
84
+ *
85
+ * expectOk(result, 42);
86
+ * ```
87
+ */
427
88
  readonly fromUnknown: (value: unknown, options?: ValidationOptions) => Result<Output, Errors>;
428
89
  /**
429
90
  * Formats an error returned by `fromUnknown` or `from` as one human-readable
@@ -433,6 +94,19 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
433
94
  * Structural errors retain nested errors and their locations in the typed
434
95
  * error value. This formatter does not encode paths or enumerate nested
435
96
  * errors in its message.
97
+ *
98
+ * ### Example
99
+ *
100
+ * ```ts
101
+ * import { String } from "@evolu/common";
102
+ *
103
+ * const result = String.fromUnknown(42);
104
+ *
105
+ * expectErr(result, { type: "TypeOf", expected: "String", value: 42 });
106
+ * expect(String.formatError(result.error)).toBe(
107
+ * "A value 42 is not a string.",
108
+ * );
109
+ * ```
436
110
  */
437
111
  readonly formatError: TypeErrorFormatter<Errors>;
438
112
  /**
@@ -468,6 +142,52 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
468
142
  * developer error. The Error message identifies the expected boundary Type,
469
143
  * and its cause preserves the structured validation error. Only failures
470
144
  * introduced after that boundary are returned through `Result`.
145
+ *
146
+ * ### Example
147
+ *
148
+ * A form already guarantees every constraint on a title, while a new note
149
+ * input guarantees only trimmed text. The note's parent boundary validates
150
+ * only the constraints that the form does not guarantee:
151
+ *
152
+ * ```ts
153
+ * import {
154
+ * flatMapResult,
155
+ * NonEmptyTrimmedString100,
156
+ * object,
157
+ * TrimmedString,
158
+ * type MaxLengthError,
159
+ * type MinLengthError,
160
+ * type Result,
161
+ * } from "@evolu/common";
162
+ *
163
+ * const Todo = object({
164
+ * title: NonEmptyTrimmedString100,
165
+ * note: NonEmptyTrimmedString100,
166
+ * });
167
+ *
168
+ * const saveTodo = (
169
+ * title: NonEmptyTrimmedString100,
170
+ * note: TrimmedString,
171
+ * ) => {
172
+ * // @ts-expect-error TrimmedString does not guarantee a non-empty value
173
+ * // with at most 100 characters.
174
+ * Todo.from({ title, note });
175
+ *
176
+ * return flatMapResult(
177
+ * Todo.props.note.from.parent.parent(note),
178
+ * (note) => Todo.from({ title, note }),
179
+ * );
180
+ * };
181
+ *
182
+ * const title = NonEmptyTrimmedString100.orThrow("Buy milk");
183
+ * const note = TrimmedString.orThrow("Remember oat milk");
184
+ * const result = saveTodo(title, note);
185
+ *
186
+ * expectTypeOf(result).toEqualTypeOf<
187
+ * Result<typeof Todo.Output, MaxLengthError<100> | MinLengthError<1>>
188
+ * >();
189
+ * expectOk(result, { title, note });
190
+ * ```
471
191
  */
472
192
  readonly from: [CustomFrom] extends [never] ? [Parent] extends [infer P extends TypeNode] ? FromOperation<Output, Error, P> : TypeOperationFn<"from", Output, Output, never> : CustomFrom;
473
193
  /**
@@ -477,6 +197,16 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
477
197
  * `to` runs the complete encoding pipeline. Its first `.parent` stops at the
478
198
  * immediate parent Output, and each additional suffix stops one Type closer
479
199
  * to the root. Every entry point accepts this Type's `Output`.
200
+ *
201
+ * ### Example
202
+ *
203
+ * ```ts
204
+ * import { Int64, Int64FromInt64String } from "@evolu/common";
205
+ *
206
+ * const value = Int64.orThrow(42n);
207
+ *
208
+ * expect(Int64FromInt64String.to(value)).toBe("42");
209
+ * ```
480
210
  */
481
211
  readonly to: [Parent] extends [infer P extends TypeNode] ? ToOperation<Output, CanonicalInput, P> : (value: Output) => CanonicalInput;
482
212
  /**
@@ -506,6 +236,9 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
506
236
  *
507
237
  * // Equivalent because `from.parent` is this Type's deepest `from` operation:
508
238
  * const sameValue = getOrThrow(NonEmptyString.from.parent("Evolu"));
239
+ *
240
+ * expect(value).toBe("Evolu");
241
+ * expect(sameValue).toBe(value);
509
242
  * ```
510
243
  */
511
244
  readonly orThrow: TypeOperationFn<"orThrow", Input, Output, never>;
@@ -536,11 +269,19 @@ export interface Type<Name extends TypeName, in out Input, in out Output, Error
536
269
  *
537
270
  * // Equivalent because `from.parent` is this Type's deepest `from` operation:
538
271
  * const sameValue = getOrNull(NonEmptyString.from.parent("Evolu"));
272
+ *
273
+ * expect(value).toBe("Evolu");
274
+ * expect(sameValue).toBe(value);
275
+ * expect(NonEmptyString.orNull("")).toBeNull();
539
276
  * ```
540
277
  */
541
278
  readonly orNull: TypeOperationFn<"orNull", Input, Output, never>;
542
279
  }
543
- /** @group Core Types */
280
+ /**
281
+ * A capitalized name identifying a {@link Type} node.
282
+ *
283
+ * @group Core
284
+ */
544
285
  export type TypeName = Capitalize<string>;
545
286
  /**
546
287
  * A plain structured error produced by a {@link Type} operation.
@@ -552,7 +293,7 @@ export type TypeName = Capitalize<string>;
552
293
  * only when it intentionally represents the same error contract. Accidental
553
294
  * reuse prevents reliable discriminated-union narrowing.
554
295
  *
555
- * @group Core Types
296
+ * @group Core
556
297
  */
557
298
  export interface TypeError<Name extends TypeName = TypeName> {
558
299
  readonly type: Name;
@@ -567,7 +308,7 @@ interface TransparentTypeError {
567
308
  * Structural errors such as Array and Union errors extend {@link TypeError}
568
309
  * instead because they locate nested errors rather than own one value.
569
310
  *
570
- * @group Core Types
311
+ * @group Core
571
312
  */
572
313
  export interface TypeValueError<Name extends TypeName = TypeName> extends TypeError<Name> {
573
314
  readonly value: unknown;
@@ -575,10 +316,15 @@ export interface TypeValueError<Name extends TypeName = TypeName> extends TypeEr
575
316
  /**
576
317
  * Formats a structured {@link TypeError} as a human-readable message.
577
318
  *
578
- * @group Core Types
319
+ * @group Core
579
320
  */
580
321
  export type TypeErrorFormatter<Error extends TypeError> = (error: Error) => string;
581
- /** @group Core Types */
322
+ /**
323
+ * The common structural shape of every {@link Type}, with its specific type
324
+ * parameters erased.
325
+ *
326
+ * @group Core
327
+ */
582
328
  export interface TypeNode {
583
329
  readonly name: TypeName;
584
330
  readonly "~standard": StandardSchemaV1.Props<unknown, unknown>;
@@ -601,7 +347,7 @@ export interface TypeNode {
601
347
  * Asserts that a value belongs to a {@link Type} Output domain.
602
348
  *
603
349
  * Use this for internal invariants, not external input. Validate external input
604
- * with `Type.fromUnknown` so expected failures remain typed values. A failed
350
+ * with `Type.fromUnknown` so validation failures remain typed values. A failed
605
351
  * assertion uses the Type name for its message and preserves the exact Output
606
352
  * validation error as the thrown Error's cause.
607
353
  *
@@ -621,38 +367,36 @@ export interface TypeNode {
621
367
  * >();
622
368
  * ```
623
369
  *
624
- * @group Core Types
370
+ * @group Core
625
371
  */
626
372
  export declare const assertType: <T extends TypeNode>(type: T, value: unknown) => asserts value is T["Output"];
627
373
  /**
628
- * Creates localized copies of selected {@link Type | Types} for every locale.
374
+ * Creates localized copies of selected {@link Type} declarations.
629
375
  *
630
- * Each locale supplies one formatter for every Type that can own a formatted
631
- * error. Structural Types use their own formatter for structural failures and
632
- * delegate contained failures to the Type that produced them. A Union owns its
633
- * complete failure because no member matched. Formatter requirements are
634
- * inferred recursively from structured errors, including mutually recursive
635
- * Lazy error interfaces.
376
+ * Pass the Types used together in one localization scope and formatter maps
377
+ * keyed by locale. TypeScript infers every formatter required by the selected
378
+ * Types, including errors from nested structural Types and recursive Lazy
379
+ * Types. Every locale must provide the complete inferred formatter set;
380
+ * missing and unrelated formatters are compile-time errors.
636
381
  *
637
- * Parents and Types exposed through public reflection are localized recursively
638
- * through one shared cache. Lazy definitions stay opaque and are not evaluated;
639
- * their declared error types provide the formatter requirements instead.
382
+ * The result preserves the locale names, selected Type names, and exact
383
+ * TypeScript types. A localized Type validates exactly like its source Type;
384
+ * only its human-readable `formatError` and Standard Schema messages change.
385
+ * The source Types remain unchanged.
640
386
  *
641
- * The selected Type map, locale map, and each formatter map must be plain
642
- * objects whose entries are own enumerable string-keyed data properties.
387
+ * Parents and reflected child Types are localized with the same formatter set.
388
+ * Structural Types retain error paths and delegate nested messages to the Type
389
+ * that produced them. Different localized Type sets can coexist in separate
390
+ * application or dependency-injection scopes.
643
391
  *
644
- * All localized Types for one locale share the same formatter set. The result
645
- * preserves the selected names and exact TypeScript types under every locale
646
- * key, making one localized set easy to provide through dependency injection or
647
- * application context. Canonical Types remain unchanged.
392
+ * Localization is scoped to the selected Types instead of a package-wide
393
+ * translation registry. Static imports give bundlers an explicit dependency
394
+ * graph, so unrelated Types, locales, and formatters can be removed. Bundling
395
+ * every locale an app supports also allows language changes without a network
396
+ * connection.
648
397
  *
649
- * Localization is scoped to the Types an app imports instead of a package-wide
650
- * translation registry. Ordinary static imports give bundlers an explicit
651
- * dependency graph from those Types to their formatters, so unrelated Type and
652
- * localization code can be removed. An app supplies all locales it supports in
653
- * the same self-contained bundle, allowing users to change language without a
654
- * network connection. Different localized Type sets can coexist on one page or
655
- * in separate dependency-injection scopes.
398
+ * The selected Type map, locale map, and formatter maps must be plain objects
399
+ * with own enumerable string-keyed data properties.
656
400
  *
657
401
  * ### Example
658
402
  *
@@ -662,7 +406,7 @@ export declare const assertType: <T extends TypeNode>(type: T, value: unknown) =
662
406
  *
663
407
  * const Label = minLength(1)(String);
664
408
  *
665
- * const TypesByLocale = localizeTypes(
409
+ * const typesByLocale = localizeTypes(
666
410
  * { Label },
667
411
  * {
668
412
  * cs: {
@@ -672,11 +416,66 @@ export declare const assertType: <T extends TypeNode>(type: T, value: unknown) =
672
416
  * },
673
417
  * );
674
418
  *
675
- * expectTypeOf<typeof TypesByLocale.cs.Label>().toEqualTypeOf<
419
+ * expectTypeOf<typeof typesByLocale.cs.Label>().toEqualTypeOf<
676
420
  * typeof Label
677
421
  * >();
422
+ *
423
+ * const result = typesByLocale.cs.Label.fromUnknown("");
424
+ * expectErr(result, { type: "MinLength1", min: 1, value: "" });
425
+ * expect(typesByLocale.cs.Label.formatError(result.error)).toBe(
426
+ * "Text nesmí být prázdný.",
427
+ * );
678
428
  * ```
679
429
  *
430
+ * ### Supported locales
431
+ *
432
+ * English is built in; use {@link Type} directly for its default formatters.
433
+ * The following additional locales are available:
434
+ *
435
+ * - Arabic (`ar`)
436
+ * - Bengali (`bn`)
437
+ * - Catalan (`ca`)
438
+ * - Chinese, Simplified (`zhCN`)
439
+ * - Chinese, Traditional (`zhTW`)
440
+ * - Croatian (`hr`)
441
+ * - Czech (`cs`)
442
+ * - Danish (`da`)
443
+ * - Dutch (`nl`)
444
+ * - Filipino (`fil`)
445
+ * - Finnish (`fi`)
446
+ * - French (`fr`)
447
+ * - German (`de`)
448
+ * - Greek (`el`)
449
+ * - Hebrew (`he`)
450
+ * - Hindi (`hi`)
451
+ * - Hungarian (`hu`)
452
+ * - Indonesian (`id`)
453
+ * - Italian (`it`)
454
+ * - Japanese (`ja`)
455
+ * - Korean (`ko`)
456
+ * - Malay (`ms`)
457
+ * - Malayalam (`ml`)
458
+ * - Marathi (`mr`)
459
+ * - Norwegian Bokmål (`nb`)
460
+ * - Persian (`fa`)
461
+ * - Polish (`pl`)
462
+ * - Portuguese (`pt`)
463
+ * - Portuguese, Brazilian (`ptBR`)
464
+ * - Punjabi (`pa`)
465
+ * - Romanian (`ro`)
466
+ * - Slovak (`sk`)
467
+ * - Slovenian (`sl`)
468
+ * - Spanish (`es`)
469
+ * - Swahili (`sw`)
470
+ * - Swedish (`sv`)
471
+ * - Tamil (`ta`)
472
+ * - Telugu (`te`)
473
+ * - Thai (`th`)
474
+ * - Turkish (`tr`)
475
+ * - Ukrainian (`uk`)
476
+ * - Urdu (`ur`)
477
+ * - Vietnamese (`vi`)
478
+ *
680
479
  * @group Localization
681
480
  */
682
481
  export declare const localizeTypes: LocalizeTypes;
@@ -762,15 +561,20 @@ declare const identityEncodingSymbol: unique symbol;
762
561
  * age: optional(PositiveInt),
763
562
  * });
764
563
  * interface User extends InferType<typeof User> {}
564
+ *
565
+ * const user = User.orThrow({ name: "Ada", age: 37 });
566
+ *
567
+ * expectTypeOf(user).toExtend<User>();
568
+ * expect(user.name).toBe("Ada");
765
569
  * ```
766
570
  *
767
- * @group Type utilities
571
+ * @group Core
768
572
  */
769
573
  export type InferType<T extends TypeNode> = T["Output"];
770
574
  /**
771
575
  * The union of errors a {@link Type} can return from `fromUnknown`.
772
576
  *
773
- * @group Type utilities
577
+ * @group Core
774
578
  */
775
579
  export type InferErrors<T extends TypeNode> = T[typeof errorsSymbol];
776
580
  type CanonicalInputOf<T extends TypeNode> = T extends TypeNode ? T["CanonicalInput"] : never;
@@ -842,7 +646,7 @@ type TypeOperationFn<Kind extends "from" | "orThrow" | "orNull", Input, Output,
842
646
  /**
843
647
  * Configures how container {@link Type} operations report errors.
844
648
  *
845
- * @group Core Types
649
+ * @group Core
846
650
  */
847
651
  export interface ValidationOptions {
848
652
  /** Controls whether container {@link Type} operations return one or all errors. */
@@ -914,10 +718,12 @@ type ConcreteChildTypeNameError = CompileTimeError<"Type", "Name must be one con
914
718
  * expectErr(Text.fromUnknown(42), { type: "Text", value: 42 });
915
719
  * ```
916
720
  *
917
- * @group Type construction
721
+ * @group Construction
918
722
  */
919
723
  export declare function createType<Name extends TypeName, Output, Error extends TypeError>(name: ValidateConcreteTypeName<Name>, fromUnknown: (value: unknown) => Result<Output, Error>, formatError: TypeErrorFormatter<NoInfer<Error>>): Type<Name, Output, Output, Error, null, Error, never, Output>;
724
+ /** Creates an infallible child Type that preserves its parent's Output. */
920
725
  export declare function createType<Name extends TypeName, ParentType extends ConcreteTypeNode>(name: ValidateConcreteTypeName<Name>, parent: ValidateParent<ParentType>, fromParent: (value: ParentType["Output"]) => Result<ParentType["Output"], never>): Type<Name, ParentType["Input"], ParentType["Output"], never, ParentType, InferErrors<ParentType>, ChildCustomFrom<ParentType, ParentType["Output"], never>, CanonicalInputForChild<ParentType, ParentType["Output"]>, IdentityEncodingOf<ParentType>>;
726
+ /** Creates a fallible child Type that narrows its parent's Output. */
921
727
  export declare function createType<Name extends TypeName, ParentType extends ConcreteTypeNode, Output extends ParentType["Output"], Error extends TypeError<Name>>(name: Name, parent: ValidateBrandParent<Name, ParentType>, fromParent: (value: ParentType["Output"]) => Result<Output, Error>, formatError: [Error] extends [never] ? never : TypeErrorFormatter<NoInfer<Error>>): Type<Name, ParentType["Input"], Output, Error, ParentType, Error | InferErrors<ParentType>, ChildCustomFrom<ParentType, Output, Error>, CanonicalInputForChild<ParentType, Output>, IdentityEncodingOf<ParentType>>;
922
728
  /**
923
729
  * Transform {@link Type}.
@@ -925,12 +731,19 @@ export declare function createType<Name extends TypeName, ParentType extends Con
925
731
  * `from` accepts the semantic Output. `from.parent` converts the parent Output
926
732
  * to the output Type Input, then runs the complete output Type pipeline. `to`
927
733
  * canonically encodes every output Type value before converting it back through
928
- * the parent Type. Transformation callbacks are Type construction code. Their
929
- * successful results are asserted against the declared boundary so a broken
930
- * callback fails as a developer error rather than becoming a validation error.
931
- * Like all Type-construction callbacks, they are trusted to follow their
932
- * declared TypeScript types. A `Result<_, never>` callback is therefore trusted
933
- * never to return an `Err`.
734
+ * the parent Type.
735
+ *
736
+ * The callbacks must form a lawful codec: decoding `to(output)` must reproduce
737
+ * the same semantic Output for every valid output value. Encoding may
738
+ * canonicalize multiple parent representations, but it must be total and must
739
+ * not lose distinctions present in the Output domain.
740
+ *
741
+ * Transformation callbacks are Type construction code. Their successful
742
+ * results are asserted against the declared boundary so a broken callback fails
743
+ * as a developer error rather than becoming a validation error. Like all
744
+ * Type-construction callbacks, they are trusted to follow their declared
745
+ * TypeScript types. A `Result<_, never>` callback is therefore trusted never to
746
+ * return an `Err`.
934
747
  *
935
748
  * Errors from the parent and the forward callback remain unchanged. A forward
936
749
  * callback error must use the transformation name as its type. Errors from the
@@ -943,23 +756,36 @@ export declare function createType<Name extends TypeName, ParentType extends Con
943
756
  * ### Example
944
757
  *
945
758
  * ```ts
946
- * import { Number, String, ok, transform } from "@evolu/common";
759
+ * import {
760
+ * Boolean,
761
+ * literal,
762
+ * ok,
763
+ * transform,
764
+ * union,
765
+ * } from "@evolu/common";
947
766
  *
948
- * const NumberFromString = transform("NumberFromString", String, Number, {
949
- * from: (value) => ok(globalThis.Number(value)),
950
- * to: globalThis.String,
951
- * });
767
+ * const BooleanString = union(literal("false"), literal("true"));
768
+ * const BooleanFromString = transform(
769
+ * "BooleanFromString",
770
+ * BooleanString,
771
+ * Boolean,
772
+ * {
773
+ * from: (value) => ok(value === "true"),
774
+ * to: (value) => (value ? "true" : "false"),
775
+ * },
776
+ * );
952
777
  *
953
- * expectOk(NumberFromString.from.parent("42"), 42);
954
- * assert(NumberFromString.to(42) === "42");
778
+ * expectOk(BooleanFromString.from.parent("true"), true);
779
+ * expect(BooleanFromString.to(false)).toBe("false");
955
780
  * ```
956
781
  *
957
- * @group Type construction
782
+ * @group Construction
958
783
  */
959
784
  export declare function transform<Name extends TypeName, ParentType extends ConcreteTypeNode, OutputType extends ConcreteTypeNode, ToOutput extends ParentType["Output"]>(name: ValidateChildTypeName<Name, ParentType>, parent: ValidateParent<ParentType>, output: ValidateOutput<OutputType>, operations: {
960
785
  readonly from: (value: ParentType["Output"]) => Result<OutputType["Input"], never>;
961
786
  readonly to: (value: CanonicalInputOf<OutputType>) => ToOutput;
962
787
  }): TransformType<ParentType, OutputType, Name, never, ToOutput>;
788
+ /** Creates a fallible transformed Type with its own error formatter. */
963
789
  export declare function transform<Name extends TypeName, ParentType extends ConcreteTypeNode, OutputType extends ConcreteTypeNode, ToOutput extends ParentType["Output"], FromError extends {
964
790
  readonly type: Name;
965
791
  readonly outputError?: never;
@@ -967,17 +793,26 @@ export declare function transform<Name extends TypeName, ParentType extends Conc
967
793
  readonly from: (value: ParentType["Output"]) => Result<OutputType["Input"], FromError>;
968
794
  readonly to: (value: CanonicalInputOf<OutputType>) => ToOutput;
969
795
  }, formatError: [FromError] extends [never] ? never : TypeErrorFormatter<NoInfer<FromError>>): TransformType<ParentType, OutputType, Name, FromError, ToOutput>;
970
- /** @group Type construction */
796
+ /**
797
+ * The {@link Type} returned by {@link transform}.
798
+ *
799
+ * @group Construction
800
+ */
971
801
  export interface TransformType<ParentType extends TypeNode, OutputType extends TypeNode, Name extends TypeName, FromError extends TypeError<Name>, ToOutput extends ParentType["Output"] = ParentType["Output"]> extends Type<Name, ParentType["Input"], OutputType["Output"], TransformError<Name, FromError, TypeFromError<OutputType>>, ParentType, TransformError<Name, FromError, TypeFromError<OutputType>> | InferErrors<ParentType>, ChildCustomFrom<ParentType, OutputType["Output"], TransformError<Name, FromError, TypeFromError<OutputType>>>, CanonicalInputForChild<ParentType, ToOutput>, false> {
972
802
  readonly [reflectedTypesSymbol]?: OutputType;
973
803
  readonly output: OutputType;
974
804
  }
975
- /** @group Type construction */
805
+ /**
806
+ * An error produced by {@link transform} while decoding or validating its
807
+ * output.
808
+ *
809
+ * @group Construction
810
+ */
976
811
  export type TransformError<Name extends TypeName, OwnError extends TypeError<Name>, OutputError extends TypeError> = OwnError | ([OutputError] extends [never] ? never : TransformOutputError<Name, OutputError>);
977
812
  /**
978
813
  * Wraps an error produced by the output {@link Type} of {@link transform}.
979
814
  *
980
- * @group Type construction
815
+ * @group Construction
981
816
  */
982
817
  export interface TransformOutputError<Name extends TypeName, OutputError extends TypeError> extends TypeError<Name>, TransparentTypeError {
983
818
  /** The error returned by the output Type. */
@@ -988,10 +823,25 @@ type DeepestFromError<Operation> = DeepestFromOperation<Operation> extends (...a
988
823
  readonly error: infer Error extends TypeError;
989
824
  } ? Error : never : never;
990
825
  type TypeFromError<T extends TypeNode> = unknown extends T[typeof customFromSymbol] ? DeepestFromError<TypeFromOperation<T>> : [T[typeof customFromSymbol]] extends [never] ? NonRootErrors<T> : DeepestFromError<T[typeof customFromSymbol]>;
991
- /** @group Base Types */
826
+ declare const templateLiteralSyntaxSymbolType: unique symbol;
827
+ declare const templateLiteralSyntaxSymbol: typeof templateLiteralSyntaxSymbolType;
828
+ /**
829
+ * An infallible {@link Type} accepting every value.
830
+ *
831
+ * @group Base
832
+ */
992
833
  export declare const Unknown: Type<"Unknown", unknown, unknown, never, null, never, never, unknown, true>;
993
- /** @group Base Types */
834
+ /**
835
+ * A {@link Type} rejecting every value.
836
+ *
837
+ * @group Base
838
+ */
994
839
  export declare const Never: Type<"Never", never, never, NeverError, null, NeverError, never, never, true>;
840
+ /**
841
+ * Error returned by {@link Never} for every value.
842
+ *
843
+ * @group Base
844
+ */
995
845
  export interface NeverError extends TypeError<"Never"> {
996
846
  readonly value: unknown;
997
847
  }
@@ -1003,7 +853,11 @@ interface TypeOfOutputByName {
1003
853
  readonly Symbol: symbol;
1004
854
  readonly Function: globalThis.Function;
1005
855
  }
1006
- /** @group Base Types */
856
+ /**
857
+ * Error returned when `typeof` does not match the expected JavaScript type.
858
+ *
859
+ * @group Base
860
+ */
1007
861
  export interface TypeOfError<Name extends keyof TypeOfOutputByName> extends TypeError<"TypeOf"> {
1008
862
  readonly expected: Name;
1009
863
  readonly value: unknown;
@@ -1031,6 +885,8 @@ export interface TypeOfError<Name extends keyof TypeOfOutputByName> extends Type
1031
885
  * expectTypeOf<WireValue100>().toEqualTypeOf<
1032
886
  * string & Brand<"MaxLength100">
1033
887
  * >();
888
+ * expectOk(WireValue100.fromUnknown(""), "");
889
+ * expectOk(WireValue100.fromUnknown(" value "), " value ");
1034
890
  * ```
1035
891
  *
1036
892
  * @group String
@@ -1105,21 +961,37 @@ export declare const String: Type<"String", string, string, TypeOfError<"String"
1105
961
  * @group Number
1106
962
  */
1107
963
  export declare const Number: Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>;
1108
- /** @group Base Types */
964
+ /**
965
+ * A JavaScript bigint {@link Type}.
966
+ *
967
+ * @group Base
968
+ */
1109
969
  export declare const BigInt: Type<"BigInt", bigint, bigint, TypeOfError<"BigInt">, null, TypeOfError<"BigInt">, never, bigint, true>;
1110
- /** @group Base Types */
970
+ /**
971
+ * A JavaScript boolean {@link Type}.
972
+ *
973
+ * @group Base
974
+ */
1111
975
  export declare const Boolean: Type<"Boolean", boolean, boolean, TypeOfError<"Boolean">, null, TypeOfError<"Boolean">, never, boolean, true>;
1112
- /** @group Base Types */
976
+ /**
977
+ * A JavaScript symbol {@link Type}.
978
+ *
979
+ * @group Base
980
+ */
1113
981
  export declare const Symbol: Type<"Symbol", symbol, symbol, TypeOfError<"Symbol">, null, TypeOfError<"Symbol">, never, symbol, true>;
1114
- /** @group Base Types */
982
+ /**
983
+ * A JavaScript function {@link Type}.
984
+ *
985
+ * @group Base
986
+ */
1115
987
  export declare const Function: Type<"Function", Function, Function, TypeOfError<"Function">, null, TypeOfError<"Function">, never, Function, true>;
1116
988
  /**
1117
- * An Evolu {@link Type} validating other Evolu Types.
989
+ * A {@link Type} validating Evolu Type declarations.
1118
990
  *
1119
991
  * This is useful when a Type itself crosses an unknown boundary or must be
1120
992
  * asserted with {@link assertType}.
1121
993
  *
1122
- * @group Core Types
994
+ * @group Core
1123
995
  */
1124
996
  export declare const EvoluType: Type<"EvoluType", AnyType, AnyType, EvoluTypeError, null, EvoluTypeError, never, AnyType, true>;
1125
997
  /**
@@ -1132,22 +1004,22 @@ export declare const EvoluType: Type<"EvoluType", AnyType, AnyType, EvoluTypeErr
1132
1004
  * the recursive `TypeNode` shape so composing Types does not repeatedly add its
1133
1005
  * compiler cost; {@link EvoluType} bridges that runtime evidence to this type.
1134
1006
  *
1135
- * @group Core Types
1007
+ * @group Core
1136
1008
  */
1137
1009
  export interface AnyType extends TypeNode {
1138
1010
  readonly [concreteTypeSymbol]: true;
1139
1011
  }
1140
1012
  /**
1141
- * Error returned when a value is not an Evolu {@link Type}.
1013
+ * Error returned when {@link EvoluType} rejects a value.
1142
1014
  *
1143
- * @group Core Types
1015
+ * @group Core
1144
1016
  */
1145
1017
  export interface EvoluTypeError extends TypeValueError<"EvoluType"> {
1146
1018
  }
1147
1019
  /**
1148
1020
  * Nominal evidence that a value has one object tag.
1149
1021
  *
1150
- * @group Base Types
1022
+ * @group Base
1151
1023
  */
1152
1024
  export interface ObjectTag<Name extends TypeName> {
1153
1025
  readonly [objectTagSymbol]: Name;
@@ -1155,7 +1027,7 @@ export interface ObjectTag<Name extends TypeName> {
1155
1027
  /**
1156
1028
  * The {@link Type} returned by {@link objectTag}.
1157
1029
  *
1158
- * @group Base Types
1030
+ * @group Base
1159
1031
  */
1160
1032
  export interface ObjectTagType<Name extends TypeName, OutputType extends TypeNode & {
1161
1033
  readonly Output: object;
@@ -1166,7 +1038,7 @@ export interface ObjectTagType<Name extends TypeName, OutputType extends TypeNod
1166
1038
  /**
1167
1039
  * An error returned when an object does not report the expected tag.
1168
1040
  *
1169
- * @group Base Types
1041
+ * @group Base
1170
1042
  */
1171
1043
  export interface ObjectTagError<Expected extends TypeName = TypeName> extends TypeError<"ObjectTag"> {
1172
1044
  readonly expected: Expected;
@@ -1178,7 +1050,7 @@ interface ObjectTagOutputByName {
1178
1050
  readonly ArrayBuffer: globalThis.ArrayBuffer;
1179
1051
  }
1180
1052
  /**
1181
- * Creates a realm-neutral {@link Type} that trusts an object's reported tag.
1053
+ * Realm-neutral {@link Type} trusting an object's reported tag.
1182
1054
  *
1183
1055
  * Predefined built-in tags expose their native Output type under the assumption
1184
1056
  * that trusted code does not forge their tags. They do not verify native
@@ -1193,9 +1065,21 @@ interface ObjectTagOutputByName {
1193
1065
  * assumption of the predefined Type. Primitive Outputs are rejected at compile
1194
1066
  * time.
1195
1067
  *
1196
- * @group Base Types
1068
+ * ### Example
1069
+ *
1070
+ * ```ts
1071
+ * import { objectTag } from "@evolu/common";
1072
+ *
1073
+ * const DateType = objectTag("Date");
1074
+ * const date = new globalThis.Date("2025-01-01T00:00:00.000Z");
1075
+ *
1076
+ * expectOk(DateType.fromUnknown(date), date);
1077
+ * ```
1078
+ *
1079
+ * @group Base
1197
1080
  */
1198
1081
  export declare function objectTag<Name extends keyof ObjectTagOutputByName>(name: ValidateConcreteTypeName<Name>): Type<Name, ObjectTagOutputByName[Name], ObjectTagOutputByName[Name], ObjectTagError<Name>, null, ObjectTagError<Name>, never, ObjectTagOutputByName[Name]>;
1082
+ /** Creates an object-tag Type by refining an existing object Type. */
1199
1083
  export declare function objectTag<Name extends TypeName, OutputType extends ConcreteTypeNode & {
1200
1084
  readonly Output: object;
1201
1085
  }>(name: ValidateConcreteTypeName<Name>, outputType: ValidateOutput<OutputType> & ([ChildTypeNameValidationError<"ObjectTag", OutputType>] extends [never] ? unknown : ChildTypeNameValidationError<"ObjectTag", OutputType>)): ObjectTagType<Name, OutputType>;
@@ -1205,7 +1089,7 @@ declare const objectTagSymbol: unique symbol;
1205
1089
  *
1206
1090
  * It trusts the reported object tag and does not verify Date internal slots.
1207
1091
  *
1208
- * @group Base Types
1092
+ * @group Base
1209
1093
  */
1210
1094
  export declare const Date: Type<"Date", Date, Date, ObjectTagError<"Date">, null, ObjectTagError<"Date">, never, Date, true>;
1211
1095
  /**
@@ -1214,7 +1098,7 @@ export declare const Date: Type<"Date", Date, Date, ObjectTagError<"Date">, null
1214
1098
  * It trusts the reported object tag and does not verify Uint8Array internal
1215
1099
  * slots.
1216
1100
  *
1217
- * @group Base Types
1101
+ * @group Base
1218
1102
  */
1219
1103
  export declare const Uint8Array: Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, ObjectTagError<"Uint8Array">, null, ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>;
1220
1104
  /**
@@ -1223,11 +1107,11 @@ export declare const Uint8Array: Type<"Uint8Array", Uint8Array<ArrayBufferLike>,
1223
1107
  * It trusts the reported object tag and does not verify ArrayBuffer internal
1224
1108
  * slots.
1225
1109
  *
1226
- * @group Base Types
1110
+ * @group Base
1227
1111
  */
1228
1112
  export declare const ArrayBuffer: Type<"ArrayBuffer", ArrayBuffer, ArrayBuffer, ObjectTagError<"ArrayBuffer">, null, ObjectTagError<"ArrayBuffer">, never, ArrayBuffer, true>;
1229
1113
  /**
1230
- * Creates a {@link Type} for instances of one constructor.
1114
+ * Instance {@link Type} for one constructor.
1231
1115
  *
1232
1116
  * Membership uses the intrinsic prototype chain, so subclasses are accepted,
1233
1117
  * equivalent constructors from other realms are rejected, and custom
@@ -1254,25 +1138,33 @@ export declare const ArrayBuffer: Type<"ArrayBuffer", ArrayBuffer, ArrayBuffer,
1254
1138
  * assert(!UserInstance.is({ name: "Ada" }));
1255
1139
  * ```
1256
1140
  *
1257
- * @group Base Types
1141
+ * @group Base
1258
1142
  */
1259
1143
  export declare const instanceOf: <Constructor extends InstanceConstructor>(constructor: ValidateInstanceConstructor<Constructor>) => InstanceOfType<Constructor>;
1260
1144
  /**
1261
1145
  * A JavaScript class constructor accepted by {@link instanceOf}.
1262
1146
  *
1263
- * @group Base Types
1147
+ * @group Base
1264
1148
  */
1265
1149
  export type InstanceConstructor<Instance extends object = object> = (abstract new (...args: ReadonlyArray<never>) => Instance) & {
1266
1150
  readonly name: string;
1267
1151
  };
1268
- /** @group Base Types */
1152
+ /**
1153
+ * The {@link Type} returned by {@link instanceOf}.
1154
+ *
1155
+ * @group Base
1156
+ */
1269
1157
  export interface InstanceOfType<Constructor extends InstanceConstructor> extends Type<"InstanceOf", InstanceOfOutput<Constructor>, InstanceOfOutput<Constructor>, InstanceOfError, null, InstanceOfError, never, InstanceOfOutput<Constructor>> {
1270
1158
  readonly constructor: Constructor;
1271
1159
  }
1272
1160
  type InstanceOfOutput<Constructor extends InstanceConstructor> = Constructor extends {
1273
1161
  readonly prototype: infer Output extends object;
1274
1162
  } ? Output : InstanceType<Constructor>;
1275
- /** @group Base Types */
1163
+ /**
1164
+ * Error returned when a value is not an instance of the expected constructor.
1165
+ *
1166
+ * @group Base
1167
+ */
1276
1168
  export interface InstanceOfError extends TypeValueError<"InstanceOf"> {
1277
1169
  readonly constructorName: string;
1278
1170
  }
@@ -1286,25 +1178,62 @@ type InstanceConstructorCompileTimeError = CompileTimeError<"Type", "Constructor
1286
1178
  * primitive through `from.parent`. The expected value must have one exact
1287
1179
  * literal type. Validation uses `===`, so `-0` matches `0`.
1288
1180
  *
1181
+ * In {@link templateLiteralParser}, use a string Literal Type when the literal
1182
+ * should be decoded into the Output Tuple. Use a raw string when it should only
1183
+ * frame the canonical string.
1184
+ *
1185
+ * ### Example
1186
+ *
1187
+ * ```ts
1188
+ * import { literal } from "@evolu/common";
1189
+ *
1190
+ * const Ready = literal("ready");
1191
+ *
1192
+ * expectTypeOf<typeof Ready.Output>().toEqualTypeOf<"ready">();
1193
+ * expectOk(Ready.fromUnknown("ready"), "ready");
1194
+ * expectErr(Ready.fromUnknown("pending"), {
1195
+ * type: "Literal",
1196
+ * expected: "ready",
1197
+ * value: "pending",
1198
+ * });
1199
+ * ```
1200
+ *
1289
1201
  * @group Unions
1290
1202
  */
1291
1203
  export declare const literal: <const Expected extends Literal>(expected: ValidateLiteral<Expected>) => LiteralType<Expected>;
1292
- /** @group Unions */
1204
+ /**
1205
+ * The {@link Type} returned by {@link literal}.
1206
+ *
1207
+ * @group Unions
1208
+ */
1293
1209
  export interface LiteralType<Expected extends Literal> extends Type<"Literal", WidenLiteral<Expected>, Expected, LiteralError<Expected>, LiteralParent<Expected>, LiteralError<Expected> | LiteralParentErrors<Expected>, never, CanonicalInputSubset<WidenLiteral<Expected>, CanonicalInputForParent<LiteralParent<Expected>, Expected>>, IdentityEncodingForParent<LiteralParent<Expected>>> {
1210
+ readonly [templateLiteralSyntaxSymbol]: true;
1294
1211
  readonly expected: Expected;
1295
1212
  }
1296
1213
  type LiteralParent<Expected extends Literal> = Expected extends string ? typeof String : Expected extends number ? typeof Number : Expected extends bigint ? typeof BigInt : Expected extends boolean ? typeof Boolean : null;
1297
1214
  type LiteralParentErrors<Expected extends Literal> = LiteralParent<Expected> extends infer Parent extends TypeNode ? InferErrors<Parent> : never;
1298
1215
  type ValidateLiteral<Expected extends Literal> = IsUnion<Expected> extends false ? {} extends Readonly<Record<`${Expected}`, never>> ? LiteralCompileTimeError : Expected : LiteralCompileTimeError;
1299
1216
  type LiteralCompileTimeError = CompileTimeError<"Type", "Expected must be one concrete literal value.">;
1300
- /** @group Unions */
1217
+ /**
1218
+ * Error returned when a value does not equal the expected literal.
1219
+ *
1220
+ * @group Unions
1221
+ */
1301
1222
  export interface LiteralError<Expected extends Literal = Literal> extends TypeError<"Literal"> {
1302
1223
  readonly expected: Expected;
1303
1224
  readonly value: unknown;
1304
1225
  }
1305
- /** @group Unions */
1226
+ /**
1227
+ * Literal {@link Type} accepting only `undefined`.
1228
+ *
1229
+ * @group Unions
1230
+ */
1306
1231
  export declare const Undefined: LiteralType<undefined>;
1307
- /** @group Unions */
1232
+ /**
1233
+ * Literal {@link Type} accepting only `null`.
1234
+ *
1235
+ * @group Unions
1236
+ */
1308
1237
  export declare const Null: LiteralType<null>;
1309
1238
  /**
1310
1239
  * Union {@link Type}.
@@ -1329,7 +1258,10 @@ export declare const Null: LiteralType<null>;
1329
1258
  * within each member.
1330
1259
  *
1331
1260
  * Member order matters when multiple members accept the same value: validation
1332
- * and encoding use the first matching member.
1261
+ * and encoding use the first matching member. When member Inputs overlap,
1262
+ * decoding the value emitted by the first member selected for an Output must
1263
+ * reproduce that semantic Output; otherwise the Union violates the round-trip
1264
+ * law.
1333
1265
  *
1334
1266
  * ### Example
1335
1267
  *
@@ -1366,29 +1298,62 @@ export declare function union<const Expected extends AtLeastTwoReadonlyArray<Lit
1366
1298
  }): UnionType<{
1367
1299
  readonly [Index in keyof Expected]: LiteralType<Expected[Index]>;
1368
1300
  }>;
1301
+ /** Creates a Union Type from Type members. */
1369
1302
  export declare function union<const Members extends AtLeastTwoReadonlyArray<TypeNode>>(...members: {
1370
1303
  readonly [Index in keyof Members]: ValidateUnionTypeMember<Members[Index]>;
1371
1304
  }): UnionType<Members>;
1305
+ /** Creates a Union Type from Type and literal members. */
1372
1306
  export declare function union<const Members extends AtLeastTwoReadonlyArray<TypeNode | Literal>>(...members: {
1373
1307
  readonly [Index in keyof Members]: ValidateUnionMember<Members[Index]>;
1374
1308
  }): UnionType<NormalizeUnionMembers<Members>>;
1375
1309
  /**
1376
- * Shorthand for passing a {@link Type} and `undefined` to {@link union}.
1310
+ * Union {@link Type} containing the supplied Type and `undefined`.
1377
1311
  *
1378
1312
  * This does not make an object property optional. It changes only the values
1379
1313
  * accepted when the property is present.
1380
1314
  *
1315
+ * ### Example
1316
+ *
1317
+ * ```ts
1318
+ * import { String, undefinedOr } from "@evolu/common";
1319
+ *
1320
+ * const StringOrUndefined = undefinedOr(String);
1321
+ *
1322
+ * expectOk(StringOrUndefined.fromUnknown(undefined), undefined);
1323
+ * ```
1324
+ *
1381
1325
  * @group Unions
1382
1326
  */
1383
1327
  export declare const undefinedOr: <ValueType extends TypeNode>(type: ValidateUnionTypeMember<ValueType>) => UnionType<readonly [ValueType, typeof Undefined]>;
1384
1328
  /**
1385
- * Shorthand for passing a {@link Type} and `null` to {@link union}.
1329
+ * Union {@link Type} containing the supplied Type and `null`.
1330
+ *
1331
+ * ### Example
1332
+ *
1333
+ * ```ts
1334
+ * import { String, nullOr } from "@evolu/common";
1335
+ *
1336
+ * const NullableString = nullOr(String);
1337
+ *
1338
+ * expectOk(NullableString.fromUnknown(null), null);
1339
+ * ```
1386
1340
  *
1387
1341
  * @group Unions
1388
1342
  */
1389
1343
  export declare const nullOr: <ValueType extends TypeNode>(type: ValidateUnionTypeMember<ValueType>) => UnionType<readonly [ValueType, typeof Null]>;
1390
1344
  /**
1391
- * Shorthand for passing a {@link Type}, `null`, and `undefined` to {@link union}.
1345
+ * Union {@link Type} containing the supplied Type, `null`, and `undefined`.
1346
+ *
1347
+ * ### Example
1348
+ *
1349
+ * ```ts
1350
+ * import { String, nullishOr } from "@evolu/common";
1351
+ *
1352
+ * const NullishString = nullishOr(String);
1353
+ *
1354
+ * expectOk(NullishString.fromUnknown(undefined), undefined);
1355
+ * expectOk(NullishString.fromUnknown(null), null);
1356
+ * ```
1392
1357
  *
1393
1358
  * @group Unions
1394
1359
  */
@@ -1401,8 +1366,13 @@ type UnionMemberConcreteTypeError = CompileTimeError<"Type", "Union member must
1401
1366
  type NormalizeUnionMembers<Members extends AtLeastTwoReadonlyArray<TypeNode | Literal>> = {
1402
1367
  readonly [Index in keyof Members]: Members[Index] extends TypeNode ? Members[Index] : Members[Index] extends Literal ? LiteralType<Members[Index]> : never;
1403
1368
  } extends infer Normalized extends AtLeastTwoReadonlyArray<TypeNode> ? Normalized : never;
1404
- /** @group Unions */
1369
+ /**
1370
+ * The {@link Type} returned by {@link union}.
1371
+ *
1372
+ * @group Unions
1373
+ */
1405
1374
  export interface UnionType<Members extends AtLeastTwoReadonlyArray<TypeNode>> extends Type<"Union", Members[number]["Input"], Members[number]["Output"], UnionTypeError<Members>, UnionInputParent<Members>, UnionTypeError<Members>, never, CanonicalInputOf<Members[number]>, AllTypesUseIdentityEncoding<Members[number]>> {
1375
+ readonly [templateLiteralSyntaxSymbol]: true;
1406
1376
  readonly [reflectedTypesSymbol]?: Members[number];
1407
1377
  readonly members: Members;
1408
1378
  }
@@ -1418,9 +1388,17 @@ type UnionInputTypeError<Members extends AtLeastTwoReadonlyArray<TypeNode>, Root
1418
1388
  type RootUnionMembers<Members extends AtLeastTwoReadonlyArray<TypeNode>> = {
1419
1389
  readonly [Index in keyof Members]: RootType<Members[Index]>;
1420
1390
  } extends infer RootMembers extends AtLeastTwoReadonlyArray<TypeNode> ? RootMembers : never;
1421
- /** @group Unions */
1391
+ /**
1392
+ * Error returned when every member of a {@link union} rejects an input.
1393
+ *
1394
+ * @group Unions
1395
+ */
1422
1396
  export type UnionError<Error extends TypeError = TypeError, MemberError extends UnionMemberError<Error> = UnionMemberError<Error>> = [Error] extends [never] ? never : UnionErrorValue<Error, MemberError>;
1423
- /** @group Unions */
1397
+ /**
1398
+ * An error returned by one {@link union} member and its index.
1399
+ *
1400
+ * @group Unions
1401
+ */
1424
1402
  export interface UnionMemberError<Error extends TypeError, Index extends number = number> {
1425
1403
  readonly index: Index;
1426
1404
  readonly error: Error;
@@ -1448,130 +1426,335 @@ interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemb
1448
1426
  readonly errors: NonEmptyReadonlyArray<MemberError>;
1449
1427
  }
1450
1428
  /**
1451
- * Branded {@link Type}.
1429
+ * Template literal {@link Type} that parses canonical strings into Tuples.
1452
1430
  *
1453
- * Branding is the recommended way to define domain-specific primitive Types in
1454
- * Evolu. A {@link Brand} distinguishes values that share the same runtime
1455
- * representation, preventing values with different meanings from being used
1456
- * interchangeably.
1431
+ * Accepts the same template parts as {@link templateLiteral}: fixed string
1432
+ * literals and Types canonically encoded as strings. Instead of keeping Output
1433
+ * as a string, fixed literals define the framing and Output is a readonly Tuple
1434
+ * of the decoded Type parts. `to` encodes that Tuple back into the canonical
1435
+ * string represented by the parent Type. At least one Type part is required.
1457
1436
  *
1458
- * `brand` takes the name of the new Brand, the parent Type to brand, and an
1459
- * optional validation callback for an additional constraint. Its Output retains
1460
- * the parent Output and its brands, and adds the new Brand.
1437
+ * When every capture uses identity encoding, the parent Output is the exact
1438
+ * TypeScript template literal type. A transforming capture makes it nominal;
1439
+ * create such strings with `to` or validate them with the parent Type.
1461
1440
  *
1462
- * Without a validation callback, the brand adds no errors and inherits its
1463
- * parent's formatter. A validation callback returns `ok()` when the parent
1464
- * value satisfies the constraint or an `Err` describing the failure. `brand`
1465
- * always preserves the parent value; representation-changing work belongs in
1466
- * {@link transform}. A fallible validation callback must format only the error
1467
- * it introduces; inherited errors are formatted by the parent Type
1468
- * automatically. A fallible brand's error `type` must equal the Brand name.
1441
+ * Deterministic framing is a core correctness guarantee. It preserves
1442
+ * reversibility and keeps capture boundaries unambiguous. Different capture
1443
+ * Tuples must never encode to the same string. The parser provides predictable
1444
+ * parsing without pathological backtracking and decodes each capture once, so
1445
+ * adversarial input cannot trigger exponential parser work. Fixed-width
1446
+ * captures may be adjacent, but only one variable-width capture is allowed.
1447
+ * Declarations that could join UTF-16 surrogate halves across parts are
1448
+ * rejected during construction.
1449
+ *
1450
+ * Keep capture unions reasonably small to avoid excessive compiler work.
1451
+ *
1452
+ * TypeScript template literal types can describe a fixed number of digit
1453
+ * positions, but not an arbitrarily long sequence of digits. Such grammars use
1454
+ * branded Types such as {@link DecimalString}; `templateLiteralParser` preserves
1455
+ * that exactness by requiring a validated branded capture when encoding.
1469
1456
  *
1470
1457
  * ### Example
1471
1458
  *
1472
- * A signed 64-bit integer:
1459
+ * A template literal Type defines both a canonical string representation and
1460
+ * the structured data decoded from it:
1473
1461
  *
1474
1462
  * ```ts
1475
- * import {
1476
- * BigInt,
1477
- * brand,
1478
- * err,
1479
- * ok,
1480
- * type Brand,
1481
- * type TypeError,
1482
- * } from "@evolu/common";
1463
+ * import { templateLiteralParser, union } from "@evolu/common";
1483
1464
  *
1484
- * const Int64 = brand(
1485
- * "Int64",
1486
- * BigInt,
1487
- * (value) =>
1488
- * globalThis.BigInt.asIntN(64, value) === value
1489
- * ? ok()
1490
- * : err<Int64Error>({ type: "Int64", value }),
1491
- * () => "Expected a signed 64-bit integer.",
1492
- * );
1493
- * type Int64 = typeof Int64.Output;
1465
+ * const Language = union("en", "cs");
1466
+ * const Region = union("US", "CZ");
1494
1467
  *
1495
- * // Note the Brand.
1496
- * expectTypeOf<Int64>().toEqualTypeOf<bigint & Brand<"Int64">>();
1468
+ * // Define a Type for "en-US" | "en-CZ" | "cs-US" | "cs-CZ".
1469
+ * const SupportedLocale = templateLiteralParser(Language, "-", Region);
1497
1470
  *
1498
- * interface Int64Error extends TypeError<"Int64"> {
1499
- * readonly value: bigint;
1500
- * }
1471
+ * // Output is the decoded language and region.
1472
+ * type SupportedLocale = typeof SupportedLocale.Output;
1473
+ * expectTypeOf<SupportedLocale>().toEqualTypeOf<
1474
+ * readonly ["en" | "cs", "US" | "CZ"]
1475
+ * >();
1501
1476
  *
1502
- * expectOk(Int64.fromUnknown(42n), 42n);
1503
- * expectErr(Int64.fromUnknown(2n ** 63n), {
1504
- * type: "Int64",
1505
- * value: 2n ** 63n,
1477
+ * // The parent Output is the canonical locale string.
1478
+ * type SupportedLocaleLiteral = typeof SupportedLocale.parent.Output;
1479
+ * expectTypeOf<SupportedLocaleLiteral>().toEqualTypeOf<
1480
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1481
+ * >();
1482
+ *
1483
+ * // Parse an unknown string into structured data.
1484
+ * const result = SupportedLocale.fromUnknown("cs-CZ");
1485
+ * expectOk(result, ["cs", "CZ"]);
1486
+ * const locale = result.value;
1487
+ * expectTypeOf(locale).toEqualTypeOf<SupportedLocale>();
1488
+ * expectErr(SupportedLocale.fromUnknown("cs/CZ"), {
1489
+ * type: "TemplateLiteral",
1490
+ * value: "cs/CZ",
1506
1491
  * });
1492
+ *
1493
+ * // Encode structured data into its canonical string.
1494
+ * const localeLiteral = SupportedLocale.to(locale);
1495
+ * expectTypeOf(localeLiteral).toEqualTypeOf<SupportedLocaleLiteral>();
1496
+ * expect(localeLiteral).toBe("cs-CZ");
1497
+ *
1498
+ * // Validate a string configuration value.
1499
+ * const configValue: unknown = "cs-CZ";
1500
+ * assert(SupportedLocale.parent.is(configValue));
1501
+ * expectTypeOf(configValue).toEqualTypeOf<SupportedLocaleLiteral>();
1502
+ * expect(SupportedLocale.parent.is("fr-CZ")).toBe(false);
1507
1503
  * ```
1508
1504
  *
1509
- * To reuse a Brand constraint with different parent Types, define a
1510
- * {@link BrandFactory}. Brand Factories can then be composed:
1505
+ * `SupportedLocale` is structured data for application code.
1506
+ * `SupportedLocaleLiteral` is its canonical representation for configuration
1507
+ * and other APIs that require a string, such as URL parameters, environment
1508
+ * variables, and storage keys.
1509
+ *
1510
+ * Use branded captures for strings that TypeScript template literal types
1511
+ * cannot express exactly, such as arbitrary-length canonical decimals:
1511
1512
  *
1512
1513
  * ```ts
1513
1514
  * import {
1514
- * String,
1515
- * brand,
1516
- * err,
1517
- * minLength,
1518
- * ok,
1519
- * type Brand,
1520
- * type BrandFactory,
1521
- * type TypeError,
1515
+ * NonNegativeDecimalString,
1516
+ * templateLiteralParser,
1522
1517
  * } from "@evolu/common";
1523
1518
  *
1524
- * const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
1525
- * parent,
1526
- * ) =>
1527
- * brand(
1528
- * "Trimmed",
1529
- * parent,
1530
- * (value) =>
1531
- * value === value.trim()
1532
- * ? ok()
1533
- * : err<TrimmedError>({ type: "Trimmed", value }),
1534
- * () => "Expected a string without surrounding whitespace.",
1535
- * );
1519
+ * const DecimalText = templateLiteralParser(
1520
+ * "decimal:",
1521
+ * NonNegativeDecimalString,
1522
+ * );
1536
1523
  *
1537
- * const TrimmedString = trimmed(String);
1538
- * type TrimmedString = typeof TrimmedString.Output;
1524
+ * // DecimalText.to requires a validated NonNegativeDecimalString.
1525
+ * const zero = NonNegativeDecimalString.orThrow("0");
1539
1526
  *
1540
- * expectTypeOf<TrimmedString>().toEqualTypeOf<string & Brand<"Trimmed">>();
1527
+ * expectOk(DecimalText.fromUnknown("decimal:0"), [zero]);
1528
+ * expect(DecimalText.to([zero])).toBe("decimal:0");
1529
+ * ```
1541
1530
  *
1542
- * const NonEmptyString = minLength(1)(String);
1543
- * type NonEmptyString = typeof NonEmptyString.Output;
1531
+ * Capture Types (the Type arguments passed to `templateLiteralParser`) can use
1532
+ * transformations to decode substrings into non-string data:
1544
1533
  *
1545
- * expectTypeOf<NonEmptyString>().toEqualTypeOf<
1546
- * string & Brand<"MinLength1">
1547
- * >();
1534
+ * ```ts
1535
+ * import {
1536
+ * Int64FromInt64String,
1537
+ * templateLiteralParser,
1538
+ * } from "@evolu/common";
1539
+ *
1540
+ * const ItemId = templateLiteralParser("item-", Int64FromInt64String);
1541
+ * type ItemId = typeof ItemId.Output;
1542
+ * type ItemIdLiteral = typeof ItemId.parent.Output;
1543
+ *
1544
+ * // Decode the string into structured data.
1545
+ * const result = ItemId.fromUnknown("item-42");
1546
+ * expectOk(result, [42n]);
1547
+ * const itemId = result.value;
1548
+ * expectTypeOf(itemId).toEqualTypeOf<ItemId>();
1549
+ *
1550
+ * // Encode the structured data into its canonical string.
1551
+ * const itemIdLiteral = ItemId.to(itemId);
1552
+ * expectTypeOf(itemIdLiteral).toEqualTypeOf<ItemIdLiteral>();
1553
+ * expect(itemIdLiteral).toBe("item-42");
1554
+ *
1555
+ * // TypeScript cannot prove from the literal alone that "42" is a valid Int64 encoding.
1556
+ * // @ts-expect-error Validate it with ItemId.parent or create it with ItemId.to.
1557
+ * const invalidItemIdLiteral: ItemIdLiteral = "item-42";
1558
+ * ```
1559
+ *
1560
+ * Fixed-width captures can be adjacent:
1561
+ *
1562
+ * ```ts
1563
+ * import { templateLiteralParser, union } from "@evolu/common";
1564
+ *
1565
+ * const Digit = union("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
1566
+ * const TwoDigits = templateLiteralParser(Digit, Digit);
1567
+ * type TwoDigits = typeof TwoDigits.Output;
1568
+ * type TwoDigitsLiteral = typeof TwoDigits.parent.Output;
1569
+ *
1570
+ * const twoDigits: TwoDigits = ["4", "2"];
1571
+ * const twoDigitsLiteral: TwoDigitsLiteral = "42";
1572
+ * // @ts-expect-error TwoDigitsLiteral requires exactly two digits.
1573
+ * const threeDigitsLiteral: TwoDigitsLiteral = "123";
1548
1574
  *
1549
- * const NonEmptyTrimmedString = minLength(1)(TrimmedString);
1550
- * type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Output;
1575
+ * expectOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
1576
+ * expect(TwoDigits.to(twoDigits)).toBe(twoDigitsLiteral);
1577
+ * ```
1578
+ *
1579
+ * TypeScript rejects multiple variable-width captures because their encoded
1580
+ * boundaries would be ambiguous:
1581
+ *
1582
+ * ```ts
1583
+ * import { String, templateLiteralParser } from "@evolu/common";
1584
+ *
1585
+ * // @ts-expect-error At most one Type capture can have a variable-width string representation.
1586
+ * templateLiteralParser(String, ":", String);
1587
+ * ```
1588
+ *
1589
+ * This restriction keeps encoding reversible: different capture Tuples must
1590
+ * never produce the same string. A delimiter alone is not enough because it can
1591
+ * also occur inside a capture. Some formats could provide stronger guarantees,
1592
+ * such as captures that exclude a delimiter; support for those can be added
1593
+ * when concrete use cases justify the additional framing rules.
1594
+ *
1595
+ * @group Template literals
1596
+ */
1597
+ export declare const templateLiteralParser: <const Parts extends TemplateLiteralParts>(...parts: { readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>; } & TemplateLiteralValidation<Parts>) => TemplateLiteralParserType<Parts>;
1598
+ /**
1599
+ * The parsing {@link Type} returned by {@link templateLiteralParser}.
1600
+ *
1601
+ * @group Template literals
1602
+ */
1603
+ export interface TemplateLiteralParserType<Parts extends TemplateLiteralParts> extends Type<"TemplateLiteral", string, TemplateLiteralCaptureTuple<Parts>["Output"], never, TemplateLiteralType<Parts>, InferErrors<TemplateLiteralType<Parts>>, never, TemplateLiteralStringOutput<Parts>, false> {
1604
+ readonly [templateLiteralSyntaxSymbol]: true;
1605
+ readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
1606
+ readonly output: TemplateLiteralCaptureTuple<Parts>;
1607
+ readonly parts: Parts;
1608
+ }
1609
+ /**
1610
+ * The validating string {@link Type} returned by {@link templateLiteral}.
1611
+ *
1612
+ * @group Template literals
1613
+ */
1614
+ export interface TemplateLiteralType<Parts extends TemplateLiteralParts> extends Type<"TemplateLiteral", string, TemplateLiteralStringOutput<Parts>, TemplateLiteralParseError<Parts>, typeof String, TypeOfError<"String"> | TemplateLiteralParseError<Parts>, never, TemplateLiteralStringOutput<Parts>, true> {
1615
+ readonly [templateLiteralSyntaxSymbol]: true;
1616
+ readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
1617
+ readonly output: TemplateLiteralCaptureTuple<Parts>;
1618
+ readonly parts: Parts;
1619
+ }
1620
+ /**
1621
+ * Template literal {@link Type} for validation.
1622
+ *
1623
+ * Creates a canonical string Type from fixed strings and string-encoded Types.
1624
+ *
1625
+ * Use this factory when Output should remain a string. Switch to
1626
+ * {@link templateLiteralParser} when the individual Type parts should be decoded
1627
+ * into a Tuple.
1628
+ *
1629
+ * ### Example
1630
+ *
1631
+ * ```ts
1632
+ * import { templateLiteral, union } from "@evolu/common";
1551
1633
  *
1552
- * expectTypeOf<NonEmptyTrimmedString>().toEqualTypeOf<
1553
- * string & Brand<"Trimmed"> & Brand<"MinLength1">
1634
+ * const Language = union("en", "cs");
1635
+ * const Region = union("US", "CZ");
1636
+ * const Locale = templateLiteral(Language, "-", Region);
1637
+ *
1638
+ * expectTypeOf<typeof Locale.Output>().toEqualTypeOf<
1639
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1554
1640
  * >();
1641
+ * expectOk(Locale.fromUnknown("cs-CZ"), "cs-CZ");
1642
+ * expect(Locale.is("fr-CZ")).toBe(false);
1643
+ * ```
1555
1644
  *
1556
- * interface TrimmedError extends TypeError<"Trimmed"> {
1557
- * readonly value: string;
1558
- * }
1645
+ * @group Template literals
1646
+ */
1647
+ export declare const templateLiteral: <const Parts extends TemplateLiteralParts>(...parts: { readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>; } & TemplateLiteralValidation<Parts>) => TemplateLiteralType<Parts>;
1648
+ type TemplateLiteralParseError<Parts extends TemplateLiteralParts> = TransformError<"TemplateLiteral", TemplateLiteralIsFrameless<Parts> extends true ? never : TemplateLiteralError, TemplateLiteralCaptureTupleError<Parts>>;
1649
+ type TemplateLiteralCaptureTupleError<Parts extends TemplateLiteralParts> = TupleElementsError<TemplateLiteralCaptureFromStringError<TemplateLiteralCaptureTypes<Parts>[number]>>;
1650
+ type TemplateLiteralCaptureFromStringError<T extends TypeNode> = T extends TypeNode ? string extends RootType<T>["Output"] ? TypeFromError<T> : InferErrors<T> : never;
1651
+ /**
1652
+ * Error returned when a string does not match a template literal declaration.
1653
+ *
1654
+ * @group Template literals
1655
+ */
1656
+ export interface TemplateLiteralError extends TypeError<"TemplateLiteral"> {
1657
+ readonly value: string;
1658
+ }
1659
+ declare const templateLiteralStringBrandSymbol: unique symbol;
1660
+ interface TemplateLiteralStringBrand<Parts extends TemplateLiteralParts> {
1661
+ readonly [templateLiteralStringBrandSymbol]: Parts;
1662
+ }
1663
+ type TemplateLiteralPart = string | TypeNode;
1664
+ type TemplateLiteralParts = NonEmptyReadonlyArray<TemplateLiteralPart>;
1665
+ type TemplateLiteralValidation<Parts extends TemplateLiteralParts> = number extends Parts["length"] ? readonly [ValidationFailure<TemplateLiteralPartsTupleError>] : IsUnion<Parts["length"]> extends true ? readonly [ValidationFailure<TemplateLiteralPartsTupleError>] : [Extract<Parts[number], TypeNode>] extends [never] ? readonly [ValidationFailure<TemplateLiteralCaptureRequiredError>] : TemplateLiteralHasAmbiguousCaptures<Parts> extends true ? readonly [ValidationFailure<TemplateLiteralAmbiguousCapturesError>] : unknown;
1666
+ type TemplateLiteralPartsTupleError = CompileTimeError<"TemplateLiteral", "Parts must use one concrete finite non-empty tuple.">;
1667
+ type TemplateLiteralCaptureRequiredError = CompileTimeError<"TemplateLiteral", "At least one part must be a Type capture.">;
1668
+ type TemplateLiteralAmbiguousCapturesError = CompileTimeError<"TemplateLiteral", "At most one Type capture can have a variable-width string representation.">;
1669
+ type TemplateLiteralCaptureTypes<Parts extends ReadonlyArray<TemplateLiteralPart>, Captures extends ReadonlyArray<TypeNode> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralCaptureTypes<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, Head extends TypeNode ? readonly [...Captures, Head] : Captures> : Extract<Captures, NonEmptyReadonlyArray<TypeNode>>;
1670
+ type TemplateLiteralCaptureTuple<Parts extends TemplateLiteralParts> = TupleType<TemplateLiteralCaptureTypes<Parts>>;
1671
+ type TemplateLiteralCanonicalInput<Parts extends ReadonlyArray<TemplateLiteralPart>, Input extends string = ""> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralCanonicalInput<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, `${Input}${TemplateLiteralPartCanonicalInput<Extract<Head, TemplateLiteralPart>>}`> : Input;
1672
+ type TemplateLiteralStringOutput<Parts extends TemplateLiteralParts> = AllTypesUseIdentityEncoding<Extract<Parts[number], TypeNode>> extends true ? TemplateLiteralCanonicalInput<Parts> : TemplateLiteralCanonicalInput<Parts> & TemplateLiteralStringBrand<Parts>;
1673
+ type TemplateLiteralPartCanonicalInput<Part extends TemplateLiteralPart> = Part extends string ? Part : Part extends TypeNode ? Extract<CanonicalInputOf<Part>, string> : never;
1674
+ type ValidateTemplateLiteralPart<Part extends TemplateLiteralPart> = IsUnion<Part> extends false ? Part extends string ? ValidateLiteral<Part> : Part extends ConcreteTypeNode ? IsTemplateLiteralPartType<Part> extends true ? Part : TemplateLiteralPartCompileTimeError : TemplateLiteralPartCompileTimeError : TemplateLiteralPartCompileTimeError;
1675
+ type IsTemplateLiteralPartType<T extends TypeNode> = [
1676
+ CanonicalInputOf<T>
1677
+ ] extends [never] ? false : [CanonicalInputOf<T>] extends [string] ? true : false;
1678
+ type TemplateLiteralPartCompileTimeError = CompileTimeError<"TemplateLiteral", "Part must be a raw string literal or a Type canonically encoded as a string.">;
1679
+ type TemplateLiteralHasAmbiguousCaptures<Parts extends ReadonlyArray<TemplateLiteralPart>, VariableCaptures extends ReadonlyArray<unknown> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? Head extends TypeNode ? [TemplateLiteralTypeWidth<Head>] extends [null] ? VariableCaptures extends readonly [unknown] ? true : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, readonly [unknown]> : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, VariableCaptures> : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, VariableCaptures> : false;
1680
+ type TemplateLiteralTypeWidth<T extends TypeNode> = T extends LiteralType<infer Expected extends string> ? TemplateLiteralStringWidth<Expected> : T extends UnionType<infer Members> ? NormalizeTemplateLiteralWidth<TemplateLiteralTypeWidth<Members[number]>> : T extends TemplateLiteralParserType<infer Parts> ? TemplateLiteralPartsWidth<Parts> : T extends TemplateLiteralType<infer Parts> ? TemplateLiteralPartsWidth<Parts> : T["parent"] extends infer Parent extends TypeNode ? TemplateLiteralTypeWidth<Parent> : null;
1681
+ type NormalizeTemplateLiteralWidth<Width> = IsUnion<Width> extends true ? null : Width extends ReadonlyArray<unknown> ? Width : null;
1682
+ type TemplateLiteralPartsWidth<Parts extends ReadonlyArray<TemplateLiteralPart>, Width extends ReadonlyArray<unknown> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralPartWidth<Extract<Head, TemplateLiteralPart>> extends infer PartWidth ? [PartWidth] extends [ReadonlyArray<unknown>] ? TemplateLiteralPartsWidth<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, readonly [...Width, ...PartWidth]> : null : never : Width;
1683
+ type TemplateLiteralPartWidth<Part extends TemplateLiteralPart> = Part extends string ? TemplateLiteralStringWidth<Part> : Part extends TypeNode ? TemplateLiteralTypeWidth<Part> : never;
1684
+ type TemplateLiteralIsFrameless<Parts extends ReadonlyArray<TemplateLiteralPart>, HasVariableCapture extends boolean = false> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralPartWidth<Extract<Head, TemplateLiteralPart>> extends infer Width ? [Width] extends [null] ? HasVariableCapture extends true ? false : TemplateLiteralIsFrameless<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, true> : [Width] extends [readonly []] ? TemplateLiteralIsFrameless<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, HasVariableCapture> : false : false : HasVariableCapture;
1685
+ type TemplateLiteralStringWidth<Value extends string, Width extends ReadonlyArray<unknown> = readonly []> = string extends Value ? null : Value extends "" ? Width : Value extends `${infer _CodePoint}${infer Tail}` ? TemplateLiteralStringWidth<Tail, readonly [...Width, unknown]> : null;
1686
+ /**
1687
+ * Branded {@link Type}.
1688
+ *
1689
+ * Branding is the recommended way to define domain-specific primitive Types in
1690
+ * Evolu. A {@link Brand} distinguishes values that share the same runtime
1691
+ * representation, preventing values with different meanings from being used
1692
+ * interchangeably.
1559
1693
  *
1560
- * // Validation from unknown.
1561
- * expectOk(NonEmptyTrimmedString.fromUnknown("Evolu"), "Evolu");
1694
+ * `brand` takes the name of the new Brand, the parent Type to brand, and an
1695
+ * optional validation callback for an additional constraint. Its Output retains
1696
+ * the parent Output and its brands, and adds the new Brand.
1562
1697
  *
1563
- * // The typed input selects TrimmedString as the validated boundary.
1564
- * expectOk(
1565
- * NonEmptyTrimmedString.from.parent(TrimmedString.orThrow("Evolu")),
1566
- * "Evolu",
1698
+ * Without a validation callback, the brand adds no errors and inherits its
1699
+ * parent's formatter. A validation callback returns `ok()` when the parent
1700
+ * value satisfies the constraint or an `Err` describing the failure. `brand`
1701
+ * always preserves the parent value; representation-changing work belongs in
1702
+ * {@link transform}. A fallible validation callback must format only the error
1703
+ * it introduces; inherited errors are formatted by the parent Type
1704
+ * automatically. A fallible brand's error `type` must equal the Brand name.
1705
+ *
1706
+ * ### Example
1707
+ *
1708
+ * A signed 64-bit integer:
1709
+ *
1710
+ * ```ts
1711
+ * import {
1712
+ * BigInt,
1713
+ * brand,
1714
+ * err,
1715
+ * ok,
1716
+ * type Brand,
1717
+ * type TypeError,
1718
+ * } from "@evolu/common";
1719
+ *
1720
+ * const Int64 = brand(
1721
+ * "Int64",
1722
+ * BigInt,
1723
+ * (value) =>
1724
+ * globalThis.BigInt.asIntN(64, value) === value
1725
+ * ? ok()
1726
+ * : err<Int64Error>({ type: "Int64", value }),
1727
+ * () => "Expected a signed 64-bit integer.",
1567
1728
  * );
1729
+ * type Int64 = typeof Int64.Output;
1730
+ *
1731
+ * // Note the Brand.
1732
+ * expectTypeOf<Int64>().toEqualTypeOf<bigint & Brand<"Int64">>();
1733
+ *
1734
+ * interface Int64Error extends TypeError<"Int64"> {
1735
+ * readonly value: bigint;
1736
+ * }
1737
+ *
1738
+ * expectOk(Int64.fromUnknown(42n), 42n);
1739
+ * expectErr(Int64.fromUnknown(2n ** 63n), {
1740
+ * type: "Int64",
1741
+ * value: 2n ** 63n,
1742
+ * });
1568
1743
  * ```
1569
1744
  *
1570
- * @group Type construction
1745
+ * To reuse and compose a Brand constraint with different parent Types, define a
1746
+ * {@link BrandFactory}.
1747
+ *
1748
+ * @group Construction
1571
1749
  */
1572
1750
  export declare function brand<Name extends TypeName, ParentType extends ConcreteTypeNode>(name: ValidateConcreteTypeName<Name>, parent: ValidateParent<ParentType>, validate?: (value: ParentType["Output"]) => Result<void, never>): BrandType<ParentType, Name, never>;
1751
+ /** Creates a validated Brand Type with its own error formatter. */
1573
1752
  export declare function brand<Name extends TypeName, ParentType extends ConcreteTypeNode, Error extends TypeError<NoInfer<Name>>>(name: Name, parent: ValidateBrandParent<Name, ParentType>, validate: (value: ParentType["Output"]) => Result<void, Error>, formatError: TypeErrorFormatter<NoInfer<Error>>): BrandType<ParentType, Name, Error>;
1574
- /** @group Type construction */
1753
+ /**
1754
+ * The {@link Type} returned by {@link brand}.
1755
+ *
1756
+ * @group Construction
1757
+ */
1575
1758
  export interface BrandType<ParentType extends TypeNode, Name extends TypeName, Error extends TypeError> extends Type<Name, ParentType["Input"], ParentType["Output"] & Brand<Name>, Error, ParentType, Error | InferErrors<ParentType>, ChildCustomFrom<ParentType, ParentType["Output"] & Brand<Name>, Error>, CanonicalInputForChild<ParentType, ParentType["Output"] & Brand<Name>>, IdentityEncodingOf<ParentType>> {
1576
1759
  }
1577
1760
  /**
@@ -1604,17 +1787,37 @@ export interface BrandType<ParentType extends TypeNode, Name extends TypeName, E
1604
1787
  */
1605
1788
  export declare const DateIso: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DateIso", DateIsoError>;
1606
1789
  export type DateIso = typeof DateIso.Output;
1607
- /** @group String */
1790
+ /**
1791
+ * Error returned when a string is not a canonical {@link DateIso}.
1792
+ *
1793
+ * @group String
1794
+ */
1608
1795
  export interface DateIsoError extends TypeError<"DateIso"> {
1609
1796
  readonly value: string;
1610
1797
  }
1611
1798
  /**
1612
1799
  * Safely transforms a {@link Date} into a canonical {@link DateIso}.
1613
1800
  *
1801
+ * ### Example
1802
+ *
1803
+ * ```ts
1804
+ * import { DateIsoFromDate } from "@evolu/common";
1805
+ *
1806
+ * const date = new globalThis.Date("2025-01-01T12:00:00.000Z");
1807
+ * const result = DateIsoFromDate.fromUnknown(date);
1808
+ *
1809
+ * expectOk(result, "2025-01-01T12:00:00.000Z");
1810
+ * expect(DateIsoFromDate.to(result.value)).toEqual(date);
1811
+ * ```
1812
+ *
1614
1813
  * @group String
1615
1814
  */
1616
1815
  export declare const DateIsoFromDate: TransformType<Type<"Date", Date, Date, ObjectTagError<"Date">, null, ObjectTagError<"Date">, never, Date, true>, BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DateIso", DateIsoError>, "DateIsoFromDate", DateIsoFromDateError, Date>;
1617
- /** @group String */
1816
+ /**
1817
+ * Error returned when a {@link Date} cannot be represented as {@link DateIso}.
1818
+ *
1819
+ * @group String
1820
+ */
1618
1821
  export interface DateIsoFromDateError extends TypeError<"DateIsoFromDate"> {
1619
1822
  readonly value: globalThis.Date;
1620
1823
  }
@@ -1625,7 +1828,11 @@ export interface DateIsoFromDateError extends TypeError<"DateIsoFromDate"> {
1625
1828
  */
1626
1829
  export declare const Int64: BrandType<Type<"BigInt", bigint, bigint, TypeOfError<"BigInt">, null, TypeOfError<"BigInt">, never, bigint, true>, "Int64", Int64Error>;
1627
1830
  export type Int64 = typeof Int64.Output;
1628
- /** @group Number */
1831
+ /**
1832
+ * Error returned when a bigint is outside the signed 64-bit {@link Int64} range.
1833
+ *
1834
+ * @group Number
1835
+ */
1629
1836
  export interface Int64Error extends TypeError<"Int64"> {
1630
1837
  readonly value: bigint;
1631
1838
  }
@@ -1636,7 +1843,12 @@ export interface Int64Error extends TypeError<"Int64"> {
1636
1843
  */
1637
1844
  export declare const UInt64: BrandType<Type<"BigInt", bigint, bigint, TypeOfError<"BigInt">, null, TypeOfError<"BigInt">, never, bigint, true>, "UInt64", UInt64Error>;
1638
1845
  export type UInt64 = typeof UInt64.Output;
1639
- /** @group Number */
1846
+ /**
1847
+ * Error returned when a bigint is outside the unsigned 64-bit {@link UInt64}
1848
+ * range.
1849
+ *
1850
+ * @group Number
1851
+ */
1640
1852
  export interface UInt64Error extends TypeError<"UInt64"> {
1641
1853
  readonly value: bigint;
1642
1854
  }
@@ -1692,7 +1904,7 @@ export interface UInt64Error extends TypeError<"UInt64"> {
1692
1904
  * For numeric parameters encoded in a Brand name, use
1693
1905
  * {@link ValidateBrandFactoryNumber}.
1694
1906
  *
1695
- * @group Type construction
1907
+ * @group Construction
1696
1908
  */
1697
1909
  export type BrandFactory<Name extends TypeName, Value, Error extends TypeError<Name>> = <ParentType extends ConcreteTypeNode & {
1698
1910
  readonly Output: Value;
@@ -1757,7 +1969,7 @@ export type BrandFactory<Name extends TypeName, Value, Error extends TypeError<N
1757
1969
  * lessThan(100 - 1)(Number);
1758
1970
  * ```
1759
1971
  *
1760
- * @group Type utilities
1972
+ * @group Construction
1761
1973
  */
1762
1974
  export type ValidateBrandFactoryNumber<Value extends number> = IsUnion<Value> extends false ? {} extends Record<`${Value}`, never> ? Value & Readonly<Record<BrandFactoryNumberError, never>> : Value : Value & Readonly<Record<BrandFactoryNumberError, never>>;
1763
1975
  type BrandFactoryNumberError = CompileTimeError<"Brand Factory", "Parameter must be one concrete numeric literal instead of a widened, union, or branded number.">;
@@ -1788,7 +2000,11 @@ type BrandFactoryNumberError = CompileTimeError<"Brand Factory", "Parameter must
1788
2000
  * @group String
1789
2001
  */
1790
2002
  export declare const capitalized: BrandFactory<"Capitalized", string, CapitalizedError>;
1791
- /** @group String */
2003
+ /**
2004
+ * Error returned when {@link capitalized} rejects a string.
2005
+ *
2006
+ * @group String
2007
+ */
1792
2008
  export interface CapitalizedError extends TypeError<"Capitalized"> {
1793
2009
  readonly value: string;
1794
2010
  }
@@ -1800,12 +2016,26 @@ export interface CapitalizedError extends TypeError<"Capitalized"> {
1800
2016
  export declare const CapitalizedString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Capitalized", CapitalizedError>;
1801
2017
  export type CapitalizedString = typeof CapitalizedString.Output;
1802
2018
  /**
1803
- * Adds a {@link Brand} requiring a string without surrounding whitespace.
2019
+ * String {@link Brand} without surrounding whitespace.
2020
+ *
2021
+ * ### Example
2022
+ *
2023
+ * ```ts
2024
+ * import { String, trimmed } from "@evolu/common";
2025
+ *
2026
+ * const Trimmed = trimmed(String);
2027
+ *
2028
+ * expectOk(Trimmed.fromUnknown("Evolu"), "Evolu");
2029
+ * ```
1804
2030
  *
1805
2031
  * @group String
1806
2032
  */
1807
2033
  export declare const trimmed: BrandFactory<"Trimmed", string, TrimmedError>;
1808
- /** @group String */
2034
+ /**
2035
+ * Error returned when {@link trimmed} rejects a string.
2036
+ *
2037
+ * @group String
2038
+ */
1809
2039
  export interface TrimmedError extends TypeError<"Trimmed"> {
1810
2040
  readonly value: string;
1811
2041
  }
@@ -1825,17 +2055,41 @@ export type TrimmedString = typeof TrimmedString.Output;
1825
2055
  /**
1826
2056
  * Trims a string and returns a {@link TrimmedString}.
1827
2057
  *
2058
+ * ### Example
2059
+ *
2060
+ * ```ts
2061
+ * import { trim } from "@evolu/common";
2062
+ *
2063
+ * expect(trim(" Evolu ")).toBe("Evolu");
2064
+ * ```
2065
+ *
1828
2066
  * @group String
1829
2067
  */
1830
2068
  export declare const trim: (value: string) => TrimmedString;
1831
2069
  /**
1832
- * Adds a {@link Brand} requiring a value to have at least `min` items.
2070
+ * Minimum-length {@link Brand} for values whose `length` is at least `min`.
2071
+ *
2072
+ * ### Example
2073
+ *
2074
+ * ```ts
2075
+ * import { String, array, minLength } from "@evolu/common";
2076
+ *
2077
+ * const AtLeastThreeCharacters = minLength(3)(String);
2078
+ * const AtLeastTwoItems = minLength(2)(array(String));
2079
+ *
2080
+ * expectOk(AtLeastThreeCharacters.fromUnknown("abc"), "abc");
2081
+ * expectOk(AtLeastTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2082
+ * ```
1833
2083
  *
1834
2084
  * @group String
1835
2085
  * @group Collection
1836
2086
  */
1837
2087
  export declare const minLength: <Min extends number>(min: ValidateBrandFactoryNumber<Min>) => BrandFactory<`MinLength${Min}`, ValueWithLength, MinLengthError<Min>>;
1838
- /** @group Collection */
2088
+ /**
2089
+ * Error returned when {@link minLength} rejects a value.
2090
+ *
2091
+ * @group Collection
2092
+ */
1839
2093
  export interface MinLengthError<Min extends number = number> extends TypeError<`MinLength${Min}`> {
1840
2094
  readonly value: ValueWithLength;
1841
2095
  readonly min: Min;
@@ -1857,13 +2111,29 @@ export interface MinLengthError<Min extends number = number> extends TypeError<`
1857
2111
  export declare const NonEmptyTrimmedString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>;
1858
2112
  export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Output;
1859
2113
  /**
1860
- * Adds a {@link Brand} requiring a value to have at most `max` items.
2114
+ * Maximum-length {@link Brand} for values whose `length` is at most `max`.
2115
+ *
2116
+ * ### Example
2117
+ *
2118
+ * ```ts
2119
+ * import { String, array, maxLength } from "@evolu/common";
2120
+ *
2121
+ * const AtMostThreeCharacters = maxLength(3)(String);
2122
+ * const AtMostTwoItems = maxLength(2)(array(String));
2123
+ *
2124
+ * expectOk(AtMostThreeCharacters.fromUnknown("abc"), "abc");
2125
+ * expectOk(AtMostTwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2126
+ * ```
1861
2127
  *
1862
2128
  * @group String
1863
2129
  * @group Collection
1864
2130
  */
1865
2131
  export declare const maxLength: <Max extends number>(max: ValidateBrandFactoryNumber<Max>) => BrandFactory<`MaxLength${Max}`, ValueWithLength, MaxLengthError<Max>>;
1866
- /** @group Collection */
2132
+ /**
2133
+ * Error returned when {@link maxLength} rejects a value.
2134
+ *
2135
+ * @group Collection
2136
+ */
1867
2137
  export interface MaxLengthError<Max extends number = number> extends TypeError<`MaxLength${Max}`> {
1868
2138
  readonly value: ValueWithLength;
1869
2139
  readonly max: Max;
@@ -1883,19 +2153,35 @@ export type NonEmptyTrimmedString100 = typeof NonEmptyTrimmedString100.Output;
1883
2153
  export declare const NonEmptyTrimmedString1000: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "MaxLength1000", MaxLengthError<1000>>;
1884
2154
  export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Output;
1885
2155
  /**
1886
- * Adds a {@link Brand} requiring a value to have exactly `exact` items.
2156
+ * Exact-length {@link Brand} for values whose `length` equals `exact`.
2157
+ *
2158
+ * ### Example
2159
+ *
2160
+ * ```ts
2161
+ * import { String, array, length } from "@evolu/common";
2162
+ *
2163
+ * const ThreeCharacters = length(3)(String);
2164
+ * const TwoItems = length(2)(array(String));
2165
+ *
2166
+ * expectOk(ThreeCharacters.fromUnknown("abc"), "abc");
2167
+ * expectOk(TwoItems.fromUnknown(["a", "b"]), ["a", "b"]);
2168
+ * ```
1887
2169
  *
1888
2170
  * @group String
1889
2171
  * @group Collection
1890
2172
  */
1891
2173
  export declare const length: <Exact extends number>(exact: ValidateBrandFactoryNumber<Exact>) => BrandFactory<`Length${Exact}`, ValueWithLength, LengthError<Exact>>;
1892
- /** @group Collection */
2174
+ /**
2175
+ * Error returned when {@link length} rejects a value.
2176
+ *
2177
+ * @group Collection
2178
+ */
1893
2179
  export interface LengthError<Exact extends number = number> extends TypeError<`Length${Exact}`> {
1894
2180
  readonly value: ValueWithLength;
1895
2181
  readonly exact: Exact;
1896
2182
  }
1897
2183
  /**
1898
- * Creates a string {@link Brand} that must match a regular expression.
2184
+ * String {@link Brand} constrained by a regular expression.
1899
2185
  *
1900
2186
  * ### Example
1901
2187
  *
@@ -1923,7 +2209,12 @@ export interface LengthError<Exact extends number = number> extends TypeError<`L
1923
2209
  * @group String
1924
2210
  */
1925
2211
  export declare const regex: <const Name extends TypeName>(name: ValidateConcreteTypeName<Name>, pattern: RegExp) => BrandFactory<Name, string, RegexError<Name>>;
1926
- /** @group String */
2212
+ /**
2213
+ * Error returned when a string does not match the regular expression supplied
2214
+ * to {@link regex}.
2215
+ *
2216
+ * @group String
2217
+ */
1927
2218
  export interface RegexError<Name extends TypeName = TypeName> extends TypeError<Name> {
1928
2219
  readonly value: string;
1929
2220
  readonly source: string;
@@ -1950,19 +2241,45 @@ export type UrlSafeString = typeof UrlSafeString.Output;
1950
2241
  */
1951
2242
  export declare const Base64Url: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Base64Url", Base64UrlError>;
1952
2243
  export type Base64Url = typeof Base64Url.Output;
1953
- /** @group String */
2244
+ /**
2245
+ * Error returned when a string is not valid {@link Base64Url} text.
2246
+ *
2247
+ * @group String
2248
+ */
1954
2249
  export interface Base64UrlError extends TypeError<"Base64Url"> {
1955
2250
  readonly value: string;
1956
2251
  }
1957
2252
  /**
1958
2253
  * Encodes bytes as {@link Base64Url}.
1959
2254
  *
2255
+ * ### Example
2256
+ *
2257
+ * ```ts
2258
+ * import { uint8ArrayToBase64Url } from "@evolu/common";
2259
+ *
2260
+ * expect(uint8ArrayToBase64Url(new Uint8Array([0, 1, 2, 255]))).toBe(
2261
+ * "AAEC_w",
2262
+ * );
2263
+ * ```
2264
+ *
1960
2265
  * @group String
1961
2266
  */
1962
2267
  export declare const uint8ArrayToBase64Url: (bytes: Uint8Array) => Base64Url;
1963
2268
  /**
1964
2269
  * Decodes {@link Base64Url} as bytes.
1965
2270
  *
2271
+ * ### Example
2272
+ *
2273
+ * ```ts
2274
+ * import { Base64Url, base64UrlToUint8Array } from "@evolu/common";
2275
+ *
2276
+ * const value = Base64Url.orThrow("AAEC_w");
2277
+ *
2278
+ * expect(base64UrlToUint8Array(value)).toEqual(
2279
+ * new Uint8Array([0, 1, 2, 255]),
2280
+ * );
2281
+ * ```
2282
+ *
1966
2283
  * @group String
1967
2284
  */
1968
2285
  export declare const base64UrlToUint8Array: (value: Base64Url) => Uint8Array;
@@ -1973,7 +2290,11 @@ export declare const base64UrlToUint8Array: (value: Base64Url) => Uint8Array;
1973
2290
  */
1974
2291
  export declare const Name: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "UrlSafeString", RegexError<"UrlSafeString">>, "Name", NameError>;
1975
2292
  export type Name = typeof Name.Output;
1976
- /** @group String */
2293
+ /**
2294
+ * Error returned when a string is not a valid {@link Name}.
2295
+ *
2296
+ * @group String
2297
+ */
1977
2298
  export interface NameError extends TypeError<"Name"> {
1978
2299
  readonly value: string;
1979
2300
  }
@@ -1997,7 +2318,11 @@ export type SimplePassword = typeof SimplePassword.Output;
1997
2318
  */
1998
2319
  export declare const Mnemonic: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "Mnemonic", MnemonicError>;
1999
2320
  export type Mnemonic = typeof Mnemonic.Output;
2000
- /** @group String */
2321
+ /**
2322
+ * Error returned when a string is not a valid English BIP39 {@link Mnemonic}.
2323
+ *
2324
+ * @group String
2325
+ */
2001
2326
  export interface MnemonicError extends TypeError<"Mnemonic"> {
2002
2327
  readonly value: string;
2003
2328
  }
@@ -2013,7 +2338,11 @@ export interface MnemonicError extends TypeError<"Mnemonic"> {
2013
2338
  */
2014
2339
  export declare const Id: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Id", IdError>;
2015
2340
  export type Id = typeof Id.Output;
2016
- /** @group String */
2341
+ /**
2342
+ * Error returned when a string is not a valid {@link Id}.
2343
+ *
2344
+ * @group String
2345
+ */
2017
2346
  export interface IdError extends TypeError<"Id"> {
2018
2347
  readonly value: string;
2019
2348
  }
@@ -2023,6 +2352,22 @@ export interface IdError extends TypeError<"Id"> {
2023
2352
  * This is the recommended default because it does not encode creation time.
2024
2353
  * Pass a Brand name when the returned Id belongs to one domain entity.
2025
2354
  *
2355
+ * ### Example
2356
+ *
2357
+ * ```ts
2358
+ * import {
2359
+ * Id,
2360
+ * createId,
2361
+ * createRandomBytes,
2362
+ * type Brand,
2363
+ * } from "@evolu/common";
2364
+ *
2365
+ * const userId = createId<"User">({ randomBytes: createRandomBytes() });
2366
+ *
2367
+ * expect(Id.is(userId)).toBe(true);
2368
+ * expectTypeOf(userId).toEqualTypeOf<Id & Brand<"User">>();
2369
+ * ```
2370
+ *
2026
2371
  * @group String
2027
2372
  */
2028
2373
  export declare const createId: <B extends string = never>(deps: RandomBytesDep, ..._validation: IdBrandValidation<B>) => CreatedId<B>;
@@ -2057,20 +2402,64 @@ export declare const createIdFromString: <B extends string = never>(value: strin
2057
2402
  * logs, URLs, and exports. Prefer {@link createId} unless that tradeoff is
2058
2403
  * deliberate.
2059
2404
  *
2060
- * @group String
2061
- */
2062
- export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBytesDep & TimeDep, ..._validation: IdBrandValidation<B>) => CreatedId<B>;
2063
- /**
2064
- * A table-specific {@link Id} Type.
2405
+ * ### Example
2065
2406
  *
2066
- * @group String
2407
+ * ```ts
2408
+ * import {
2409
+ * createIdAsUuidv7,
2410
+ * createRandomBytes,
2411
+ * createTime,
2412
+ * idToIdBytes,
2413
+ * } from "@evolu/common";
2414
+ *
2415
+ * const value = createIdAsUuidv7({
2416
+ * randomBytes: createRandomBytes(),
2417
+ * time: createTime(),
2418
+ * });
2419
+ * const bytes = idToIdBytes(value);
2420
+ *
2421
+ * expect(bytes[6] >> 4).toBe(0x7);
2422
+ * expect(bytes[8] & 0xc0).toBe(0x80);
2423
+ * ```
2424
+ *
2425
+ * @group String
2426
+ */
2427
+ export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBytesDep & TimeDep, ..._validation: IdBrandValidation<B>) => CreatedId<B>;
2428
+ /**
2429
+ * Table-specific {@link Id} Type.
2430
+ *
2431
+ * ### Example
2432
+ *
2433
+ * ```ts
2434
+ * import {
2435
+ * createIdFromString,
2436
+ * id,
2437
+ * type Brand,
2438
+ * type Id,
2439
+ * } from "@evolu/common";
2440
+ *
2441
+ * const TodoId = id("Todo");
2442
+ * const todoId = TodoId.orThrow(createIdFromString("todo"));
2443
+ *
2444
+ * expectTypeOf(todoId).toEqualTypeOf<Id & Brand<"Todo">>();
2445
+ * ```
2446
+ *
2447
+ * @group String
2067
2448
  */
2068
2449
  export declare const id: <Table extends TypeName>(table: ValidateTableName<Table>) => TableId<Table>;
2069
- /** @group String */
2450
+ /**
2451
+ * The {@link Type} returned by {@link id} for one table.
2452
+ *
2453
+ * @group String
2454
+ */
2070
2455
  export interface TableId<Table extends TypeName> extends Type<"TableId", string, Id & Brand<Table>, TableIdError<Table>, typeof String, TableIdError<Table> | InferErrors<typeof String>, ChildCustomFrom<typeof String, Id & Brand<Table>, TableIdError<Table>>, CanonicalInputForChild<typeof String, Id & Brand<Table>>, IdentityEncodingOf<typeof String>> {
2071
2456
  readonly table: Table;
2072
2457
  }
2073
- /** @group String */
2458
+ /**
2459
+ * Error returned when a string is not a valid {@link Id} for the expected table.
2460
+ *
2461
+ * @group String
2462
+ */
2074
2463
  export interface TableIdError<Table extends TypeName = TypeName> extends TypeError<"TableId"> {
2075
2464
  readonly table: Table;
2076
2465
  readonly value: string;
@@ -2090,11 +2479,47 @@ type ConcreteIdBrandError = CompileTimeError<"Id", "Brand must be one concrete s
2090
2479
  */
2091
2480
  export declare const IdBytes: BrandType<BrandType<Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, ObjectTagError<"Uint8Array">, null, ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Length16", LengthError<16>>, "IdBytes", never>;
2092
2481
  export type IdBytes = typeof IdBytes.Output;
2093
- /** @group String */
2482
+ /**
2483
+ * Byte length of an {@link IdBytes} value.
2484
+ *
2485
+ * @group String
2486
+ */
2094
2487
  export declare const idBytesTypeValueLength: NonNegativeInt;
2095
- /** @group String */
2488
+ /**
2489
+ * Converts an {@link Id} to {@link IdBytes}.
2490
+ *
2491
+ * ### Example
2492
+ *
2493
+ * ```ts
2494
+ * import { createIdFromString, idToIdBytes } from "@evolu/common";
2495
+ *
2496
+ * const bytes = idToIdBytes(createIdFromString("todo"));
2497
+ *
2498
+ * expect(bytes).toHaveLength(16);
2499
+ * ```
2500
+ *
2501
+ * @group String
2502
+ */
2096
2503
  export declare const idToIdBytes: (value: Id) => IdBytes;
2097
- /** @group String */
2504
+ /**
2505
+ * Converts {@link IdBytes} to an {@link Id}.
2506
+ *
2507
+ * ### Example
2508
+ *
2509
+ * ```ts
2510
+ * import {
2511
+ * createIdFromString,
2512
+ * idBytesToId,
2513
+ * idToIdBytes,
2514
+ * } from "@evolu/common";
2515
+ *
2516
+ * const value = createIdFromString("todo");
2517
+ *
2518
+ * expect(idBytesToId(idToIdBytes(value))).toBe(value);
2519
+ * ```
2520
+ *
2521
+ * @group String
2522
+ */
2098
2523
  export declare const idBytesToId: (value: IdBytes) => Id;
2099
2524
  /**
2100
2525
  * Decimal string representation of a signed {@link Int64}.
@@ -2103,7 +2528,11 @@ export declare const idBytesToId: (value: IdBytes) => Id;
2103
2528
  */
2104
2529
  export declare const Int64String: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "Int64String", Int64StringError>;
2105
2530
  export type Int64String = typeof Int64String.Output;
2106
- /** @group Number */
2531
+ /**
2532
+ * Error returned when a string is not a canonical {@link Int64String}.
2533
+ *
2534
+ * @group Number
2535
+ */
2107
2536
  export interface Int64StringError extends TypeError<"Int64String"> {
2108
2537
  readonly value: string;
2109
2538
  }
@@ -2114,16 +2543,41 @@ export interface Int64StringError extends TypeError<"Int64String"> {
2114
2543
  * text, including SQLite queries that cast INTEGER values to TEXT to avoid a
2115
2544
  * lossy JavaScript number conversion.
2116
2545
  *
2546
+ * ### Example
2547
+ *
2548
+ * ```ts
2549
+ * import { Int64FromInt64String } from "@evolu/common";
2550
+ *
2551
+ * const result = Int64FromInt64String.fromUnknown("9223372036854775807");
2552
+ *
2553
+ * expectOk(result, 9223372036854775807n);
2554
+ * expect(Int64FromInt64String.to(result.value)).toBe("9223372036854775807");
2555
+ * ```
2556
+ *
2117
2557
  * @group Number
2118
2558
  */
2119
2559
  export declare const Int64FromInt64String: TransformType<BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "Int64String", Int64StringError>, BrandType<Type<"BigInt", bigint, bigint, TypeOfError<"BigInt">, null, TypeOfError<"BigInt">, never, bigint, true>, "Int64", Int64Error>, "Int64FromInt64String", never, string & Brand<"Trimmed"> & Brand<"MinLength1"> & Brand<"Int64String">>;
2120
2560
  /**
2121
- * Adds a {@link Brand} requiring a number greater than or equal to zero.
2561
+ * Number {@link Brand} requiring a value greater than or equal to zero.
2562
+ *
2563
+ * ### Example
2564
+ *
2565
+ * ```ts
2566
+ * import { Number, nonNegative } from "@evolu/common";
2567
+ *
2568
+ * const NonNegative = nonNegative(Number);
2569
+ *
2570
+ * expectOk(NonNegative.fromUnknown(0), 0);
2571
+ * ```
2122
2572
  *
2123
2573
  * @group Number
2124
2574
  */
2125
2575
  export declare const nonNegative: BrandFactory<"NonNegative", number, NonNegativeError>;
2126
- /** @group Number */
2576
+ /**
2577
+ * Error returned when {@link nonNegative} rejects a number.
2578
+ *
2579
+ * @group Number
2580
+ */
2127
2581
  export interface NonNegativeError extends TypeError<"NonNegative"> {
2128
2582
  readonly value: number;
2129
2583
  }
@@ -2135,12 +2589,26 @@ export interface NonNegativeError extends TypeError<"NonNegative"> {
2135
2589
  export declare const NonNegativeNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNegative", NonNegativeError>;
2136
2590
  export type NonNegativeNumber = typeof NonNegativeNumber.Output;
2137
2591
  /**
2138
- * Adds a {@link Brand} requiring a number greater than zero.
2592
+ * Number {@link Brand} requiring a value greater than zero.
2593
+ *
2594
+ * ### Example
2595
+ *
2596
+ * ```ts
2597
+ * import { Number, positive } from "@evolu/common";
2598
+ *
2599
+ * const Positive = positive(Number);
2600
+ *
2601
+ * expectOk(Positive.fromUnknown(1), 1);
2602
+ * ```
2139
2603
  *
2140
2604
  * @group Number
2141
2605
  */
2142
2606
  export declare const positive: BrandFactory<"Positive", number, PositiveError>;
2143
- /** @group Number */
2607
+ /**
2608
+ * Error returned when {@link positive} rejects a number.
2609
+ *
2610
+ * @group Number
2611
+ */
2144
2612
  export interface PositiveError extends TypeError<"Positive"> {
2145
2613
  readonly value: number;
2146
2614
  }
@@ -2155,12 +2623,26 @@ export interface PositiveError extends TypeError<"Positive"> {
2155
2623
  export declare const PositiveNumber: BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNegative", NonNegativeError>, "Positive", PositiveError>;
2156
2624
  export type PositiveNumber = typeof PositiveNumber.Output;
2157
2625
  /**
2158
- * Adds a {@link Brand} requiring a number less than or equal to zero.
2626
+ * Number {@link Brand} requiring a value less than or equal to zero.
2627
+ *
2628
+ * ### Example
2629
+ *
2630
+ * ```ts
2631
+ * import { Number, nonPositive } from "@evolu/common";
2632
+ *
2633
+ * const NonPositive = nonPositive(Number);
2634
+ *
2635
+ * expectOk(NonPositive.fromUnknown(0), 0);
2636
+ * ```
2159
2637
  *
2160
2638
  * @group Number
2161
2639
  */
2162
2640
  export declare const nonPositive: BrandFactory<"NonPositive", number, NonPositiveError>;
2163
- /** @group Number */
2641
+ /**
2642
+ * Error returned when {@link nonPositive} rejects a number.
2643
+ *
2644
+ * @group Number
2645
+ */
2164
2646
  export interface NonPositiveError extends TypeError<"NonPositive"> {
2165
2647
  readonly value: number;
2166
2648
  }
@@ -2172,12 +2654,26 @@ export interface NonPositiveError extends TypeError<"NonPositive"> {
2172
2654
  export declare const NonPositiveNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonPositive", NonPositiveError>;
2173
2655
  export type NonPositiveNumber = typeof NonPositiveNumber.Output;
2174
2656
  /**
2175
- * Adds a {@link Brand} requiring a number less than zero.
2657
+ * Number {@link Brand} requiring a value less than zero.
2658
+ *
2659
+ * ### Example
2660
+ *
2661
+ * ```ts
2662
+ * import { Number, negative } from "@evolu/common";
2663
+ *
2664
+ * const Negative = negative(Number);
2665
+ *
2666
+ * expectOk(Negative.fromUnknown(-1), -1);
2667
+ * ```
2176
2668
  *
2177
2669
  * @group Number
2178
2670
  */
2179
2671
  export declare const negative: BrandFactory<"Negative", number, NegativeError>;
2180
- /** @group Number */
2672
+ /**
2673
+ * Error returned when {@link negative} rejects a number.
2674
+ *
2675
+ * @group Number
2676
+ */
2181
2677
  export interface NegativeError extends TypeError<"Negative"> {
2182
2678
  readonly value: number;
2183
2679
  }
@@ -2192,12 +2688,26 @@ export interface NegativeError extends TypeError<"Negative"> {
2192
2688
  export declare const NegativeNumber: BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonPositive", NonPositiveError>, "Negative", NegativeError>;
2193
2689
  export type NegativeNumber = typeof NegativeNumber.Output;
2194
2690
  /**
2195
- * Adds a {@link Brand} requiring a number other than `NaN`.
2691
+ * Number {@link Brand} requiring a value other than `NaN`.
2692
+ *
2693
+ * ### Example
2694
+ *
2695
+ * ```ts
2696
+ * import { Number, nonNaN } from "@evolu/common";
2697
+ *
2698
+ * const NonNaN = nonNaN(Number);
2699
+ *
2700
+ * expectOk(NonNaN.fromUnknown(Infinity), Infinity);
2701
+ * ```
2196
2702
  *
2197
2703
  * @group Number
2198
2704
  */
2199
2705
  export declare const nonNaN: BrandFactory<"NonNaN", number, NonNaNError>;
2200
- /** @group Number */
2706
+ /**
2707
+ * Error returned when {@link nonNaN} rejects `NaN`.
2708
+ *
2709
+ * @group Number
2710
+ */
2201
2711
  export interface NonNaNError extends TypeError<"NonNaN"> {
2202
2712
  readonly value: number;
2203
2713
  }
@@ -2214,12 +2724,26 @@ export interface NonNaNError extends TypeError<"NonNaN"> {
2214
2724
  export declare const NonNaNNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>;
2215
2725
  export type NonNaNNumber = typeof NonNaNNumber.Output;
2216
2726
  /**
2217
- * Adds a {@link Brand} requiring a finite number.
2727
+ * Number {@link Brand} requiring a finite value.
2728
+ *
2729
+ * ### Example
2730
+ *
2731
+ * ```ts
2732
+ * import { Number, finite } from "@evolu/common";
2733
+ *
2734
+ * const Finite = finite(Number);
2735
+ *
2736
+ * expectOk(Finite.fromUnknown(42), 42);
2737
+ * ```
2218
2738
  *
2219
2739
  * @group Number
2220
2740
  */
2221
2741
  export declare const finite: BrandFactory<"Finite", number, FiniteError>;
2222
- /** @group Number */
2742
+ /**
2743
+ * Error returned when {@link finite} rejects a non-finite number.
2744
+ *
2745
+ * @group Number
2746
+ */
2223
2747
  export interface FiniteError extends TypeError<"Finite"> {
2224
2748
  readonly value: number;
2225
2749
  }
@@ -2267,7 +2791,11 @@ export type PositiveFiniteNumber = typeof PositiveFiniteNumber.Output;
2267
2791
  * @group Number
2268
2792
  */
2269
2793
  export declare const int: BrandFactory<"Int", number, IntError>;
2270
- /** @group Number */
2794
+ /**
2795
+ * Error returned when {@link int} rejects a number that is not a safe integer.
2796
+ *
2797
+ * @group Number
2798
+ */
2271
2799
  export interface IntError extends TypeError<"Int"> {
2272
2800
  readonly value: number;
2273
2801
  }
@@ -2285,12 +2813,6 @@ export type Int = typeof Int.Output;
2285
2813
  */
2286
2814
  export declare const NonNegativeInt: BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>;
2287
2815
  export type NonNegativeInt = typeof NonNegativeInt.Output;
2288
- /**
2289
- * 0-100 as a literal, or any already-validated {@link NonNegativeInt}.
2290
- *
2291
- * @group Number
2292
- */
2293
- export type Int0To100OrNonNegativeInt = 0 | Int1To100 | NonNegativeInt;
2294
2816
  /**
2295
2817
  * Minimum {@link NonNegativeInt} value.
2296
2818
  *
@@ -2307,12 +2829,6 @@ export declare const zeroNonNegativeInt: number & Brand<"NonNaN"> & Brand<"Finit
2307
2829
  */
2308
2830
  export declare const PositiveInt: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>, "Positive", PositiveError>;
2309
2831
  export type PositiveInt = typeof PositiveInt.Output;
2310
- /**
2311
- * 1-100 as a literal, or any already-validated {@link PositiveInt}.
2312
- *
2313
- * @group Number
2314
- */
2315
- export type Int1To100OrPositiveInt = Int1To100 | PositiveInt;
2316
2832
  /**
2317
2833
  * Minimum {@link PositiveInt} value.
2318
2834
  *
@@ -2343,34 +2859,76 @@ export type NonPositiveInt = typeof NonPositiveInt.Output;
2343
2859
  export declare const NegativeInt: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonPositive", NonPositiveError>, "Negative", NegativeError>;
2344
2860
  export type NegativeInt = typeof NegativeInt.Output;
2345
2861
  /**
2346
- * Adds a {@link Brand} requiring a number greater than `min`.
2862
+ * Number {@link Brand} requiring a value greater than `min`.
2863
+ *
2864
+ * ### Example
2865
+ *
2866
+ * ```ts
2867
+ * import { Number, greaterThan } from "@evolu/common";
2868
+ *
2869
+ * const GreaterThanTen = greaterThan(10)(Number);
2870
+ *
2871
+ * expectOk(GreaterThanTen.fromUnknown(11), 11);
2872
+ * ```
2347
2873
  *
2348
2874
  * @group Number
2349
2875
  */
2350
2876
  export declare const greaterThan: <Min extends number>(min: ValidateBrandFactoryNumber<Min>) => BrandFactory<`GreaterThan${Min}`, number, GreaterThanError<Min>>;
2351
- /** @group Number */
2877
+ /**
2878
+ * Error returned when {@link greaterThan} rejects a number.
2879
+ *
2880
+ * @group Number
2881
+ */
2352
2882
  export interface GreaterThanError<Min extends number = number> extends TypeError<`GreaterThan${Min}`> {
2353
2883
  readonly value: number;
2354
2884
  readonly min: Min;
2355
2885
  }
2356
2886
  /**
2357
- * Adds a {@link Brand} requiring a number greater than or equal to `min`.
2887
+ * Number {@link Brand} requiring a value greater than or equal to `min`.
2888
+ *
2889
+ * ### Example
2890
+ *
2891
+ * ```ts
2892
+ * import { Number, greaterThanOrEqualTo } from "@evolu/common";
2893
+ *
2894
+ * const AtLeastTen = greaterThanOrEqualTo(10)(Number);
2895
+ *
2896
+ * expectOk(AtLeastTen.fromUnknown(10), 10);
2897
+ * ```
2358
2898
  *
2359
2899
  * @group Number
2360
2900
  */
2361
2901
  export declare const greaterThanOrEqualTo: <Min extends number>(min: ValidateBrandFactoryNumber<Min>) => BrandFactory<`GreaterThanOrEqualTo${Min}`, number, GreaterThanOrEqualToError<Min>>;
2362
- /** @group Number */
2902
+ /**
2903
+ * Error returned when {@link greaterThanOrEqualTo} rejects a number.
2904
+ *
2905
+ * @group Number
2906
+ */
2363
2907
  export interface GreaterThanOrEqualToError<Min extends number = number> extends TypeError<`GreaterThanOrEqualTo${Min}`> {
2364
2908
  readonly value: number;
2365
2909
  readonly min: Min;
2366
2910
  }
2367
2911
  /**
2368
- * Adds a {@link Brand} requiring a number less than `max`.
2912
+ * Number {@link Brand} requiring a value less than `max`.
2913
+ *
2914
+ * ### Example
2915
+ *
2916
+ * ```ts
2917
+ * import { Number, lessThan } from "@evolu/common";
2918
+ *
2919
+ * const LessThanTen = lessThan(10)(Number);
2920
+ *
2921
+ * expectOk(LessThanTen.fromUnknown(9), 9);
2922
+ * ```
2369
2923
  *
2370
2924
  * @group Number
2371
2925
  */
2372
2926
  export declare const lessThan: <Max extends number>(max: ValidateBrandFactoryNumber<Max>) => BrandFactory<`LessThan${Max}`, number, LessThanError<Max>>;
2373
- /** @group Number */
2927
+ /**
2928
+ * Error returned when {@link lessThan} rejects a number.
2929
+ *
2930
+ * @group Number
2931
+ */
2374
2932
  export interface LessThanError<Max extends number = number> extends TypeError<`LessThan${Max}`> {
2375
2933
  readonly value: number;
2376
2934
  readonly max: Max;
@@ -2383,12 +2941,26 @@ export interface LessThanError<Max extends number = number> extends TypeError<`L
2383
2941
  export declare const Age: BrandType<BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>, "LessThan200", LessThanError<200>>, "Age", never>;
2384
2942
  export type Age = typeof Age.Output;
2385
2943
  /**
2386
- * Adds a {@link Brand} requiring a number less than or equal to `max`.
2944
+ * Number {@link Brand} requiring a value less than or equal to `max`.
2945
+ *
2946
+ * ### Example
2947
+ *
2948
+ * ```ts
2949
+ * import { Number, lessThanOrEqualTo } from "@evolu/common";
2950
+ *
2951
+ * const AtMostTen = lessThanOrEqualTo(10)(Number);
2952
+ *
2953
+ * expectOk(AtMostTen.fromUnknown(10), 10);
2954
+ * ```
2387
2955
  *
2388
2956
  * @group Number
2389
2957
  */
2390
2958
  export declare const lessThanOrEqualTo: <Max extends number>(max: ValidateBrandFactoryNumber<Max>) => BrandFactory<`LessThanOrEqualTo${Max}`, number, LessThanOrEqualToError<Max>>;
2391
- /** @group Number */
2959
+ /**
2960
+ * Error returned when {@link lessThanOrEqualTo} rejects a number.
2961
+ *
2962
+ * @group Number
2963
+ */
2392
2964
  export interface LessThanOrEqualToError<Max extends number = number> extends TypeError<`LessThanOrEqualTo${Max}`> {
2393
2965
  readonly value: number;
2394
2966
  readonly max: Max;
@@ -2403,46 +2975,188 @@ export interface LessThanOrEqualToError<Max extends number = number> extends Typ
2403
2975
  export declare const Ratio: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "NonNegative", NonNegativeError>, "LessThanOrEqualTo1", LessThanOrEqualToError<1>>, "Ratio", never>;
2404
2976
  export type Ratio = typeof Ratio.Output;
2405
2977
  /**
2406
- * Canonical string representation of a positive base-10 decimal value.
2978
+ * Canonical string representation of a signed base-10 decimal value.
2407
2979
  *
2408
2980
  * Use this Type when a decimal value must remain exact instead of being
2409
2981
  * converted to an IEEE-754 number. Equivalent values have one accepted
2410
- * representation, so leading zeroes, trailing fractional zeroes, signs, and
2411
- * exponent notation are rejected.
2982
+ * representation, so leading zeroes, trailing fractional zeroes, `-0`, plus
2983
+ * signs, and exponent notation are rejected.
2412
2984
  *
2413
2985
  * The decoded value remains a string. Arithmetic requires an explicit decimal
2414
2986
  * or fixed-point representation.
2415
2987
  *
2988
+ * TypeScript template literal types can describe a fixed number of digit
2989
+ * positions, but not the arbitrarily long integer and fractional parts accepted
2990
+ * here. `DecimalString` therefore uses a {@link Brand} so its TypeScript type
2991
+ * does not accept strings that have not been validated.
2992
+ *
2993
+ * Use these predefined Types or their corresponding factories to add sign
2994
+ * constraints to compatible decimal string Types:
2995
+ *
2996
+ * - {@link NonNegativeDecimalString} / {@link nonNegativeDecimalString}
2997
+ * - {@link PositiveDecimalString} / {@link positiveDecimalString}
2998
+ * - {@link NonPositiveDecimalString} / {@link nonPositiveDecimalString}
2999
+ * - {@link NegativeDecimalString} / {@link negativeDecimalString}
3000
+ *
2416
3001
  * ### Example
2417
3002
  *
2418
3003
  * ```ts
2419
- * import { PositiveDecimalString } from "@evolu/common";
3004
+ * import { DecimalString } from "@evolu/common";
2420
3005
  *
2421
- * expectOk(PositiveDecimalString.fromUnknown("0.3"), "0.3");
2422
- * expectOk(PositiveDecimalString.fromUnknown("25"), "25");
2423
- * expectOk(PositiveDecimalString.fromUnknown("10.01"), "10.01");
3006
+ * expectOk(DecimalString.fromUnknown("-10.25"), "-10.25");
3007
+ * expectOk(DecimalString.fromUnknown("0"), "0");
3008
+ * expectOk(DecimalString.fromUnknown("10.25"), "10.25");
2424
3009
  *
2425
- * expectErr(PositiveDecimalString.fromUnknown("0"), {
2426
- * type: "PositiveDecimalString",
2427
- * value: "0",
2428
- * });
2429
- * expectErr(PositiveDecimalString.fromUnknown("0.30"), {
2430
- * type: "PositiveDecimalString",
2431
- * value: "0.30",
3010
+ * expectErr(DecimalString.fromUnknown("10.250"), {
3011
+ * type: "DecimalString",
3012
+ * value: "10.250",
2432
3013
  * });
2433
3014
  * ```
2434
3015
  *
2435
3016
  * @group Number
2436
3017
  */
2437
- export declare const PositiveDecimalString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "PositiveDecimalString", PositiveDecimalStringError>;
2438
- export type PositiveDecimalString = typeof PositiveDecimalString.Output;
2439
- /** @group Number */
3018
+ export declare const DecimalString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>;
3019
+ export type DecimalString = typeof DecimalString.Output;
3020
+ /**
3021
+ * Error returned when a string is not a canonical {@link DecimalString}.
3022
+ *
3023
+ * @group Number
3024
+ */
3025
+ export interface DecimalStringError extends TypeError<"DecimalString"> {
3026
+ readonly value: string;
3027
+ }
3028
+ /**
3029
+ * {@link DecimalString} Brand requiring a value greater than or equal to zero.
3030
+ *
3031
+ * ### Example
3032
+ *
3033
+ * ```ts
3034
+ * import { DecimalString, nonNegativeDecimalString } from "@evolu/common";
3035
+ *
3036
+ * const NonNegative = nonNegativeDecimalString(DecimalString);
3037
+ *
3038
+ * expectOk(NonNegative.fromUnknown("0.5"), "0.5");
3039
+ * ```
3040
+ *
3041
+ * @group Number
3042
+ */
3043
+ export declare const nonNegativeDecimalString: BrandFactory<"NonNegativeDecimalString", DecimalString, NonNegativeDecimalStringError>;
3044
+ /**
3045
+ * Error returned when {@link nonNegativeDecimalString} rejects a decimal string.
3046
+ *
3047
+ * @group Number
3048
+ */
3049
+ export interface NonNegativeDecimalStringError extends TypeError<"NonNegativeDecimalString"> {
3050
+ readonly value: string;
3051
+ }
3052
+ /**
3053
+ * Non-negative {@link DecimalString}.
3054
+ *
3055
+ * @group Number
3056
+ */
3057
+ export declare const NonNegativeDecimalString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonNegativeDecimalString", NonNegativeDecimalStringError>;
3058
+ export type NonNegativeDecimalString = typeof NonNegativeDecimalString.Output;
3059
+ /**
3060
+ * {@link DecimalString} Brand requiring a value greater than zero.
3061
+ *
3062
+ * ### Example
3063
+ *
3064
+ * ```ts
3065
+ * import { DecimalString, positiveDecimalString } from "@evolu/common";
3066
+ *
3067
+ * const Positive = positiveDecimalString(DecimalString);
3068
+ *
3069
+ * expectOk(Positive.fromUnknown("0.5"), "0.5");
3070
+ * ```
3071
+ *
3072
+ * @group Number
3073
+ */
3074
+ export declare const positiveDecimalString: BrandFactory<"PositiveDecimalString", DecimalString, PositiveDecimalStringError>;
3075
+ /**
3076
+ * Error returned when {@link positiveDecimalString} rejects a decimal string.
3077
+ *
3078
+ * @group Number
3079
+ */
2440
3080
  export interface PositiveDecimalStringError extends TypeError<"PositiveDecimalString"> {
2441
3081
  readonly value: string;
2442
3082
  }
2443
3083
  /**
2444
- * Adds a {@link Brand} requiring a number to be a multiple of an exact decimal
2445
- * `divisor`.
3084
+ * Positive {@link DecimalString}.
3085
+ *
3086
+ * Also satisfies {@link NonNegativeDecimalString}, so it can be used wherever a
3087
+ * non-negative decimal string is required.
3088
+ *
3089
+ * @group Number
3090
+ */
3091
+ export declare const PositiveDecimalString: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonNegativeDecimalString", NonNegativeDecimalStringError>, "PositiveDecimalString", PositiveDecimalStringError>;
3092
+ export type PositiveDecimalString = typeof PositiveDecimalString.Output;
3093
+ /**
3094
+ * {@link DecimalString} Brand requiring a value less than or equal to zero.
3095
+ *
3096
+ * ### Example
3097
+ *
3098
+ * ```ts
3099
+ * import { DecimalString, nonPositiveDecimalString } from "@evolu/common";
3100
+ *
3101
+ * const NonPositive = nonPositiveDecimalString(DecimalString);
3102
+ *
3103
+ * expectOk(NonPositive.fromUnknown("-0.5"), "-0.5");
3104
+ * ```
3105
+ *
3106
+ * @group Number
3107
+ */
3108
+ export declare const nonPositiveDecimalString: BrandFactory<"NonPositiveDecimalString", DecimalString, NonPositiveDecimalStringError>;
3109
+ /**
3110
+ * Error returned when {@link nonPositiveDecimalString} rejects a decimal string.
3111
+ *
3112
+ * @group Number
3113
+ */
3114
+ export interface NonPositiveDecimalStringError extends TypeError<"NonPositiveDecimalString"> {
3115
+ readonly value: string;
3116
+ }
3117
+ /**
3118
+ * Non-positive {@link DecimalString}.
3119
+ *
3120
+ * @group Number
3121
+ */
3122
+ export declare const NonPositiveDecimalString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonPositiveDecimalString", NonPositiveDecimalStringError>;
3123
+ export type NonPositiveDecimalString = typeof NonPositiveDecimalString.Output;
3124
+ /**
3125
+ * {@link DecimalString} Brand requiring a value less than zero.
3126
+ *
3127
+ * ### Example
3128
+ *
3129
+ * ```ts
3130
+ * import { DecimalString, negativeDecimalString } from "@evolu/common";
3131
+ *
3132
+ * const Negative = negativeDecimalString(DecimalString);
3133
+ *
3134
+ * expectOk(Negative.fromUnknown("-0.5"), "-0.5");
3135
+ * ```
3136
+ *
3137
+ * @group Number
3138
+ */
3139
+ export declare const negativeDecimalString: BrandFactory<"NegativeDecimalString", DecimalString, NegativeDecimalStringError>;
3140
+ /**
3141
+ * Error returned when {@link negativeDecimalString} rejects a decimal string.
3142
+ *
3143
+ * @group Number
3144
+ */
3145
+ export interface NegativeDecimalStringError extends TypeError<"NegativeDecimalString"> {
3146
+ readonly value: string;
3147
+ }
3148
+ /**
3149
+ * Negative {@link DecimalString}.
3150
+ *
3151
+ * Also satisfies {@link NonPositiveDecimalString}, so it can be used wherever a
3152
+ * non-positive decimal string is required.
3153
+ *
3154
+ * @group Number
3155
+ */
3156
+ export declare const NegativeDecimalString: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonPositiveDecimalString", NonPositiveDecimalStringError>, "NegativeDecimalString", NegativeDecimalStringError>;
3157
+ export type NegativeDecimalString = typeof NegativeDecimalString.Output;
3158
+ /**
3159
+ * Number {@link Brand} requiring an exact decimal multiple of `divisor`.
2446
3160
  *
2447
3161
  * The divisor must be one canonical positive decimal string literal because its
2448
3162
  * exact value is encoded in the resulting Brand name. The declaration is
@@ -2474,7 +3188,11 @@ export interface PositiveDecimalStringError extends TypeError<"PositiveDecimalSt
2474
3188
  * @group Number
2475
3189
  */
2476
3190
  export declare const multipleOf: <const Divisor extends string>(divisor: ValidateMultipleOfDivisor<Divisor>) => BrandFactory<`MultipleOf${Divisor}`, number, MultipleOfError<Divisor>>;
2477
- /** @group Number */
3191
+ /**
3192
+ * Error returned when {@link multipleOf} rejects a number.
3193
+ *
3194
+ * @group Number
3195
+ */
2478
3196
  export interface MultipleOfError<Divisor extends string = string> extends TypeError<`MultipleOf${Divisor}`> {
2479
3197
  readonly value: number;
2480
3198
  readonly divisor: Divisor;
@@ -2488,12 +3206,26 @@ type IsDecimalDigits<Value extends string> = Value extends "" ? true : Value ext
2488
3206
  type InvalidMultipleOfDivisor<Divisor extends string> = Divisor & Readonly<Record<MultipleOfDivisorError, never>>;
2489
3207
  type MultipleOfDivisorError = CompileTimeError<"MultipleOf", 'Divisor must be one canonical positive decimal string literal such as "0.1".'>;
2490
3208
  /**
2491
- * Adds a {@link Brand} requiring a number to be within an inclusive range.
3209
+ * Number {@link Brand} requiring a value within an inclusive range.
3210
+ *
3211
+ * ### Example
3212
+ *
3213
+ * ```ts
3214
+ * import { Number, between } from "@evolu/common";
3215
+ *
3216
+ * const Percentage = between(0, 100)(Number);
3217
+ *
3218
+ * expectOk(Percentage.fromUnknown(75), 75);
3219
+ * ```
3220
+ *
3221
+ * @group Number
3222
+ */
3223
+ export declare const between: <Min extends number, Max extends number>(min: ValidateBrandFactoryNumber<Min>, max: ValidateBrandFactoryNumber<Max>) => BrandFactory<`Between${Min}-${Max}`, number, BetweenError<Min, Max>>;
3224
+ /**
3225
+ * Error returned when {@link between} rejects a number.
2492
3226
  *
2493
3227
  * @group Number
2494
3228
  */
2495
- export declare const between: <Min extends number, Max extends number>(min: ValidateBrandFactoryNumber<Min>, max: ValidateBrandFactoryNumber<Max>) => BrandFactory<`Between${Min}-${Max}`, number, BetweenError<Min, Max>>;
2496
- /** @group Number */
2497
3229
  export interface BetweenError<Min extends number = number, Max extends number = number> extends TypeError<`Between${Min}-${Max}`> {
2498
3230
  readonly value: number;
2499
3231
  readonly min: Min;
@@ -2550,7 +3282,11 @@ export interface BetweenError<Min extends number = number, Max extends number =
2550
3282
  * @group Collection
2551
3283
  */
2552
3284
  export declare const array: <ElementType extends ConcreteTypeNode>(element: ValidateElement<ElementType>) => ArrayType<ElementType>;
2553
- /** @group Collection */
3285
+ /**
3286
+ * The homogeneous readonly-array {@link Type} returned by {@link array}.
3287
+ *
3288
+ * @group Collection
3289
+ */
2554
3290
  export interface ArrayType<ElementType extends TypeNode> extends Type<"Array", ReadonlyArray<ElementType["Input"]>, ReadonlyArray<ElementType["Output"]>, ArrayNodeError<ElementType>, ArrayParent<ElementType>, ArrayError<InferErrors<ElementType>>, ArrayCustomFrom<ElementType>, ReadonlyArray<CanonicalInputOf<ElementType>>, AllTypesUseIdentityEncoding<ElementType>> {
2555
3291
  readonly [reflectedTypesSymbol]?: ElementType;
2556
3292
  readonly element: ElementType;
@@ -2571,42 +3307,78 @@ type ArrayParent<ElementType extends TypeNode> = [
2571
3307
  type ArrayNodeError<ElementType extends TypeNode> = [
2572
3308
  ElementType["parent"]
2573
3309
  ] extends [TypeNode] ? ArrayElementsError<ElementType["Error"]> : ArrayError<ElementType["Error"]>;
2574
- /** @group Collection */
3310
+ /**
3311
+ * Error returned by {@link array} for a non-array value or invalid array items.
3312
+ *
3313
+ * @group Collection
3314
+ */
2575
3315
  export type ArrayError<Error extends TypeError = TypeError> = ArrayNotArrayError | ArrayItemsErrorValue<Error, true>;
2576
- /** @group Collection */
3316
+ /**
3317
+ * Error returned when an {@link array} input is not an array.
3318
+ *
3319
+ * @group Collection
3320
+ */
2577
3321
  export interface ArrayNotArrayError extends TypeError<"Array"> {
2578
3322
  readonly reason: {
2579
3323
  readonly kind: "NotArray";
2580
3324
  readonly value: unknown;
2581
3325
  };
2582
3326
  }
2583
- /** @group Collection */
3327
+ /**
3328
+ * An {@link array} error containing structural or element issues.
3329
+ *
3330
+ * @group Collection
3331
+ */
2584
3332
  export type ArrayItemsError<Error extends TypeError> = ArrayItemsErrorValue<Error, true>;
2585
- /** @group Collection */
3333
+ /**
3334
+ * One structural or element issue found by {@link array}.
3335
+ *
3336
+ * @group Collection
3337
+ */
2586
3338
  export type ArrayIssue<Error extends TypeError> = ArrayStructuralIssue | ArrayElementIssue<Error>;
2587
- /** @group Collection */
3339
+ /**
3340
+ * A missing array element.
3341
+ *
3342
+ * @group Collection
3343
+ */
2588
3344
  export interface ArrayHoleIssue {
2589
3345
  readonly kind: "Hole";
2590
3346
  readonly index: number;
2591
3347
  }
2592
- /** @group Collection */
3348
+ /**
3349
+ * An array element defined by an accessor instead of a data property.
3350
+ *
3351
+ * @group Collection
3352
+ */
2593
3353
  export interface ArrayAccessorIssue {
2594
3354
  readonly kind: "Accessor";
2595
3355
  readonly index: number;
2596
3356
  }
2597
- /** @group Collection */
3357
+ /**
3358
+ * An own array property other than `length` or an indexed element.
3359
+ *
3360
+ * @group Collection
3361
+ */
2598
3362
  export interface ArrayExcessPropertyIssue {
2599
3363
  readonly kind: "ExcessProperty";
2600
3364
  readonly key: string | symbol;
2601
3365
  }
2602
3366
  type ArrayStructuralIssue = ArrayHoleIssue | ArrayAccessorIssue | ArrayExcessPropertyIssue;
2603
- /** @group Collection */
3367
+ /**
3368
+ * An invalid array element and its index.
3369
+ *
3370
+ * @group Collection
3371
+ */
2604
3372
  export type ArrayElementIssue<Error extends TypeError> = Error extends TypeError ? {
2605
3373
  readonly kind: "Element";
2606
3374
  readonly index: number;
2607
3375
  readonly error: Error;
2608
3376
  } : never;
2609
- /** @group Collection */
3377
+ /**
3378
+ * An {@link array} error containing element errors from a typed boundary.
3379
+ *
3380
+ * @group Collection
3381
+ */
2610
3382
  export type ArrayElementsError<Error extends TypeError> = [Error] extends [
2611
3383
  never
2612
3384
  ] ? never : ArrayItemsErrorValue<Error, false>;
@@ -2624,10 +3396,25 @@ interface ArrayItemsErrorValue<Error extends TypeError, IncludeStructuralIssues
2624
3396
  * iteration order. Classification uses the realm-neutral object tag and
2625
3397
  * prototype structure under Evolu Type's trusted JavaScript policy.
2626
3398
  *
3399
+ * ### Example
3400
+ *
3401
+ * ```ts
3402
+ * import { String, set } from "@evolu/common";
3403
+ *
3404
+ * const Tags = set(String);
3405
+ * const tags = new Set(["local-first", "offline"]);
3406
+ *
3407
+ * expectOk(Tags.fromUnknown(tags), tags);
3408
+ * ```
3409
+ *
2627
3410
  * @group Collection
2628
3411
  */
2629
3412
  export declare const set: <ElementType extends ConcreteTypeNode>(element: ValidateElement<ElementType>) => SetType<ElementType>;
2630
- /** @group Collection */
3413
+ /**
3414
+ * The homogeneous readonly-set {@link Type} returned by {@link set}.
3415
+ *
3416
+ * @group Collection
3417
+ */
2631
3418
  export interface SetType<ElementType extends TypeNode> extends Type<"Set", ReadonlySet<ElementType["Input"]>, ReadonlySet<ElementType["Output"]>, SetNodeError<ElementType>, SetParent<ElementType>, SetError<InferErrors<ElementType>>, SetCustomFrom<ElementType>, ReadonlySet<CanonicalInputOf<ElementType>>, AllTypesUseIdentityEncoding<ElementType>> {
2632
3419
  readonly [reflectedTypesSymbol]?: ElementType;
2633
3420
  readonly element: ElementType;
@@ -2647,9 +3434,18 @@ type SetParent<ElementType extends TypeNode> = [ElementType["parent"]] extends [
2647
3434
  type SetNodeError<ElementType extends TypeNode> = [
2648
3435
  ElementType["parent"]
2649
3436
  ] extends [TypeNode] ? SetElementsError<ElementType["Error"]> : SetError<ElementType["Error"]>;
2650
- /** @group Collection */
3437
+ /**
3438
+ * Error returned by {@link set} for a non-Set value, an invalid Set prototype,
3439
+ * or invalid Set items.
3440
+ *
3441
+ * @group Collection
3442
+ */
2651
3443
  export type SetError<Error extends TypeError = TypeError> = SetNotSetError | SetUnexpectedPrototypeError | SetItemsErrorValue<Error, true>;
2652
- /** @group Collection */
3444
+ /**
3445
+ * Error returned when a {@link set} input is not a Set.
3446
+ *
3447
+ * @group Collection
3448
+ */
2653
3449
  export interface SetNotSetError extends TypeError<"Set"> {
2654
3450
  readonly reason: {
2655
3451
  readonly kind: "NotSet";
@@ -2667,21 +3463,37 @@ export interface SetUnexpectedPrototypeError extends TypeError<"Set"> {
2667
3463
  readonly value: ReadonlySet<unknown>;
2668
3464
  };
2669
3465
  }
2670
- /** @group Collection */
3466
+ /**
3467
+ * An own property found on a Set value.
3468
+ *
3469
+ * @group Collection
3470
+ */
2671
3471
  export interface SetExcessPropertyIssue {
2672
3472
  readonly kind: "ExcessProperty";
2673
3473
  readonly key: string | symbol;
2674
3474
  }
2675
3475
  type SetStructuralIssue = SetExcessPropertyIssue;
2676
- /** @group Collection */
3476
+ /**
3477
+ * An invalid Set element and its iteration index.
3478
+ *
3479
+ * @group Collection
3480
+ */
2677
3481
  export type SetElementIssue<Error extends TypeError> = Error extends TypeError ? {
2678
3482
  readonly kind: "Element";
2679
3483
  readonly index: number;
2680
3484
  readonly error: Error;
2681
3485
  } : never;
2682
- /** @group Collection */
3486
+ /**
3487
+ * A {@link set} error containing structural or element issues.
3488
+ *
3489
+ * @group Collection
3490
+ */
2683
3491
  export type SetItemsError<Error extends TypeError> = SetItemsErrorValue<Error, true>;
2684
- /** @group Collection */
3492
+ /**
3493
+ * A {@link set} error containing element errors from a typed boundary.
3494
+ *
3495
+ * @group Collection
3496
+ */
2685
3497
  export type SetElementsError<Error extends TypeError> = [Error] extends [never] ? never : SetItemsErrorValue<Error, false>;
2686
3498
  interface SetItemsErrorValue<Error extends TypeError, IncludeStructuralIssues extends boolean> extends TypeError<"Set"> {
2687
3499
  readonly reason: {
@@ -2714,17 +3526,12 @@ interface SetItemsErrorValue<Error extends TypeError, IncludeStructuralIssues ex
2714
3526
  * ### Example
2715
3527
  *
2716
3528
  * ```ts
2717
- * import { Number, String, ok, transform, tuple } from "@evolu/common";
2718
- *
2719
- * const NumberFromString = transform("NumberFromString", String, Number, {
2720
- * from: (value) => ok(globalThis.Number(value)),
2721
- * to: globalThis.String,
2722
- * });
3529
+ * import { Int64FromInt64String, String, tuple } from "@evolu/common";
2723
3530
  *
2724
- * const Entry = tuple(String, NumberFromString);
3531
+ * const Entry = tuple(String, Int64FromInt64String);
2725
3532
  *
2726
- * expectOk(Entry.fromUnknown(["count", "1"]), ["count", 1]);
2727
- * expectOk(Entry.from.parent(["count", "1"]), ["count", 1]);
3533
+ * expectOk(Entry.fromUnknown(["count", "1"]), ["count", 1n]);
3534
+ * expectOk(Entry.from.parent(["count", "1"]), ["count", 1n]);
2728
3535
  * ```
2729
3536
  *
2730
3537
  * @group Collection
@@ -2859,6 +3666,55 @@ interface TupleItemsErrorValue<Error extends TypeError, IncludeStructuralIssues
2859
3666
  readonly issues: NonEmptyReadonlyArray<(true extends IncludeStructuralIssues ? TupleStructuralIssue : never) | TupleElementIssue<Error>>;
2860
3667
  };
2861
3668
  }
3669
+ /**
3670
+ * Decimal digit from `"0"` to `"9"`.
3671
+ *
3672
+ * @group String
3673
+ */
3674
+ export declare const Digit: UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>;
3675
+ export type Digit = typeof Digit.Output;
3676
+ /**
3677
+ * Decimal digit from `"1"` to `"9"`.
3678
+ *
3679
+ * @group String
3680
+ */
3681
+ export declare const Digit1To9: UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>;
3682
+ export type Digit1To9 = typeof Digit1To9.Output;
3683
+ /**
3684
+ * Decimal string from `"1"` to `"6"`.
3685
+ *
3686
+ * @group String
3687
+ */
3688
+ export declare const Digit1To6: UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">]>;
3689
+ export type Digit1To6 = typeof Digit1To6.Output;
3690
+ /**
3691
+ * Decimal string from `"1"` to `"23"`.
3692
+ *
3693
+ * @group String
3694
+ */
3695
+ export declare const Digit1To23: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly ["1", UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>, TemplateLiteralType<readonly ["2", UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">]>]>]>;
3696
+ export type Digit1To23 = typeof Digit1To23.Output;
3697
+ /**
3698
+ * Decimal string from `"1"` to `"51"`.
3699
+ *
3700
+ * @group String
3701
+ */
3702
+ export declare const Digit1To51: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>, TemplateLiteralType<readonly ["5", UnionType<readonly [LiteralType<"0">, LiteralType<"1">]>]>]>;
3703
+ export type Digit1To51 = typeof Digit1To51.Output;
3704
+ /**
3705
+ * Decimal string from `"1"` to `"99"`.
3706
+ *
3707
+ * @group String
3708
+ */
3709
+ export declare const Digit1To99: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>]>;
3710
+ export type Digit1To99 = typeof Digit1To99.Output;
3711
+ /**
3712
+ * Decimal string from `"1"` to `"59"`.
3713
+ *
3714
+ * @group String
3715
+ */
3716
+ export declare const Digit1To59: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>]>;
3717
+ export type Digit1To59 = typeof Digit1To59.Output;
2862
3718
  type PlainObjectError = ObjectError<Readonly<Record<never, never>>, ObjectPropertyAccessError | ObjectExcessPropertyError>;
2863
3719
  /**
2864
3720
  * A {@link Type} for readonly plain objects with unknown property values.
@@ -2874,7 +3730,7 @@ type PlainObjectError = ObjectError<Readonly<Record<never, never>>, ObjectProper
2874
3730
  * values have their own Types, and {@link instanceOf} when an instance belongs
2875
3731
  * to the domain.
2876
3732
  *
2877
- * @group Base Types
3733
+ * @group Base
2878
3734
  */
2879
3735
  export declare const Object: Type<"Object", Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>, PlainObjectError, null, PlainObjectError, never, Readonly<Record<string, unknown>>>;
2880
3736
  /**
@@ -2907,20 +3763,13 @@ export declare const Object: Type<"Object", Readonly<Record<string, unknown>>, R
2907
3763
  *
2908
3764
  * ```ts
2909
3765
  * import {
2910
- * Number,
3766
+ * Int64FromInt64String,
2911
3767
  * String,
2912
- * ok,
2913
3768
  * record,
2914
- * transform,
2915
- * type Result,
3769
+ * type Int64,
2916
3770
  * } from "@evolu/common";
2917
3771
  *
2918
- * const ScoreFromString = transform("ScoreFromString", String, Number, {
2919
- * from: (value) => ok(globalThis.Number(value)),
2920
- * to: globalThis.String,
2921
- * });
2922
- *
2923
- * const ScoresByUser = record(String, ScoreFromString);
3772
+ * const ScoresByUser = record(String, Int64FromInt64String);
2924
3773
  * type ScoresByUser = typeof ScoresByUser.Output;
2925
3774
  *
2926
3775
  * // Validate an unknown value.
@@ -2929,7 +3778,7 @@ export declare const Object: Type<"Object", Readonly<Record<string, unknown>>, R
2929
3778
  * grace: "20",
2930
3779
  * });
2931
3780
  *
2932
- * expectOk(scoresFromUnknown, { ada: 10, grace: 20 });
3781
+ * expectOk(scoresFromUnknown, { ada: 10n, grace: 20n });
2933
3782
  *
2934
3783
  * // Validate keys and values with their root Types.
2935
3784
  * const scoresInput = ScoresByUser.parent.fromUnknown({
@@ -2941,11 +3790,10 @@ export declare const Object: Type<"Object", Readonly<Record<string, unknown>>, R
2941
3790
  * // Run the remaining key and value stages.
2942
3791
  * const scoresFromInput = ScoresByUser.from.parent(scoresInput.value);
2943
3792
  *
2944
- * expectTypeOf(scoresFromInput).toEqualTypeOf<
2945
- * Result<Readonly<Partial<Record<string, number>>>, never>
3793
+ * expectOk(scoresFromInput, { ada: 10n, grace: 20n });
3794
+ * expectTypeOf(scoresFromInput.value).toEqualTypeOf<
3795
+ * Readonly<Partial<Record<string, Int64>>>
2946
3796
  * >();
2947
- *
2948
- * expectOk(scoresFromInput, { ada: 10, grace: 20 });
2949
3797
  * ```
2950
3798
  *
2951
3799
  * Note that TypeScript does not model an object's runtime prototype. This can
@@ -2989,7 +3837,11 @@ export declare const Object: Type<"Object", Readonly<Record<string, unknown>>, R
2989
3837
  * @group Objects
2990
3838
  */
2991
3839
  export declare const record: <KeyType extends ConcreteTypeNode, ValueType extends ConcreteTypeNode>(key: ValidateRecordKeyType<KeyType>, value: ValidateRecordValueType<ValueType>) => RecordType<KeyType, ValueType>;
2992
- /** @group Objects */
3840
+ /**
3841
+ * The {@link Type} returned by {@link record}.
3842
+ *
3843
+ * @group Objects
3844
+ */
2993
3845
  export interface RecordType<KeyType extends TypeNode, ValueType extends TypeNode> extends Type<"Record", RecordShape<KeyType, ValueType, "Input">, RecordShape<KeyType, ValueType, "Output">, RecordNodeError<KeyType, ValueType>, RecordParent<KeyType, ValueType>, RecordError<InferErrors<KeyType>, InferErrors<ValueType>, RecordCollisionFor<KeyType>>, never, CanonicalInputSubset<RecordShape<KeyType, ValueType, "Input">, RecordShape<KeyType, ValueType, "CanonicalInput">>, AllTypesUseIdentityEncoding<KeyType | ValueType>> {
2994
3846
  readonly [reflectedTypesSymbol]?: KeyType | ValueType;
2995
3847
  readonly key: KeyType;
@@ -3019,16 +3871,28 @@ type ValidateRecordValueType<T extends ConcreteTypeNode> = IsUnion<T> extends fa
3019
3871
  type RecordKeyConcreteTypeError = CompileTimeError<"Type", "Record key must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.">;
3020
3872
  type RecordValueConcreteTypeError = CompileTimeError<"Type", "Record value must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.">;
3021
3873
  type RecordKeyStringTypeError = CompileTimeError<"Type", "Record key Type Input and Output must extend string.">;
3022
- /** @group Objects */
3874
+ /**
3875
+ * Error returned while validating a {@link record} and its entries.
3876
+ *
3877
+ * @group Objects
3878
+ */
3023
3879
  export type RecordError<KeyError extends TypeError = TypeError, ValueError extends TypeError = TypeError, Collision extends RecordCollisionIssue = RecordCollisionIssue> = RecordNotRecordError | RecordNotPlainRecordError | RecordEntriesErrorValue<KeyError, ValueError, Collision | RecordAccessorIssue | RecordNonEnumerableIssue>;
3024
- /** @group Objects */
3880
+ /**
3881
+ * Error returned when a {@link record} input is not an object.
3882
+ *
3883
+ * @group Objects
3884
+ */
3025
3885
  export interface RecordNotRecordError extends TypeError<"Record"> {
3026
3886
  readonly reason: {
3027
3887
  readonly kind: "NotRecord";
3028
3888
  readonly value: unknown;
3029
3889
  };
3030
3890
  }
3031
- /** @group Objects */
3891
+ /**
3892
+ * Error returned when a {@link record} input is not a plain object.
3893
+ *
3894
+ * @group Objects
3895
+ */
3032
3896
  export interface RecordNotPlainRecordError extends TypeError<"Record"> {
3033
3897
  readonly reason: {
3034
3898
  readonly kind: "NotPlainRecord";
@@ -3045,9 +3909,17 @@ export interface RecordNotPlainRecordError extends TypeError<"Record"> {
3045
3909
  * @group Objects
3046
3910
  */
3047
3911
  export type RecordEntriesError<KeyError extends TypeError, ValueError extends TypeError, StructuralIssue extends RecordStructuralIssue = RecordCollisionIssue> = [KeyError | ValueError | StructuralIssue] extends [never] ? never : RecordEntriesErrorValue<KeyError, ValueError, StructuralIssue>;
3048
- /** @group Objects */
3912
+ /**
3913
+ * An invalid key, value, or property structure in a {@link record}.
3914
+ *
3915
+ * @group Objects
3916
+ */
3049
3917
  export type RecordIssue<KeyError extends TypeError, ValueError extends TypeError, StructuralIssue extends RecordStructuralIssue = RecordCollisionIssue> = RecordKeyIssue<KeyError> | RecordValueIssue<ValueError> | StructuralIssue;
3050
- /** @group Objects */
3918
+ /**
3919
+ * A property-structure issue returned by {@link record}.
3920
+ *
3921
+ * @group Objects
3922
+ */
3051
3923
  export type RecordStructuralIssue = RecordAccessorIssue | RecordCollisionIssue | RecordNonEnumerableIssue;
3052
3924
  /**
3053
3925
  * An accessor property rejected by {@link record}.
@@ -3067,19 +3939,31 @@ export interface RecordNonEnumerableIssue {
3067
3939
  readonly kind: "NonEnumerable";
3068
3940
  readonly key: string | symbol;
3069
3941
  }
3070
- /** @group Objects */
3942
+ /**
3943
+ * An invalid key and its source property key in a {@link record}.
3944
+ *
3945
+ * @group Objects
3946
+ */
3071
3947
  export type RecordKeyIssue<Error extends TypeError> = Error extends TypeError ? {
3072
3948
  readonly kind: "Key";
3073
3949
  readonly key: string | symbol;
3074
3950
  readonly error: Error;
3075
3951
  } : never;
3076
- /** @group Objects */
3952
+ /**
3953
+ * An invalid value and its property key in a {@link record}.
3954
+ *
3955
+ * @group Objects
3956
+ */
3077
3957
  export type RecordValueIssue<Error extends TypeError> = Error extends TypeError ? {
3078
3958
  readonly kind: "Value";
3079
3959
  readonly key: string | symbol;
3080
3960
  readonly error: Error;
3081
3961
  } : never;
3082
- /** @group Objects */
3962
+ /**
3963
+ * Two {@link record} keys that decode to the same output key.
3964
+ *
3965
+ * @group Objects
3966
+ */
3083
3967
  export interface RecordCollisionIssue {
3084
3968
  readonly kind: "Collision";
3085
3969
  readonly key: string | symbol;
@@ -3170,48 +4054,38 @@ type ObjectProperty = ObjectProps[string];
3170
4054
  *
3171
4055
  * ```ts
3172
4056
  * import {
3173
- * Number,
4057
+ * Int64FromInt64String,
3174
4058
  * String,
3175
4059
  * object,
3176
- * ok,
3177
- * transform,
3178
4060
  * type InferType,
3179
- * type Result,
3180
4061
  * } from "@evolu/common";
3181
4062
  *
3182
- * const AgeFromString = transform("AgeFromString", String, Number, {
3183
- * from: (value) => ok(globalThis.Number(value)),
3184
- * to: globalThis.String,
3185
- * });
3186
- *
3187
4063
  * const User = object({
3188
4064
  * name: String,
3189
- * age: AgeFromString,
4065
+ * loginCount: Int64FromInt64String,
3190
4066
  * });
3191
4067
  * interface User extends InferType<typeof User> {}
3192
4068
  *
3193
4069
  * // Validate an unknown value.
3194
- * const userFromUnknown = User.fromUnknown({ name: "Ada", age: "42" });
4070
+ * const userFromUnknown = User.fromUnknown({
4071
+ * name: "Ada",
4072
+ * loginCount: "42",
4073
+ * });
3195
4074
  *
3196
- * expectOk(userFromUnknown, { name: "Ada", age: 42 });
4075
+ * expectOk(userFromUnknown, { name: "Ada", loginCount: 42n });
3197
4076
  *
3198
4077
  * // Validate the object and root property Types.
3199
4078
  * const userInput = User.parent.fromUnknown({
3200
4079
  * name: "Ada",
3201
- * age: "42",
4080
+ * loginCount: "42",
3202
4081
  * });
3203
- * expectOk(userInput, { name: "Ada", age: "42" });
4082
+ * expectOk(userInput, { name: "Ada", loginCount: "42" });
3204
4083
  *
3205
4084
  * // Run the remaining property stages.
3206
- * const userFromInput: Result<
3207
- * { readonly name: string; readonly age: number },
3208
- * never
3209
- * > = User.from.parent(userInput.value);
4085
+ * const userFromInput = User.from.parent(userInput.value);
3210
4086
  *
3211
- * expectTypeOf(userFromInput).toEqualTypeOf<
3212
- * Result<{ readonly name: string; readonly age: number }, never>
3213
- * >();
3214
- * expectOk(userFromInput, { name: "Ada", age: 42 });
4087
+ * expectOk(userFromInput, { name: "Ada", loginCount: 42n });
4088
+ * expectTypeOf(userFromInput.value).toExtend<User>();
3215
4089
  * ```
3216
4090
  *
3217
4091
  * Note that TypeScript does not model an object's runtime prototype. This can
@@ -3259,6 +4133,7 @@ type ObjectProperty = ObjectProps[string];
3259
4133
  * @group Objects
3260
4134
  */
3261
4135
  export declare function object<const Props extends ObjectProps>(props: Props, ...validation: [ObjectValidationError<Props>] extends [never] ? [] : [ValidationFailure<ObjectValidationError<Props>>]): StrictObjectType<Props>;
4136
+ /** Creates an Object Type with additional record properties. */
3262
4137
  export declare function object<const Props extends ObjectProps, const Rest extends RecordTypeNode & ConcreteTypeNode>(props: Props, record: Rest, ...validation: [
3263
4138
  ObjectValidationError<Props> | ObjectRecordValidationError<Props, Rest>
3264
4139
  ] extends [never] ? [] : [
@@ -3300,7 +4175,11 @@ type ObjectRecordKeyTypeError = CompileTimeError<"Type", "Object Record key must
3300
4175
  type ObjectRecordInputTypeError = CompileTimeError<"Type", "Every declared property Type Input must extend the Object Record value Type Input.">;
3301
4176
  type ObjectRecordOutputTypeError = CompileTimeError<"Type", "Every declared property Type Output must extend the Object Record value Type Output.">;
3302
4177
  type ObjectRecordCanonicalInputTypeError = CompileTimeError<"Type", "Every declared property Type CanonicalInput must extend the Object Record value Type CanonicalInput.">;
3303
- /** @group Objects */
4178
+ /**
4179
+ * The {@link Type} returned by {@link object}.
4180
+ *
4181
+ * @group Objects
4182
+ */
3304
4183
  export type ObjectType<Props extends ObjectProps, Rest extends ObjectRecordTypeNode | undefined = undefined> = Rest extends ObjectRecordTypeNode ? ObjectWithRecordType<Props, Rest> : StrictObjectType<Props>;
3305
4184
  type StrictObjectType<Props extends ObjectProps> = Type<"Object", StrictObjectShape<Props, "Input">, StrictObjectShape<Props, "Output">, [
3306
4185
  StrictObjectParents<Props>
@@ -3470,7 +4349,7 @@ type ObjectUnknownPropertyErrors<Error extends TypeError, RestError extends Type
3470
4349
  type ObjectRestFromUnknownError<Rest extends ObjectRecordTypeNode | undefined> = Rest extends ObjectRecordTypeNode ? ObjectPropertyAccessError | RecordEntriesErrorValue<TypeOfError<"String">, InferErrors<Rest["value"]>, never> : ObjectExcessPropertyError;
3471
4350
  type ObjectRestFromParentError<Rest extends ObjectRecordTypeNode | undefined> = Rest extends ObjectRecordTypeNode ? RecordEntriesError<never, TypeFromError<Rest["value"]>, never> : never;
3472
4351
  /**
3473
- * Creates an {@link object} Type with every property optional.
4352
+ * Object {@link Type} with every property optional.
3474
4353
  *
3475
4354
  * No property is required, but every present property must still satisfy its
3476
4355
  * Type.
@@ -3490,12 +4369,17 @@ type ObjectRestFromParentError<Rest extends ObjectRecordTypeNode | undefined> =
3490
4369
  * @group Objects
3491
4370
  */
3492
4371
  export declare const partial: <const Props extends ObjectProps>(props: Props, ..._validation: [ObjectValidationError<Props>] extends [never] ? [] : [ValidationFailure<ObjectValidationError<Props>>]) => ObjectType<PartialObjectProps<Props>>;
3493
- /** @group Objects */
4372
+ /**
4373
+ * Maps every required object property Type to an optional property.
4374
+ *
4375
+ * @group Objects
4376
+ */
3494
4377
  export type PartialObjectProps<Props extends ObjectProps> = {
3495
4378
  readonly [Key in keyof Props]: Props[Key] extends OptionalProperty<TypeNode> ? Props[Key] : Props[Key] extends TypeNode ? OptionalProperty<Props[Key]> : never;
3496
4379
  };
3497
4380
  /**
3498
- * Makes every property whose Union Type includes {@link Null} optional.
4381
+ * Object {@link Type} making every property whose Union Type includes
4382
+ * {@link Null} optional.
3499
4383
  *
3500
4384
  * The property retains its original Union Type, so consumers may omit it, set
3501
4385
  * it to `null`, or provide any other member of that Union. Properties without
@@ -3521,12 +4405,28 @@ export type PartialObjectProps<Props extends ObjectProps> = {
3521
4405
  * @group Objects
3522
4406
  */
3523
4407
  export declare const nullableToOptional: <const Props extends ObjectProps>(props: Props, ..._validation: [ObjectValidationError<Props>] extends [never] ? [] : [ValidationFailure<ObjectValidationError<Props>>]) => ObjectType<NullableToOptionalProps<Props>>;
3524
- /** @group Objects */
4408
+ /**
4409
+ * Maps object properties whose Union Type includes {@link Null} to optional
4410
+ * properties.
4411
+ *
4412
+ * @group Objects
4413
+ */
3525
4414
  export type NullableToOptionalProps<Props extends ObjectProps> = {
3526
4415
  readonly [Key in keyof Props]: Props[Key] extends OptionalProperty<TypeNode> ? Props[Key] : Props[Key] extends UnionType<infer Members> ? typeof Null extends Members[number] ? OptionalProperty<Props[Key]> : Props[Key] : Props[Key];
3527
4416
  };
3528
4417
  /**
3529
- * Creates an {@link object} Type without the selected declared properties.
4418
+ * Object {@link Type} without the selected declared properties.
4419
+ *
4420
+ * ### Example
4421
+ *
4422
+ * ```ts
4423
+ * import { String, object, omit } from "@evolu/common";
4424
+ *
4425
+ * const User = object({ name: String, password: String });
4426
+ * const PublicUser = omit(User, "password");
4427
+ *
4428
+ * expectOk(PublicUser.fromUnknown({ name: "Ada" }), { name: "Ada" });
4429
+ * ```
3530
4430
  *
3531
4431
  * @group Objects
3532
4432
  */
@@ -3540,8 +4440,8 @@ type OmitKeyConcreteTypeError = CompileTimeError<"Type", "Each omitted key must
3540
4440
  * Creates a {@link Type} for {@link Result} values.
3541
4441
  *
3542
4442
  * Use this to validate Results crossing a storage, worker, API, or other
3543
- * serialization boundary. The operation returns an outer validation Result. Its
3544
- * successful value is the inner domain Result described by `okType` and
4443
+ * serialization boundary. `fromUnknown` returns an outer validation Result.
4444
+ * Its successful value is the inner domain Result described by `okType` and
3545
4445
  * `errorType`.
3546
4446
  *
3547
4447
  * ### Example
@@ -3557,34 +4457,16 @@ type OmitKeyConcreteTypeError = CompileTimeError<"Type", "Each omitted key must
3557
4457
  *
3558
4458
  * const SyncResponse = result(
3559
4459
  * object({ timestamp: NonNegativeInt }),
3560
- * typed("SyncError", { message: String }),
4460
+ * typed("SyncFailed", { message: String }),
3561
4461
  * );
3562
4462
  *
3563
- * const describeResponse = (input: unknown): string => {
3564
- * const validated = SyncResponse.fromUnknown(input);
3565
- * if (!validated.ok) return "Invalid response";
3566
- *
3567
- * const response = validated.value;
3568
- * return response.ok
3569
- * ? `Synced at ${response.value.timestamp}`
3570
- * : response.error.message;
3571
- * };
4463
+ * const validated = SyncResponse.fromUnknown({
4464
+ * ok: true,
4465
+ * value: { timestamp: 42 },
4466
+ * });
3572
4467
  *
3573
- * expect(
3574
- * describeResponse({
3575
- * ok: true,
3576
- * value: { timestamp: 42 },
3577
- * }),
3578
- * ).toBe("Synced at 42");
3579
- * expect(
3580
- * describeResponse({
3581
- * ok: false,
3582
- * error: { type: "SyncError", message: "Offline" },
3583
- * }),
3584
- * ).toBe("Offline");
3585
- * expect(describeResponse({ ok: true, value: { timestamp: -1 } })).toBe(
3586
- * "Invalid response",
3587
- * );
4468
+ * expectOk(validated, { ok: true, value: { timestamp: 42 } });
4469
+ * expectOk(validated.value, { timestamp: 42 });
3588
4470
  * ```
3589
4471
  *
3590
4472
  * @group Results
@@ -3616,7 +4498,7 @@ export declare const UnknownResult: DiscriminatedUnionType<"ok", readonly [Stric
3616
4498
  }>]>;
3617
4499
  export type UnknownResult = typeof UnknownResult.Output;
3618
4500
  /**
3619
- * Tagged {@link ObjectType}.
4501
+ * Creates an {@link ObjectType} with a literal `type` property.
3620
4502
  *
3621
4503
  * The discriminator belongs to `typed`, so additional properties cannot declare
3622
4504
  * `type`. The discriminator Input is `string`, inherited from {@link String},
@@ -3628,35 +4510,38 @@ export type UnknownResult = typeof UnknownResult.Output;
3628
4510
  * ### Example
3629
4511
  *
3630
4512
  * ```ts
3631
- * import { String, discriminatedUnion, typed } from "@evolu/common";
4513
+ * import { String, typed } from "@evolu/common";
3632
4514
  *
3633
4515
  * const Pending = typed("Pending", {
3634
4516
  * label: String,
3635
4517
  * });
3636
4518
  *
3637
- * const Completed = typed("Completed");
3638
- * const Status = discriminatedUnion(Pending, Completed);
3639
- *
3640
- * expectOk(Status.fromUnknown({ type: "Pending", label: "Waiting" }), {
4519
+ * expectOk(Pending.fromUnknown({ type: "Pending", label: "Waiting" }), {
3641
4520
  * type: "Pending",
3642
4521
  * label: "Waiting",
3643
4522
  * });
3644
- * expectOk(Status.fromUnknown({ type: "Completed" }), {
3645
- * type: "Completed",
3646
- * });
3647
4523
  * ```
3648
4524
  *
3649
4525
  * @group Discriminated unions
3650
4526
  */
3651
4527
  export declare function typed<const Tag extends TypeName>(tag: ValidateTypedTag<Tag>): TypedType<Tag>;
4528
+ /** Creates a Tagged Object Type with declared properties. */
3652
4529
  export declare function typed<const Tag extends TypeName, const Props extends ObjectProps>(tag: ValidateTypedTag<Tag>, props: Props, ...validation: [TypedValidationError<Props>] extends [never] ? [] : [ValidationFailure<TypedValidationError<Props>>]): TypedType<Tag, Props>;
4530
+ /** Creates a Tagged Object Type with additional record properties. */
3653
4531
  export declare function typed<const Tag extends TypeName, const Props extends ObjectProps, const Rest extends RecordTypeNode & ConcreteTypeNode>(tag: ValidateTypedTag<Tag>, props: Props, record: Rest, ...validation: [
3654
4532
  TypedValidationError<Props> | ObjectRecordValidationError<TypedProps<Tag, Props>, Rest>
3655
4533
  ] extends [never] ? [] : [
3656
4534
  ValidationFailure<TypedValidationError<Props> | ObjectRecordValidationError<TypedProps<Tag, Props>, Rest>>
3657
4535
  ]): TypedType<Tag, Props, Rest extends ObjectRecordTypeNode ? Rest : never>;
3658
4536
  /**
3659
- * A structurally tagged value created by {@link typed}.
4537
+ * A TypeScript interface with a literal `type` property.
4538
+ *
4539
+ * Use `Typed` for both domain objects in discriminated unions and plain domain
4540
+ * errors returned by {@link Result}. Name a domain error interface `XError`.
4541
+ * When `X` already describes a failure, use `X` for its `type` discriminant
4542
+ * because `Error` describes the interface's role rather than the runtime error
4543
+ * kind. Keep `Error` when it is needed to make the discriminant unambiguous,
4544
+ * such as `TimeoutError`.
3660
4545
  *
3661
4546
  * Typed unions model mutually exclusive states as separate variants instead of
3662
4547
  * combinations of flags and optional properties. TypeScript narrows a union by
@@ -3667,20 +4552,23 @@ export declare function typed<const Tag extends TypeName, const Props extends Ob
3667
4552
  * ### Example
3668
4553
  *
3669
4554
  * ```ts
3670
- * import { type Typed } from "@evolu/common";
4555
+ * import { err, ok, type Result, type Typed } from "@evolu/common";
4556
+ *
4557
+ * interface User extends Typed<"User"> {
4558
+ * readonly id: string;
4559
+ * }
3671
4560
  *
3672
- * type Status = Typed<"Pending"> | Typed<"Completed">;
4561
+ * const getUser = (id: string): Result<User, UserNotFoundError> =>
4562
+ * id === "user-1"
4563
+ * ? ok({ type: "User", id })
4564
+ * : err({ type: "UserNotFound", id });
3673
4565
  *
3674
- * const getStatusMessage = (status: Status): string => {
3675
- * switch (status.type) {
3676
- * case "Pending":
3677
- * return "Waiting";
3678
- * case "Completed":
3679
- * return "Done";
3680
- * }
3681
- * };
4566
+ * interface UserNotFoundError extends Typed<"UserNotFound"> {
4567
+ * readonly id: string;
4568
+ * }
3682
4569
  *
3683
- * expect(getStatusMessage({ type: "Pending" })).toBe("Waiting");
4570
+ * expectOk(getUser("user-1"), { type: "User", id: "user-1" });
4571
+ * expectErr(getUser("missing"), { type: "UserNotFound", id: "missing" });
3684
4572
  * ```
3685
4573
  *
3686
4574
  * @group Discriminated unions
@@ -3691,9 +4579,8 @@ export interface Typed<Tag extends TypeName> {
3691
4579
  /**
3692
4580
  * Extracts members of a {@link Typed} Output union by their `type` literal.
3693
4581
  *
3694
- * The requested name is constrained to the union's actual discriminator values,
3695
- * so a misspelling is a TypeScript error instead of silently producing
3696
- * `never`.
4582
+ * The requested tag is constrained to the union's actual discriminator values,
4583
+ * so a misspelling is a TypeScript error instead of silently producing `never`.
3697
4584
  *
3698
4585
  * ### Example
3699
4586
  *
@@ -3737,7 +4624,8 @@ type ConcreteTypedTagError = CompileTimeError<"Type", "Tag must be one concrete
3737
4624
  type TypedValidationError<Props extends ObjectProps> = "type" extends keyof Props ? TypedTypePropertyError : ObjectValidationError<Props>;
3738
4625
  type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties must not declare the reserved "type" property.'>;
3739
4626
  /**
3740
- * Creates a {@link Type} for a producer's value, error, or done {@link Result}.
4627
+ * Creates a {@link Type} for producer Results with value, error, or done
4628
+ * outcomes.
3741
4629
  *
3742
4630
  * The three outcomes are `Ok<Value>`, `Err<Error>`, and `Err<Typed<"Done"> & {
3743
4631
  * done: Done }>`. This keeps normal completion distinct from failure while
@@ -3750,7 +4638,7 @@ type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties mu
3750
4638
  *
3751
4639
  * const StringNextResult = nextResult(
3752
4640
  * String,
3753
- * typed("ProducerError", { message: String }),
4641
+ * typed("ReadFailed", { message: String }),
3754
4642
  * String,
3755
4643
  * );
3756
4644
  *
@@ -3774,7 +4662,7 @@ type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties mu
3774
4662
  * expect(
3775
4663
  * describeNext({
3776
4664
  * ok: false,
3777
- * error: { type: "ProducerError", message: "Offline" },
4665
+ * error: { type: "ReadFailed", message: "Offline" },
3778
4666
  * }),
3779
4667
  * ).toBe("Error: Offline");
3780
4668
  * ```
@@ -3806,7 +4694,7 @@ export declare const UnknownNextResult: DiscriminatedUnionType<"ok", readonly [S
3806
4694
  }>]>;
3807
4695
  export type UnknownNextResult = typeof UnknownNextResult.Output;
3808
4696
  /**
3809
- * Discriminated Union {@link Type}.
4697
+ * Discriminated union {@link Type}.
3810
4698
  *
3811
4699
  * With no explicit key, the conventional `type` property created by
3812
4700
  * {@link typed} is used. Pass a key first to discriminate
@@ -3837,6 +4725,34 @@ export type UnknownNextResult = typeof UnknownNextResult.Output;
3837
4725
  * @group Discriminated unions
3838
4726
  */
3839
4727
  export declare function discriminatedUnion<const Members extends DiscriminatedUnionMembers>(...members: Members & DiscriminatedUnionValidation<"type", Members>): DiscriminatedUnionType<"type", Members>;
4728
+ /**
4729
+ * Creates a Discriminated Union Type with an explicit discriminator key.
4730
+ *
4731
+ * ### Example
4732
+ *
4733
+ * ```ts
4734
+ * import {
4735
+ * Number,
4736
+ * String,
4737
+ * discriminatedUnion,
4738
+ * literal,
4739
+ * object,
4740
+ * } from "@evolu/common";
4741
+ *
4742
+ * const Added = object({ kind: literal("added"), value: String });
4743
+ * const Removed = object({ kind: literal("removed"), id: Number });
4744
+ * const Event = discriminatedUnion("kind", Added, Removed);
4745
+ *
4746
+ * expectOk(Event.fromUnknown({ kind: "added", value: "Evolu" }), {
4747
+ * kind: "added",
4748
+ * value: "Evolu",
4749
+ * });
4750
+ * expectOk(Event.fromUnknown({ kind: "removed", id: 1 }), {
4751
+ * kind: "removed",
4752
+ * id: 1,
4753
+ * });
4754
+ * ```
4755
+ */
3840
4756
  export declare function discriminatedUnion<const Key extends string, const Members extends DiscriminatedUnionMembers>(key: ValidateDiscriminatedUnionKey<Key>, ...members: Members & DiscriminatedUnionValidation<Key, Members>): DiscriminatedUnionType<Key, Members>;
3841
4757
  /**
3842
4758
  * The routed {@link Type} returned by {@link discriminatedUnion}.
@@ -3959,10 +4875,10 @@ type DiscriminatedUnionMemberConcreteTypeError = CompileTimeError<"Type", "Membe
3959
4875
  type DiscriminatedUnionPropertyError<Key extends string> = CompileTimeError<"Type", `Property "${Key}" must be a required String, Number, BigInt, or Boolean Literal Type in every member.`>;
3960
4876
  type DiscriminatedUnionDuplicateValueError<Key extends string, Expected extends DiscriminatedUnionLiteral> = CompileTimeError<"Type", `Duplicate value "${Expected}" at property "${Key}".`>;
3961
4877
  /**
3962
- * Lazy {@link Type} for recursive definitions.
4878
+ * Creates a lazy {@link Type} for recursive definitions.
3963
4879
  *
3964
- * Lazy defers and caches a Type definition, allowing recursive data such as
3965
- * trees and mutually recursive models.
4880
+ * The definition is evaluated on first use and then cached, allowing recursive
4881
+ * data such as trees and mutually recursive models.
3966
4882
  *
3967
4883
  * A recursive declaration refers to its own variable while that variable is
3968
4884
  * being initialized, so TypeScript cannot infer it reliably. Getter-based
@@ -3982,12 +4898,10 @@ type DiscriminatedUnionDuplicateValueError<Key extends string, Expected extends
3982
4898
  *
3983
4899
  * ```ts
3984
4900
  * import {
3985
- * Number,
3986
4901
  * String,
3987
4902
  * array,
3988
4903
  * lazy,
3989
4904
  * object,
3990
- * optional,
3991
4905
  * type ArrayError,
3992
4906
  * type LazyType,
3993
4907
  * type ObjectError,
@@ -4008,35 +4922,19 @@ type DiscriminatedUnionDuplicateValueError<Key extends string, Expected extends
4008
4922
  * () => object({ value: String, children: array(Tree) }),
4009
4923
  * );
4010
4924
  *
4011
- * interface Left {
4012
- * readonly label: string;
4013
- * readonly right?: Right;
4014
- * }
4015
- *
4016
- * interface Right {
4017
- * readonly count: number;
4018
- * readonly left?: Left;
4019
- * }
4020
- *
4021
- * interface LeftError extends ObjectError<{
4022
- * readonly label: TypeOfError<"String">;
4023
- * readonly right?: RightError;
4024
- * }> {}
4025
- *
4026
- * interface RightError extends ObjectError<{
4027
- * readonly count: TypeOfError<"Number">;
4028
- * readonly left?: LeftError;
4029
- * }> {}
4030
- *
4031
- * const Left: LazyType<Left, Left, never, LeftError, LeftError> = lazy(
4032
- * () => object({ label: String, right: optional(Right) }),
4033
- * );
4925
+ * const result = Tree.fromUnknown({
4926
+ * value: "root",
4927
+ * children: [{ value: "leaf", children: [] }],
4928
+ * });
4034
4929
  *
4035
- * const Right: LazyType<Right, Right, never, RightError, RightError> =
4036
- * lazy(() => object({ count: Number, left: optional(Left) }));
4930
+ * expectOk(result, {
4931
+ * value: "root",
4932
+ * children: [{ value: "leaf", children: [] }],
4933
+ * });
4934
+ * expectTypeOf(result.value).toEqualTypeOf<Tree>();
4037
4935
  * ```
4038
4936
  *
4039
- * @group Recursive Types
4937
+ * @group Recursive
4040
4938
  */
4041
4939
  export declare function lazy<Target extends ConcreteTypeNode>(getType: Thunk<ValidateLazyTarget<Target>>): LazyType<Target["Input"], Target["Output"], TypeFromError<Target>, InferErrors<RootType<Target>>, InferErrors<Target>, CanonicalInputOf<Target>, IdentityEncodingOf<Target>>;
4042
4940
  /**
@@ -4054,7 +4952,7 @@ export declare function lazy<Target extends ConcreteTypeNode>(getType: Thunk<Val
4054
4952
  * recursive declaration finite for TypeScript while preserving structured
4055
4953
  * errors at every boundary.
4056
4954
  *
4057
- * @group Recursive Types
4955
+ * @group Recursive
4058
4956
  */
4059
4957
  export interface LazyType<in out Input, in out Output, in out FromError extends TypeError, in out InputError extends TypeError, in out Errors extends TypeError, in out CanonicalInput extends Input = Input, in out UsesIdentityEncoding extends boolean = true> extends Type<"Lazy", Input, Output, FromError, Type<"Lazy", Input, Input, InputError, null, InputError, never, Input>, Errors, never, CanonicalInput, UsesIdentityEncoding> {
4060
4958
  readonly [lazyTypeSymbol]: true;
@@ -4110,7 +5008,7 @@ export interface JsonObject {
4110
5008
  readonly [key: string]: JsonValue;
4111
5009
  }
4112
5010
  /**
4113
- * One issue found while validating an exact {@link JsonValue}.
5011
+ * One issue found while validating a candidate as an exact {@link JsonValue}.
4114
5012
  *
4115
5013
  * @group JSON
4116
5014
  */
@@ -4148,7 +5046,8 @@ export type JsonValueIssue = {
4148
5046
  readonly ancestorPath: ReadonlyArray<string | number | symbol>;
4149
5047
  };
4150
5048
  /**
4151
- * An error containing one or more issues found in a {@link JsonValue}.
5049
+ * An error containing one or more issues found while validating a candidate as
5050
+ * an exact {@link JsonValue}.
4152
5051
  *
4153
5052
  * @group JSON
4154
5053
  */
@@ -4186,6 +5085,21 @@ export interface JsonError extends TypeError<"Json"> {
4186
5085
  /**
4187
5086
  * Exact root Type for {@link JsonValue} data trees.
4188
5087
  *
5088
+ * ### Example
5089
+ *
5090
+ * ```ts
5091
+ * import { JsonValue, type JsonValueInput } from "@evolu/common";
5092
+ *
5093
+ * const input: JsonValueInput = {
5094
+ * name: "Ada",
5095
+ * scores: [10, 20],
5096
+ * };
5097
+ * const result = JsonValue.fromUnknown(input);
5098
+ *
5099
+ * expectOk(result, input);
5100
+ * expectTypeOf(result.value).toEqualTypeOf<JsonValue>();
5101
+ * ```
5102
+ *
4189
5103
  * @group JSON
4190
5104
  */
4191
5105
  export declare const JsonValue: JsonValueType;
@@ -4215,26 +5129,57 @@ export type Json = typeof Json.Output;
4215
5129
  /**
4216
5130
  * Totally parses proven {@link Json} text into an exact {@link JsonValue}.
4217
5131
  *
5132
+ * ### Example
5133
+ *
5134
+ * ```ts
5135
+ * import { Json, jsonToJsonValue } from "@evolu/common";
5136
+ *
5137
+ * const value = jsonToJsonValue(Json.orThrow('{"name":"Ada"}'));
5138
+ *
5139
+ * expect(value).toEqual({ name: "Ada" });
5140
+ * ```
5141
+ *
4218
5142
  * @group JSON
4219
5143
  */
4220
5144
  export declare const jsonToJsonValue: (value: Json) => JsonValue;
4221
5145
  /**
4222
5146
  * Totally encodes an exact {@link JsonValue} as canonical {@link Json} text.
4223
5147
  *
5148
+ * ### Example
5149
+ *
5150
+ * ```ts
5151
+ * import { jsonValueToJson, type JsonValue } from "@evolu/common";
5152
+ *
5153
+ * const value: JsonValue = { name: "Ada" };
5154
+ *
5155
+ * expect(jsonValueToJson(value)).toBe('{"name":"Ada"}');
5156
+ * ```
5157
+ *
4224
5158
  * @group JSON
4225
5159
  */
4226
5160
  export declare const jsonValueToJson: (value: JsonValue) => Json;
4227
5161
  /**
4228
- * {@link Json} to {@link JsonValue} transformation.
5162
+ * Transformation {@link Type} that parses {@link Json} into {@link JsonValue}.
4229
5163
  *
4230
5164
  * Decoding unknown input first validates the Json Brand. Starting from the
4231
5165
  * typed Json parent is infallible. Encoding canonicalizes JSON text.
4232
5166
  *
5167
+ * ### Example
5168
+ *
5169
+ * ```ts
5170
+ * import { JsonValueFromJson } from "@evolu/common";
5171
+ *
5172
+ * const result = JsonValueFromJson.fromUnknown('{ "name": "Ada" }');
5173
+ *
5174
+ * expectOk(result, { name: "Ada" });
5175
+ * expect(JsonValueFromJson.to(result.value)).toBe('{"name":"Ada"}');
5176
+ * ```
5177
+ *
4233
5178
  * @group JSON
4234
5179
  */
4235
5180
  export declare const JsonValueFromJson: TransformType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Json", JsonError>, JsonValueType, "JsonValueFromJson", never, string & Brand<"Json">>;
4236
5181
  /**
4237
- * Creates a branded {@link Json} Type and total conversions for another Type.
5182
+ * Branded {@link Json} Type and total conversions for another Type.
4238
5183
  *
4239
5184
  * Use this factory when a domain value must be stored as JSON text while its
4240
5185
  * exact Type remains visible to TypeScript, such as a JSON column in an Evolu