@evolu/common 8.3.0 → 8.3.2
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/dist/src/Type.d.ts +65 -87
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +226 -136
- package/package.json +5 -5
- package/src/Type.ts +252 -166
package/src/Type.ts
CHANGED
|
@@ -8,17 +8,84 @@
|
|
|
8
8
|
* valid `Output` into a `CanonicalInput`. Types can validate, refine,
|
|
9
9
|
* transform, and compose without losing the contracts TypeScript can express.
|
|
10
10
|
*
|
|
11
|
+
* Decoding failures are explicit {@link Result} values, and their structured
|
|
12
|
+
* errors preserve the exact error types each Type can return.
|
|
13
|
+
*
|
|
14
|
+
* Evolu Type is designed to make correct code the easiest code to write:
|
|
15
|
+
*
|
|
16
|
+
* - Predefined constraints add a {@link Brand} to their Output.
|
|
17
|
+
* - Invalid declarations produce readable {@link CompileTimeError} types when the
|
|
18
|
+
* compiler can detect them.
|
|
19
|
+
* - Evolu Type uses runtime {@link assert | assertions} to detect developer errors
|
|
20
|
+
* that TypeScript cannot express, such as excess properties and sparse
|
|
21
|
+
* arrays.
|
|
22
|
+
* - Typed `from` boundaries allow connecting value producers to domain fields
|
|
23
|
+
* through their exact TypeScript types, so incompatible contract changes are
|
|
24
|
+
* compile-time errors rather than runtime validation errors.
|
|
25
|
+
* - Lawful codecs compose without creating unencodable values: every valid Output
|
|
26
|
+
* has a canonical Input representation and round-trips to the same semantic
|
|
27
|
+
* value.
|
|
28
|
+
* - Type-safe localization infers the required error formatters from selected
|
|
29
|
+
* Types, so missing validation messages are compile-time errors.
|
|
30
|
+
*
|
|
31
|
+
* Correctness is especially important for local-first data: application authors
|
|
32
|
+
* cannot inspect or repair a user's data.
|
|
33
|
+
*
|
|
34
|
+
* Evolu Type is optimized for small real-world bundles: composed Types share
|
|
35
|
+
* runtime code, while unused validators and formatters are tree-shaken. It
|
|
36
|
+
* could be smaller with less descriptive assertion messages, but Evolu favors
|
|
37
|
+
* actionable diagnostics over micro-optimizing isolated Types.
|
|
38
|
+
*
|
|
39
|
+
* Predefined Types use the names of corresponding JavaScript built-ins. When a
|
|
40
|
+
* Type shadows one, access the JavaScript built-in through `globalThis`, such
|
|
41
|
+
* as `globalThis.String` or `globalThis.Date`.
|
|
42
|
+
*
|
|
43
|
+
* Evolu Type supports [Standard Schema](https://standardschema.dev/) and
|
|
44
|
+
* requires TypeScript 7+ with `exactOptionalPropertyTypes` enabled.
|
|
45
|
+
*
|
|
46
|
+
* ## Examples
|
|
47
|
+
*
|
|
48
|
+
* Define a domain object with a custom `Age` Type, then validate unknown input:
|
|
49
|
+
*
|
|
11
50
|
* ```ts
|
|
12
51
|
* import {
|
|
52
|
+
* Number,
|
|
13
53
|
* NonEmptyTrimmedString100,
|
|
14
|
-
*
|
|
54
|
+
* brand,
|
|
55
|
+
* finite,
|
|
56
|
+
* int,
|
|
57
|
+
* lessThan,
|
|
58
|
+
* nonNaN,
|
|
59
|
+
* nonNegative,
|
|
15
60
|
* object,
|
|
61
|
+
* type Brand,
|
|
62
|
+
* type InferErrors,
|
|
16
63
|
* type InferType,
|
|
17
64
|
* } from "@evolu/common";
|
|
18
65
|
*
|
|
66
|
+
* // Age and its parent Types are predefined by Evolu. They are reconstructed
|
|
67
|
+
* // here to reveal every constraint behind a seemingly simple domain value.
|
|
68
|
+
* const NonNaNNumber = nonNaN(Number);
|
|
69
|
+
* const FiniteNumber = finite(NonNaNNumber);
|
|
70
|
+
* const Int = int(FiniteNumber);
|
|
71
|
+
* const NonNegativeInt = nonNegative(Int);
|
|
72
|
+
*
|
|
73
|
+
* const Age = brand("Age", lessThan(200)(NonNegativeInt));
|
|
74
|
+
* type Age = typeof Age.Output;
|
|
75
|
+
*
|
|
76
|
+
* expectTypeOf<Age>().toEqualTypeOf<
|
|
77
|
+
* number &
|
|
78
|
+
* Brand<"NonNaN"> &
|
|
79
|
+
* Brand<"Finite"> &
|
|
80
|
+
* Brand<"Int"> &
|
|
81
|
+
* Brand<"NonNegative"> &
|
|
82
|
+
* Brand<"LessThan200"> &
|
|
83
|
+
* Brand<"Age">
|
|
84
|
+
* >();
|
|
85
|
+
*
|
|
19
86
|
* const User = object({
|
|
20
87
|
* name: NonEmptyTrimmedString100,
|
|
21
|
-
* age:
|
|
88
|
+
* age: Age,
|
|
22
89
|
* });
|
|
23
90
|
* interface User extends InferType<typeof User> {}
|
|
24
91
|
*
|
|
@@ -27,74 +94,114 @@
|
|
|
27
94
|
*
|
|
28
95
|
* expectOk(user, { name: "Ada", age: 37 });
|
|
29
96
|
* expectTypeOf(user.value).toExtend<User>();
|
|
97
|
+
*
|
|
98
|
+
* const invalidUser = User.fromUnknown({ name: "Ada", age: 37.5 });
|
|
99
|
+
*
|
|
100
|
+
* expectErr(invalidUser, {
|
|
101
|
+
* type: "Object",
|
|
102
|
+
* reason: {
|
|
103
|
+
* kind: "Properties",
|
|
104
|
+
* errors: {
|
|
105
|
+
* age: { type: "Int", value: 37.5 },
|
|
106
|
+
* },
|
|
107
|
+
* },
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* // InferErrors includes every structured error User.fromUnknown can return.
|
|
111
|
+
* expectTypeOf(invalidUser.error).toEqualTypeOf<
|
|
112
|
+
* InferErrors<typeof User>
|
|
113
|
+
* >();
|
|
30
114
|
* ```
|
|
31
115
|
*
|
|
32
|
-
*
|
|
33
|
-
* separate from validation, so structured errors remain exhaustively typed and
|
|
34
|
-
* can be localized without changing the Type.
|
|
116
|
+
* A Type can format its structured errors into user-facing messages:
|
|
35
117
|
*
|
|
36
|
-
*
|
|
118
|
+
* ```ts
|
|
119
|
+
* import { Age } from "@evolu/common";
|
|
37
120
|
*
|
|
38
|
-
*
|
|
39
|
-
* - Invalid declarations produce readable {@link CompileTimeError} types when the
|
|
40
|
-
* compiler can detect them; runtime assertions enforce construction contracts
|
|
41
|
-
* it cannot prove.
|
|
42
|
-
* - Typed `from` boundaries allow connecting value producers to domain fields
|
|
43
|
-
* through their exact TypeScript types, so incompatible contract changes are
|
|
44
|
-
* compile-time errors rather than runtime validation errors.
|
|
45
|
-
* - Lawful codecs compose without creating unencodable values: every valid
|
|
46
|
-
* Output has a canonical Input representation and round-trips to the same
|
|
47
|
-
* semantic value.
|
|
48
|
-
* - Type-safe localization infers the required error formatters from selected
|
|
49
|
-
* Types, so missing validation messages are compile-time errors.
|
|
121
|
+
* const age = Age.fromUnknown(37.5);
|
|
50
122
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
123
|
+
* expectErr(age, { type: "Int", value: 37.5 });
|
|
124
|
+
* expect(Age.formatError(age.error)).toBe(
|
|
125
|
+
* "The value 37.5 must be a safe integer.",
|
|
126
|
+
* );
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* Use {@link localizeTypes} to derive Types with localized messages without
|
|
130
|
+
* changing validation behavior.
|
|
131
|
+
*
|
|
132
|
+
* One of Evolu Type's strongest features is typed `from` boundaries. A value
|
|
133
|
+
* producer, such as a form input, carries the precise constraints it
|
|
134
|
+
* guarantees, and TypeScript checks them against the consuming domain field.
|
|
135
|
+
* Unlike validation from `unknown` or `string`, this checks the contract
|
|
136
|
+
* between the producer and consumer, not merely whether the current value
|
|
137
|
+
* passes:
|
|
138
|
+
*
|
|
139
|
+
* ```ts
|
|
140
|
+
* import {
|
|
141
|
+
* NonEmptyTrimmedString100,
|
|
142
|
+
* NonEmptyTrimmedString1000,
|
|
143
|
+
* object,
|
|
144
|
+
* trim,
|
|
145
|
+
* type MaxLengthError,
|
|
146
|
+
* type MinLengthError,
|
|
147
|
+
* type Result,
|
|
148
|
+
* type TrimmedString,
|
|
149
|
+
* } from "@evolu/common";
|
|
150
|
+
*
|
|
151
|
+
* const Todo = object({ title: NonEmptyTrimmedString100 });
|
|
152
|
+
*
|
|
153
|
+
* // This is type-checked: Todo.from expects NonEmptyTrimmedString100.
|
|
154
|
+
* const title = NonEmptyTrimmedString100.orThrow("Buy milk");
|
|
155
|
+
* expectOk(Todo.from({ title }), { title });
|
|
156
|
+
*
|
|
157
|
+
* // Imagine the UI input component is changed to allow longer titles.
|
|
158
|
+
* // TypeScript rejects the mismatch, so users never see a save error
|
|
159
|
+
* // for a title the UI accepts but the domain cannot save.
|
|
160
|
+
* const longerTitle = NonEmptyTrimmedString1000.orThrow("Buy milk");
|
|
161
|
+
* // @ts-expect-error MaxLength1000 does not guarantee MaxLength100.
|
|
162
|
+
* Todo.from({ title: longerTitle });
|
|
163
|
+
*
|
|
164
|
+
* // Imagine a UI input component that returns TrimmedString.
|
|
165
|
+
* // from.parent.parent connects it to the domain field and validates the
|
|
166
|
+
* // remaining constraints.
|
|
167
|
+
* const titleFromTrimmingInput: TrimmedString = trim(" Buy milk ");
|
|
168
|
+
* const validatedTitle = Todo.props.title.from.parent.parent(
|
|
169
|
+
* titleFromTrimmingInput,
|
|
170
|
+
* );
|
|
171
|
+
*
|
|
172
|
+
* // No "not a string" or "not trimmed" errors: the input guarantees both.
|
|
173
|
+
* expectTypeOf(validatedTitle).toEqualTypeOf<
|
|
174
|
+
* Result<
|
|
175
|
+
* NonEmptyTrimmedString100,
|
|
176
|
+
* MaxLengthError<100> | MinLengthError<1>
|
|
177
|
+
* >
|
|
178
|
+
* >();
|
|
179
|
+
* expectOk(validatedTitle, "Buy milk");
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* Evolu includes dozens of predefined Types and Type factories. Use Types such
|
|
183
|
+
* as {@link Age}, {@link PositiveInt}, {@link DateIso},
|
|
184
|
+
* {@link NonEmptyTrimmedString100}, {@link Base64Url}, and {@link Json} directly.
|
|
185
|
+
* Build domain Types with factories such as {@link brand}, {@link typed},
|
|
186
|
+
* {@link minLength}, {@link maxLength}, {@link array}, {@link object},
|
|
187
|
+
* {@link union}, {@link templateLiteral}, {@link transform},
|
|
188
|
+
* {@link discriminatedUnion}, and {@link json}.
|
|
189
|
+
*
|
|
190
|
+
* ## Guarantees
|
|
191
|
+
*
|
|
192
|
+
* Evolu Type validates values; it does not defend against adversarial
|
|
193
|
+
* JavaScript such as malicious Proxies, mutation during validation, throwing
|
|
194
|
+
* traps, forged built-ins, or code deliberately bypassing TypeScript with `any`
|
|
195
|
+
* or casts.
|
|
196
|
+
*
|
|
197
|
+
* Evolu Type trusts application code and audited dependencies. Untrusted code
|
|
198
|
+
* can cause harm far beyond validation and must not run in the application.
|
|
199
|
+
* Defending against it would add complexity without creating a meaningful
|
|
200
|
+
* security boundary.
|
|
201
|
+
*
|
|
202
|
+
* Runtime assertions still detect accidental developer errors that TypeScript
|
|
203
|
+
* cannot express. They are correctness checks, not defenses against malicious
|
|
204
|
+
* code.
|
|
98
205
|
*
|
|
99
206
|
* ## FAQ
|
|
100
207
|
*
|
|
@@ -244,24 +351,18 @@
|
|
|
244
351
|
*
|
|
245
352
|
* ### How should values from another realm be handled?
|
|
246
353
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* realm.
|
|
354
|
+
* Values returned by legacy code or another realm can still be uncertain and
|
|
355
|
+
* should be validated. Realm-neutral Types accept an otherwise legitimate
|
|
356
|
+
* representation without requiring conversion merely because its JavaScript
|
|
357
|
+
* built-ins belong to another realm.
|
|
252
358
|
*
|
|
253
359
|
* When an application trusts both the producer and its return contract, expose
|
|
254
|
-
* that contract as an accurate TypeScript type and use the typed value
|
|
255
|
-
* If the boundary returns `unknown`, validate it instead of bypassing
|
|
256
|
-
* boundary with a cast. Use a specialized Type or explicit transformation
|
|
257
|
-
* the producer uses a different representation that needs adaptation or
|
|
360
|
+
* that contract as an accurate TypeScript type and use the typed value
|
|
361
|
+
* directly. If the boundary returns `unknown`, validate it instead of bypassing
|
|
362
|
+
* the boundary with a cast. Use a specialized Type or explicit transformation
|
|
363
|
+
* when the producer uses a different representation that needs adaptation or
|
|
258
364
|
* normalization.
|
|
259
365
|
*
|
|
260
|
-
* All executing JavaScript remains trusted. Deliberately forged built-ins,
|
|
261
|
-
* hostile Proxies, throwing traps, or sabotaged executable behavior can throw;
|
|
262
|
-
* Evolu Type does not selectively contain them or claim to be a security
|
|
263
|
-
* boundary for untrusted code.
|
|
264
|
-
*
|
|
265
366
|
* ### Why doesn't Evolu Type extract data from rich objects?
|
|
266
367
|
*
|
|
267
368
|
* Some validation libraries parse an object's data projection. An imaginary
|
|
@@ -485,7 +586,7 @@ export interface Type<
|
|
|
485
586
|
|
|
486
587
|
/**
|
|
487
588
|
* Formats an error returned by `fromUnknown` or `from` as one human-readable
|
|
488
|
-
* message.
|
|
589
|
+
* message. Predefined Types use English; {@link localizeTypes} derives Types
|
|
489
590
|
* with localized formatters.
|
|
490
591
|
*
|
|
491
592
|
* Structural errors retain nested errors and their locations in the typed
|
|
@@ -723,7 +824,7 @@ interface TransparentTypeError {
|
|
|
723
824
|
*
|
|
724
825
|
* @group Core
|
|
725
826
|
*/
|
|
726
|
-
//
|
|
827
|
+
// Predefined errors intentionally repeat narrower `value` properties. Making
|
|
727
828
|
// `value` generic here and sharing this base regresses `pnpm bench:type`.
|
|
728
829
|
export interface TypeValueError<
|
|
729
830
|
Name extends TypeName = TypeName,
|
|
@@ -938,8 +1039,8 @@ const assertTypeOutput = <Error extends TypeError>(
|
|
|
938
1039
|
* Pass the Types used together in one localization scope and formatter maps
|
|
939
1040
|
* keyed by locale. TypeScript infers every formatter required by the selected
|
|
940
1041
|
* Types, including errors from nested structural Types and recursive Lazy
|
|
941
|
-
* Types. Every locale must provide the complete inferred formatter set;
|
|
942
|
-
*
|
|
1042
|
+
* Types. Every locale must provide the complete inferred formatter set; missing
|
|
1043
|
+
* and unrelated formatters are compile-time errors.
|
|
943
1044
|
*
|
|
944
1045
|
* The result preserves the locale names, selected Type names, and exact
|
|
945
1046
|
* TypeScript types. A localized Type validates exactly like its source Type;
|
|
@@ -991,8 +1092,8 @@ const assertTypeOutput = <Error extends TypeError>(
|
|
|
991
1092
|
*
|
|
992
1093
|
* ### Supported locales
|
|
993
1094
|
*
|
|
994
|
-
* English is built in; use {@link Type} directly for its default formatters.
|
|
995
|
-
*
|
|
1095
|
+
* English is built in; use {@link Type} directly for its default formatters. The
|
|
1096
|
+
* following additional locales are available:
|
|
996
1097
|
*
|
|
997
1098
|
* - Arabic (`ar`)
|
|
998
1099
|
* - Bengali (`bn`)
|
|
@@ -2029,9 +2130,9 @@ const createChildType = <
|
|
|
2029
2130
|
* canonicalize multiple parent representations, but it must be total and must
|
|
2030
2131
|
* not lose distinctions present in the Output domain.
|
|
2031
2132
|
*
|
|
2032
|
-
* Transformation callbacks are Type construction code. Their successful
|
|
2033
|
-
*
|
|
2034
|
-
*
|
|
2133
|
+
* Transformation callbacks are Type construction code. Their successful results
|
|
2134
|
+
* are asserted against the declared boundary so a broken callback fails as a
|
|
2135
|
+
* developer error rather than becoming a validation error. Like all
|
|
2035
2136
|
* Type-construction callbacks, they are trusted to follow their declared
|
|
2036
2137
|
* TypeScript types. A `Result<_, never>` callback is therefore trusted never to
|
|
2037
2138
|
* return an `Err`.
|
|
@@ -2047,13 +2148,7 @@ const createChildType = <
|
|
|
2047
2148
|
* ### Example
|
|
2048
2149
|
*
|
|
2049
2150
|
* ```ts
|
|
2050
|
-
* import {
|
|
2051
|
-
* Boolean,
|
|
2052
|
-
* literal,
|
|
2053
|
-
* ok,
|
|
2054
|
-
* transform,
|
|
2055
|
-
* union,
|
|
2056
|
-
* } from "@evolu/common";
|
|
2151
|
+
* import { Boolean, literal, ok, transform, union } from "@evolu/common";
|
|
2057
2152
|
*
|
|
2058
2153
|
* const BooleanString = union(literal("false"), literal("true"));
|
|
2059
2154
|
* const BooleanFromString = transform(
|
|
@@ -2815,18 +2910,18 @@ interface ObjectTagOutputByName {
|
|
|
2815
2910
|
/**
|
|
2816
2911
|
* Realm-neutral {@link Type} trusting an object's reported tag.
|
|
2817
2912
|
*
|
|
2818
|
-
* Predefined built-
|
|
2819
|
-
* that trusted code does not forge their tags. They do not
|
|
2820
|
-
* internal slots. A custom tag refines the supplied Type and adds
|
|
2821
|
-
* evidence to its Output, so only a value validated by the resulting
|
|
2822
|
-
* accepted by its typed operations.
|
|
2913
|
+
* Predefined tags for JavaScript built-ins expose their native Output type
|
|
2914
|
+
* under the assumption that trusted code does not forge their tags. They do not
|
|
2915
|
+
* verify native internal slots. A custom tag refines the supplied Type and adds
|
|
2916
|
+
* nominal evidence to its Output, so only a value validated by the resulting
|
|
2917
|
+
* Type is accepted by its typed operations.
|
|
2823
2918
|
*
|
|
2824
|
-
* `Object.prototype.toString` recognizes legitimate built-ins from
|
|
2825
|
-
* realm, but any object can customize the result with
|
|
2826
|
-
* Types returned by this factory therefore classify
|
|
2827
|
-
* not security boundaries. Passing a forged
|
|
2828
|
-
* assumption of the predefined Type. Primitive
|
|
2829
|
-
* time.
|
|
2919
|
+
* `Object.prototype.toString` recognizes legitimate JavaScript built-ins from
|
|
2920
|
+
* another realm, but any object can customize the result with
|
|
2921
|
+
* `Symbol.toStringTag`. Types returned by this factory therefore classify
|
|
2922
|
+
* trusted values; they are not security boundaries. Passing a forged JavaScript
|
|
2923
|
+
* built-in tag violates the trust assumption of the predefined Type. Primitive
|
|
2924
|
+
* Outputs are rejected at compile time.
|
|
2830
2925
|
*
|
|
2831
2926
|
* ### Example
|
|
2832
2927
|
*
|
|
@@ -5331,8 +5426,8 @@ const base64UrlStringToUint8Array = (value: string): Uint8Array => {
|
|
|
5331
5426
|
/**
|
|
5332
5427
|
* Base64Url text without padding.
|
|
5333
5428
|
*
|
|
5334
|
-
*
|
|
5335
|
-
* {@link base64UrlToUint8Array}.
|
|
5429
|
+
* Convert bytes to Base64Url with {@link uint8ArrayToBase64Url} and convert
|
|
5430
|
+
* Base64Url to bytes with {@link base64UrlToUint8Array}.
|
|
5336
5431
|
*
|
|
5337
5432
|
* @group String
|
|
5338
5433
|
*/
|
|
@@ -5361,7 +5456,7 @@ export interface Base64UrlError extends TypeError<"Base64Url"> {
|
|
|
5361
5456
|
}
|
|
5362
5457
|
|
|
5363
5458
|
/**
|
|
5364
|
-
*
|
|
5459
|
+
* Converts bytes to {@link Base64Url}.
|
|
5365
5460
|
*
|
|
5366
5461
|
* ### Example
|
|
5367
5462
|
*
|
|
@@ -5379,7 +5474,7 @@ export const uint8ArrayToBase64Url = (bytes: Uint8Array): Base64Url =>
|
|
|
5379
5474
|
uint8ArrayToBase64UrlString(bytes) as Base64Url;
|
|
5380
5475
|
|
|
5381
5476
|
/**
|
|
5382
|
-
*
|
|
5477
|
+
* Converts {@link Base64Url} to bytes.
|
|
5383
5478
|
*
|
|
5384
5479
|
* ### Example
|
|
5385
5480
|
*
|
|
@@ -5823,7 +5918,9 @@ export interface Int64StringError extends TypeError<"Int64String"> {
|
|
|
5823
5918
|
* const result = Int64FromInt64String.fromUnknown("9223372036854775807");
|
|
5824
5919
|
*
|
|
5825
5920
|
* expectOk(result, 9223372036854775807n);
|
|
5826
|
-
* expect(Int64FromInt64String.to(result.value)).toBe(
|
|
5921
|
+
* expect(Int64FromInt64String.to(result.value)).toBe(
|
|
5922
|
+
* "9223372036854775807",
|
|
5923
|
+
* );
|
|
5827
5924
|
* ```
|
|
5828
5925
|
*
|
|
5829
5926
|
* @group Number
|
|
@@ -8467,7 +8564,7 @@ type PlainObjectError = ObjectError<
|
|
|
8467
8564
|
*
|
|
8468
8565
|
* @group Base
|
|
8469
8566
|
*/
|
|
8470
|
-
|
|
8567
|
+
const _Object: Type<
|
|
8471
8568
|
"Object",
|
|
8472
8569
|
Readonly<Record<string, unknown>>,
|
|
8473
8570
|
Readonly<Record<string, unknown>>,
|
|
@@ -8588,6 +8685,13 @@ export const Object: Type<
|
|
|
8588
8685
|
}) as TypeErrorFormatter<TypeError>),
|
|
8589
8686
|
);
|
|
8590
8687
|
|
|
8688
|
+
// Avoid a local `Object` binding because Babel's CommonJS transform injects
|
|
8689
|
+
// `Object.defineProperty` before it is initialized:
|
|
8690
|
+
// https://github.com/babel/babel/issues/16943
|
|
8691
|
+
// https://github.com/react/metro/issues/1331
|
|
8692
|
+
// https://github.com/expo/expo/issues/31167
|
|
8693
|
+
export { _Object as Object };
|
|
8694
|
+
|
|
8591
8695
|
const isPlainObject = (value: object): boolean => {
|
|
8592
8696
|
const prototype: unknown = globalThis.Object.getPrototypeOf(value);
|
|
8593
8697
|
return (
|
|
@@ -10280,10 +10384,10 @@ export interface ObjectNotObjectError extends TypeError<"Object"> {
|
|
|
10280
10384
|
*
|
|
10281
10385
|
* Object Types accept a `null` prototype or a prototype whose own prototype is
|
|
10282
10386
|
* `null`. This includes ordinary and cross-realm plain objects as well as
|
|
10283
|
-
* objects created from an immediate root prototype. Arrays, built-in
|
|
10284
|
-
* class instances, and objects with deeper custom prototype chains
|
|
10285
|
-
* error instead of having their prototype or inherited state
|
|
10286
|
-
* `reason.value` is the rejected object.
|
|
10387
|
+
* objects created from an immediate root prototype. Arrays, JavaScript built-in
|
|
10388
|
+
* objects, class instances, and objects with deeper custom prototype chains
|
|
10389
|
+
* return this error instead of having their prototype or inherited state
|
|
10390
|
+
* discarded. `reason.value` is the rejected object.
|
|
10287
10391
|
*
|
|
10288
10392
|
* @group Objects
|
|
10289
10393
|
*/
|
|
@@ -10634,8 +10738,8 @@ type OmitKeyConcreteTypeError = CompileTimeError<
|
|
|
10634
10738
|
* Creates a {@link Type} for {@link Result} values.
|
|
10635
10739
|
*
|
|
10636
10740
|
* Use this to validate Results crossing a storage, worker, API, or other
|
|
10637
|
-
* serialization boundary. `fromUnknown` returns an outer validation Result.
|
|
10638
|
-
*
|
|
10741
|
+
* serialization boundary. `fromUnknown` returns an outer validation Result. Its
|
|
10742
|
+
* successful value is the inner domain Result described by `okType` and
|
|
10639
10743
|
* `errorType`.
|
|
10640
10744
|
*
|
|
10641
10745
|
* ### Example
|
|
@@ -10844,7 +10948,8 @@ export interface Typed<Tag extends TypeName> {
|
|
|
10844
10948
|
* Extracts members of a {@link Typed} Output union by their `type` literal.
|
|
10845
10949
|
*
|
|
10846
10950
|
* The requested tag is constrained to the union's actual discriminator values,
|
|
10847
|
-
* so a misspelling is a TypeScript error instead of silently producing
|
|
10951
|
+
* so a misspelling is a TypeScript error instead of silently producing
|
|
10952
|
+
* `never`.
|
|
10848
10953
|
*
|
|
10849
10954
|
* ### Example
|
|
10850
10955
|
*
|
|
@@ -12517,7 +12622,7 @@ export const JsonObject = /*#__PURE__*/ record(
|
|
|
12517
12622
|
* A {@link String} Brand proving that its exact text parses to {@link JsonValue}.
|
|
12518
12623
|
*
|
|
12519
12624
|
* The Brand preserves whitespace, property order, and number spelling. Convert
|
|
12520
|
-
* it
|
|
12625
|
+
* it to {@link JsonValue} through {@link JsonValueFromJson} or
|
|
12521
12626
|
* {@link jsonToJsonValue}.
|
|
12522
12627
|
*
|
|
12523
12628
|
* @group JSON
|
|
@@ -12536,7 +12641,7 @@ export const Json = /*#__PURE__*/ brand(
|
|
|
12536
12641
|
export type Json = typeof Json.Output;
|
|
12537
12642
|
|
|
12538
12643
|
/**
|
|
12539
|
-
*
|
|
12644
|
+
* Converts proven {@link Json} text to an exact {@link JsonValue}.
|
|
12540
12645
|
*
|
|
12541
12646
|
* ### Example
|
|
12542
12647
|
*
|
|
@@ -12553,7 +12658,7 @@ export type Json = typeof Json.Output;
|
|
|
12553
12658
|
export const jsonToJsonValue = (value: Json): JsonValue => parseJson(value);
|
|
12554
12659
|
|
|
12555
12660
|
/**
|
|
12556
|
-
*
|
|
12661
|
+
* Converts an exact {@link JsonValue} to canonical {@link Json} text.
|
|
12557
12662
|
*
|
|
12558
12663
|
* ### Example
|
|
12559
12664
|
*
|
|
@@ -12600,67 +12705,48 @@ export const JsonValueFromJson = /*#__PURE__*/ transform(
|
|
|
12600
12705
|
);
|
|
12601
12706
|
|
|
12602
12707
|
/**
|
|
12603
|
-
* Branded {@link Json} Type and
|
|
12604
|
-
*
|
|
12605
|
-
* Use this
|
|
12606
|
-
*
|
|
12607
|
-
*
|
|
12608
|
-
*
|
|
12609
|
-
* Output.
|
|
12610
|
-
*
|
|
12611
|
-
* The supplied Type's `CanonicalInput` must be JSON-compatible. The encoder
|
|
12612
|
-
* first uses the Type's canonical `to` operation, then encodes that
|
|
12613
|
-
* representation as canonical Json. Runtime representation constraints
|
|
12614
|
-
* TypeScript cannot prove, such as dense Arrays and enumerable data properties,
|
|
12615
|
-
* are asserted as developer errors.
|
|
12616
|
-
*
|
|
12617
|
-
* The branded Json Type is the validation boundary for unknown JSON text. It
|
|
12618
|
-
* grants its {@link Brand} only when the text is valid Json and decoding it
|
|
12619
|
-
* through the supplied Type succeeds. The supplied Type is responsible for
|
|
12620
|
-
* preserving semantic Outputs across canonical JSON encoding and decoding. This
|
|
12621
|
-
* law cannot be checked generically because Types do not define semantic
|
|
12622
|
-
* equality. Before granting the Brand, the encoder asserts the weaker runtime
|
|
12623
|
-
* guarantee that the final Json successfully decodes through the supplied Type.
|
|
12624
|
-
* Failed decodability therefore throws as a developer error.
|
|
12625
|
-
*
|
|
12626
|
-
* Consequently, the two typed conversions return their values directly without
|
|
12627
|
-
* exposing a validation {@link Result}: an Output satisfying the JSON
|
|
12628
|
-
* representation contract of a correctly declared Type can always be encoded,
|
|
12629
|
-
* and the branded Json proves decoding will succeed. Decoding still runs the
|
|
12630
|
-
* Type pipeline because transformations may need to construct different Output
|
|
12631
|
-
* values.
|
|
12708
|
+
* Branded {@link Json} Type and conversions for another {@link Type}.
|
|
12709
|
+
*
|
|
12710
|
+
* Use this when a value must be stored as JSON text, such as in a JSON column
|
|
12711
|
+
* in an Evolu Schema. It returns a branded Json Type and functions for
|
|
12712
|
+
* converting the supplied Type's Output to and from that branded JSON
|
|
12713
|
+
* representation.
|
|
12632
12714
|
*
|
|
12633
12715
|
* ### Example
|
|
12634
12716
|
*
|
|
12635
12717
|
* ```ts
|
|
12636
12718
|
* import {
|
|
12637
12719
|
* Age,
|
|
12720
|
+
* NonEmptyTrimmedString100,
|
|
12638
12721
|
* json,
|
|
12639
12722
|
* object,
|
|
12640
|
-
* String,
|
|
12641
12723
|
* type Brand,
|
|
12642
|
-
* type InferType,
|
|
12643
|
-
* type Json,
|
|
12644
12724
|
* } from "@evolu/common";
|
|
12645
12725
|
*
|
|
12646
|
-
* const
|
|
12647
|
-
*
|
|
12726
|
+
* const User = object({
|
|
12727
|
+
* name: NonEmptyTrimmedString100,
|
|
12728
|
+
* age: Age,
|
|
12729
|
+
* });
|
|
12648
12730
|
*
|
|
12649
|
-
* const [
|
|
12650
|
-
*
|
|
12651
|
-
* "
|
|
12731
|
+
* const [UserJson, userToUserJson, userJsonToUser] = json(
|
|
12732
|
+
* User,
|
|
12733
|
+
* "UserJson",
|
|
12652
12734
|
* );
|
|
12653
|
-
* type PersonJson = typeof PersonJson.Output;
|
|
12654
|
-
*
|
|
12655
|
-
* expectTypeOf<PersonJson>().toEqualTypeOf<Json & Brand<"PersonJson">>();
|
|
12656
12735
|
*
|
|
12657
|
-
* const
|
|
12658
|
-
* const
|
|
12659
|
-
* const decodedPerson = personJsonToPerson(personJson);
|
|
12736
|
+
* const user = User.orThrow({ name: "Ada", age: 37 });
|
|
12737
|
+
* const userJson = userToUserJson(user);
|
|
12660
12738
|
*
|
|
12661
|
-
*
|
|
12739
|
+
* expectTypeOf(userJson).toEqualTypeOf<
|
|
12740
|
+
* string & Brand<"Json"> & Brand<"UserJson">
|
|
12741
|
+
* >();
|
|
12742
|
+
* expect(userJson).toBe('{"name":"Ada","age":37}');
|
|
12743
|
+
* expect(userJsonToUser(userJson)).toEqual(user);
|
|
12662
12744
|
* ```
|
|
12663
12745
|
*
|
|
12746
|
+
* The supplied Type must have a JSON-compatible `CanonicalInput`. The branded
|
|
12747
|
+
* Json Type accepts only valid JSON text whose parsed value can be decoded by
|
|
12748
|
+
* the supplied Type.
|
|
12749
|
+
*
|
|
12664
12750
|
* @group JSON
|
|
12665
12751
|
*/
|
|
12666
12752
|
export const json = <T extends ConcreteTypeNode, Name extends TypeName>(
|