@evolu/common 6.0.1-preview.3 → 6.0.1-preview.30

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 (173) hide show
  1. package/dist/src/Array.d.ts +58 -5
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +53 -5
  4. package/dist/src/Assert.d.ts +6 -16
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +6 -18
  7. package/dist/src/Brand.d.ts +75 -0
  8. package/dist/src/Brand.d.ts.map +1 -0
  9. package/dist/src/Brand.js +1 -0
  10. package/dist/src/Buffer.d.ts +1 -1
  11. package/dist/src/Buffer.d.ts.map +1 -1
  12. package/dist/src/Buffer.js +8 -7
  13. package/dist/src/Cache.d.ts +44 -0
  14. package/dist/src/Cache.d.ts.map +1 -0
  15. package/dist/src/Cache.js +52 -0
  16. package/dist/src/Callbacks.d.ts +45 -12
  17. package/dist/src/Callbacks.d.ts.map +1 -1
  18. package/dist/src/Callbacks.js +14 -7
  19. package/dist/src/Console.d.ts +31 -6
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +72 -9
  22. package/dist/src/Crypto.d.ts +61 -34
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +32 -45
  25. package/dist/src/Evolu/Db.d.ts +158 -65
  26. package/dist/src/Evolu/Db.d.ts.map +1 -1
  27. package/dist/src/Evolu/Db.js +286 -694
  28. package/dist/src/Evolu/Diff.d.ts +3 -3
  29. package/dist/src/Evolu/Diff.d.ts.map +1 -1
  30. package/dist/src/Evolu/Diff.js +7 -5
  31. package/dist/src/Evolu/Evolu.d.ts +208 -133
  32. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  33. package/dist/src/Evolu/Evolu.js +188 -183
  34. package/dist/src/Evolu/Internal.d.ts +0 -2
  35. package/dist/src/Evolu/Internal.d.ts.map +1 -1
  36. package/dist/src/Evolu/Internal.js +0 -2
  37. package/dist/src/Evolu/LocalAuth.d.ts +150 -0
  38. package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
  39. package/dist/src/Evolu/LocalAuth.js +174 -0
  40. package/dist/src/Evolu/Owner.d.ts +264 -120
  41. package/dist/src/Evolu/Owner.d.ts.map +1 -1
  42. package/dist/src/Evolu/Owner.js +130 -104
  43. package/dist/src/Evolu/Platform.d.ts +9 -7
  44. package/dist/src/Evolu/Platform.d.ts.map +1 -1
  45. package/dist/src/Evolu/Protocol.d.ts +277 -232
  46. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  47. package/dist/src/Evolu/Protocol.js +603 -378
  48. package/dist/src/Evolu/Public.d.ts +6 -8
  49. package/dist/src/Evolu/Public.d.ts.map +1 -1
  50. package/dist/src/Evolu/Public.js +2 -3
  51. package/dist/src/Evolu/PublicKysely.js +3 -3
  52. package/dist/src/Evolu/Query.d.ts +2 -1
  53. package/dist/src/Evolu/Query.d.ts.map +1 -1
  54. package/dist/src/Evolu/Relay.d.ts +92 -7
  55. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  56. package/dist/src/Evolu/Relay.js +243 -76
  57. package/dist/src/Evolu/Schema.d.ts +129 -73
  58. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  59. package/dist/src/Evolu/Schema.js +169 -89
  60. package/dist/src/Evolu/Storage.d.ts +212 -26
  61. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  62. package/dist/src/Evolu/Storage.js +137 -79
  63. package/dist/src/Evolu/Sync.d.ts +68 -13
  64. package/dist/src/Evolu/Sync.d.ts.map +1 -1
  65. package/dist/src/Evolu/Sync.js +422 -20
  66. package/dist/src/Evolu/Timestamp.d.ts +85 -27
  67. package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
  68. package/dist/src/Evolu/Timestamp.js +77 -18
  69. package/dist/src/Identicon.d.ts +35 -0
  70. package/dist/src/Identicon.d.ts.map +1 -0
  71. package/dist/src/Identicon.js +143 -0
  72. package/dist/src/Instances.d.ts +34 -0
  73. package/dist/src/Instances.d.ts.map +1 -0
  74. package/dist/src/Instances.js +44 -0
  75. package/dist/src/ManyToManyMap.d.ts +71 -10
  76. package/dist/src/ManyToManyMap.d.ts.map +1 -1
  77. package/dist/src/ManyToManyMap.js +41 -6
  78. package/dist/src/Number.d.ts +4 -3
  79. package/dist/src/Number.d.ts.map +1 -1
  80. package/dist/src/Number.js +5 -4
  81. package/dist/src/Platform.d.ts +20 -0
  82. package/dist/src/Platform.d.ts.map +1 -0
  83. package/dist/src/Platform.js +22 -0
  84. package/dist/src/Random.d.ts +3 -2
  85. package/dist/src/Random.d.ts.map +1 -1
  86. package/dist/src/Resources.d.ts +118 -0
  87. package/dist/src/Resources.d.ts.map +1 -0
  88. package/dist/src/Resources.js +197 -0
  89. package/dist/src/Result.d.ts +184 -52
  90. package/dist/src/Result.d.ts.map +1 -1
  91. package/dist/src/Result.js +30 -241
  92. package/dist/src/Skiplist.js +2 -1
  93. package/dist/src/Sqlite.d.ts +63 -5
  94. package/dist/src/Sqlite.d.ts.map +1 -1
  95. package/dist/src/Sqlite.js +110 -9
  96. package/dist/src/Task.d.ts +586 -0
  97. package/dist/src/Task.d.ts.map +1 -0
  98. package/dist/src/Task.js +469 -0
  99. package/dist/src/Time.d.ts +66 -1
  100. package/dist/src/Time.d.ts.map +1 -1
  101. package/dist/src/Time.js +99 -5
  102. package/dist/src/Type.d.ts +621 -340
  103. package/dist/src/Type.d.ts.map +1 -1
  104. package/dist/src/Type.js +665 -464
  105. package/dist/src/Types.d.ts +1 -75
  106. package/dist/src/Types.d.ts.map +1 -1
  107. package/dist/src/WebSocket.d.ts +5 -2
  108. package/dist/src/WebSocket.d.ts.map +1 -1
  109. package/dist/src/WebSocket.js +12 -18
  110. package/dist/src/Worker.d.ts +39 -11
  111. package/dist/src/Worker.d.ts.map +1 -1
  112. package/dist/src/Worker.js +22 -4
  113. package/dist/src/index.d.ts +7 -2
  114. package/dist/src/index.d.ts.map +1 -1
  115. package/dist/src/index.js +7 -2
  116. package/package.json +14 -13
  117. package/src/Array.ts +76 -11
  118. package/src/Assert.ts +6 -24
  119. package/src/Brand.ts +75 -0
  120. package/src/Buffer.ts +7 -7
  121. package/src/Cache.ts +85 -0
  122. package/src/Callbacks.ts +62 -22
  123. package/src/Console.ts +91 -11
  124. package/src/Crypto.ts +97 -82
  125. package/src/Evolu/Db.ts +514 -1020
  126. package/src/Evolu/Diff.ts +7 -5
  127. package/src/Evolu/Evolu.ts +464 -355
  128. package/src/Evolu/Internal.ts +0 -2
  129. package/src/Evolu/LocalAuth.ts +463 -0
  130. package/src/Evolu/Owner.ts +369 -228
  131. package/src/Evolu/Platform.ts +9 -9
  132. package/src/Evolu/Protocol.ts +859 -676
  133. package/src/Evolu/Public.ts +7 -14
  134. package/src/Evolu/PublicKysely.ts +3 -3
  135. package/src/Evolu/Query.ts +2 -1
  136. package/src/Evolu/Relay.ts +420 -92
  137. package/src/Evolu/Schema.ts +391 -191
  138. package/src/Evolu/Storage.ts +451 -118
  139. package/src/Evolu/Sync.ts +720 -37
  140. package/src/Evolu/Timestamp.ts +88 -35
  141. package/src/Identicon.ts +197 -0
  142. package/src/Instances.ts +90 -0
  143. package/src/ManyToManyMap.ts +124 -24
  144. package/src/Number.ts +6 -10
  145. package/src/Platform.ts +26 -0
  146. package/src/Random.ts +3 -2
  147. package/src/Resources.ts +367 -0
  148. package/src/Result.ts +191 -54
  149. package/src/Skiplist.ts +1 -1
  150. package/src/Sqlite.ts +122 -17
  151. package/src/Task.ts +901 -0
  152. package/src/Time.ts +180 -5
  153. package/src/Type.ts +1083 -727
  154. package/src/Types.ts +1 -77
  155. package/src/WebSocket.ts +27 -25
  156. package/src/Worker.ts +72 -23
  157. package/src/index.ts +7 -2
  158. package/dist/src/Evolu/Config.d.ts +0 -69
  159. package/dist/src/Evolu/Config.d.ts.map +0 -1
  160. package/dist/src/Evolu/Config.js +0 -9
  161. package/dist/src/Evolu/Kysely.d.ts +0 -6
  162. package/dist/src/Evolu/Kysely.d.ts.map +0 -1
  163. package/dist/src/Evolu/Kysely.js +0 -21
  164. package/dist/src/NanoId.d.ts +0 -27
  165. package/dist/src/NanoId.d.ts.map +0 -1
  166. package/dist/src/NanoId.js +0 -6
  167. package/dist/src/Promise.d.ts +0 -180
  168. package/dist/src/Promise.d.ts.map +0 -1
  169. package/dist/src/Promise.js +0 -176
  170. package/src/Evolu/Config.ts +0 -83
  171. package/src/Evolu/Kysely.ts +0 -38
  172. package/src/NanoId.ts +0 -39
  173. package/src/Promise.ts +0 -295
