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