@evolu/common 6.0.1-preview.8 → 7.0.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.
- package/README.md +2 -2
- package/dist/src/Array.d.ts +256 -12
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +215 -9
- package/dist/src/Assert.d.ts +0 -13
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +0 -15
- package/dist/src/Brand.d.ts +75 -0
- package/dist/src/Brand.d.ts.map +1 -0
- package/dist/src/Brand.js +1 -0
- package/dist/src/Buffer.d.ts +1 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +8 -7
- package/dist/src/Cache.d.ts +44 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +52 -0
- package/dist/src/Callbacks.d.ts +45 -12
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +14 -7
- package/dist/src/Console.d.ts +31 -6
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +72 -9
- package/dist/src/Crypto.d.ts +56 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +40 -53
- package/dist/src/Evolu/Db.d.ts +162 -74
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +284 -702
- package/dist/src/Evolu/Diff.d.ts +3 -3
- package/dist/src/Evolu/Diff.d.ts.map +1 -1
- package/dist/src/Evolu/Diff.js +7 -5
- package/dist/src/Evolu/Evolu.d.ts +214 -134
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +189 -180
- package/dist/src/Evolu/Internal.d.ts +0 -2
- package/dist/src/Evolu/Internal.d.ts.map +1 -1
- package/dist/src/Evolu/Internal.js +0 -2
- package/dist/src/Evolu/LocalAuth.d.ts +150 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +174 -0
- package/dist/src/Evolu/Owner.d.ts +273 -120
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +130 -104
- package/dist/src/Evolu/Platform.d.ts +9 -7
- package/dist/src/Evolu/Platform.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.d.ts +268 -240
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +600 -454
- package/dist/src/Evolu/Public.d.ts +6 -8
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Public.js +2 -3
- package/dist/src/Evolu/PublicKysely.d.ts.map +1 -1
- package/dist/src/Evolu/PublicKysely.js +3 -4
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Query.js +1 -1
- package/dist/src/Evolu/Relay.d.ts +91 -8
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +214 -88
- package/dist/src/Evolu/Schema.d.ts +125 -47
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +175 -31
- package/dist/src/Evolu/Storage.d.ts +249 -27
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +198 -92
- package/dist/src/Evolu/Sync.d.ts +68 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +469 -20
- package/dist/src/Evolu/Timestamp.d.ts +83 -30
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +79 -34
- package/dist/src/Identicon.d.ts +35 -0
- package/dist/src/Identicon.d.ts.map +1 -0
- package/dist/src/Identicon.js +143 -0
- package/dist/src/Instances.d.ts +34 -0
- package/dist/src/Instances.d.ts.map +1 -0
- package/dist/src/Instances.js +44 -0
- package/dist/src/Number.d.ts +4 -3
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +5 -4
- package/dist/src/Object.d.ts +10 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +9 -3
- package/dist/src/Platform.d.ts +20 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +22 -0
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Relation.d.ts +79 -0
- package/dist/src/Relation.d.ts.map +1 -0
- package/dist/src/Relation.js +127 -0
- package/dist/src/Resources.d.ts +118 -0
- package/dist/src/Resources.d.ts.map +1 -0
- package/dist/src/Resources.js +197 -0
- package/dist/src/Result.d.ts +184 -52
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Skiplist.js +2 -1
- package/dist/src/Sqlite.d.ts +89 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +136 -9
- package/dist/src/Task.d.ts +586 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +469 -0
- package/dist/src/Time.d.ts +66 -1
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +99 -5
- package/dist/src/Type.d.ts +676 -343
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +718 -467
- package/dist/src/Types.d.ts +1 -75
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts +5 -2
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +12 -18
- package/dist/src/Worker.d.ts +39 -11
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +22 -4
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/package.json +15 -14
- package/src/Array.ts +301 -19
- package/src/Assert.ts +0 -21
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +7 -7
- package/src/Cache.ts +85 -0
- package/src/Callbacks.ts +62 -22
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +94 -90
- package/src/Evolu/Db.ts +519 -1026
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +477 -361
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +463 -0
- package/src/Evolu/Owner.ts +355 -228
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +842 -742
- package/src/Evolu/Public.ts +7 -14
- package/src/Evolu/PublicKysely.ts +4 -5
- package/src/Evolu/Query.ts +3 -2
- package/src/Evolu/Relay.ts +406 -103
- package/src/Evolu/Schema.ts +323 -91
- package/src/Evolu/Storage.ts +559 -137
- package/src/Evolu/Sync.ts +819 -36
- package/src/Evolu/Timestamp.ts +90 -58
- package/src/Identicon.ts +197 -0
- package/src/Instances.ts +90 -0
- package/src/Number.ts +6 -10
- package/src/Object.ts +13 -5
- package/src/Platform.ts +26 -0
- package/src/Random.ts +3 -2
- package/src/Relation.ts +234 -0
- package/src/Resources.ts +367 -0
- package/src/Result.ts +191 -54
- package/src/Skiplist.ts +1 -1
- package/src/Sqlite.ts +152 -17
- package/src/Task.ts +901 -0
- package/src/Time.ts +180 -5
- package/src/Type.ts +1135 -730
- package/src/Types.ts +1 -77
- package/src/WebSocket.ts +27 -25
- package/src/Worker.ts +72 -23
- package/src/index.ts +8 -3
- package/dist/src/Evolu/Config.d.ts +0 -69
- package/dist/src/Evolu/Config.d.ts.map +0 -1
- package/dist/src/Evolu/Config.js +0 -9
- package/dist/src/Evolu/Kysely.d.ts +0 -6
- package/dist/src/Evolu/Kysely.d.ts.map +0 -1
- package/dist/src/Evolu/Kysely.js +0 -21
- package/dist/src/ManyToManyMap.d.ts +0 -26
- package/dist/src/ManyToManyMap.d.ts.map +0 -1
- package/dist/src/ManyToManyMap.js +0 -92
- package/dist/src/NanoId.d.ts +0 -27
- package/dist/src/NanoId.d.ts.map +0 -1
- package/dist/src/NanoId.js +0 -6
- package/dist/src/Promise.d.ts +0 -180
- package/dist/src/Promise.d.ts.map +0 -1
- package/dist/src/Promise.js +0 -176
- package/src/Evolu/Config.ts +0 -83
- package/src/Evolu/Kysely.ts +0 -38
- package/src/ManyToManyMap.ts +0 -140
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
package/dist/src/Type.d.ts
CHANGED
|
@@ -1,77 +1,210 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 🧩
|
|
2
|
+
* 🧩 Type-safe runtime types
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
+
* composable typed error telling us exactly why validation failed.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
8
|
+
* Why another validation library?
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* - **Result-based error handling** – no exceptions for normal control flow.
|
|
11
|
+
* - **Typed errors with decoupled formatters** – validation logic ≠ user
|
|
12
|
+
* messages.
|
|
13
|
+
* - **Consistent constraints via {@link Brand}** – every constraint becomes part
|
|
14
|
+
* of the type.
|
|
15
|
+
* - **Skippable validation** – parent validations can be skipped when already
|
|
16
|
+
* proved by types.
|
|
17
|
+
* - **Simple, top-down implementation** – readable source code from top to
|
|
18
|
+
* bottom.
|
|
19
|
+
* - **No user-land chaining DSL** – prepared for TC39 Hack pipes.
|
|
10
20
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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.
|
|
21
|
+
* A distinctive feature of Evolu Type compared to other validation libraries is
|
|
22
|
+
* that it returns typed errors rather than string messages. This allows
|
|
23
|
+
* TypeScript to enforce that all validation errors are handled by type
|
|
24
|
+
* checking, significantly improving the developer experience.
|
|
22
25
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* practical demonstrations of the API. Or dang, just read the code. It's
|
|
26
|
-
* simple.
|
|
26
|
+
* Evolu Type supports [Standard Schema](https://standardschema.dev/) for
|
|
27
|
+
* interoperability with 40+ validation-compatible tools and frameworks.
|
|
27
28
|
*
|
|
28
|
-
*
|
|
29
|
+
* ### Base Types Quick Start
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* // Validate unknown values
|
|
33
|
+
* const value: unknown = "hello";
|
|
34
|
+
* const stringResult = String.fromUnknown(value);
|
|
35
|
+
* if (!stringResult.ok) {
|
|
36
|
+
* // console.error(formatStringError(stringResult.error));
|
|
37
|
+
* return stringResult; // inside a function returning Result<string, _>
|
|
38
|
+
* }
|
|
39
|
+
* // Safe branch: value is now string
|
|
40
|
+
* const upper = stringResult.value.toUpperCase();
|
|
41
|
+
*
|
|
42
|
+
* // Type guard style
|
|
43
|
+
* if (String.is(value)) {
|
|
44
|
+
* // narrowed to string
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* // Composing: arrays & objects
|
|
48
|
+
* const Numbers = array(Number); // ReadonlyArray<number>
|
|
49
|
+
* const Point = object({ x: Number, y: Number });
|
|
50
|
+
*
|
|
51
|
+
* Numbers.from([1, 2, 3]); // ok
|
|
52
|
+
* Point.from({ x: 1, y: 2 }); // ok
|
|
53
|
+
* Point.from({ x: 1, y: "2" }); // err -> nested Number error
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* ### Branding Basics
|
|
57
|
+
*
|
|
58
|
+
* Branding adds semantic meaning & constraints while preserving the runtime
|
|
59
|
+
* shape:
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const CurrencyCode = brand("CurrencyCode", String, (value) =>
|
|
63
|
+
* /^[A-Z]{3}$/.test(value)
|
|
64
|
+
* ? ok(value)
|
|
65
|
+
* : err<CurrencyCodeError>({ type: "CurrencyCode", value }),
|
|
66
|
+
* );
|
|
67
|
+
* type CurrencyCode = typeof CurrencyCode.Type; // string & Brand<"CurrencyCode">
|
|
68
|
+
*
|
|
69
|
+
* interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
|
|
70
|
+
*
|
|
71
|
+
* const formatCurrencyCodeError =
|
|
72
|
+
* createTypeErrorFormatter<CurrencyCodeError>(
|
|
73
|
+
* (error) => `Invalid currency code: ${error.value}`,
|
|
74
|
+
* );
|
|
75
|
+
*
|
|
76
|
+
* const r = CurrencyCode.from("USD"); // ok("USD")
|
|
77
|
+
* const e = CurrencyCode.from("usd"); // err(...)
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* See also reusable brand factories like `minLength`, `maxLength`, `trimmed`,
|
|
81
|
+
* `positive`, `between`, etc.
|
|
82
|
+
*
|
|
83
|
+
* ### Objects & Optional Fields
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const User = object({
|
|
87
|
+
* name: NonEmptyTrimmedString100,
|
|
88
|
+
* age: optional(PositiveInt),
|
|
89
|
+
* });
|
|
90
|
+
* type User = typeof User.Type;
|
|
91
|
+
*
|
|
92
|
+
* User.from({ name: "Alice" }); // ok
|
|
93
|
+
* User.from({ name: "Alice", age: -1 }); // err(PositiveInt)
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* ### Deriving JSON String Types
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* const Person = object({
|
|
100
|
+
* name: NonEmptyString50,
|
|
101
|
+
* // Did you know that JSON.stringify converts NaN (a number) into null?
|
|
102
|
+
* // To prevent this, use FiniteNumber.
|
|
103
|
+
* age: FiniteNumber,
|
|
104
|
+
* });
|
|
105
|
+
* type Person = typeof Person.Type;
|
|
106
|
+
*
|
|
107
|
+
* const [PersonJson, personToPersonJson, personJsonToPerson] = json(
|
|
108
|
+
* Person,
|
|
109
|
+
* "PersonJson",
|
|
110
|
+
* );
|
|
111
|
+
* // string & Brand<"PersonJson">
|
|
112
|
+
* type PersonJson = typeof PersonJson.Type;
|
|
113
|
+
*
|
|
114
|
+
* const person = Person.orThrow({
|
|
115
|
+
* name: "Alice",
|
|
116
|
+
* age: 30,
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* const personJson = personToPersonJson(person);
|
|
120
|
+
* expect(personJsonToPerson(personJson)).toEqual(person);
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* ### Error Formatting
|
|
34
124
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* transformations. For example, `NonEmptyTrimmedString100` chain looks like
|
|
38
|
-
* this:
|
|
125
|
+
* Evolu separates validation logic from human-readable messages. There are two
|
|
126
|
+
* layers:
|
|
39
127
|
*
|
|
40
|
-
*
|
|
128
|
+
* 1. Per-type formatters (e.g. `formatStringError`) – simple, focused, already
|
|
129
|
+
* used earlier in the quick start example.
|
|
130
|
+
* 2. A unified formatter via `createFormatTypeError` – composes all built-in and
|
|
131
|
+
* custom errors (including nested composite types) and lets us override
|
|
132
|
+
* selected messages.
|
|
41
133
|
*
|
|
42
|
-
*
|
|
43
|
-
* `TrimmedString`, the parent Type is `String`.
|
|
134
|
+
* #### 1. Per-Type Formatter (recap)
|
|
44
135
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* const r = String.fromUnknown(42);
|
|
138
|
+
* if (!r.ok) console.error(formatStringError(r.error));
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* #### 2. Unified Formatter with Overrides
|
|
48
142
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
143
|
+
* ```ts
|
|
144
|
+
* // Override only what we care about; fall back to built-ins for the rest.
|
|
145
|
+
* const formatTypeError = createFormatTypeError((error) => {
|
|
146
|
+
* if (error.type === "MinLength") return `Min length is ${error.min}`;
|
|
147
|
+
* });
|
|
51
148
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* {
|
|
149
|
+
* const User = object({ name: NonEmptyTrimmedString100 });
|
|
150
|
+
* const resultUser = User.from({ name: "" });
|
|
151
|
+
* if (!resultUser.ok) console.error(formatTypeError(resultUser.error));
|
|
152
|
+
*
|
|
153
|
+
* const badPoint = object({ x: Number, y: Number }).from({
|
|
154
|
+
* x: 1,
|
|
155
|
+
* y: "foo",
|
|
156
|
+
* });
|
|
157
|
+
* if (!badPoint.ok) console.error(formatTypeError(badPoint.error));
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* The unified formatter walks nested structures (object / array / record /
|
|
161
|
+
* tuple / union) and applies overrides only where specified, greatly reducing
|
|
162
|
+
* boilerplate when formatting complex validation errors.
|
|
57
163
|
*
|
|
58
164
|
* ### Tip
|
|
59
165
|
*
|
|
60
166
|
* If necessary, write `globalThis.String` instead of `String` to avoid naming
|
|
61
|
-
* clashes with
|
|
167
|
+
* clashes with native types.
|
|
168
|
+
*
|
|
169
|
+
* ### Design Decision: No Bidirectional Transformations
|
|
170
|
+
*
|
|
171
|
+
* Evolu Type intentionally does not support bidirectional transformations. It
|
|
172
|
+
* previously did, but supporting that while keeping typed error fidelity added
|
|
173
|
+
* complexity that hurt readability & reliability. Most persistence pipelines
|
|
174
|
+
* (e.g. SQLite) already require explicit mapping of query results, so implicit
|
|
175
|
+
* reverse transforms would not buy much. We may revisit this if we can design a
|
|
176
|
+
* minimal, 100% safe API that preserves simplicity.
|
|
177
|
+
*
|
|
178
|
+
* ### Prepared for TC39 Hack Pipes
|
|
62
179
|
*
|
|
63
|
-
*
|
|
180
|
+
* Take a look how `SimplePassword` is defined:
|
|
64
181
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
182
|
+
* ```ts
|
|
183
|
+
* export const SimplePassword = brand(
|
|
184
|
+
* "SimplePassword",
|
|
185
|
+
* minLength(8)(maxLength(64)(TrimmedString)),
|
|
186
|
+
* );
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
* Nested functions are often OK (if not, make a helper) and read well, but with
|
|
190
|
+
* TC39 Hack pipes it would be clearer:
|
|
191
|
+
*
|
|
192
|
+
* ```ts
|
|
193
|
+
* // TrimmedString
|
|
194
|
+
* // |> minLength(8)(%)
|
|
195
|
+
* // |> maxLength(64)(%)
|
|
196
|
+
* // |> brand("SimplePassword", %)
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* Note `minLength` and `maxLength` are curried because they are factories.
|
|
69
200
|
*
|
|
70
201
|
* @module
|
|
71
202
|
*/
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
74
|
-
import
|
|
203
|
+
import type { Brand } from "./Brand.js";
|
|
204
|
+
import { type RandomBytesDep } from "./Crypto.js";
|
|
205
|
+
import { Result } from "./Result.js";
|
|
206
|
+
import type { TimeDep } from "./Time.js";
|
|
207
|
+
import type { Literal, Simplify, WidenLiteral } from "./Types.js";
|
|
75
208
|
export interface Type<Name extends TypeName,
|
|
76
209
|
/** The type this Type resolves to. */
|
|
77
210
|
T,
|
|
@@ -82,7 +215,7 @@ Error extends TypeError = never,
|
|
|
82
215
|
/** The parent type. */
|
|
83
216
|
Parent = T,
|
|
84
217
|
/** The parent's error. */
|
|
85
|
-
ParentError extends TypeError = Error> {
|
|
218
|
+
ParentError extends TypeError = Error> extends StandardSchemaV1<Input, T> {
|
|
86
219
|
readonly name: Name;
|
|
87
220
|
/**
|
|
88
221
|
* Creates `T` from an `Input` value.
|
|
@@ -93,38 +226,95 @@ ParentError extends TypeError = Error> {
|
|
|
93
226
|
*/
|
|
94
227
|
readonly from: (value: Input) => Result<T, ParentError | Error>;
|
|
95
228
|
/**
|
|
96
|
-
* Creates `T` from an
|
|
229
|
+
* Creates `T` from an `Input` value, throwing an error if validation fails.
|
|
97
230
|
*
|
|
98
|
-
*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* The opposite of `from` and `fromUnknown`.
|
|
231
|
+
* Throws an Error with the Type validation error in its `cause` property,
|
|
232
|
+
* making it debuggable while avoiding the need for custom error messages.
|
|
233
|
+
*
|
|
234
|
+
* This is a convenience method that combines `from` with `getOrThrow`.
|
|
103
235
|
*
|
|
104
|
-
*
|
|
236
|
+
* **When to use:**
|
|
105
237
|
*
|
|
106
|
-
*
|
|
238
|
+
* - Configuration values that are guaranteed to be valid (e.g., hardcoded
|
|
239
|
+
* constants)
|
|
240
|
+
* - Application startup where failure should crash the program
|
|
241
|
+
* - As an alternative to assertions when the Type error in the thrown Error's
|
|
242
|
+
* `cause` provides sufficient debugging information
|
|
243
|
+
* - Test code with known valid inputs (when error message clarity is not
|
|
244
|
+
* critical; for better test error messages, use Vitest `schemaMatching` +
|
|
245
|
+
* `assert` with `.is()`)
|
|
246
|
+
*
|
|
247
|
+
* ### Example
|
|
248
|
+
*
|
|
249
|
+
* ```ts
|
|
250
|
+
* // ✅ Good: Known valid constant
|
|
251
|
+
* const maxRetries = PositiveInt.orThrow(3);
|
|
252
|
+
*
|
|
253
|
+
* // ✅ Good: App configuration that should crash on invalid values
|
|
254
|
+
* const appName = SimpleName.orThrow("MyApp");
|
|
255
|
+
*
|
|
256
|
+
* // ✅ Good: Instead of assert when Type error is clear enough
|
|
257
|
+
* // Context makes it obvious: count increments from non-negative value
|
|
258
|
+
* const currentCount = counts.get(id) ?? 0;
|
|
259
|
+
* const newCount = PositiveInt.orThrow(currentCount + 1);
|
|
260
|
+
*
|
|
261
|
+
* // ✅ Good: Test setup with known valid values
|
|
262
|
+
* const testUser = User.orThrow({ name: "Alice", age: 30 });
|
|
263
|
+
*
|
|
264
|
+
* // ❌ Avoid: User input (use `from` instead)
|
|
265
|
+
* const userAge = PositiveInt.orThrow(userInput); // Could crash!
|
|
266
|
+
*
|
|
267
|
+
* // ✅ Better: Handle user input gracefully
|
|
268
|
+
* const ageResult = PositiveInt.from(userInput);
|
|
269
|
+
* if (!ageResult.ok) {
|
|
270
|
+
* // Handle validation error
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
107
273
|
*/
|
|
108
|
-
readonly
|
|
274
|
+
readonly orThrow: (value: Input) => T;
|
|
109
275
|
/**
|
|
110
|
-
* Creates `T` from `
|
|
276
|
+
* Creates `T` from an `Input` value, returning `null` if validation fails.
|
|
277
|
+
*
|
|
278
|
+
* This is a convenience method that combines `from` with `getOrNull`.
|
|
111
279
|
*
|
|
112
|
-
*
|
|
113
|
-
* already partially validated/transformed value.
|
|
280
|
+
* **When to use:**
|
|
114
281
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
282
|
+
* - When you need to convert a validation result to a nullable value
|
|
283
|
+
* - When the error is not important and you just want the value or nothing
|
|
117
284
|
*
|
|
118
285
|
* ### Example
|
|
119
286
|
*
|
|
120
287
|
* ```ts
|
|
121
|
-
* //
|
|
122
|
-
* const
|
|
288
|
+
* // ✅ Good: Optional user input
|
|
289
|
+
* const age = PositiveInt.orNull(userInput);
|
|
290
|
+
* if (age != null) {
|
|
291
|
+
* console.log("Valid age:", age);
|
|
292
|
+
* }
|
|
293
|
+
*
|
|
294
|
+
* // ✅ Good: Default fallback
|
|
295
|
+
* const maxRetries = PositiveInt.orNull(config.retries) ?? 3;
|
|
296
|
+
*
|
|
297
|
+
* // ❌ Avoid: When you need to know why validation failed (use `from` instead)
|
|
298
|
+
* const result = PositiveInt.from(userInput);
|
|
299
|
+
* if (!result.ok) {
|
|
300
|
+
* console.error(formatPositiveError(result.error));
|
|
301
|
+
* }
|
|
123
302
|
* ```
|
|
124
303
|
*/
|
|
304
|
+
readonly orNull: (value: Input) => T | null;
|
|
305
|
+
/**
|
|
306
|
+
* Creates `T` from an unknown value.
|
|
307
|
+
*
|
|
308
|
+
* This is useful when a value is unknown.
|
|
309
|
+
*/
|
|
310
|
+
readonly fromUnknown: (value: unknown) => Result<T, ParentError | Error>;
|
|
311
|
+
/**
|
|
312
|
+
* Creates `T` from `Parent` type.
|
|
313
|
+
*
|
|
314
|
+
* This function skips parent Types validations when we have already partially
|
|
315
|
+
* validated value.
|
|
316
|
+
*/
|
|
125
317
|
readonly fromParent: (value: Parent) => Result<T, Error>;
|
|
126
|
-
/** The opposite of `fromParent`. */
|
|
127
|
-
readonly toParent: (value: T) => Parent;
|
|
128
318
|
/**
|
|
129
319
|
* A **type guard** that checks whether an unknown value satisfies the
|
|
130
320
|
* {@link Type}.
|
|
@@ -197,8 +387,6 @@ ParentError extends TypeError = Error> {
|
|
|
197
387
|
*/
|
|
198
388
|
readonly ParentError: ParentError;
|
|
199
389
|
/**
|
|
200
|
-
* Error | ParentError
|
|
201
|
-
*
|
|
202
390
|
* ### Example
|
|
203
391
|
*
|
|
204
392
|
* ```ts
|
|
@@ -230,12 +418,47 @@ export interface TypeErrorWithReason<Name extends TypeName = TypeName, Reason ex
|
|
|
230
418
|
readonly reason: Reason;
|
|
231
419
|
}
|
|
232
420
|
export type AnyType = Type<any, any, any, any, any, any>;
|
|
421
|
+
/**
|
|
422
|
+
* Extracts the name from a {@link Type}.
|
|
423
|
+
*
|
|
424
|
+
* @category Utilities
|
|
425
|
+
*/
|
|
233
426
|
export type InferName<A extends AnyType> = A extends Type<infer Name, any, any, any, any, any> ? Name : never;
|
|
427
|
+
/**
|
|
428
|
+
* Extracts the type from a {@link Type}.
|
|
429
|
+
*
|
|
430
|
+
* @category Utilities
|
|
431
|
+
*/
|
|
234
432
|
export type InferType<A extends AnyType> = A extends Type<any, infer T, any, any, any, any> ? T : never;
|
|
433
|
+
/**
|
|
434
|
+
* Extracts the input type from a {@link Type}.
|
|
435
|
+
*
|
|
436
|
+
* @category Utilities
|
|
437
|
+
*/
|
|
235
438
|
export type InferInput<A extends AnyType> = A extends Type<any, any, infer Input, any, any, any> ? Input : never;
|
|
439
|
+
/**
|
|
440
|
+
* Extracts the specific error type from a {@link Type}.
|
|
441
|
+
*
|
|
442
|
+
* @category Utilities
|
|
443
|
+
*/
|
|
236
444
|
export type InferError<A extends AnyType> = A extends Type<any, any, any, infer Error, any, any> ? Error : never;
|
|
445
|
+
/**
|
|
446
|
+
* Extracts the parent type from a {@link Type}.
|
|
447
|
+
*
|
|
448
|
+
* @category Utilities
|
|
449
|
+
*/
|
|
237
450
|
export type InferParent<A extends AnyType> = A extends Type<any, any, any, any, infer Parent, any> ? Parent : never;
|
|
451
|
+
/**
|
|
452
|
+
* Extracts the parent error type from a {@link Type}.
|
|
453
|
+
*
|
|
454
|
+
* @category Utilities
|
|
455
|
+
*/
|
|
238
456
|
export type InferParentError<A extends AnyType> = A extends Type<any, any, any, any, any, infer ParentError> ? ParentError : never;
|
|
457
|
+
/**
|
|
458
|
+
* Extracts all error types from a {@link Type}.
|
|
459
|
+
*
|
|
460
|
+
* @category Utilities
|
|
461
|
+
*/
|
|
239
462
|
export type InferErrors<T extends AnyType> = T extends Type<any, any, any, infer Error, any, infer ParentError> ? Error | ParentError : never;
|
|
240
463
|
declare const EvoluTypeSymbol: unique symbol;
|
|
241
464
|
/**
|
|
@@ -268,12 +491,6 @@ export type TypeErrorFormatter<Error extends TypeError> = (error: Error) => stri
|
|
|
268
491
|
* Base {@link Type}.
|
|
269
492
|
*
|
|
270
493
|
* 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
494
|
*
|
|
278
495
|
* ### Example
|
|
279
496
|
*
|
|
@@ -410,7 +627,7 @@ export declare const formatIsTypeError: TypeErrorFormatter<EvoluTypeError>;
|
|
|
410
627
|
* The `brand` Type Factory takes the name of a new {@link Brand}, a parent Type
|
|
411
628
|
* to be branded, and the optional `refine` function for additional constraint.
|
|
412
629
|
*
|
|
413
|
-
*
|
|
630
|
+
* The `refine` function can be omitted if we only want to add a brand.
|
|
414
631
|
*
|
|
415
632
|
* ### Examples
|
|
416
633
|
*
|
|
@@ -489,7 +706,7 @@ export declare const formatIsTypeError: TypeErrorFormatter<EvoluTypeError>;
|
|
|
489
706
|
* confirmPassword: SimplePassword,
|
|
490
707
|
* });
|
|
491
708
|
*
|
|
492
|
-
* const ValidForm = brand("
|
|
709
|
+
* const ValidForm = brand("ValidForm", Form, (value) => {
|
|
493
710
|
* if (value.password !== value.confirmPassword)
|
|
494
711
|
* return err<ValidFormError>({
|
|
495
712
|
* type: "ValidForm",
|
|
@@ -567,17 +784,19 @@ export declare const formatCurrencyCodeError: TypeErrorFormatter<CurrencyCodeErr
|
|
|
567
784
|
* ### Example
|
|
568
785
|
*
|
|
569
786
|
* ```ts
|
|
570
|
-
* const result =
|
|
571
|
-
* const error =
|
|
787
|
+
* const result = DateIso.from("2023-01-01T12:00:00.000Z"); // ok
|
|
788
|
+
* const error = DateIso.from("10000-01-01T00:00:00.000Z"); // err
|
|
572
789
|
* ```
|
|
573
790
|
*
|
|
574
791
|
* @category String
|
|
575
792
|
*/
|
|
576
|
-
export declare const
|
|
577
|
-
export type
|
|
578
|
-
export interface
|
|
793
|
+
export declare const DateIso: BrandType<Type<"String", string, string, StringError, string, StringError>, "DateIso", DateIsoError, StringError>;
|
|
794
|
+
export type DateIso = typeof DateIso.Type;
|
|
795
|
+
export interface DateIsoError extends TypeError<"DateIso"> {
|
|
579
796
|
}
|
|
580
|
-
export declare const
|
|
797
|
+
export declare const formatDateIsoError: TypeErrorFormatter<DateIsoError>;
|
|
798
|
+
export declare const dateToDateIso: (value: Date) => Result<DateIso, DateIsoError>;
|
|
799
|
+
export declare const dateIsoToDate: (value: DateIso) => Date;
|
|
581
800
|
/**
|
|
582
801
|
* Helper type for Type Factory that creates a branded Type.
|
|
583
802
|
*
|
|
@@ -600,18 +819,12 @@ export type BrandFactory<Name extends TypeName, Input, RefineError extends TypeE
|
|
|
600
819
|
/**
|
|
601
820
|
* Trimmed string.
|
|
602
821
|
*
|
|
603
|
-
* This Type Factory
|
|
604
|
-
*
|
|
605
|
-
* Factory.
|
|
822
|
+
* This Type Factory validates whether a string has no leading or trailing
|
|
823
|
+
* whitespaces.
|
|
606
824
|
*
|
|
607
|
-
* ###
|
|
825
|
+
* ### Example
|
|
608
826
|
*
|
|
609
827
|
* ```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
828
|
* const TrimmedNonEmptyString = trimmed(minLength(1)(String));
|
|
616
829
|
* // string & Brand<"MinLength1"> & Brand<"Trimmed">
|
|
617
830
|
* type TrimmedNonEmptyString = typeof TrimmedNonEmptyString.Type;
|
|
@@ -623,33 +836,6 @@ export declare const trimmed: BrandFactory<"Trimmed", string, TrimmedError>;
|
|
|
623
836
|
export interface TrimmedError extends TypeError<"Trimmed"> {
|
|
624
837
|
}
|
|
625
838
|
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
839
|
/**
|
|
654
840
|
* Trimmed string
|
|
655
841
|
*
|
|
@@ -660,6 +846,7 @@ export declare const trim: TransformBrandFactory<"Trimmed", string>;
|
|
|
660
846
|
*/
|
|
661
847
|
export declare const TrimmedString: BrandType<Type<"String", string, string, StringError, string, StringError>, "Trimmed", TrimmedError, StringError>;
|
|
662
848
|
export type TrimmedString = typeof TrimmedString.Type;
|
|
849
|
+
export declare const trim: (value: string) => TrimmedString;
|
|
663
850
|
/**
|
|
664
851
|
* Minimum length.
|
|
665
852
|
*
|
|
@@ -782,9 +969,9 @@ export interface RegexError<Name extends TypeName = TypeName> extends TypeError<
|
|
|
782
969
|
}
|
|
783
970
|
export declare const formatRegexError: TypeErrorFormatter<RegexError<Capitalize<string>>>;
|
|
784
971
|
/**
|
|
785
|
-
* URL-safe
|
|
972
|
+
* URL-safe string.
|
|
786
973
|
*
|
|
787
|
-
* A `
|
|
974
|
+
* A `UrlSafeString` uses a limited alphabet that is safe for URLs:
|
|
788
975
|
*
|
|
789
976
|
* - Uppercase letters (`A-Z`)
|
|
790
977
|
* - Lowercase letters (`a-z`)
|
|
@@ -792,33 +979,49 @@ export declare const formatRegexError: TypeErrorFormatter<RegexError<Capitalize<
|
|
|
792
979
|
* - Dash (`-`)
|
|
793
980
|
* - Underscore (`_`)
|
|
794
981
|
*
|
|
982
|
+
* This is the same character set used by Base64Url encoding, but this type does
|
|
983
|
+
* not validate that the string is actually Base64Url-encoded data.
|
|
984
|
+
*
|
|
795
985
|
* ### Example
|
|
796
986
|
*
|
|
797
987
|
* ```ts
|
|
798
|
-
* const result =
|
|
988
|
+
* const result = UrlSafeString.from("abc123_-");
|
|
799
989
|
* if (result.ok) {
|
|
800
|
-
* console.log("Valid
|
|
990
|
+
* console.log("Valid URL-safe string:", result.value);
|
|
801
991
|
* } else {
|
|
802
|
-
* console.error("Invalid
|
|
992
|
+
* console.error("Invalid URL-safe string:", result.error);
|
|
803
993
|
* }
|
|
804
994
|
* ```
|
|
805
995
|
*
|
|
806
996
|
* @category String
|
|
807
997
|
*/
|
|
808
|
-
export declare const
|
|
809
|
-
export type
|
|
810
|
-
export type
|
|
998
|
+
export declare const UrlSafeString: BrandType<Type<"String", string, string, StringError, string, StringError>, "UrlSafeString", RegexError<"UrlSafeString">, StringError>;
|
|
999
|
+
export type UrlSafeString = typeof UrlSafeString.Type;
|
|
1000
|
+
export type UrlSafeStringError = typeof UrlSafeString.Error;
|
|
811
1001
|
/**
|
|
812
|
-
*
|
|
1002
|
+
* Base64Url without padding.
|
|
813
1003
|
*
|
|
814
|
-
*
|
|
1004
|
+
* Encode with {@link uint8ArrayToBase64Url}, decode with
|
|
1005
|
+
* {@link base64UrlToUint8Array}.
|
|
815
1006
|
*
|
|
816
|
-
*
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1007
|
+
* @category String
|
|
1008
|
+
*/
|
|
1009
|
+
export declare const Base64Url: BrandType<Type<"String", string, string, StringError, string, StringError>, "Base64Url", Base64UrlError, StringError>;
|
|
1010
|
+
export type Base64Url = typeof Base64Url.Type;
|
|
1011
|
+
export interface Base64UrlError extends TypeError<"Base64Url"> {
|
|
1012
|
+
}
|
|
1013
|
+
export declare const formatBase64UrlError: TypeErrorFormatter<Base64UrlError>;
|
|
1014
|
+
/** Encodes a Uint8Array to a {@link Base64Url} string. */
|
|
1015
|
+
export declare const uint8ArrayToBase64Url: (bytes: Uint8Array) => Base64Url;
|
|
1016
|
+
/** Decodes a {@link Base64Url} string to a Uint8Array. */
|
|
1017
|
+
export declare const base64UrlToUint8Array: (str: Base64Url) => Uint8Array;
|
|
1018
|
+
/**
|
|
1019
|
+
* Simple alphanumeric string for naming in file systems, URLs, and identifiers.
|
|
1020
|
+
*
|
|
1021
|
+
* Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
|
|
1022
|
+
* `_`). See `UrlSafeString` for details.
|
|
820
1023
|
*
|
|
821
|
-
* The string must be between 1 and
|
|
1024
|
+
* The string must be between 1 and 64 characters.
|
|
822
1025
|
*
|
|
823
1026
|
* ### Example
|
|
824
1027
|
*
|
|
@@ -833,20 +1036,32 @@ export type Base64UrlError = typeof Base64Url.Error;
|
|
|
833
1036
|
*
|
|
834
1037
|
* @category String
|
|
835
1038
|
*/
|
|
836
|
-
export declare const SimpleName: BrandType<Type<"String", string, string, StringError, string, StringError>, "
|
|
1039
|
+
export declare const SimpleName: BrandType<BrandType<Type<"String", string, string, StringError, string, StringError>, "UrlSafeString", RegexError<"UrlSafeString">, StringError>, "SimpleName", SimpleNameError, StringError | RegexError<"UrlSafeString">>;
|
|
837
1040
|
export type SimpleName = typeof SimpleName.Type;
|
|
838
|
-
export
|
|
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;
|
|
1041
|
+
export interface SimpleNameError extends TypeError<"SimpleName"> {
|
|
1042
|
+
}
|
|
847
1043
|
/**
|
|
848
1044
|
* Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
|
|
849
1045
|
*
|
|
1046
|
+
* Take a look how `SimplePassword` is defined:
|
|
1047
|
+
*
|
|
1048
|
+
* ```ts
|
|
1049
|
+
* export const SimplePassword = brand(
|
|
1050
|
+
* "SimplePassword",
|
|
1051
|
+
* minLength(8)(maxLength(64)(TrimmedString)),
|
|
1052
|
+
* );
|
|
1053
|
+
* ```
|
|
1054
|
+
*
|
|
1055
|
+
* Nested functions are often OK (if not, make a helper), but with TC39 Hack
|
|
1056
|
+
* pipes it would be clearer:
|
|
1057
|
+
*
|
|
1058
|
+
* ```ts
|
|
1059
|
+
* // TrimmedString
|
|
1060
|
+
* // |> minLength(8)(%)
|
|
1061
|
+
* // |> maxLength(64)(%)
|
|
1062
|
+
* // |> brand("SimplePassword", %)
|
|
1063
|
+
* ```
|
|
1064
|
+
*
|
|
850
1065
|
* @category String
|
|
851
1066
|
*/
|
|
852
1067
|
export declare const SimplePassword: BrandType<BrandType<Type<"Brand", string & Brand<"Trimmed"> & Brand<"MaxLength64">, string, MaxLengthError<64>, string & Brand<"Trimmed">, StringError | TrimmedError>, "MinLength8", MinLengthError<8>, StringError | TrimmedError | MaxLengthError<64>>, "SimplePassword", BrandWithoutRefineError<"SimplePassword", StringError | TrimmedError | MinLengthError<8> | MaxLengthError<64>>, never>;
|
|
@@ -854,29 +1069,104 @@ export type SimplePassword = typeof SimplePassword.Type;
|
|
|
854
1069
|
export type SimplePasswordError = typeof SimplePassword.Error;
|
|
855
1070
|
export declare const formatSimplePasswordError: (formatTypeError: TypeErrorFormatter<StringError | MinLengthError<8> | MaxLengthError<64> | TrimmedError>) => TypeErrorFormatter<SimplePasswordError>;
|
|
856
1071
|
/**
|
|
857
|
-
*
|
|
1072
|
+
* Evolu Id: 16 bytes encoded as a 22‑character Base64Url string.
|
|
1073
|
+
*
|
|
1074
|
+
* There are three ways to create an Evolu Id:
|
|
1075
|
+
*
|
|
1076
|
+
* - {@link createId} – default cryptographically secure random bytes
|
|
1077
|
+
* (privacy‑preserving)
|
|
1078
|
+
* - {@link createIdFromString} – deterministic: first 16 bytes of SHA‑256 of a
|
|
1079
|
+
* string
|
|
1080
|
+
* - {@link createIdAsUuidv7} – optional: embeds timestamp bits (UUID v7 layout)
|
|
1081
|
+
*
|
|
1082
|
+
* Privacy: the default random Id does not leak creation time and is safe to
|
|
1083
|
+
* share or log. The UUID v7 variant leaks creation time anywhere the Id is
|
|
1084
|
+
* copied (logs, URLs, exports); only use it when you explicitly want insertion
|
|
1085
|
+
* locality for very large write‑heavy tables and accept timestamp exposure.
|
|
1086
|
+
*
|
|
1087
|
+
* ### Future
|
|
858
1088
|
*
|
|
859
|
-
*
|
|
860
|
-
*
|
|
1089
|
+
* A possible hybrid masked‑time approach (`timestamp ^ H(cluster_id, timestamp
|
|
1090
|
+
*
|
|
1091
|
+
* > > N)`) could provide locality without exposing raw creation time. See
|
|
1092
|
+
* > > https://brooker.co.za/blog/2025/10/22/uuidv7.html
|
|
861
1093
|
*
|
|
862
1094
|
* @category String
|
|
863
1095
|
*/
|
|
864
|
-
export declare const Id: BrandType<Type<"String", string, string, StringError, string, StringError>, "Id",
|
|
1096
|
+
export declare const Id: BrandType<Type<"String", string, string, StringError, string, StringError>, "Id", IdError, StringError>;
|
|
865
1097
|
export type Id = typeof Id.Type;
|
|
866
|
-
export
|
|
1098
|
+
export interface IdError extends TypeError<"Id"> {
|
|
1099
|
+
}
|
|
1100
|
+
export declare const formatIdError: TypeErrorFormatter<IdError>;
|
|
867
1101
|
/**
|
|
868
|
-
* Creates
|
|
1102
|
+
* Creates a random {@link Id}. This is the recommended default.
|
|
1103
|
+
*
|
|
1104
|
+
* Use {@link createIdFromString} for deterministic mapping of external IDs or
|
|
1105
|
+
* {@link createIdAsUuidv7} when you accept timestamp leakage for index
|
|
1106
|
+
* locality.
|
|
869
1107
|
*
|
|
870
1108
|
* ### Example
|
|
871
1109
|
*
|
|
872
1110
|
* ```ts
|
|
873
|
-
* // string & Brand<"Id">
|
|
874
1111
|
* const id = createId(deps);
|
|
1112
|
+
* const todoId = createId<"Todo">(deps);
|
|
875
1113
|
* ```
|
|
876
1114
|
*/
|
|
877
|
-
export declare const createId: (deps:
|
|
1115
|
+
export declare const createId: <B extends string = never>(deps: RandomBytesDep) => [B] extends [never] ? Id : Id & Brand<B>;
|
|
878
1116
|
/**
|
|
879
|
-
*
|
|
1117
|
+
* Creates an {@link Id} from a string using SHA-256.
|
|
1118
|
+
*
|
|
1119
|
+
* When integrating with external systems that use different ID formats, use
|
|
1120
|
+
* this function to convert external IDs into valid Evolu IDs.
|
|
1121
|
+
*
|
|
1122
|
+
* In Evolu's CRDT, the ID serves as the unique identifier for conflict
|
|
1123
|
+
* resolution across distributed clients. When multiple clients create records
|
|
1124
|
+
* with the same external identifier, they must resolve to the same Evolu ID to
|
|
1125
|
+
* ensure data consistency.
|
|
1126
|
+
*
|
|
1127
|
+
* ### Example
|
|
1128
|
+
*
|
|
1129
|
+
* ```ts
|
|
1130
|
+
* // Both clients will generate the same ID
|
|
1131
|
+
* const id1 = createIdFromString("user-api-123");
|
|
1132
|
+
* const id2 = createIdFromString("user-api-123");
|
|
1133
|
+
* console.log(id1 === id2); // true
|
|
1134
|
+
*
|
|
1135
|
+
* upsert("todo", {
|
|
1136
|
+
* id: createIdFromString("external-todo-456"),
|
|
1137
|
+
* title: "Synced from external system",
|
|
1138
|
+
* });
|
|
1139
|
+
* ```
|
|
1140
|
+
*
|
|
1141
|
+
* **Important**: This transformation uses the first 16 bytes of SHA-256 hash of
|
|
1142
|
+
* the string bytes, therefore it's not possible to recover the original
|
|
1143
|
+
* external string from the generated {@link Id}. If you need to preserve the
|
|
1144
|
+
* original external ID, store it in a separate column.
|
|
1145
|
+
*
|
|
1146
|
+
* @category String
|
|
1147
|
+
*/
|
|
1148
|
+
export declare const createIdFromString: <B extends string = never>(value: string) => [B] extends [never] ? Id : Id & Brand<B>;
|
|
1149
|
+
/**
|
|
1150
|
+
* Creates an {@link Id} embedding timestamp bits (UUID v7 layout) before
|
|
1151
|
+
* Base64Url encoding.
|
|
1152
|
+
*
|
|
1153
|
+
* Tradeoff: better insertion locality / index performance for huge datasets vs
|
|
1154
|
+
* leaking creation time everywhere the Id appears. Evolu uses {@link createId}
|
|
1155
|
+
* by default to avoid activity leakage; choose this only if you explicitly
|
|
1156
|
+
* accept timestamp exposure.
|
|
1157
|
+
*
|
|
1158
|
+
* ### Example
|
|
1159
|
+
*
|
|
1160
|
+
* ```ts
|
|
1161
|
+
* const id = createIdAsUuidv7({ randomBytes, time });
|
|
1162
|
+
* const todoId = createIdAsUuidv7<"Todo">({ randomBytes, time });
|
|
1163
|
+
* ```
|
|
1164
|
+
*/
|
|
1165
|
+
export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBytesDep & TimeDep) => [B] extends [never] ? Id : Id & Brand<B>;
|
|
1166
|
+
/**
|
|
1167
|
+
* Creates a branded {@link Id} Type for a table's primary key.
|
|
1168
|
+
*
|
|
1169
|
+
* The table name becomes an additional brand for type safety.
|
|
880
1170
|
*
|
|
881
1171
|
* ### Example
|
|
882
1172
|
*
|
|
@@ -888,16 +1178,22 @@ export declare const createId: (deps: NanoIdLibDep) => Id;
|
|
|
888
1178
|
*
|
|
889
1179
|
* @category String
|
|
890
1180
|
*/
|
|
891
|
-
export declare const id: <Table extends TypeName>(table: Table) =>
|
|
892
|
-
export interface
|
|
1181
|
+
export declare const id: <Table extends TypeName>(table: Table) => TableId<Table>;
|
|
1182
|
+
export interface TableId<Table extends TypeName> extends Type<"Id", string & Brand<"Id"> & Brand<Table>, string, TableIdError<Table>, string, StringError> {
|
|
893
1183
|
table: Table;
|
|
894
1184
|
}
|
|
895
|
-
export interface
|
|
1185
|
+
export interface TableIdError<Table extends TypeName = TypeName> extends TypeError<"TableId"> {
|
|
896
1186
|
readonly table: Table;
|
|
897
1187
|
}
|
|
898
|
-
export declare const
|
|
1188
|
+
export declare const formatTableIdError: TypeErrorFormatter<TableIdError<Capitalize<string>>>;
|
|
1189
|
+
/** Binary representation of an {@link Id}. */
|
|
1190
|
+
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>;
|
|
1191
|
+
export type IdBytes = typeof IdBytes.Type;
|
|
1192
|
+
export declare const idBytesTypeValueLength: NonNegativeInt;
|
|
1193
|
+
export declare const idToIdBytes: (id: Id) => IdBytes;
|
|
1194
|
+
export declare const idBytesToId: (idBytes: IdBytes) => Id;
|
|
899
1195
|
/**
|
|
900
|
-
* Positive number.
|
|
1196
|
+
* Positive number (> 0).
|
|
901
1197
|
*
|
|
902
1198
|
* ### Example
|
|
903
1199
|
*
|
|
@@ -915,7 +1211,7 @@ export interface PositiveError extends TypeError<"Positive"> {
|
|
|
915
1211
|
}
|
|
916
1212
|
export declare const formatPositiveError: TypeErrorFormatter<PositiveError>;
|
|
917
1213
|
/**
|
|
918
|
-
* Negative number.
|
|
1214
|
+
* Negative number (< 0).
|
|
919
1215
|
*
|
|
920
1216
|
* ### Example
|
|
921
1217
|
*
|
|
@@ -930,7 +1226,7 @@ export interface NegativeError extends TypeError<"Negative"> {
|
|
|
930
1226
|
}
|
|
931
1227
|
export declare const formatNegativeError: TypeErrorFormatter<NegativeError>;
|
|
932
1228
|
/**
|
|
933
|
-
* Non-positive number.
|
|
1229
|
+
* Non-positive number (≤ 0).
|
|
934
1230
|
*
|
|
935
1231
|
* ### Example
|
|
936
1232
|
*
|
|
@@ -945,7 +1241,7 @@ export interface NonPositiveError extends TypeError<"NonPositive"> {
|
|
|
945
1241
|
}
|
|
946
1242
|
export declare const formatNonPositiveError: TypeErrorFormatter<NonPositiveError>;
|
|
947
1243
|
/**
|
|
948
|
-
* Non-negative number.
|
|
1244
|
+
* Non-negative number (≥ 0).
|
|
949
1245
|
*
|
|
950
1246
|
* ### Example
|
|
951
1247
|
*
|
|
@@ -959,16 +1255,32 @@ export declare const nonNegative: BrandFactory<"NonNegative", number, NonNegativ
|
|
|
959
1255
|
export interface NonNegativeError extends TypeError<"NonNegative"> {
|
|
960
1256
|
}
|
|
961
1257
|
export declare const formatNonNegativeError: TypeErrorFormatter<NonNegativeError>;
|
|
962
|
-
/**
|
|
1258
|
+
/**
|
|
1259
|
+
* Non-negative number (≥ 0).
|
|
1260
|
+
*
|
|
1261
|
+
* @category Number
|
|
1262
|
+
*/
|
|
963
1263
|
export declare const NonNegativeNumber: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "NonNegative", NonNegativeError, NumberError>;
|
|
964
1264
|
export type NonNegativeNumber = typeof NonNegativeNumber.Type;
|
|
965
|
-
/**
|
|
966
|
-
|
|
1265
|
+
/**
|
|
1266
|
+
* Positive number (> 0).
|
|
1267
|
+
*
|
|
1268
|
+
* @category Number
|
|
1269
|
+
*/
|
|
1270
|
+
export declare const PositiveNumber: BrandType<Type<"Brand", number & Brand<"NonNegative">, number, NonNegativeError, number, NumberError>, "Positive", PositiveError, NonNegativeError | NumberError>;
|
|
967
1271
|
export type PositiveNumber = typeof PositiveNumber.Type;
|
|
968
|
-
/**
|
|
1272
|
+
/**
|
|
1273
|
+
* Non-positive number (≤ 0).
|
|
1274
|
+
*
|
|
1275
|
+
* @category Number
|
|
1276
|
+
*/
|
|
969
1277
|
export declare const NonPositiveNumber: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "NonPositive", NonPositiveError, NumberError>;
|
|
970
1278
|
export type NonPositiveNumber = typeof NonPositiveNumber.Type;
|
|
971
|
-
/**
|
|
1279
|
+
/**
|
|
1280
|
+
* Negative number (< 0).
|
|
1281
|
+
*
|
|
1282
|
+
* @category Number
|
|
1283
|
+
*/
|
|
972
1284
|
export declare const NegativeNumber: BrandType<Type<"Brand", number & Brand<"NonPositive">, number, NonPositiveError, number, NumberError>, "Negative", NegativeError, NumberError | NonPositiveError>;
|
|
973
1285
|
export type NegativeNumber = typeof NegativeNumber.Type;
|
|
974
1286
|
/**
|
|
@@ -993,17 +1305,35 @@ export declare const formatIntError: TypeErrorFormatter<IntError>;
|
|
|
993
1305
|
*/
|
|
994
1306
|
export declare const Int: BrandType<Type<"Number", number, number, NumberError, number, NumberError>, "Int", IntError, NumberError>;
|
|
995
1307
|
export type Int = typeof Int.Type;
|
|
996
|
-
/**
|
|
997
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* Non-negative integer (≥ 0).
|
|
1310
|
+
*
|
|
1311
|
+
* @category Number
|
|
1312
|
+
*/
|
|
1313
|
+
export declare const NonNegativeInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonNegative", NonNegativeError, IntError | NumberError>;
|
|
998
1314
|
export type NonNegativeInt = typeof NonNegativeInt.Type;
|
|
999
|
-
/**
|
|
1000
|
-
|
|
1315
|
+
/**
|
|
1316
|
+
* Positive integer (> 0).
|
|
1317
|
+
*
|
|
1318
|
+
* @category Number
|
|
1319
|
+
*/
|
|
1320
|
+
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
1321
|
export type PositiveInt = typeof PositiveInt.Type;
|
|
1002
|
-
/**
|
|
1003
|
-
export declare const
|
|
1322
|
+
/** Maximum safe positive integer value for practically infinite operations. */
|
|
1323
|
+
export declare const maxPositiveInt: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"Positive">;
|
|
1324
|
+
/**
|
|
1325
|
+
* Non-positive integer (≤ 0).
|
|
1326
|
+
*
|
|
1327
|
+
* @category Number
|
|
1328
|
+
*/
|
|
1329
|
+
export declare const NonPositiveInt: BrandType<Type<"Brand", number & Brand<"Int">, number, IntError, number, NumberError>, "NonPositive", NonPositiveError, IntError | NumberError>;
|
|
1004
1330
|
export type NonPositiveInt = typeof NonPositiveInt.Type;
|
|
1005
|
-
/**
|
|
1006
|
-
|
|
1331
|
+
/**
|
|
1332
|
+
* Negative integer (< 0).
|
|
1333
|
+
*
|
|
1334
|
+
* @category Number
|
|
1335
|
+
*/
|
|
1336
|
+
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
1337
|
export type NegativeInt = typeof NegativeInt.Type;
|
|
1008
1338
|
/**
|
|
1009
1339
|
* Number greater than a specified value.
|
|
@@ -1110,9 +1440,6 @@ export interface BetweenError<Min extends number = number, Max extends number =
|
|
|
1110
1440
|
readonly max: Max;
|
|
1111
1441
|
}
|
|
1112
1442
|
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
1443
|
/**
|
|
1117
1444
|
* Literal {@link Type}.
|
|
1118
1445
|
*
|
|
@@ -1138,67 +1465,6 @@ export interface LiteralError<T extends Literal = Literal> extends TypeError<"Li
|
|
|
1138
1465
|
readonly expected: T;
|
|
1139
1466
|
}
|
|
1140
1467
|
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
1468
|
/**
|
|
1203
1469
|
* Array of a specific {@link Type}.
|
|
1204
1470
|
*
|
|
@@ -1656,16 +1922,12 @@ export type Int64 = typeof Int64.Type;
|
|
|
1656
1922
|
export interface Int64Error extends TypeError<"Int64"> {
|
|
1657
1923
|
}
|
|
1658
1924
|
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
1925
|
/**
|
|
1664
1926
|
* Stringified {@link Int64}.
|
|
1665
1927
|
*
|
|
1666
|
-
* @category
|
|
1928
|
+
* @category String
|
|
1667
1929
|
*/
|
|
1668
|
-
export declare const Int64String: BrandType<Type<"
|
|
1930
|
+
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
1931
|
export type Int64String = typeof Int64String.Type;
|
|
1670
1932
|
export interface Int64StringError extends TypeError<"Int64String"> {
|
|
1671
1933
|
}
|
|
@@ -1700,42 +1962,63 @@ export declare const JsonArray: ArrayType<RecursiveType<UnionType<[Type<"String"
|
|
|
1700
1962
|
* @category Object
|
|
1701
1963
|
*/
|
|
1702
1964
|
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>>]>>>;
|
|
1965
|
+
export declare const parseJson: (value: string) => Result<JsonValue, JsonError>;
|
|
1703
1966
|
/**
|
|
1704
|
-
*
|
|
1705
|
-
* JsonValue back into a JSON string.
|
|
1967
|
+
* JSON-string {@link Type}.
|
|
1706
1968
|
*
|
|
1707
1969
|
* ### Example
|
|
1708
1970
|
*
|
|
1709
1971
|
* ```ts
|
|
1710
|
-
*
|
|
1711
|
-
*
|
|
1972
|
+
* const result = Json.from('{"key":"value"}'); // ok
|
|
1973
|
+
* const error = Json.from("invalid json"); // err
|
|
1712
1974
|
* ```
|
|
1713
1975
|
*
|
|
1714
1976
|
* @category String
|
|
1715
1977
|
*/
|
|
1716
|
-
export declare const
|
|
1717
|
-
export
|
|
1978
|
+
export declare const Json: BrandType<Type<"String", string, string, StringError, string, StringError>, "Json", JsonError, StringError>;
|
|
1979
|
+
export type Json = typeof Json.Type;
|
|
1980
|
+
export interface JsonError extends TypeError<"Json"> {
|
|
1718
1981
|
readonly message: string;
|
|
1719
1982
|
}
|
|
1720
|
-
export declare const
|
|
1983
|
+
export declare const formatJsonError: TypeErrorFormatter<JsonError>;
|
|
1984
|
+
export declare const jsonValueToJson: (value: JsonValue) => Json;
|
|
1985
|
+
export declare const jsonToJsonValue: (value: Json) => JsonValue;
|
|
1721
1986
|
/**
|
|
1722
|
-
* JSON
|
|
1987
|
+
* Creates a branded JSON string {@link Type} and type-safe conversion functions
|
|
1988
|
+
* for a given Type.
|
|
1989
|
+
*
|
|
1990
|
+
* This factory creates:
|
|
1991
|
+
*
|
|
1992
|
+
* 1. A branded string Type that validates JSON parsing and structural conformity
|
|
1993
|
+
* 2. A serialization function (Type → branded JSON string)
|
|
1994
|
+
* 3. A parsing function (branded JSON string → Type, skipping validation)
|
|
1995
|
+
*
|
|
1996
|
+
* Optimized for Evolu's SQLite workflow where we store typed JSON strings and
|
|
1997
|
+
* need type-safe conversions without double parsing.
|
|
1723
1998
|
*
|
|
1724
1999
|
* ### Example
|
|
1725
2000
|
*
|
|
1726
2001
|
* ```ts
|
|
1727
|
-
* const
|
|
1728
|
-
*
|
|
1729
|
-
*
|
|
2002
|
+
* const Person = object({
|
|
2003
|
+
* name: NonEmptyString100,
|
|
2004
|
+
* age: FiniteNumber,
|
|
2005
|
+
* });
|
|
2006
|
+
* type Person = typeof Person.Type;
|
|
1730
2007
|
*
|
|
1731
|
-
*
|
|
2008
|
+
* const [PersonJson, personToPersonJson, personJsonToPerson] = json(
|
|
2009
|
+
* Person,
|
|
2010
|
+
* "PersonJson",
|
|
2011
|
+
* );
|
|
2012
|
+
* // string & Brand<"PersonJson">
|
|
2013
|
+
* type PersonJson = typeof PersonJson.Type;
|
|
2014
|
+
*
|
|
2015
|
+
* // Usage:
|
|
2016
|
+
* const person: Person = { name: "Alice", age: 30 };
|
|
2017
|
+
* const jsonString = personToPersonJson(person); // PersonJson
|
|
2018
|
+
* const backToPerson = personJsonToPerson(jsonString); // Person
|
|
2019
|
+
* ```
|
|
1732
2020
|
*/
|
|
1733
|
-
export declare const
|
|
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>;
|
|
2021
|
+
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
2022
|
/**
|
|
1740
2023
|
* Optional {@link Type}.
|
|
1741
2024
|
*
|
|
@@ -1766,7 +2049,7 @@ export declare const isOptionalType: (x: unknown) => x is OptionalType<any>;
|
|
|
1766
2049
|
/**
|
|
1767
2050
|
* Creates a partial object type where all properties are optional.
|
|
1768
2051
|
*
|
|
1769
|
-
* This is useful when
|
|
2052
|
+
* This is useful when we want to validate an object in which none of the keys
|
|
1770
2053
|
* are required, but if they are present they must conform to their
|
|
1771
2054
|
* corresponding Types.
|
|
1772
2055
|
*
|
|
@@ -1812,36 +2095,18 @@ export type NullTypeInMembers<Members extends [AnyType, ...Array<AnyType>]> = Me
|
|
|
1812
2095
|
* @category Object
|
|
1813
2096
|
*/
|
|
1814
2097
|
export declare function omit<T extends ObjectType<any>, Keys extends keyof T["props"]>(objectType: T, ...keys: ReadonlyArray<Keys>): ObjectType<Omit<T["props"], Keys>>;
|
|
2098
|
+
export declare const maxMutationSize = 655360;
|
|
1815
2099
|
/**
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
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
|
-
* ```
|
|
2100
|
+
* Evolu has to limit the maximum mutation size. Otherwise, sync couldn't use
|
|
2101
|
+
* the `maxProtocolMessageRangesSize`. The max size is 640KB in bytes, measured
|
|
2102
|
+
* via MessagePack. Evolu Protocol DbChange will be smaller thanks to various
|
|
2103
|
+
* optimizations.
|
|
1843
2104
|
*/
|
|
1844
|
-
export declare const
|
|
2105
|
+
export declare const validMutationSize: <T extends AnyType>(type: T) => BrandType<T, "ValidMutationSize", ValidMutationSizeError, InferErrors<T>>;
|
|
2106
|
+
export interface ValidMutationSizeError extends TypeError<"ValidMutationSize"> {
|
|
2107
|
+
}
|
|
2108
|
+
export declare const formatValidMutationSizeError: TypeErrorFormatter<ValidMutationSizeError>;
|
|
2109
|
+
export type ValidMutationSize<Props extends Record<string, AnyType>> = BrandType<ObjectType<Props>, "ValidMutationSize", ValidMutationSizeError, InferErrors<ObjectType<Props>>>;
|
|
1845
2110
|
/**
|
|
1846
2111
|
* Union of all `TypeError`s defined in the `Type.ts` file, including base type
|
|
1847
2112
|
* errors (e.g., `StringError`, `NumberError`), composite type errors
|
|
@@ -1855,72 +2120,76 @@ export declare const json: <T extends AnyType, Name extends TypeName>(type: T, n
|
|
|
1855
2120
|
*
|
|
1856
2121
|
* @category Utilities
|
|
1857
2122
|
*/
|
|
1858
|
-
export type TypeErrors<ExtraErrors extends TypeError = never> = StringError | NumberError | BigIntError | BooleanError | UndefinedError | NullError | FunctionError | Uint8ArrayError | InstanceOfError | EvoluTypeError | CurrencyCodeError |
|
|
2123
|
+
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
2124
|
/**
|
|
1860
|
-
*
|
|
1861
|
-
* {@link TypeErrors} and custom errors. It also lets us override the default
|
|
1862
|
-
* formatting for specific errors.
|
|
2125
|
+
* Formats Evolu Type errors into user-friendly messages.
|
|
1863
2126
|
*
|
|
1864
|
-
*
|
|
1865
|
-
*
|
|
2127
|
+
* Evolu Type typed errors ensure every error type must have a formatter.
|
|
2128
|
+
* TypeScript enforces this at compile-time, preventing unhandled validation
|
|
2129
|
+
* errors from reaching users.
|
|
1866
2130
|
*
|
|
1867
|
-
*
|
|
2131
|
+
* The `createFormatTypeError` function handles both built-in {@link TypeErrors}
|
|
2132
|
+
* and custom errors, and lets us override default formatting for specific
|
|
2133
|
+
* errors.
|
|
2134
|
+
*
|
|
2135
|
+
* ### Example
|
|
1868
2136
|
*
|
|
1869
2137
|
* ```ts
|
|
1870
|
-
* const
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
2138
|
+
* const formatTypeError = createFormatTypeError<
|
|
2139
|
+
* MinLengthError | MaxLengthError
|
|
2140
|
+
* >((error): string => {
|
|
2141
|
+
* switch (error.type) {
|
|
2142
|
+
* case "MinLength":
|
|
2143
|
+
* return `Text must be at least ${error.min} character${error.min === 1 ? "" : "s"} long`;
|
|
2144
|
+
* case "MaxLength":
|
|
2145
|
+
* return `Text is too long (maximum ${error.max} characters)`;
|
|
2146
|
+
* }
|
|
2147
|
+
* });
|
|
1873
2148
|
* ```
|
|
1874
2149
|
*
|
|
1875
|
-
*
|
|
2150
|
+
* Alternatively, write a custom formatter from scratch without using
|
|
2151
|
+
* `createFormatTypeError`. This gives us full control over error formatting:
|
|
1876
2152
|
*
|
|
1877
2153
|
* ```ts
|
|
1878
|
-
*
|
|
1879
|
-
*
|
|
2154
|
+
* const Person = object({
|
|
2155
|
+
* name: NonEmptyTrimmedString100,
|
|
2156
|
+
* age: optional(PositiveInt),
|
|
2157
|
+
* });
|
|
2158
|
+
*
|
|
2159
|
+
* // Define only the errors actually used by Person Type
|
|
2160
|
+
* type PersonErrors =
|
|
1880
2161
|
* | StringError
|
|
1881
|
-
* | MinLengthError
|
|
1882
2162
|
* | MaxLengthError
|
|
1883
|
-
* |
|
|
1884
|
-
* | IdError
|
|
2163
|
+
* | MinLengthError
|
|
1885
2164
|
* | TrimmedError
|
|
1886
|
-
* |
|
|
1887
|
-
* |
|
|
1888
|
-
*
|
|
1889
|
-
* |
|
|
1890
|
-
* |
|
|
1891
|
-
*
|
|
1892
|
-
* const formatTypeError: TypeErrorFormatter<
|
|
1893
|
-
* // In the real code, we would use the createTypeErrorFormatter helper
|
|
1894
|
-
* // that safely stringifies error value.
|
|
2165
|
+
* | PositiveError
|
|
2166
|
+
* | NonNegativeError
|
|
2167
|
+
* | IntError
|
|
2168
|
+
* | NumberError
|
|
2169
|
+
* | ObjectError<Record<string, PersonErrors>>;
|
|
2170
|
+
*
|
|
2171
|
+
* const formatTypeError: TypeErrorFormatter<PersonErrors> = (error) => {
|
|
1895
2172
|
* 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
2173
|
* case "String":
|
|
1907
|
-
* // We can reuse existing formatter.
|
|
1908
2174
|
* return formatStringError(error);
|
|
2175
|
+
* case "Number":
|
|
2176
|
+
* return "Must be a number";
|
|
2177
|
+
* case "MinLength":
|
|
2178
|
+
* return `Must be at least ${error.min} characters`;
|
|
2179
|
+
* case "MaxLength":
|
|
2180
|
+
* return `Cannot exceed ${error.max} characters`;
|
|
1909
2181
|
* case "Trimmed":
|
|
1910
|
-
* return "
|
|
1911
|
-
* case "
|
|
1912
|
-
* return "
|
|
1913
|
-
* case "
|
|
1914
|
-
* return
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
* return `Union errors: ${error.errors.map(formatTypeError).join(", ")}`;
|
|
2182
|
+
* return "Cannot have leading or trailing spaces";
|
|
2183
|
+
* case "Positive":
|
|
2184
|
+
* return "Must be a positive number";
|
|
2185
|
+
* case "NonNegative":
|
|
2186
|
+
* return "Must be zero or positive";
|
|
2187
|
+
* case "Int":
|
|
2188
|
+
* return "Must be an integer";
|
|
1918
2189
|
* case "Object": {
|
|
1919
|
-
* if (
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1922
|
-
* )
|
|
1923
|
-
* return "A developer made an error, this should not happen.";
|
|
2190
|
+
* if (error.reason.kind === "NotObject") return "Must be an object";
|
|
2191
|
+
* if (error.reason.kind === "ExtraKeys")
|
|
2192
|
+
* return "Contains unexpected fields";
|
|
1924
2193
|
* const firstError = Object.values(error.reason.errors).find(
|
|
1925
2194
|
* (e) => e !== undefined,
|
|
1926
2195
|
* )!;
|
|
@@ -1933,5 +2202,69 @@ export type TypeErrors<ExtraErrors extends TypeError = never> = StringError | Nu
|
|
|
1933
2202
|
* @category Utilities
|
|
1934
2203
|
*/
|
|
1935
2204
|
export declare const createFormatTypeError: <ExtraErrors extends TypeError = never>(extraFormatter?: TypeErrorFormatter<ExtraErrors>) => TypeErrorFormatter<TypeErrors<ExtraErrors>>;
|
|
2205
|
+
/**
|
|
2206
|
+
* Converts an Evolu {@link TypeError} to Standard Schema V1 issues format.
|
|
2207
|
+
*
|
|
2208
|
+
* This function recursively converts Evolu's typed errors into the Standard
|
|
2209
|
+
* Schema issue format with proper path tracking for nested structures.
|
|
2210
|
+
*
|
|
2211
|
+
* @category Utilities
|
|
2212
|
+
*/
|
|
2213
|
+
export declare const typeErrorToStandardSchemaIssues: <ExtraErrors extends TypeError = never>(error: TypeErrors<ExtraErrors>, formatTypeError: TypeErrorFormatter<TypeErrors<ExtraErrors>>, path?: ReadonlyArray<PropertyKey>) => ReadonlyArray<StandardSchemaV1.Issue>;
|
|
2214
|
+
/** The Standard Schema interface. */
|
|
2215
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
2216
|
+
/** The Standard Schema properties. */
|
|
2217
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
2218
|
+
}
|
|
2219
|
+
export declare namespace StandardSchemaV1 {
|
|
2220
|
+
/** The Standard Schema properties interface. */
|
|
2221
|
+
interface Props<Input = unknown, Output = Input> {
|
|
2222
|
+
/** The version number of the standard. */
|
|
2223
|
+
readonly version: 1;
|
|
2224
|
+
/** The vendor name of the schema library. */
|
|
2225
|
+
readonly vendor: string;
|
|
2226
|
+
/** Validates unknown input values. */
|
|
2227
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
2228
|
+
/** Inferred types associated with the schema. */
|
|
2229
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
2230
|
+
}
|
|
2231
|
+
/** The result interface of the validate function. */
|
|
2232
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
2233
|
+
/** The result interface if validation succeeds. */
|
|
2234
|
+
interface SuccessResult<Output> {
|
|
2235
|
+
/** The typed output value. */
|
|
2236
|
+
readonly value: Output;
|
|
2237
|
+
/** The non-existent issues. */
|
|
2238
|
+
readonly issues?: undefined;
|
|
2239
|
+
}
|
|
2240
|
+
/** The result interface if validation fails. */
|
|
2241
|
+
interface FailureResult {
|
|
2242
|
+
/** The issues of failed validation. */
|
|
2243
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
2244
|
+
}
|
|
2245
|
+
/** The issue interface of the failure output. */
|
|
2246
|
+
interface Issue {
|
|
2247
|
+
/** The error message of the issue. */
|
|
2248
|
+
readonly message: string;
|
|
2249
|
+
/** The path of the issue, if any. */
|
|
2250
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
2251
|
+
}
|
|
2252
|
+
/** The path segment interface of the issue. */
|
|
2253
|
+
interface PathSegment {
|
|
2254
|
+
/** The key representing a path segment. */
|
|
2255
|
+
readonly key: PropertyKey;
|
|
2256
|
+
}
|
|
2257
|
+
/** The Standard Schema types interface. */
|
|
2258
|
+
interface Types<Input = unknown, Output = Input> {
|
|
2259
|
+
/** The input type of the schema. */
|
|
2260
|
+
readonly input: Input;
|
|
2261
|
+
/** The output type of the schema. */
|
|
2262
|
+
readonly output: Output;
|
|
2263
|
+
}
|
|
2264
|
+
/** Infers the input type of a Standard Schema. */
|
|
2265
|
+
type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
2266
|
+
/** Infers the output type of a Standard Schema. */
|
|
2267
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
2268
|
+
}
|
|
1936
2269
|
export {};
|
|
1937
2270
|
//# sourceMappingURL=Type.d.ts.map
|