@@ -1,77 +1,182 @@
1
1
  /**
2
- * 🧩 Validation, Parsing, and Transformation
2
+ * 🧩 Type-safe runtime types
3
3
  *
4
- * ## Intro
4
+ * Evolu {@link Type} is like a type guard that returns typed errors (via
5
+ * {@link Result}) instead of throwing. We either get a safely typed value or a
6
+ * precise, composable error value telling us exactly why validation failed.
5
7
  *
6
- * You probably know [Zod](https://zod.dev). Evolu has {@link Type}.
8
+ * Evolu Type supports [Standard Schema](https://standardschema.dev/) for
9
+ * interoperability with 40+ validation-compatible tools and frameworks.
7
10
  *
8
- * Evolu Type exists because no existing validation/parsing/transformation
9
- * library fully met our needs:
11
+ * Why another validation library?
10
12
  *
11
- * - **Result-based error handling**: Leveraging {@link Result} instead of throwing
12
- * exceptions.
13
- * - **Consistent constraints**: Enforcing {@link Brand} for all constraints.
14
- * - **Typed errors with decoupled formatters**: Avoiding coupling error messages
15
- * with validators.
16
- * - **No user-land chaining**: Designed with ES pipe operator in mind.
17
- * - **Selective validation/transformation**: Skipping parent Type validations and
18
- * transformations when TypeScript's type system can be relied upon.
19
- * - **Bidirectional transformations**: Supporting transformations in both
20
- * directions.
21
- * - **Minimal and transparent code**: No runtime dependencies or hidden magic.
13
+ * - **Result-based error handling** no exceptions for normal control flow.
14
+ * - **Typed errors with decoupled formatters** – validation logic ≠ user
15
+ * messages.
16
+ * - **Consistent constraints via {@link Brand}** every constraint becomes part
17
+ * of the type.
18
+ * - **No user-land chaining DSL** – designed with the upcoming ES pipe operator
19
+ * in mind.
20
+ * - **Selective validation** parent validations are skipped when already proved
21
+ * by typing.
22
+ * - **Simple, top-down implementation** – readable source code from top to bottom
23
+ * with no hidden magic; just plain functions and composition.
22
24
  *
23
- * **Note**: A proper quickstart guide is on the way. In the meantime, each type
24
- * includes its own usage example, and you can (and should) check the tests for
25
- * practical demonstrations of the API. Or dang, just read the code. It's
26
- * simple.
25
+ * ### Base Types Quick Start
27
26
  *
28
- * - Evolu `Type` is:
27
+ * ```ts
28
+ * // Validate unknown values
29
+ * const value: unknown = "hello";
30
+ * const stringResult = String.fromUnknown(value);
31
+ * if (!stringResult.ok) {
32
+ * // console.error(formatStringError(stringResult.error));
33
+ * return stringResult; // inside a function returning Result<string, _>
34
+ * }
35
+ * // Safe branch: value is now string
36
+ * const upper = stringResult.value.toUpperCase();
37
+ *
38
+ * // Type guard style
39
+ * if (String.is(value)) {
40
+ * // narrowed to string
41
+ * }
42
+ *
43
+ * // Composing: arrays & objects
44
+ * const Numbers = array(Number); // ReadonlyArray<number>
45
+ * const Point = object({ x: Number, y: Number });
46
+ *
47
+ * Numbers.from([1, 2, 3]); // ok
48
+ * Point.from({ x: 1, y: 2 }); // ok
49
+ * Point.from({ x: 1, y: "2" }); // err -> nested Number error
50
+ * ```
51
+ *
52
+ * ### Branding Basics
53
+ *
54
+ * Branding adds semantic meaning & constraints while preserving the runtime
55
+ * shape:
56
+ *
57
+ * ```ts
58
+ * const CurrencyCode = brand("CurrencyCode", String, (value) =>
59
+ * /^[A-Z]{3}$/.test(value)
60
+ * ? ok(value)
61
+ * : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
62
+ * );
63
+ * type CurrencyCode = typeof CurrencyCode.Type; // string & Brand<"CurrencyCode">
64
+ *
65
+ * interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
66
+ *
67
+ * const formatCurrencyCodeError =
68
+ * createTypeErrorFormatter<CurrencyCodeError>(
69
+ * (error) => `Invalid currency code: ${error.value}`,
70
+ * );
29
71
  *
30
- * - A TypeScript type with a {@link Brand} whenever it's possible.
31
- * - A function to create a value of that type, which may fail.
32
- * - A function to transform value back to its original representation, which
33
- * cannot fail.
72
+ * const r = CurrencyCode.from("USD"); // ok("USD")
73
+ * const e = CurrencyCode.from("usd"); // err(...)
74
+ * ```
34
75
  *
35
- * Types are chainable. The chain starts with a Base Type that refines an
36
- * unknown value into something and can continue with further refinements or
37
- * transformations. For example, `NonEmptyTrimmedString100` chain looks like
38
- * this:
76
+ * See also reusable brand factories like `minLength`, `maxLength`, `trimmed`,
77
+ * `positive`, `between`, etc.
39
78
  *
40
- * `Unknown` -> `String` -> `TrimmedString` -> `NonEmptyTrimmedString100`
79
+ * ### Objects & Optional Fields
41
80
  *
42
- * For `NonEmptyTrimmedString100`, the parent Type is `TrimmedString`. For
43
- * `TrimmedString`, the parent Type is `String`.
81
+ * ```ts
82
+ * const User = object({
83
+ * name: NonEmptyTrimmedString100,
84
+ * age: optional(PositiveInt),
85
+ * });
86
+ * type User = typeof User.Type;
44
87
  *
45
- * The parent of the `String` Type is the `String` Type itself. All Base Types
46
- * `fromParent` functions are just a typed alias to `fromUnknown` to ensure that
47
- * `fromParent` and `toParent` can be called on any Type.
88
+ * User.from({ name: "Alice" }); // ok
89
+ * User.from({ name: "Alice", age: -1 }); // err(PositiveInt)
90
+ * ```
48
91
  *
49
- * Speaking of `fromParent` and `toParent`, those functions exist to bypass
50
- * parent Types when we can rely on TypeScript types.
92
+ * ### Deriving JSON String Types
51
93
  *
52
- * `Type` transformations should be reversible. If you need an irreversible
53
- * transformation, such as `TrimString` (trimming is not reversible as `untrim`
54
- * can't know what has been trimmed), you can do that, but note in JSDoc that
55
- * `to` will not restore the original representation. You can also use
56
- * {@link assert}: `assert(false, "Untrim is not possible")`.
94
+ * ```ts
95
+ * const Person = object({
96
+ * name: NonEmptyString50,
97
+ * // Did you know that JSON.stringify converts NaN (a number) into null?
98
+ * // To prevent this, use FiniteNumber.
99
+ * age: FiniteNumber,
100
+ * });
101
+ * type Person = typeof Person.Type;
102
+ *
103
+ * const [PersonJson, personToPersonJson, personJsonToPerson] = json(
104
+ * Person,
105
+ * "PersonJson",
106
+ * );
107
+ * // string & Brand<"PersonJson">
108
+ * type PersonJson = typeof PersonJson.Type;
109
+ *
110
+ * const person = Person.orThrow({
111
+ * name: "Alice",
112
+ * age: 30,
113
+ * });
114
+ *
115
+ * const personJson = personToPersonJson(person);
116
+ * expect(personJsonToPerson(personJson)).toEqual(person);
117
+ * ```
118
+ *
119
+ * ### Error Formatting
120
+ *
121
+ * Evolu separates validation logic from human-readable messages. There are two
122
+ * layers:
123
+ *
124
+ * 1. Per-type formatters (e.g. `formatStringError`) – simple, focused, already
125
+ * used earlier in the quick start example.
126
+ * 2. A unified formatter via `createFormatTypeError` – composes all built-in and
127
+ * custom errors (including nested composite types) and lets us override
128
+ * selected messages.
129
+ *
130
+ * #### 1. Per-Type Formatter (recap)
131
+ *
132
+ * ```ts
133
+ * const r = String.fromUnknown(42);
134
+ * if (!r.ok) console.error(formatStringError(r.error));
135
+ * ```
136
+ *
137
+ * #### 2. Unified Formatter with Overrides
138
+ *
139
+ * ```ts
140
+ * // Override only what we care about; fall back to built-ins for the rest.
141
+ * const formatTypeError = createFormatTypeError((error) => {
142
+ * if (error.type === "MinLength") return `Min length is ${error.min}`;
143
+ * });
144
+ *
145
+ * const User = object({ name: NonEmptyTrimmedString100 });
146
+ * const resultUser = User.from({ name: "" });
147
+ * if (!resultUser.ok) console.error(formatTypeError(resultUser.error));
148
+ *
149
+ * const badPoint = object({ x: Number, y: Number }).from({
150
+ * x: 1,
151
+ * y: "foo",
152
+ * });
153
+ * if (!badPoint.ok) console.error(formatTypeError(badPoint.error));
154
+ * ```
155
+ *
156
+ * The unified formatter walks nested structures (object / array / record /
157
+ * tuple / union) and applies overrides only where specified, greatly reducing
158
+ * boilerplate when formatting complex validation errors.
57
159
  *
58
160
  * ### Tip
59
161
  *
60
162
  * If necessary, write `globalThis.String` instead of `String` to avoid naming
61
- * clashes with Base Types.
163
+ * clashes with native types.
62
164
  *
63
- * ### Design Decision:
165
+ * ### Design Decision: No Bidirectional Transformations
64
166
  *
65
- * While the `from` function can fail, the `to` function cannot. This simplifies
66
- * the model by ensuring that every valid input has a corresponding valid
67
- * output, eliminating the risk of edge cases caused by irreversible
68
- * operations.
167
+ * Evolu Type intentionally does not support bidirectional transformations. It
168
+ * previously did, but supporting that while keeping typed error fidelity added
169
+ * complexity that hurt readability & reliability. Most persistence pipelines
170
+ * (e.g. SQLite) already require explicit mapping of query results, so implicit
171
+ * reverse transforms would not buy much. We may revisit this if we can design a
172
+ * minimal, 100% safe API that preserves simplicity.
69
173
  *
70
174
  * @module
71
175
  */
72
- import { NanoIdLibDep } from "./NanoId.js";
73
- import { Ok, Result } from "./Result.js";
74
- import type { Brand, Literal, Simplify, WidenLiteral } from "./Types.js";
176
+ import type { Brand } from "./Brand.js";
177
+ import { type RandomBytesDep } from "./Crypto.js";
178
+ import { Result } from "./Result.js";
179
+ import type { Literal, Simplify, WidenLiteral } from "./Types.js";
75
180
  export interface Type<Name extends TypeName,
76
181
  /** The type this Type resolves to. */
77
182
  T,
@@ -82,7 +187,7 @@ Error extends TypeError = never,
82
187
  /** The parent type. */
83
188
  Parent = T,
84
189
  /** The parent's error. */
85
- ParentError extends TypeError = Error> {
190
+ ParentError extends TypeError = Error> extends StandardSchemaV1<Input, T> {
86
191
  readonly name: Name;
87
192
  /**
88
193
  * Creates `T` from an `Input` value.
@@ -93,38 +198,95 @@ ParentError extends TypeError = Error> {
93
198
  */
94
199
  readonly from: (value: Input) => Result<T, ParentError | Error>;
95
200
  /**
96
- * Creates `T` from an unknown value.
201
+ * Creates `T` from an `Input` value, throwing an error if validation fails.
97
202
  *
98
- * This is useful when a value is unknown.
99
- */
100
- readonly fromUnknown: (value: unknown) => Result<T, ParentError | Error>;
101
- /**
102
- * The opposite of `from` and `fromUnknown`.
203
+ * Throws an Error with the Type validation error in its `cause` property,
204
+ * making it debuggable while avoiding the need for custom error messages.
205
+ *
206
+ * This is a convenience method that combines `from` with `getOrThrow`.
207
+ *
208
+ * **When to use:**
209
+ *
210
+ * - Configuration values that are guaranteed to be valid (e.g., hardcoded
211
+ * constants)
212
+ * - Application startup where failure should crash the program
213
+ * - As an alternative to assertions when the Type error in the thrown Error's
214
+ * `cause` provides sufficient debugging information
215
+ * - Test code with known valid inputs (when error message clarity is not
216
+ * critical; for better test error messages, use Vitest `schemaMatching` +
217
+ * `assert` with `.is()`)
218
+ *
219
+ * ### Example
220
+ *
221
+ * ```ts
222
+ * // ✅ Good: Known valid constant
223
+ * const maxRetries = PositiveInt.orThrow(3);
224
+ *
225
+ * // ✅ Good: App configuration that should crash on invalid values
226
+ * const appName = SimpleName.orThrow("MyApp");
227
+ *
228
+ * // ✅ Good: Instead of assert when Type error is clear enough
229
+ * // Context makes it obvious: count increments from non-negative value
230
+ * const currentCount = counts.get(id) ?? 0;
231
+ * const newCount = PositiveInt.orThrow(currentCount + 1);
103
232
  *
104
- * This is useful to transform `T` back to its `Input` representation.
233
+ * // Good: Test setup with known valid values
234
+ * const testUser = User.orThrow({ name: "Alice", age: 30 });
105
235
  *
106
- * For `refine`, it only removes the brand. For `transform`, it changes value.
236
+ * // Avoid: User input (use `from` instead)
237
+ * const userAge = PositiveInt.orThrow(userInput); // Could crash!
238
+ *
239
+ * // ✅ Better: Handle user input gracefully
240
+ * const ageResult = PositiveInt.from(userInput);
241
+ * if (!ageResult.ok) {
242
+ * // Handle validation error
243
+ * }
244
+ * ```
107
245
  */
108
- readonly to: (value: T) => Input;
246
+ readonly orThrow: (value: Input) => T;
109
247
  /**
110
- * Creates `T` from `Parent` type.
248
+ * Creates `T` from an `Input` value, returning `null` if validation fails.
249
+ *
250
+ * This is a convenience method that combines `from` with `getOrNull`.
111
251
  *
112
- * This function skips parent Types validations/transformations when we have
113
- * already partially validated/transformed value.
252
+ * **When to use:**
114
253
  *
115
- * For example, `TrimString.from` checks whether a value is a string and trims
116
- * it. If we only want to trim a string, we can use `fromParent`.
254
+ * - When you need to convert a validation result to a nullable value
255
+ * - When the error is not important and you just want the value or nothing
117
256
  *
118
257
  * ### Example
119
258
  *
120
259
  * ```ts
121
- * // string & Brand<"Trimmed">
122
- * const value = TrimString.fromParent("a ").value; // as efficient as foo.trim()
260
+ * // Good: Optional user input
261
+ * const age = PositiveInt.orNull(userInput);
262
+ * if (age != null) {
263
+ * console.log("Valid age:", age);
264
+ * }
265
+ *
266
+ * // ✅ Good: Default fallback
267
+ * const maxRetries = PositiveInt.orNull(config.retries) ?? 3;
268
+ *
269
+ * // ❌ Avoid: When you need to know why validation failed (use `from` instead)
270
+ * const result = PositiveInt.from(userInput);
271
+ * if (!result.ok) {
272
+ * console.error(formatPositiveError(result.error));
273
+ * }
123
274
  * ```
124
275
  */
276
+ readonly orNull: (value: Input) => T | null;
277
+ /**
278
+ * Creates `T` from an unknown value.
279
+ *
280
+ * This is useful when a value is unknown.
281
+ */
282
+ readonly fromUnknown: (value: unknown) => Result<T, ParentError | Error>;
283
+ /**
284
+ * Creates `T` from `Parent` type.
285
+ *
286
+ * This function skips parent Types validations when we have already partially
287
+ * validated value.
288
+ */
125
289
  readonly fromParent: (value: Parent) => Result<T, Error>;
126
- /** The opposite of `fromParent`. */
127
- readonly toParent: (value: T) => Parent;
128
290
  /**
129
291
  * A **type guard** that checks whether an unknown value satisfies the
130
292
  * {@link Type}.
@@ -197,8 +359,6 @@ ParentError extends TypeError = Error> {
197
359
  */
198
360
  readonly ParentError: ParentError;
199
361
  /**
200
- * Error | ParentError
201
- *
202
362
  * ### Example
203
363
  *
204
364
  * ```ts
@@ -230,12 +390,47 @@ export interface TypeErrorWithReason<Name extends TypeName = TypeName, Reason ex
230
390
  readonly reason: Reason;
231
391
  }
232
392
  export type AnyType = Type<any, any, any, any, any, any>;
393
+ /**
394
+ * Extracts the name from a {@link Type}.
395
+ *
396
+ * @category Utilities
397
+ */
233
398
  export type InferName<A extends AnyType> = A extends Type<infer Name, any, any, any, any, any> ? Name : never;
399
+ /**
400
+ * Extracts the type from a {@link Type}.
401
+ *
402
+ * @category Utilities
403
+ */
234
404
  export type InferType<A extends AnyType> = A extends Type<any, infer T, any, any, any, any> ? T : never;
405
+ /**
406
+ * Extracts the input type from a {@link Type}.
407
+ *
408
+ * @category Utilities
409
+ */
235
410
  export type InferInput<A extends AnyType> = A extends Type<any, any, infer Input, any, any, any> ? Input : never;
411
+ /**
412
+ * Extracts the specific error type from a {@link Type}.
413
+ *
414
+ * @category Utilities
415
+ */
236
416
  export type InferError<A extends AnyType> = A extends Type<any, any, any, infer Error, any, any> ? Error : never;
417
+ /**
418
+ * Extracts the parent type from a {@link Type}.
419
+ *
420
+ * @category Utilities
421
+ */
237
422
  export type InferParent<A extends AnyType> = A extends Type<any, any, any, any, infer Parent, any> ? Parent : never;
423
+ /**
424
+ * Extracts the parent error type from a {@link Type}.
425
+ *
426
+ * @category Utilities
427
+ */
238
428
  export type InferParentError<A extends AnyType> = A extends Type<any, any, any, any, any, infer ParentError> ? ParentError : never;
429
+ /**
430
+ * Extracts all error types from a {@link Type}.
431
+ *
432
+ * @category Utilities
433
+ */
239
434
  export type InferErrors<T extends AnyType> = T extends Type<any, any, any, infer Error, any, infer ParentError> ? Error | ParentError : never;
240
435
  declare const EvoluTypeSymbol: unique symbol;
241
436
  /**
@@ -268,12 +463,6 @@ export type TypeErrorFormatter<Error extends TypeError> = (error: Error) => stri
268
463
  * Base {@link Type}.
269
464
  *
270
465
  * A Base Type validates that a value conforms to a specific TypeScript type.
271
- * Unlike refinements or transformations, Base Types establish the fundamental
272
- * shape of a value before any branding or transformation occurs.
273
- *
274
- * - To **refine** a Base Type further, use the {@link brand} Type Factory.
275
- * - To **transform** a Base Type into a different representation, use the
276
- * {@link transform} Type Factory.
277
466
  *
278
467
  * ### Example
279
468
  *
@@ -410,7 +599,7 @@ export declare const formatIsTypeError: TypeErrorFormatter<EvoluTypeError>;
410
599
  * The `brand` Type Factory takes the name of a new {@link Brand}, a parent Type
411
600
  * to be branded, and the optional `refine` function for additional constraint.
412
601
  *
413
- * If the `refine` function is omited, TODO:
602
+ * The `refine` function can be omitted if we only want to add a brand.
414
603
  *
415
604
  * ### Examples
416
605
  *
@@ -489,7 +678,7 @@ export declare const formatIsTypeError: TypeErrorFormatter<EvoluTypeError>;
489
678
  * confirmPassword: SimplePassword,
490
679
  * });
491
680
  *
492
- * const ValidForm = brand("Valid", Form, (value) => {
681
+ * const ValidForm = brand("ValidForm", Form, (value) => {
493
682
  * if (value.password !== value.confirmPassword)
494
683
  * return err<ValidFormError>({
495
684
  * type: "ValidForm",
@@ -567,17 +756,19 @@ export declare const formatCurrencyCodeError: TypeErrorFormatter<CurrencyCodeErr
567
756
  * ### Example
568
757
  *
569
758
  * ```ts
570
- * const result = DateIsoString.from("2023-01-01T12:00:00.000Z"); // ok
571
- * const error = DateIsoString.from("10000-01-01T00:00:00.000Z"); // err
759
+ * const result = DateIso.from("2023-01-01T12:00:00.000Z"); // ok
760
+ * const error = DateIso.from("10000-01-01T00:00:00.000Z"); // err
572
761
  * ```
573
762
  *
574
763
  * @category String
575
764
  */
576
- export declare const DateIsoString: BrandType<Type<"String", string, string, StringError, string, StringError>, "DateIso", DateIsoStringError, StringError>;
577
- export type DateIsoString = typeof DateIsoString.Type;
578
- export interface DateIsoStringError extends TypeError<"DateIsoString"> {
765
+ export declare const DateIso: BrandType<Type<"String", string, string, StringError, string, StringError>, "DateIso", DateIsoError, StringError>;
766
+ export type DateIso = typeof DateIso.Type;
767
+ export interface DateIsoError extends TypeError<"DateIso"> {
579
768
  }
580
- export declare const formatDateIsoStringError: TypeErrorFormatter<DateIsoStringError>;
769
+ export declare const formatDateIsoError: TypeErrorFormatter<DateIsoError>;
770
+ export declare const dateToDateIso: (value: Date) => Result<DateIso, DateIsoError>;
771
+ export declare const dateIsoToDate: (value: DateIso) => Date;
581
772
  /**
582
773
  * Helper type for Type Factory that creates a branded Type.
583
774
  *
@@ -600,18 +791,12 @@ export type BrandFactory<Name extends TypeName, Input, RefineError extends TypeE
600
791
  /**
601
792
  * Trimmed string.
602
793
  *
603
- * This Type Factory does not transform; it only validates whether a string has
604
- * no leading or trailing whitespaces. To trim a string, use {@link trim} Type
605
- * Factory.
794
+ * This Type Factory validates whether a string has no leading or trailing
795
+ * whitespaces.
606
796
  *
607
- * ### Examples
797
+ * ### Example
608
798
  *
609
799
  * ```ts
610
- * // this Type already exists
611
- * const TrimmedString = trimmed(String);
612
- * type TrimmedString = typeof TrimmedString.Type;
613
- *
614
- * // we can make any branded Type trimmed:
615
800
  * const TrimmedNonEmptyString = trimmed(minLength(1)(String));
616
801
  * // string & Brand<"MinLength1"> & Brand<"Trimmed">
617
802
  * type TrimmedNonEmptyString = typeof TrimmedNonEmptyString.Type;
@@ -623,33 +808,6 @@ export declare const trimmed: BrandFactory<"Trimmed", string, TrimmedError>;
623
808
  export interface TrimmedError extends TypeError<"Trimmed"> {
624
809
  }
625
810
  export declare const formatTrimmedError: TypeErrorFormatter<TrimmedError>;
626
- export type TransformBrandFactory<Name extends TypeName, Input, TransformError extends TypeError = never> = <PName extends TypeName, P extends Input, PInput, PParent, PError extends TypeError = never, PParentError extends TypeError = never>(parent: Type<PName, P, PInput, PError, PParent, PParentError>) => TransformType<Type<PName, P, PInput, PError, PParent, PParentError>, BrandType<Type<PName, P, PInput, PError, PParent, PParentError>, Name, never, PError | PParentError>, TransformError>;
627
- /**
628
- * Trims leading and trailing whitespace from a string.
629
- *
630
- * This Type Factory **transforms** the input string by removing whitespace from
631
- * both ends. For validation only, use {@link trimmed} Type Factory.
632
- *
633
- * ### Example
634
- *
635
- * ```ts
636
- * const TrimString = trim(String);
637
- * expect(TrimString.from("a ")).toEqual(ok("a"));
638
- * expect(TrimString.fromParent("a ").value).toEqual("a");
639
- *
640
- * const TrimNonEmptyString = trim(NonEmptyString);
641
- * expect(TrimNonEmptyString.from("a " as NonEmptyString)).toEqual(ok("a"));
642
- * expect(
643
- * TrimNonEmptyString.fromParent("a " as NonEmptyString).value,
644
- * ).toEqual("a");
645
- * ```
646
- *
647
- * **Note:** This transformation is irreversible. Calling `toParent` will not
648
- * restore the original representation.
649
- *
650
- * @category String
651
- */
652
- export declare const trim: TransformBrandFactory<"Trimmed", string>;
653
811
  /**
654
812
  * Trimmed string
655
813
  *
@@ -660,6 +818,7 @@ export declare const trim: TransformBrandFactory<"Trimmed", string>;
660
818
  */
661
819
  export declare const TrimmedString: BrandType<Type<"String", string, string, StringError, string, StringError>, "Trimmed", TrimmedError, StringError>;
662
820
  export type TrimmedString = typeof TrimmedString.Type;
821
+ export declare const trim: (value: string) => TrimmedString;
663
822
  /**
664
823
  * Minimum length.
665
824
  *
@@ -782,9 +941,9 @@ export interface RegexError<Name extends TypeName = TypeName> extends TypeError<
782
941
  }
783
942
  export declare const formatRegexError: TypeErrorFormatter<RegexError<Capitalize<string>>>;
784
943
  /**
785
- * URL-safe Base64 string.
944
+ * URL-safe string.
786
945
  *
787
- * A `Base64Url` string uses a limited alphabet that is URL-safe:
946
+ * A `UrlSafeString` uses a limited alphabet that is safe for URLs:
788
947
  *
789
948
  * - Uppercase letters (`A-Z`)
790
949
  * - Lowercase letters (`a-z`)
@@ -792,31 +951,47 @@ export declare const formatRegexError: TypeErrorFormatter<RegexError<Capitalize<
792
951
  * - Dash (`-`)
793
952
  * - Underscore (`_`)
794
953
  *
954
+ * This is the same character set used by Base64Url encoding, but this type does
955
+ * not validate that the string is actually Base64Url-encoded data.
956
+ *
795
957
  * ### Example
796
958
  *
797
959
  * ```ts
798
- * const result = Base64Url.from("abc123_-");
960
+ * const result = UrlSafeString.from("abc123_-");
799
961
  * if (result.ok) {
800
- * console.log("Valid Base64Url string:", result.value);
962
+ * console.log("Valid URL-safe string:", result.value);
801
963
  * } else {
802
- * console.error("Invalid Base64Url string:", result.error);
964
+ * console.error("Invalid URL-safe string:", result.error);
803
965
  * }
804
966
  * ```
805
967
  *
806
968
  * @category String
807
969
  */
808
- export declare const Base64Url: BrandType<Type<"String", string, string, StringError, string, StringError>, "Base64Url", RegexError<"Base64Url">, StringError>;
809
- export type Base64Url = typeof Base64Url.Type;
810
- export type Base64UrlError = typeof Base64Url.Error;
970
+ export declare const UrlSafeString: BrandType<Type<"String", string, string, StringError, string, StringError>, "UrlSafeString", RegexError<"UrlSafeString">, StringError>;
971
+ export type UrlSafeString = typeof UrlSafeString.Type;
972
+ export type UrlSafeStringError = typeof UrlSafeString.Error;
811
973
  /**
812
- * Simple alphanumeric string for naming.
974
+ * Base64Url without padding.
813
975
  *
814
- * A `SimpleName` string uses a limited, safe alphabet for naming purposes:
976
+ * Encode with {@link uint8ArrayToBase64Url}, decode with
977
+ * {@link base64UrlToUint8Array}.
815
978
  *
816
- * - Uppercase letters (`A-Z`)
817
- * - Lowercase letters (`a-z`)
818
- * - Digits (`0-9`)
819
- * - Dash (`-`)
979
+ * @category String
980
+ */
981
+ export declare const Base64Url: BrandType<Type<"String", string, string, StringError, string, StringError>, "Base64Url", Base64UrlError, StringError>;
982
+ export type Base64Url = typeof Base64Url.Type;
983
+ export interface Base64UrlError extends TypeError<"Base64Url"> {
984
+ }
985
+ export declare const formatBase64UrlError: TypeErrorFormatter<Base64UrlError>;
986
+ /** Encodes a Uint8Array to a {@link Base64Url} string. */
987
+ export declare const uint8ArrayToBase64Url: (bytes: Uint8Array) => Base64Url;
988
+ /** Decodes a {@link Base64Url} string to a Uint8Array. */
989
+ export declare const base64UrlToUint8Array: (str: Base64Url) => Uint8Array;
990
+ /**
991
+ * Simple alphanumeric string for naming in file systems, URLs, and identifiers.
992
+ *
993
+ * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
994
+ * `_`). See `UrlSafeString` for details.
820
995
  *
821
996
  * The string must be between 1 and 42 characters.
822
997
  *
@@ -833,17 +1008,10 @@ export type Base64UrlError = typeof Base64Url.Error;
833
1008
  *
834
1009
  * @category String
835
1010
  */
836
- export declare const SimpleName: BrandType<Type<"String", string, string, StringError, string, StringError>, "SimpleName", RegexError<"SimpleName">, StringError>;
1011
+ export declare const SimpleName: BrandType<BrandType<Type<"String", string, string, StringError, string, StringError>, "UrlSafeString", RegexError<"UrlSafeString">, StringError>, "SimpleName", SimpleNameError, StringError | RegexError<"UrlSafeString">>;
837
1012
  export type SimpleName = typeof SimpleName.Type;
838
- export type SimpleNameError = typeof SimpleName.Error;
839
- /**
840
- * Default NanoId.
841
- *
842
- * @category String
843
- */
844
- export declare const NanoId: BrandType<Type<"String", string, string, StringError, string, StringError>, "NanoId", RegexError<"NanoId">, StringError>;
845
- export type NanoId = typeof NanoId.Type;
846
- export type NanoIdError = typeof NanoId.Error;
1013
+ export interface SimpleNameError extends TypeError<"SimpleName"> {
1014
+ }
847
1015
  /**
848
1016
  * Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
849
1017
  *
@@ -854,16 +1022,49 @@ export type SimplePassword = typeof SimplePassword.Type;
854
1022
  export type SimplePasswordError = typeof SimplePassword.Error;
855
1023
  export declare const formatSimplePasswordError: (formatTypeError: TypeErrorFormatter<StringError | MinLengthError<8> | MaxLengthError<64> | TrimmedError>) => TypeErrorFormatter<SimplePasswordError>;
856
1024
  /**
857
- * `Id` {@link Type}.
1025
+ * Globally unique identifier.
1026
+ *
1027
+ * **Evolu Id** is 16 random bytes from a cryptographically secure random
1028
+ * generator, encoded as 22-character Base64Url string. This provides strong
1029
+ * collision resistance for distributed ID generation.
1030
+ *
1031
+ * ### Design Rationale
1032
+ *
1033
+ * Why Evolu Id over alternatives:
858
1034
  *
859
- * Represents a unique identifier with exactly 21 characters, using NanoID's
860
- * standard format (`A-Za-z0-9_-`).
1035
+ * - **NanoID**: No standard binary serialization format, and uses only ~126 bits
1036
+ * of entropy (21 characters from 64-symbol alphabet) compared to Evolu Id's
1037
+ * 128 bits.
1038
+ * - **UUID (v4)**: String format is 36 characters (with hyphens) compared to
1039
+ * Evolu Id's 22 characters. While UUIDs can be stored as 16 bytes, their
1040
+ * standard string representation is verbose.
1041
+ * - **UUID v7**: Includes timestamp in the ID, which leaks information about when
1042
+ * data was created. This is a privacy concern for local-first applications
1043
+ * where creation time must remain private.
1044
+ *
1045
+ * Evolu Id provides 128 bits of entropy, compact string representation (22
1046
+ * characters), standard and native string serialization (Base64Url), and no
1047
+ * privacy leaks.
1048
+ *
1049
+ * ### Future Consideration
1050
+ *
1051
+ * For database-heavy workloads where insert performance is critical, a hybrid
1052
+ * approach could be considered: `timestamp ^ H(cluster_id, timestamp >> N)`
1053
+ * where H is a keyed hash function and N is a configurable parameter. This
1054
+ * would maintain spatial locality for database caches (improving insert
1055
+ * performance by an order of magnitude) while adding entropy to prevent
1056
+ * timestamp leakage and correlation across systems. The parameter N would allow
1057
+ * trading off cache locality (larger N = better locality) versus entropy
1058
+ * distribution. See https://brooker.co.za/blog/2025/10/22/uuidv7.html for
1059
+ * details on this approach.
861
1060
  *
862
1061
  * @category String
863
1062
  */
864
- export declare const Id: BrandType<Type<"String", string, string, StringError, string, StringError>, "Id", RegexError<"Id">, StringError>;
1063
+ export declare const Id: BrandType<Type<"String", string, string, StringError, string, StringError>, "Id", IdError, StringError>;
865
1064
  export type Id = typeof Id.Type;
866
- export declare const idTypeValueLength = 21;
1065
+ export interface IdError extends TypeError<"Id"> {
1066
+ }
1067
+ export declare const formatIdError: TypeErrorFormatter<IdError>;
867
1068
  /**
868
1069
  * Creates an {@link Id}.
869
1070
  *
@@ -872,11 +1073,48 @@ export declare const idTypeValueLength = 21;
872
1073
  * ```ts
873
1074
  * // string & Brand<"Id">
874
1075
  * const id = createId(deps);
1076
+ *
1077
+ * // string & Brand<"Id"> & Brand<"Todo">
1078
+ * const todoId = createId<"Todo">(deps);
875
1079
  * ```
876
1080
  */
877
- export declare const createId: (deps: NanoIdLibDep) => Id;
1081
+ export declare const createId: <B extends string = never>(deps: RandomBytesDep) => [B] extends [never] ? Id : Id & Brand<B>;
878
1082
  /**
879
- * Type Factory to create branded {@link Id} Type for a specific table.
1083
+ * Creates an {@link Id} from a string using SHA-256.
1084
+ *
1085
+ * When integrating with external systems that use different ID formats, use
1086
+ * this function to convert external IDs into valid Evolu IDs.
1087
+ *
1088
+ * In Evolu's CRDT, the ID serves as the unique identifier for conflict
1089
+ * resolution across distributed clients. When multiple clients create records
1090
+ * with the same external identifier, they must resolve to the same Evolu ID to
1091
+ * ensure data consistency.
1092
+ *
1093
+ * ### Example
1094
+ *
1095
+ * ```ts
1096
+ * // Both clients will generate the same ID
1097
+ * const id1 = createIdFromString("user-api-123");
1098
+ * const id2 = createIdFromString("user-api-123");
1099
+ * console.log(id1 === id2); // true
1100
+ *
1101
+ * upsert("todo", {
1102
+ * id: createIdFromString("external-todo-456"),
1103
+ * title: "Synced from external system",
1104
+ * });
1105
+ * ```
1106
+ *
1107
+ * **Important**: This transformation is one-way. We cannot recover the original
1108
+ * external string from the generated {@link Id}. If we need to preserve the
1109
+ * original external ID, store it in a separate column.
1110
+ *
1111
+ * @category String
1112
+ */
1113
+ export declare const createIdFromString: <B extends string = never>(value: string) => [B] extends [never] ? Id : Id & Brand<B>;
1114
+ /**
1115
+ * Creates a branded {@link Id} Type for a table's primary key.
1116
+ *
1117
+ * The table name becomes an additional brand for type safety.
880
1118
  *
881
1119
  * ### Example
882
1120
  *
@@ -888,16 +1126,22 @@ export declare const createId: (deps: NanoIdLibDep) => Id;
888
1126
  *
889
1127
  * @category String
890
1128
  */
891
- export declare const id: <Table extends TypeName>(table: Table) => IdType<Table>;
892
- export interface IdType<Table extends TypeName> extends Type<"Id", string & Brand<"Id"> & Brand<Table>, string, IdError<Table>, string, StringError> {
1129
+ export declare const id: <Table extends TypeName>(table: Table) => TableId<Table>;
1130
+ export interface TableId<Table extends TypeName> extends Type<"Id", string & Brand<"Id"> & Brand<Table>, string, TableIdError<Table>, string, StringError> {
893
1131
  table: Table;
894
1132
  }
895
- export interface IdError<Table extends TypeName = TypeName> extends TypeError<"Id"> {
1133
+ export interface TableIdError<Table extends TypeName = TypeName> extends TypeError<"TableId"> {
896
1134
  readonly table: Table;
897
1135
  }
898
- export declare const formatIdError: TypeErrorFormatter<IdError<Capitalize<string>>>;
1136
+ export declare const formatTableIdError: TypeErrorFormatter<TableIdError<Capitalize<string>>>;
1137
+ /** Binary representation of an {@link Id}. */
1138
+ export declare const IdBytes: BrandType<BrandType<Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, Uint8ArrayError, Uint8Array<ArrayBufferLike>, Uint8ArrayError>, "Length16", LengthError<16>, Uint8ArrayError>, "IdBytes", BrandWithoutRefineError<"IdBytes", LengthError<16> | Uint8ArrayError>, never>;
1139
+ export type IdBytes = typeof IdBytes.Type;
1140
+ export declare const idBytesTypeValueLength: NonNegativeInt;
1141
+ export declare const idToIdBytes: (id: Id) => IdBytes;
1142
+ export declare const idBytesToId: (idBytes: IdBytes) => Id;
899
1143
  /**
900
- * Positive number.
1144
+ * Positive number (> 0).
901
1145
  *
902
1146
  * ### Example
903
1147
  *
@@ -915,7 +1159,7 @@ export interface PositiveError extends TypeError<"Positive"> {
915
1159
  }
916
1160
  export declare const formatPositiveError: TypeErrorFormatter<PositiveError>;
917
1161
  /**
918
- * Negative number.
1162
+ * Negative number (< 0).
919
1163
  *
920
1164
  * ### Example
921
1165
  *
@@ -930,7 +1174,7 @@ export interface NegativeError extends TypeError<"Negative"> {
930
1174
  }
931
1175
  export declare const formatNegativeError: TypeErrorFormatter<NegativeError>;
932
1176
  /**
933
- * Non-positive number.
1177
+ * Non-positive number (≤ 0).
934
1178
  *
935
1179
  * ### Example
936
1180
  *
@@ -945,7 +1189,7 @@ export interface NonPositiveError extends TypeError<"NonPositive"> {
945
1189
  }
946
1190
  export declare const formatNonPositiveError: TypeErrorFormatter<NonPositiveError>;
947
1191
  /**
948
- * Non-negative number.
1192
+ * Non-negative number (≥ 0).
949
1193
  *
950
1194
  * ### Example
951
1195
  *
@@ -959,16 +1203,32 @@ export declare const nonNegative: BrandFactory<"NonNegative", number, NonNegativ
959
1203
  export interface NonNegativeError extends TypeError<"NonNegative"> {
960
1204
  }
961
1205
  export declare const formatNonNegativeError: TypeErrorFormatter<NonNegativeError>;
962
- /** @category Number */
1206
+ /**
1207
+ * Non-negative number (≥ 0).
1208
+ *
1209
+ * @category Number
1210
+ */
963
1211
  export declare const NonNegativeNumber: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "NonNegative", NonNegativeError, NumberError>;
964
1212
  export type NonNegativeNumber = typeof NonNegativeNumber.Type;
965
- /** @category Number */
966
- export declare const PositiveNumber: BrandType<Type<"Brand", number & Brand<"NonNegative">, number, NonNegativeError, number, NumberError>, "Positive", PositiveError, NumberError | NonNegativeError>;
1213
+ /**
1214
+ * Positive number (> 0).
1215
+ *
1216
+ * @category Number
1217
+ */
1218
+ export declare const PositiveNumber: BrandType<Type<"Brand", number & Brand<"NonNegative">, number, NonNegativeError, number, NumberError>, "Positive", PositiveError, NonNegativeError | NumberError>;
967
1219
  export type PositiveNumber = typeof PositiveNumber.Type;
968
- /** @category Number */
1220
+ /**
1221
+ * Non-positive number (≤ 0).
1222
+ *
1223
+ * @category Number
1224
+ */
969
1225
  export declare const NonPositiveNumber: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "NonPositive", NonPositiveError, NumberError>;
970
1226
  export type NonPositiveNumber = typeof NonPositiveNumber.Type;
971
- /** @category Number */
1227
+ /**
1228
+ * Negative number (< 0).
1229
+ *
1230
+ * @category Number
1231
+ */
972
1232
  export declare const NegativeNumber: BrandType<Type<"Brand", number & Brand<"NonPositive">, number, NonPositiveError, number, NumberError>, "Negative", NegativeError, NumberError | NonPositiveError>;
973
1233
  export type NegativeNumber = typeof NegativeNumber.Type;
974
1234
  /**
@@ -993,17 +1253,35 @@ export declare const formatIntError: TypeErrorFormatter<IntError>;
993
1253
  */
994
1254
  export declare const Int: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Int", IntError, NumberError>;
995
1255
  export type Int = typeof Int.Type;
996
- /** @category Number */
997
- export declare const NonNegativeInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonNegative", NonNegativeError, NumberError | IntError>;
1256
+ /**
1257
+ * Non-negative integer (≥ 0).
1258
+ *
1259
+ * @category Number
1260
+ */
1261
+ export declare const NonNegativeInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonNegative", NonNegativeError, IntError | NumberError>;
998
1262
  export type NonNegativeInt = typeof NonNegativeInt.Type;
999
- /** @category Number */
1000
- export declare const PositiveInt: BrandType<Type<"Brand", number & Brand<"Int"> & Brand<"NonNegative">, number, NonNegativeError, number & Brand<"Int">, NumberError | IntError>, "Positive", PositiveError, NumberError | NonNegativeError | IntError>;
1263
+ /**
1264
+ * Positive integer (> 0).
1265
+ *
1266
+ * @category Number
1267
+ */
1268
+ export declare const PositiveInt: BrandType<Type<"Brand", number & Brand<"Int"> & Brand<"NonNegative">, number, NonNegativeError, number & Brand<"Int">, IntError | NumberError>, "Positive", PositiveError, NonNegativeError | IntError | NumberError>;
1001
1269
  export type PositiveInt = typeof PositiveInt.Type;
1002
- /** @category Number */
1003
- export declare const NonPositiveInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonPositive", NonPositiveError, NumberError | IntError>;
1270
+ /** Maximum safe positive integer value for practically infinite operations. */
1271
+ export declare const maxPositiveInt: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"Positive">;
1272
+ /**
1273
+ * Non-positive integer (≤ 0).
1274
+ *
1275
+ * @category Number
1276
+ */
1277
+ export declare const NonPositiveInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonPositive", NonPositiveError, IntError | NumberError>;
1004
1278
  export type NonPositiveInt = typeof NonPositiveInt.Type;
1005
- /** @category Number */
1006
- export declare const NegativeInt: BrandType<Type<"Brand", number & Brand<"Int"> & Brand<"NonPositive">, number, NonPositiveError, number & Brand<"Int">, NumberError | IntError>, "Negative", NegativeError, NumberError | NonPositiveError | IntError>;
1279
+ /**
1280
+ * Negative integer (< 0).
1281
+ *
1282
+ * @category Number
1283
+ */
1284
+ export declare const NegativeInt: BrandType<Type<"Brand", number & Brand<"Int"> & Brand<"NonPositive">, number, NonPositiveError, number & Brand<"Int">, IntError | NumberError>, "Negative", NegativeError, IntError | NumberError | NonPositiveError>;
1007
1285
  export type NegativeInt = typeof NegativeInt.Type;
1008
1286
  /**
1009
1287
  * Number greater than a specified value.
@@ -1110,9 +1388,6 @@ export interface BetweenError<Min extends number = number, Max extends number =
1110
1388
  readonly max: Max;
1111
1389
  }
1112
1390
  export declare const formatBetweenError: TypeErrorFormatter<BetweenError<number, number>>;
1113
- /** @category Number */
1114
- export declare const Between1And10: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Between1-10", BetweenError<1, 10>, NumberError>;
1115
- export type Between1And10 = typeof Between1And10.Type;
1116
1391
  /**
1117
1392
  * Literal {@link Type}.
1118
1393
  *
@@ -1138,67 +1413,6 @@ export interface LiteralError<T extends Literal = Literal> extends TypeError<"Li
1138
1413
  readonly expected: T;
1139
1414
  }
1140
1415
  export declare const formatLiteralError: TypeErrorFormatter<LiteralError<Literal>>;
1141
- /**
1142
- * {@link Type} that transforms values between `FromType` and `ToType`.
1143
- *
1144
- * - `fromParent`: Converts `FromType` to `ToType`, may fail.
1145
- * - `toParent`: Converts `ToType` back to `FromType`, must not fail.
1146
- *
1147
- * ### Example
1148
- *
1149
- * // TODO: Examples
1150
- *
1151
- * @category Base Factories
1152
- */
1153
- export declare const transform: <FromType extends AnyType, ToType extends AnyType, TransformError extends TypeError = never>(fromType: FromType, toType: ToType, fromParent: (parentValue: InferType<FromType>) => Result<InferType<ToType>, TransformError>, toParent: (value: InferType<ToType>) => InferType<FromType>) => TransformType<FromType, ToType, TransformError>;
1154
- /**
1155
- * TransformType extends {@link Type} with additional `fromType` and `toType`
1156
- * properties for reflection.
1157
- */
1158
- export interface TransformType<FromType extends AnyType, ToType extends AnyType, TransformError extends TypeError = never> extends Type<"Transform", InferType<ToType>, InferInput<FromType>, TransformError, InferType<FromType>, InferErrors<FromType>> {
1159
- readonly fromType: FromType;
1160
- readonly toType: ToType;
1161
- readonly fromParent: (value: InferType<FromType>) => [TransformError] extends [never] ? Ok<InferType<ToType>> : Result<InferType<ToType>, TransformError>;
1162
- }
1163
- /**
1164
- * Trims leading and trailing whitespace from a string.
1165
- *
1166
- * ### Example
1167
- *
1168
- * ```ts
1169
- * expect(TrimString.from("a ")).toEqual(ok("a"));
1170
- * expect(TrimString.fromParent("a ").value).toEqual("a");
1171
- * ```
1172
- *
1173
- * @category String
1174
- */
1175
- export declare const TrimString: TransformType<Type<"String", string, string, StringError, string, StringError>, BrandType<Type<"String", string, string, StringError, string, StringError>, "Trimmed", never, StringError>, never>;
1176
- /**
1177
- * Transforms a {@link Date} into a {@link DateIsoString} string and vice versa.
1178
- *
1179
- * ### Example
1180
- *
1181
- * TODO:
1182
- *
1183
- * @category String
1184
- */
1185
- export declare const DateIso: TransformType<InstanceOfType<DateConstructor>, BrandType<Type<"String", string, string, StringError, string, StringError>, "DateIso", DateIsoStringError, StringError>, DateIsoStringError>;
1186
- /**
1187
- * Transforms a {@link NonEmptyTrimmedString} into a {@link FiniteNumber}.
1188
- *
1189
- * ### Example
1190
- *
1191
- * ```ts
1192
- * NumberFromString.from("42"); // ok(42)
1193
- * NumberFromString.from("abc"); // err({ type: "NumberFromString", value: "abc" })
1194
- * ```
1195
- *
1196
- * @category Number
1197
- */
1198
- export declare const NumberFromString: TransformType<BrandType<Type<"Brand", string & Brand<"Trimmed">, string, TrimmedError, string, StringError>, "MinLength1", MinLengthError<1>, StringError | TrimmedError>, BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Finite", FiniteError, NumberError>, NumberFromStringError>;
1199
- export interface NumberFromStringError extends TypeError<"NumberFromString"> {
1200
- }
1201
- export declare const formatNumberFromStringError: TypeErrorFormatter<NumberFromStringError>;
1202
1416
  /**
1203
1417
  * Array of a specific {@link Type}.
1204
1418
  *
@@ -1656,16 +1870,12 @@ export type Int64 = typeof Int64.Type;
1656
1870
  export interface Int64Error extends TypeError<"Int64"> {
1657
1871
  }
1658
1872
  export declare const formatInt64Error: TypeErrorFormatter<Int64Error>;
1659
- export declare const BigIntFromString: TransformType<Type<"String", string, string, StringError, string, StringError>, Type<"BigInt", bigint, bigint, BigIntError, bigint, BigIntError>, BigIntFromStringError>;
1660
- export interface BigIntFromStringError extends TypeError<"BigIntFromString"> {
1661
- }
1662
- export declare const formatBigIntFromStringError: TypeErrorFormatter<BigIntFromStringError>;
1663
1873
  /**
1664
1874
  * Stringified {@link Int64}.
1665
1875
  *
1666
- * @category Number
1876
+ * @category String
1667
1877
  */
1668
- export declare const Int64String: BrandType<Type<"String", string, string, StringError, string, StringError>, "Int64", Int64StringError, StringError>;
1878
+ export declare const Int64String: BrandType<BrandType<Type<"Brand", string & Brand<"Trimmed">, string, TrimmedError, string, StringError>, "MinLength1", MinLengthError<1>, StringError | TrimmedError>, "Int64", Int64StringError, StringError | TrimmedError | MinLengthError<1>>;
1669
1879
  export type Int64String = typeof Int64String.Type;
1670
1880
  export interface Int64StringError extends TypeError<"Int64String"> {
1671
1881
  }
@@ -1700,42 +1910,63 @@ export declare const JsonArray: ArrayType<RecursiveType<UnionType<[Type<"String"
1700
1910
  * @category Object
1701
1911
  */
1702
1912
  export declare const JsonObject: RecordType<"String", string, string, StringError, string, StringError, RecursiveType<UnionType<[Type<"String", string, string, StringError, string, StringError>, BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Finite", FiniteError, NumberError>, Type<"Boolean", boolean, boolean, BooleanError, boolean, BooleanError>, Type<"Null", null, null, NullError, null, NullError>, ArrayType<Type<"Recursive", JsonValue, JsonValueInput, JsonValueError, JsonValueInput, JsonValueError>>, RecordType<"String", string, string, StringError, string, StringError, Type<"Recursive", JsonValue, JsonValueInput, JsonValueError, JsonValueInput, JsonValueError>>]>>>;
1913
+ export declare const parseJson: (value: string) => Result<JsonValue, JsonError>;
1703
1914
  /**
1704
- * Transform Type that parses a JSON into a {@link JsonValue} and serializes a
1705
- * JsonValue back into a JSON string.
1915
+ * JSON-string {@link Type}.
1706
1916
  *
1707
1917
  * ### Example
1708
1918
  *
1709
1919
  * ```ts
1710
- * JsonValueFromString.from(`{"key":"value"}`); // -> ok({ key: "value" })
1711
- * JsonValueFromString.to({ key: "value" }); // -> '{"key":"value"}'
1920
+ * const result = Json.from('{"key":"value"}'); // ok
1921
+ * const error = Json.from("invalid json"); // err
1712
1922
  * ```
1713
1923
  *
1714
1924
  * @category String
1715
1925
  */
1716
- export declare const JsonValueFromString: TransformType<Type<"String", string, string, StringError, string, StringError>, RecursiveType<UnionType<[Type<"String", string, string, StringError, string, StringError>, BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Finite", FiniteError, NumberError>, Type<"Boolean", boolean, boolean, BooleanError, boolean, BooleanError>, Type<"Null", null, null, NullError, null, NullError>, ArrayType<Type<"Recursive", JsonValue, JsonValueInput, JsonValueError, JsonValueInput, JsonValueError>>, RecordType<"String", string, string, StringError, string, StringError, Type<"Recursive", JsonValue, JsonValueInput, JsonValueError, JsonValueInput, JsonValueError>>]>>, JsonValueFromStringError>;
1717
- export interface JsonValueFromStringError extends TypeError<"JsonValueFromString"> {
1926
+ export declare const Json: BrandType<Type<"String", string, string, StringError, string, StringError>, "Json", JsonError, StringError>;
1927
+ export type Json = typeof Json.Type;
1928
+ export interface JsonError extends TypeError<"Json"> {
1718
1929
  readonly message: string;
1719
1930
  }
1720
- export declare const formatJsonValueFromStringError: TypeErrorFormatter<JsonValueFromStringError>;
1931
+ export declare const formatJsonError: TypeErrorFormatter<JsonError>;
1932
+ export declare const jsonValueToJson: (value: JsonValue) => Json;
1933
+ export declare const jsonToJsonValue: (value: Json) => JsonValue;
1721
1934
  /**
1722
- * JSON-string {@link Type}.
1935
+ * Creates a branded JSON string {@link Type} and type-safe conversion functions
1936
+ * for a given Type.
1937
+ *
1938
+ * This factory creates:
1939
+ *
1940
+ * 1. A branded string Type that validates JSON parsing and structural conformity
1941
+ * 2. A serialization function (Type → branded JSON string)
1942
+ * 3. A parsing function (branded JSON string → Type, skipping validation)
1943
+ *
1944
+ * Optimized for Evolu's SQLite workflow where we store typed JSON strings and
1945
+ * need type-safe conversions without double parsing.
1723
1946
  *
1724
1947
  * ### Example
1725
1948
  *
1726
1949
  * ```ts
1727
- * const result = Json.from('{"key":"value"}'); // -> ok('{"key":"value"}')
1728
- * const error = Json.from("invalid json"); // -> err({ type: "Json", value: "invalid json", message: "Unexpected token i in JSON at position 0" })
1729
- * ```
1950
+ * const Person = object({
1951
+ * name: NonEmptyString100,
1952
+ * age: FiniteNumber,
1953
+ * });
1954
+ * type Person = typeof Person.Type;
1730
1955
  *
1731
- * @category String
1956
+ * const [PersonJson, personToPersonJson, personJsonToPerson] = json(
1957
+ * Person,
1958
+ * "PersonJson",
1959
+ * );
1960
+ * // string & Brand<"PersonJson">
1961
+ * type PersonJson = typeof PersonJson.Type;
1962
+ *
1963
+ * // Usage:
1964
+ * const person: Person = { name: "Alice", age: 30 };
1965
+ * const jsonString = personToPersonJson(person); // PersonJson
1966
+ * const backToPerson = personJsonToPerson(jsonString); // Person
1967
+ * ```
1732
1968
  */
1733
- export declare const Json: BrandType<Type<"String", string, string, StringError, string, StringError>, "Json", JsonError, StringError>;
1734
- export type Json = typeof Json.Type;
1735
- export interface JsonError extends TypeError<"Json"> {
1736
- readonly message: string;
1737
- }
1738
- export declare const formatJsonError: TypeErrorFormatter<JsonError>;
1969
+ export declare const json: <T extends AnyType, Name extends TypeName>(type: T, name: Name) => [BrandType<typeof String, Name, JsonError | InferErrors<T>, StringError>, (value: InferType<T>) => InferType<BrandType<typeof String, Name, JsonError | InferErrors<T>, StringError>>, (value: InferType<BrandType<typeof String, Name, JsonError | InferErrors<T>, StringError>>) => InferType<T>];
1739
1970
  /**
1740
1971
  * Optional {@link Type}.
1741
1972
  *
@@ -1766,7 +1997,7 @@ export declare const isOptionalType: (x: unknown) => x is OptionalType<any>;
1766
1997
  /**
1767
1998
  * Creates a partial object type where all properties are optional.
1768
1999
  *
1769
- * This is useful when you want to validate an object in which none of the keys
2000
+ * This is useful when we want to validate an object in which none of the keys
1770
2001
  * are required, but if they are present they must conform to their
1771
2002
  * corresponding Types.
1772
2003
  *
@@ -1812,36 +2043,18 @@ export type NullTypeInMembers<Members extends [AnyType, ...Array<AnyType>]> = Me
1812
2043
  * @category Object
1813
2044
  */
1814
2045
  export declare function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(objectType: T, ...keys: ReadonlyArray<Keys>): ObjectType<Omit<T["props"], Keys>>;
2046
+ export declare const maxMutationSize = 655360;
1815
2047
  /**
1816
- * Creates a transform Type that serializes a given `Type` into a branded JSON
1817
- * string. The transformation is reversible, ensuring that we can safely parse
1818
- * it back.
1819
- *
1820
- * ### Example
1821
- *
1822
- * ```ts
1823
- * const Person = object({
1824
- * name: NonEmptyString50,
1825
- * age: FiniteNumber,
1826
- * });
1827
- * type Person = typeof Person.Type;
1828
- *
1829
- * const PersonJson = json(Person, "PersonJson");
1830
- * // string & Brand<"PersonJson">
1831
- * type PersonJson = typeof PersonJson.Type;
1832
- *
1833
- * // Person -> string & Brand<"PersonJson">
1834
- * const personJson = PersonJson.from({ name: "Alice", age: 30 });
1835
- * expect(personJson).toEqual(ok('{"name":"Alice","age":30}'));
1836
- *
1837
- * // string & Brand<"PersonJson"> -> Person
1838
- * const person = PersonJson.to(personJson);
1839
- *
1840
- * // serialize/parse any JSON value
1841
- * const AnyJson = json(JsonValue, "AnyJson");
1842
- * ```
2048
+ * Evolu has to limit the maximum mutation size. Otherwise, sync couldn't use
2049
+ * the `maxProtocolMessageRangesSize`. The max size is 640KB in bytes, measured
2050
+ * via MessagePack. Evolu Protocol DbChange will be smaller thanks to various
2051
+ * optimizations.
1843
2052
  */
1844
- export declare const json: <T extends AnyType, Name extends TypeName>(type: T, name: Name) => TransformType<T, BrandType<typeof String, Name, JsonValueFromStringError | T["Errors"], StringError>>;
2053
+ export declare const validMutationSize: <T extends AnyType>(type: T) => BrandType<T, "ValidMutationSize", ValidMutationSizeError, InferErrors<T>>;
2054
+ export interface ValidMutationSizeError extends TypeError<"ValidMutationSize"> {
2055
+ }
2056
+ export declare const formatValidMutationSizeError: TypeErrorFormatter<ValidMutationSizeError>;
2057
+ export type ValidMutationSize<Props extends Record<string, AnyType>> = BrandType<ObjectType<Props>, "ValidMutationSize", ValidMutationSizeError, InferErrors<ObjectType<Props>>>;
1845
2058
  /**
1846
2059
  * Union of all `TypeError`s defined in the `Type.ts` file, including base type
1847
2060
  * errors (e.g., `StringError`, `NumberError`), composite type errors
@@ -1855,72 +2068,76 @@ export declare const json: <T extends AnyType, Name extends TypeName>(type: T, n
1855
2068
  *
1856
2069
  * @category Utilities
1857
2070
  */
1858
- export type TypeErrors<ExtraErrors extends TypeError = never> = StringError | NumberError | BigIntError | BooleanError | UndefinedError | NullError | FunctionError | Uint8ArrayError | InstanceOfError | EvoluTypeError | CurrencyCodeError | DateIsoStringError | TrimmedError | MinLengthError | MaxLengthError | LengthError | MnemonicError | RegexError | NanoIdError | SimplePasswordError | IdError | PositiveError | NegativeError | NonPositiveError | NonNegativeError | IntError | GreaterThanError | LessThanError | GreaterThanOrEqualToError | LessThanOrEqualToError | NonNaNError | FiniteError | MultipleOfError | BetweenError | LiteralError | Int64Error | BigIntFromStringError | Int64StringError | JsonValueFromStringError | JsonError | ExtraErrors | ArrayError<TypeErrors<ExtraErrors>> | RecordError<TypeErrors<ExtraErrors>, TypeErrors<ExtraErrors>> | ObjectError<Record<string, TypeErrors<ExtraErrors>>> | ObjectWithRecordError<Record<string, TypeErrors<ExtraErrors>>, TypeErrors<ExtraErrors>, TypeErrors<ExtraErrors>> | UnionError<TypeErrors<ExtraErrors>> | TupleError<TypeErrors<ExtraErrors>>;
2071
+ export type TypeErrors<ExtraErrors extends TypeError = never> = StringError | NumberError | BigIntError | BooleanError | UndefinedError | NullError | FunctionError | Uint8ArrayError | InstanceOfError | EvoluTypeError | CurrencyCodeError | DateIsoError | TrimmedError | MinLengthError | MaxLengthError | LengthError | MnemonicError | RegexError | SimplePasswordError | IdError | TableIdError | PositiveError | NegativeError | NonPositiveError | NonNegativeError | IntError | GreaterThanError | LessThanError | GreaterThanOrEqualToError | LessThanOrEqualToError | NonNaNError | FiniteError | MultipleOfError | BetweenError | LiteralError | Int64Error | Int64StringError | JsonError | ValidMutationSizeError | ExtraErrors | ArrayError<TypeErrors<ExtraErrors>> | RecordError<TypeErrors<ExtraErrors>, TypeErrors<ExtraErrors>> | ObjectError<Record<string, TypeErrors<ExtraErrors>>> | ObjectWithRecordError<Record<string, TypeErrors<ExtraErrors>>, TypeErrors<ExtraErrors>, TypeErrors<ExtraErrors>> | UnionError<TypeErrors<ExtraErrors>> | TupleError<TypeErrors<ExtraErrors>>;
1859
2072
  /**
1860
- * Creates a unified error formatter that handles both Evolu Type's built-in
1861
- * {@link TypeErrors} and custom errors. It also lets us override the default
1862
- * formatting for specific errors.
2073
+ * Formats Evolu Type errors into user-friendly messages.
1863
2074
  *
1864
- * If you prefer not to reuse any built-in error formatters, you can write your
1865
- * own `formatTypeError` function from scratch.
2075
+ * Evolu Type typed errors ensure every error type must have a formatter.
2076
+ * TypeScript enforces this at compile-time, preventing unhandled validation
2077
+ * errors from reaching users.
1866
2078
  *
1867
- * ### Examples
2079
+ * The `createFormatTypeError` function handles both built-in {@link TypeErrors}
2080
+ * and custom errors, and lets us override default formatting for specific
2081
+ * errors.
2082
+ *
2083
+ * ### Example
1868
2084
  *
1869
2085
  * ```ts
1870
- * const formatError = createFormatTypeError();
1871
- * console.log(formatError({ type: "String", value: 42 }));
1872
- * // "A value 42 is not a string."
2086
+ * const formatTypeError = createFormatTypeError<
2087
+ * MinLengthError | MaxLengthError
2088
+ * >((error): string => {
2089
+ * switch (error.type) {
2090
+ * case "MinLength":
2091
+ * return `Text must be at least ${error.min} character${error.min === 1 ? "" : "s"} long`;
2092
+ * case "MaxLength":
2093
+ * return `Text is too long (maximum ${error.max} characters)`;
2094
+ * }
2095
+ * });
1873
2096
  * ```
1874
2097
  *
1875
- * A custom `formatTypeError` function:
2098
+ * Alternatively, write a custom formatter from scratch without using
2099
+ * `createFormatTypeError`. This gives us full control over error formatting:
1876
2100
  *
1877
2101
  * ```ts
1878
- * type AppErrors =
1879
- * | ValidMutationSizeError
2102
+ * const Person = object({
2103
+ * name: NonEmptyTrimmedString100,
2104
+ * age: optional(PositiveInt),
2105
+ * });
2106
+ *
2107
+ * // Define only the errors actually used by Person Type
2108
+ * type PersonErrors =
1880
2109
  * | StringError
1881
- * | MinLengthError
1882
2110
  * | MaxLengthError
1883
- * | NullError
1884
- * | IdError
2111
+ * | MinLengthError
1885
2112
  * | TrimmedError
1886
- * | MnemonicError
1887
- * | LiteralError
1888
- * // Composite errors
1889
- * | ObjectError<Record<string, AppErrors>>
1890
- * | UnionError<AppErrors>;
1891
- *
1892
- * const formatTypeError: TypeErrorFormatter<AppErrors> = (error) => {
1893
- * // In the real code, we would use the createTypeErrorFormatter helper
1894
- * // that safely stringifies error value.
2113
+ * | PositiveError
2114
+ * | NonNegativeError
2115
+ * | IntError
2116
+ * | NumberError
2117
+ * | ObjectError<Record<string, PersonErrors>>;
2118
+ *
2119
+ * const formatTypeError: TypeErrorFormatter<PersonErrors> = (error) => {
1895
2120
  * switch (error.type) {
1896
- * case "Id":
1897
- * return `Invalid Id on table: ${error.table}.`;
1898
- * case "MaxLength":
1899
- * return `Max length is ${error.max}.`;
1900
- * case "MinLength":
1901
- * return `Min length is ${error.min}.`;
1902
- * case "Mnemonic":
1903
- * return `Invalid mnemonic: ${String(error.value)}`;
1904
- * case "Null":
1905
- * return `Not null`;
1906
2121
  * case "String":
1907
- * // We can reuse existing formatter.
1908
2122
  * return formatStringError(error);
2123
+ * case "Number":
2124
+ * return "Must be a number";
2125
+ * case "MinLength":
2126
+ * return `Must be at least ${error.min} characters`;
2127
+ * case "MaxLength":
2128
+ * return `Cannot exceed ${error.max} characters`;
1909
2129
  * case "Trimmed":
1910
- * return "Value is not trimmed.";
1911
- * case "ValidMutationSize":
1912
- * return "A developer made an error, this should not happen.";
1913
- * case "Literal":
1914
- * return formatLiteralError(error);
1915
- * // Composite Types
1916
- * case "Union":
1917
- * return `Union errors: ${error.errors.map(formatTypeError).join(", ")}`;
2130
+ * return "Cannot have leading or trailing spaces";
2131
+ * case "Positive":
2132
+ * return "Must be a positive number";
2133
+ * case "NonNegative":
2134
+ * return "Must be zero or positive";
2135
+ * case "Int":
2136
+ * return "Must be an integer";
1918
2137
  * case "Object": {
1919
- * if (
1920
- * error.reason.kind === "ExtraKeys" ||
1921
- * error.reason.kind === "NotObject"
1922
- * )
1923
- * return "A developer made an error, this should not happen.";
2138
+ * if (error.reason.kind === "NotObject") return "Must be an object";
2139
+ * if (error.reason.kind === "ExtraKeys")
2140
+ * return "Contains unexpected fields";
1924
2141
  * const firstError = Object.values(error.reason.errors).find(
1925
2142
  * (e) => e !== undefined,
1926
2143
  * )!;
@@ -1933,5 +2150,69 @@ export type TypeErrors<ExtraErrors extends TypeError = never> = StringError | Nu
1933
2150
  * @category Utilities
1934
2151
  */
1935
2152
  export declare const createFormatTypeError: <ExtraErrors extends TypeError = never>(extraFormatter?: TypeErrorFormatter<ExtraErrors>) => TypeErrorFormatter<TypeErrors<ExtraErrors>>;
2153
+ /**
2154
+ * Converts an Evolu {@link TypeError} to Standard Schema V1 issues format.
2155
+ *
2156
+ * This function recursively converts Evolu's typed errors into the Standard
2157
+ * Schema issue format with proper path tracking for nested structures.
2158
+ *
2159
+ * @category Utilities
2160
+ */
2161
+ export declare const typeErrorToStandardSchemaIssues: <ExtraErrors extends TypeError = never>(error: TypeErrors<ExtraErrors>, formatTypeError: TypeErrorFormatter<TypeErrors<ExtraErrors>>, path?: ReadonlyArray<PropertyKey>) => ReadonlyArray<StandardSchemaV1.Issue>;
2162
+ /** The Standard Schema interface. */
2163
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
2164
+ /** The Standard Schema properties. */
2165
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
2166
+ }
2167
+ export declare namespace StandardSchemaV1 {
2168
+ /** The Standard Schema properties interface. */
2169
+ interface Props<Input = unknown, Output = Input> {
2170
+ /** The version number of the standard. */
2171
+ readonly version: 1;
2172
+ /** The vendor name of the schema library. */
2173
+ readonly vendor: string;
2174
+ /** Validates unknown input values. */
2175
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
2176
+ /** Inferred types associated with the schema. */
2177
+ readonly types?: Types<Input, Output> | undefined;
2178
+ }
2179
+ /** The result interface of the validate function. */
2180
+ type Result<Output> = SuccessResult<Output> | FailureResult;
2181
+ /** The result interface if validation succeeds. */
2182
+ interface SuccessResult<Output> {
2183
+ /** The typed output value. */
2184
+ readonly value: Output;
2185
+ /** The non-existent issues. */
2186
+ readonly issues?: undefined;
2187
+ }
2188
+ /** The result interface if validation fails. */
2189
+ interface FailureResult {
2190
+ /** The issues of failed validation. */
2191
+ readonly issues: ReadonlyArray<Issue>;
2192
+ }
2193
+ /** The issue interface of the failure output. */
2194
+ interface Issue {
2195
+ /** The error message of the issue. */
2196
+ readonly message: string;
2197
+ /** The path of the issue, if any. */
2198
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
2199
+ }
2200
+ /** The path segment interface of the issue. */
2201
+ interface PathSegment {
2202
+ /** The key representing a path segment. */
2203
+ readonly key: PropertyKey;
2204
+ }
2205
+ /** The Standard Schema types interface. */
2206
+ interface Types<Input = unknown, Output = Input> {
2207
+ /** The input type of the schema. */
2208
+ readonly input: Input;
2209
+ /** The output type of the schema. */
2210
+ readonly output: Output;
2211
+ }
2212
+ /** Infers the input type of a Standard Schema. */
2213
+ type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
2214
+ /** Infers the output type of a Standard Schema. */
2215
+ type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
2216
+ }
1936
2217
  export {};
1937
2218
  //# sourceMappingURL=Type.d.ts.map