@evolu/common 7.4.0 → 8.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +160 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +192 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +27 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +299 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2263 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1488 -348
- package/dist/src/Test.d.ts +122 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +66 -0
- package/dist/src/Time.d.ts +201 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +190 -86
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +582 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +362 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +113 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +210 -118
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +622 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +18 -38
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -171
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +50 -53
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +121 -173
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +36 -26
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +415 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +465 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3685 -740
- package/src/Test.ts +162 -0
- package/src/Time.ts +348 -145
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +1064 -630
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +311 -178
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +871 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -823
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +440 -303
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +91 -252
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +209 -282
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/LICENSE +0 -21
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -529
- package/src/Instances.ts +0 -90
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -960
package/dist/src/Result.d.ts
CHANGED
|
@@ -1,23 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* is always of an unknown type. The unknown type is a problem because we can't
|
|
4
|
-
* be sure all errors have been handled because the TypeScript compiler can't
|
|
5
|
-
* tell us.
|
|
2
|
+
* Type-safe error handling with Result types.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type NonEmptyReadonlyArray } from "./Array.js";
|
|
7
|
+
import type { Lazy } from "./Function.js";
|
|
8
|
+
import type { Typed } from "./Type.js";
|
|
9
|
+
/**
|
|
10
|
+
* The problem with `throw` in JavaScript is that the caught value is always of
|
|
11
|
+
* unknown type. We can't be sure all errors have been handled because the
|
|
12
|
+
* TypeScript compiler can't tell us what was thrown — we can't use
|
|
13
|
+
* {@link exhaustiveCheck}.
|
|
11
14
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* Languages like Rust and Haskell solve this with types like `Result` or
|
|
16
|
+
* `Either` where errors are part of the return type. TypeScript can do the
|
|
17
|
+
* same.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* // With throw - caught value is unknown, can't use exhaustiveCheck
|
|
21
|
+
* try {
|
|
22
|
+
* doSomething();
|
|
23
|
+
* } catch (error) {
|
|
24
|
+
* // error is unknown - TypeScript can't help us here
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // With Result - error type is known, exhaustiveCheck works
|
|
28
|
+
* const result = doSomething();
|
|
29
|
+
* if (!result.ok) {
|
|
30
|
+
* switch (result.error.type) {
|
|
31
|
+
* case "NotFound":
|
|
32
|
+
* // handle not found
|
|
33
|
+
* break;
|
|
34
|
+
* case "InvalidInput":
|
|
35
|
+
* // handle invalid input
|
|
36
|
+
* break;
|
|
37
|
+
* default:
|
|
38
|
+
* exhaustiveCheck(result.error); // TypeScript ensures all cases handled
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
15
42
|
*
|
|
16
|
-
*
|
|
43
|
+
* A `Result` is either {@link Ok} (success with a value) or {@link Err} (failure
|
|
44
|
+
* with an error). Create them with {@link ok} and {@link err}.
|
|
17
45
|
*
|
|
18
46
|
* ```ts
|
|
19
|
-
*
|
|
20
|
-
*
|
|
47
|
+
* type Result<T, E = never> = Ok<T> | Err<E>;
|
|
48
|
+
*
|
|
49
|
+
* interface Ok<T> {
|
|
50
|
+
* readonly ok: true;
|
|
51
|
+
* readonly value: T;
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* interface Err<E> {
|
|
55
|
+
* readonly ok: false;
|
|
56
|
+
* readonly error: E;
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* ## Example
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* // Typed<T> adds { type: T } for discriminated unions
|
|
64
|
+
* interface ParseJsonError extends Typed<"ParseJsonError"> {
|
|
21
65
|
* readonly message: string;
|
|
22
66
|
* }
|
|
23
67
|
*
|
|
@@ -29,21 +73,21 @@
|
|
|
29
73
|
* }
|
|
30
74
|
* };
|
|
31
75
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
76
|
+
* const json = parseJson('{"name": "Alice"}');
|
|
77
|
+
* if (!json.ok) return json; // short-circuit on error
|
|
34
78
|
*
|
|
35
|
-
* //
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* // Now, we have access to the json.value.
|
|
39
|
-
* expectTypeOf(json.value).toBeUnknown();
|
|
79
|
+
* // Now we have access to json.value (type: unknown)
|
|
80
|
+
* console.log(json.value);
|
|
40
81
|
* ```
|
|
41
82
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
83
|
+
* The caller doesn't need `try/catch`, just `if (!json.ok)`, and the error is
|
|
84
|
+
* `ParseJsonError`, not `unknown`.
|
|
85
|
+
*
|
|
86
|
+
* Use this pattern when the caller can recover or choose a different flow.
|
|
87
|
+
* Parsing unknown JSON is a good fit because invalid input is expected and
|
|
88
|
+
* actionable.
|
|
44
89
|
*
|
|
45
|
-
*
|
|
46
|
-
* wrapping unsafe code, Evolu provides the {@link trySync} helper:
|
|
90
|
+
* To avoid `try/catch` inside `parseJson` too, use {@link trySync}:
|
|
47
91
|
*
|
|
48
92
|
* ```ts
|
|
49
93
|
* const parseJson = (value: string): Result<unknown, ParseJsonError> =>
|
|
@@ -53,365 +97,180 @@
|
|
|
53
97
|
* );
|
|
54
98
|
* ```
|
|
55
99
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
100
|
+
* `trySync` and {@link tryAsync} are for intentionally converting thrown errors
|
|
101
|
+
* into typed, recoverable {@link Result} values.
|
|
58
102
|
*
|
|
59
|
-
*
|
|
103
|
+
* Do not wrap every throwing API in {@link Result}. If an error is unrecoverable
|
|
104
|
+
* and the caller has no meaningful fallback, let it throw and handle it at the
|
|
105
|
+
* app boundary.
|
|
60
106
|
*
|
|
61
|
-
*
|
|
62
|
-
* - For unsafe code, use `trySync` or `tryAsync`.
|
|
107
|
+
* Since `Result` is a plain object, imperative code works naturally:
|
|
63
108
|
*
|
|
64
|
-
*
|
|
109
|
+
* ### Stop on error, map on success
|
|
65
110
|
*
|
|
66
111
|
* ```ts
|
|
67
|
-
* const
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* // Simulate an API call
|
|
71
|
-
* return new Promise((resolve) => {
|
|
72
|
-
* setTimeout(() => {
|
|
73
|
-
* if (userId === "1") {
|
|
74
|
-
* resolve(ok({ id: "1", name: "Alice" }));
|
|
75
|
-
* } else {
|
|
76
|
-
* resolve(
|
|
77
|
-
* err({ type: "FetchUserError", reason: "user not found" }),
|
|
78
|
-
* );
|
|
79
|
-
* }
|
|
80
|
-
* }, 1000);
|
|
81
|
-
* });
|
|
82
|
-
* };
|
|
112
|
+
* const users = getActiveUsers();
|
|
113
|
+
* if (!users.ok) return users;
|
|
114
|
+
* const usernames = mapArray(users.value, (u) => u.username);
|
|
83
115
|
* ```
|
|
84
116
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* ### Naming convention
|
|
88
|
-
*
|
|
89
|
-
* - **For values you need:** use a name without Result suffix (`user`, `config`)
|
|
90
|
-
* - **For void operations:** use `result` (no value to name)
|
|
91
|
-
*
|
|
92
|
-
* For multiple void operations, use block scopes to avoid potentially long
|
|
93
|
-
* names like `createBaseTablesResult`, `createRelayTablesResult`, or counters
|
|
94
|
-
* like `result1`, `result2`:
|
|
117
|
+
* ### Iterate array, stop on first error
|
|
95
118
|
*
|
|
96
119
|
* ```ts
|
|
97
|
-
* const
|
|
98
|
-
* const
|
|
99
|
-
* if (!user.ok) return user;
|
|
100
|
-
*
|
|
101
|
-
* const result = saveToDatabase(user.value);
|
|
120
|
+
* for (const user of users) {
|
|
121
|
+
* const result = validateUser(user); // Result<ValidUser, ValidateUserError>
|
|
102
122
|
* if (!result.ok) return result;
|
|
103
|
-
*
|
|
104
|
-
* return ok();
|
|
105
|
-
* };
|
|
106
|
-
*
|
|
107
|
-
* const setupDatabase = () => {
|
|
108
|
-
* // Multiple void operations - use block scopes to avoid name clash
|
|
109
|
-
* {
|
|
110
|
-
* const result = createBaseTables();
|
|
111
|
-
* if (!result.ok) return result;
|
|
112
|
-
* }
|
|
113
|
-
* {
|
|
114
|
-
* const result = createRelayTables();
|
|
115
|
-
* if (!result.ok) return result;
|
|
116
|
-
* }
|
|
117
|
-
*
|
|
118
|
-
* return ok();
|
|
119
|
-
* };
|
|
123
|
+
* }
|
|
120
124
|
* ```
|
|
121
125
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* #### Sequential operations with short-circuiting
|
|
125
|
-
*
|
|
126
|
-
* When performing a sequence of operations where any failure should stop
|
|
127
|
-
* further processing, use the `Result` type with early returns.
|
|
126
|
+
* ## Style
|
|
128
127
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
128
|
+
* Imperative code is the preferred way to compose sequential {@link Result}
|
|
129
|
+
* operations.
|
|
131
130
|
*
|
|
132
131
|
* ```ts
|
|
133
|
-
* const
|
|
134
|
-
*
|
|
135
|
-
* if (!result.ok) return result;
|
|
132
|
+
* const user = getUser();
|
|
133
|
+
* if (!user.ok) return user;
|
|
136
134
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* if (!dbSchema.ok) return dbSchema;
|
|
140
|
-
*
|
|
141
|
-
* {
|
|
142
|
-
* const result = ensureDbSchema(deps)(
|
|
143
|
-
* message.restore.dbSchema,
|
|
144
|
-
* dbSchema.value,
|
|
145
|
-
* );
|
|
146
|
-
* if (!result.ok) return result;
|
|
147
|
-
* }
|
|
148
|
-
* {
|
|
149
|
-
* const result = initializeDb(deps)(message.restore.mnemonic);
|
|
150
|
-
* if (!result.ok) return result;
|
|
151
|
-
* }
|
|
152
|
-
* }
|
|
153
|
-
* return ok();
|
|
154
|
-
* });
|
|
135
|
+
* const profile = getProfile(user.value.id);
|
|
136
|
+
* if (!profile.ok) return profile;
|
|
155
137
|
*
|
|
156
|
-
*
|
|
157
|
-
* deps.postMessage({ type: "onError", error: result.error });
|
|
158
|
-
* return;
|
|
159
|
-
* }
|
|
138
|
+
* return ok({ user: user.value, profile: profile.value });
|
|
160
139
|
* ```
|
|
161
140
|
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* - If all operations succeed, return `ok()` (or another value if needed).
|
|
169
|
-
* - Outside the transaction, handle the final `Result` to report success or
|
|
170
|
-
* failure.
|
|
141
|
+
* This is an intentional style choice. Evolu does not provide helper
|
|
142
|
+
* combinators for every sequential pattern because that would duplicate plain
|
|
143
|
+
* control flow and create API ambiguity. Use helpers when they add semantics
|
|
144
|
+
* over ordinary control flow, such as operating on collections of results.
|
|
145
|
+
* While it may seem verbose, it is explicit, transparent, and avoids pipes and
|
|
146
|
+
* nested helpers, which are harder to debug.
|
|
171
147
|
*
|
|
172
|
-
*
|
|
173
|
-
* blocks, and clearly communicates the control flow.
|
|
148
|
+
* ## Composition
|
|
174
149
|
*
|
|
175
|
-
*
|
|
150
|
+
* Some patterns are common enough that deserve helpers. The previous example
|
|
151
|
+
* can be written with {@link mapResult}:
|
|
176
152
|
*
|
|
177
153
|
* ```ts
|
|
178
|
-
* const
|
|
179
|
-
*
|
|
180
|
-
* if (!foo.ok) return foo;
|
|
181
|
-
*
|
|
182
|
-
* const bar = getBar(foo.value);
|
|
183
|
-
* if (!bar.ok) return bar;
|
|
184
|
-
*
|
|
185
|
-
* return ok(barToNumber(bar.value));
|
|
186
|
-
* };
|
|
154
|
+
* const result = mapResult(users, validateUser);
|
|
155
|
+
* // Result<ValidUser[], ValidateUserError>
|
|
187
156
|
* ```
|
|
188
157
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* Even with disciplined use of `trySync` and `tryAsync`, unexpected errors can
|
|
192
|
-
* still occur due to programming mistakes, third-party library bugs, or edge
|
|
193
|
-
* cases. These should be logged for debugging, but **unexpected errors are not
|
|
194
|
-
* recoverable** - they represent bugs that must be fixed.
|
|
195
|
-
*
|
|
196
|
-
* **Important**: "Graceful shutdown" and error recovery can only come from
|
|
197
|
-
* expected errors handled via the `Result` type. Unexpected errors should fail
|
|
198
|
-
* fast - the operation fails immediately and the error bubbles up.
|
|
199
|
-
*
|
|
200
|
-
* #### In browser environments
|
|
158
|
+
* For an array of results, {@link allResult} extracts all values or returns the
|
|
159
|
+
* first error:
|
|
201
160
|
*
|
|
202
161
|
* ```ts
|
|
203
|
-
*
|
|
204
|
-
* window.addEventListener("error", (event) => {
|
|
205
|
-
* console.error("Uncaught error:", event.error);
|
|
206
|
-
* // Send to error reporting service
|
|
207
|
-
* errorReportingService.report(event.error);
|
|
208
|
-
* });
|
|
209
|
-
*
|
|
210
|
-
* // For unhandled promise rejections
|
|
211
|
-
* window.addEventListener("unhandledrejection", (event) => {
|
|
212
|
-
* console.error("Unhandled promise rejection:", event.reason);
|
|
213
|
-
* errorReportingService.report(event.reason);
|
|
214
|
-
* });
|
|
162
|
+
* const result = allResult(validationResults);
|
|
215
163
|
* ```
|
|
216
164
|
*
|
|
217
|
-
*
|
|
165
|
+
* For the first success, {@link anyResult} returns the first Ok or the last
|
|
166
|
+
* error if all fail:
|
|
218
167
|
*
|
|
219
168
|
* ```ts
|
|
220
|
-
*
|
|
221
|
-
* process.on("uncaughtException", (error) => {
|
|
222
|
-
* console.error("Uncaught exception:", error);
|
|
223
|
-
* errorReportingService.report(error);
|
|
224
|
-
* // Exit immediately - unexpected errors are not recoverable
|
|
225
|
-
* process.exit(1);
|
|
226
|
-
* });
|
|
227
|
-
*
|
|
228
|
-
* // Handle unhandled promise rejections
|
|
229
|
-
* process.on("unhandledRejection", (reason) => {
|
|
230
|
-
* console.error("Unhandled promise rejection:", reason);
|
|
231
|
-
* errorReportingService.report(reason);
|
|
232
|
-
* });
|
|
169
|
+
* const result = anyResult(parserResults);
|
|
233
170
|
* ```
|
|
234
171
|
*
|
|
235
|
-
*
|
|
236
|
-
* errors for debugging purposes. They do not attempt recovery - unexpected
|
|
237
|
-
* errors represent bugs that must be fixed. The discipline of explicit error
|
|
238
|
-
* handling through the `Result` pattern remains the primary approach for all
|
|
239
|
-
* recoverable scenarios.
|
|
240
|
-
*
|
|
241
|
-
* ### FAQ
|
|
242
|
-
*
|
|
243
|
-
* #### When should a function return a plain value instead of `Result<T, E>`?
|
|
244
|
-
*
|
|
245
|
-
* Use `Result<T, E>` only when a function can fail with **known, expected
|
|
246
|
-
* errors** that callers need to handle. If a function cannot fail with a known
|
|
247
|
-
* error, return the value directly.
|
|
172
|
+
* ## Naming convention
|
|
248
173
|
*
|
|
249
|
-
* -
|
|
250
|
-
* -
|
|
251
|
-
* - ❌ Don't return `Result<User, never>` - unnecessary wrapper
|
|
252
|
-
*
|
|
253
|
-
* This keeps the codebase clean and makes error handling intentional. The type
|
|
254
|
-
* system communicates which operations can fail and which cannot.
|
|
255
|
-
*
|
|
256
|
-
* Unsafe code from external libraries (not under our control) should be wrapped
|
|
257
|
-
* with `trySync` or `tryAsync` at the boundaries. Once wrapped, if the error is
|
|
258
|
-
* not important to callers, functions can safely return plain values. If the
|
|
259
|
-
* error matters, use `Result` with a typed error.
|
|
174
|
+
* - Result with a value: name it after the value (`user`, `config`)
|
|
175
|
+
* - Result without a value: name it `result`
|
|
260
176
|
*
|
|
261
177
|
* ```ts
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* );
|
|
178
|
+
* const processUser = (): Result<
|
|
179
|
+
* void,
|
|
180
|
+
* GetUserError | SaveToDatabaseError | SendWelcomeEmailError
|
|
181
|
+
* > => {
|
|
182
|
+
* const user = getUser();
|
|
183
|
+
* if (!user.ok) return user;
|
|
184
|
+
*
|
|
185
|
+
* const result = saveToDatabase(user.value);
|
|
186
|
+
* if (!result.ok) return result;
|
|
268
187
|
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
188
|
+
* // To avoid a clash with the previous `result`, use a block scope.
|
|
189
|
+
* {
|
|
190
|
+
* const result = sendWelcomeEmail(user.value);
|
|
191
|
+
* if (!result.ok) return result;
|
|
272
192
|
* }
|
|
273
193
|
*
|
|
274
|
-
*
|
|
194
|
+
* return ok();
|
|
275
195
|
* };
|
|
276
|
-
*
|
|
277
|
-
* // ✅ Can call without try-catch since it returns void
|
|
278
|
-
* processData(jsonString);
|
|
279
|
-
* ```
|
|
280
|
-
*
|
|
281
|
-
* #### What if my function doesn't return a value on success?
|
|
282
|
-
*
|
|
283
|
-
* If your function performs an operation but doesn't need to return a value on
|
|
284
|
-
* success, you can use `Result<void, E>`. Using `Result<void, E>` is clearer
|
|
285
|
-
* than using `Result<true, E>` or `Result<null, E>` because it communicates
|
|
286
|
-
* that the function doesn't produce a value but can produce errors.
|
|
287
|
-
*
|
|
288
|
-
* #### How do I short-circuit processing of an array on the first error?
|
|
289
|
-
*
|
|
290
|
-
* If you want to stop processing as soon as an error occurs (short-circuit),
|
|
291
|
-
* you should produce and check each `Result` inside a loop:
|
|
292
|
-
*
|
|
293
|
-
* ```ts
|
|
294
|
-
* for (const query of [
|
|
295
|
-
* sql`drop table evolu_config;`,
|
|
296
|
-
* sql`drop table evolu_message;`,
|
|
297
|
-
* ]) {
|
|
298
|
-
* const result = deps.sqlite.exec(query);
|
|
299
|
-
* if (!result.ok) return result;
|
|
300
|
-
* }
|
|
301
|
-
* // All queries succeeded
|
|
302
196
|
* ```
|
|
303
197
|
*
|
|
304
|
-
*
|
|
198
|
+
* ## Unrecoverable errors
|
|
305
199
|
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
200
|
+
* Some errors can't be handled locally — they must propagate to the top level.
|
|
201
|
+
* These are unrecoverable errors: expected (you know they can happen) but only
|
|
202
|
+
* handleable at the app level.
|
|
309
203
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
204
|
+
* Do not force these errors into {@link Result} just because the underlying API
|
|
205
|
+
* throws. If the local caller cannot recover, let the error propagate to a
|
|
206
|
+
* global handler or other app boundary.
|
|
312
207
|
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
* ];
|
|
208
|
+
* In Evolu apps, that boundary is typically a platform `createRun` adapter such
|
|
209
|
+
* as `@evolu/web`, `@evolu/nodejs`, or `@evolu/react-native`, which add
|
|
210
|
+
* platform-specific global error handling.
|
|
317
211
|
*
|
|
318
|
-
*
|
|
319
|
-
* const result = op();
|
|
320
|
-
* if (!result.ok) return result;
|
|
321
|
-
* }
|
|
322
|
-
* // All operations succeeded
|
|
323
|
-
* ```
|
|
212
|
+
* ## FAQ
|
|
324
213
|
*
|
|
325
|
-
*
|
|
326
|
-
* happened, so you can't short-circuit. In that case, you can check for the
|
|
327
|
-
* first error:
|
|
328
|
-
*
|
|
329
|
-
* ```ts
|
|
330
|
-
* const firstError = results.find((r) => !r.ok);
|
|
331
|
-
* if (firstError) return firstError;
|
|
332
|
-
* // All results are Ok
|
|
333
|
-
* ```
|
|
214
|
+
* ### What if a function doesn't return a value on success?
|
|
334
215
|
*
|
|
335
|
-
*
|
|
216
|
+
* Use `Result<void, E>` and return `ok()` (no argument). Don't return
|
|
217
|
+
* `ok(true)`, `ok("success")`, or `ok("done")` — `ok()` already signals
|
|
218
|
+
* success; redundant values add noise.
|
|
219
|
+
*/
|
|
220
|
+
export type Result<T, E = never> = Ok<T> | Err<E>;
|
|
221
|
+
/**
|
|
222
|
+
* Shorthand for a {@link Result} with `any` type parameters.
|
|
336
223
|
*
|
|
337
|
-
*
|
|
338
|
-
* above) over monadic helpers. Imperative code is generally more readable,
|
|
339
|
-
* easier to debug, and more familiar to most JavaScript and TypeScript
|
|
340
|
-
* developers. While monads and functional helpers can be powerful, they often
|
|
341
|
-
* obscure control flow and make debugging harder.
|
|
224
|
+
* @group Utilities
|
|
342
225
|
*/
|
|
343
|
-
export type
|
|
226
|
+
export type AnyResult = Result<any, any>;
|
|
344
227
|
/** A successful {@link Result}. */
|
|
345
|
-
export interface Ok<T> {
|
|
228
|
+
export interface Ok<out T> {
|
|
346
229
|
readonly ok: true;
|
|
347
230
|
readonly value: T;
|
|
348
231
|
}
|
|
349
232
|
/**
|
|
350
233
|
* An error {@link Result}.
|
|
351
234
|
*
|
|
352
|
-
* The `error` property can be any type that describes the error. For
|
|
353
|
-
*
|
|
354
|
-
* custom fields (e.g., `{ type: "MyError", code: 123 }`). Messages for users
|
|
355
|
-
* belong to translations, not to error objects.
|
|
235
|
+
* The `error` property can be any type that describes the error. For domain
|
|
236
|
+
* errors, use a plain object with a `type` field for discrimination.
|
|
356
237
|
*
|
|
357
|
-
*
|
|
358
|
-
* error class to include additional metadata.
|
|
359
|
-
*
|
|
360
|
-
* ### Examples
|
|
361
|
-
*
|
|
362
|
-
* #### Domain logic error (plain object, recommended)
|
|
363
|
-
*
|
|
364
|
-
* ```ts
|
|
365
|
-
* const failure = err({
|
|
366
|
-
* type: "ParseJsonError",
|
|
367
|
-
* code: 1001,
|
|
368
|
-
* input: "foo",
|
|
369
|
-
* });
|
|
370
|
-
* ```
|
|
371
|
-
*
|
|
372
|
-
* #### Debugging with stack trace (error instance)
|
|
373
|
-
*
|
|
374
|
-
* ```ts
|
|
375
|
-
* const failure = err(new Error("Something went wrong"));
|
|
376
|
-
* ```
|
|
377
|
-
*
|
|
378
|
-
* #### Custom error class
|
|
238
|
+
* ### Example
|
|
379
239
|
*
|
|
380
240
|
* ```ts
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* public code: number,
|
|
384
|
-
* public input: string,
|
|
385
|
-
* ) {
|
|
386
|
-
* super(`Error ${code} on input: ${input}`);
|
|
387
|
-
* this.name = "MyCustomError";
|
|
388
|
-
* }
|
|
241
|
+
* interface NotFoundError extends Typed<"NotFoundError"> {
|
|
242
|
+
* readonly id: string;
|
|
389
243
|
* }
|
|
390
|
-
*
|
|
244
|
+
*
|
|
245
|
+
* const findUser = (id: string): Result<User, NotFoundError> => {
|
|
246
|
+
* const user = users.get(id);
|
|
247
|
+
* if (user == null) return err({ type: "NotFoundError", id });
|
|
248
|
+
* return ok(user);
|
|
249
|
+
* };
|
|
391
250
|
* ```
|
|
392
251
|
*/
|
|
393
|
-
export interface Err<E> {
|
|
252
|
+
export interface Err<out E> {
|
|
394
253
|
readonly ok: false;
|
|
395
254
|
readonly error: E;
|
|
396
255
|
}
|
|
397
256
|
/**
|
|
398
257
|
* Extracts the value type from a {@link Result}.
|
|
399
258
|
*
|
|
400
|
-
* @
|
|
259
|
+
* @group Utilities
|
|
401
260
|
*/
|
|
402
261
|
export type InferOk<R extends Result<any, any>> = R extends Ok<infer T> ? T : never;
|
|
403
262
|
/**
|
|
404
263
|
* Extracts the error type from a {@link Result}.
|
|
405
264
|
*
|
|
406
|
-
* @
|
|
265
|
+
* @group Utilities
|
|
407
266
|
*/
|
|
408
267
|
export type InferErr<R extends Result<any, any>> = R extends Err<infer E> ? E : never;
|
|
409
268
|
/**
|
|
410
269
|
* Creates an {@link Ok} result.
|
|
411
270
|
*
|
|
412
|
-
* - `ok()` creates
|
|
413
|
-
* value.
|
|
414
|
-
* - `ok(value)` creates
|
|
271
|
+
* - `ok()` creates a `Result<void, never>` for operations that succeed without
|
|
272
|
+
* producing a value.
|
|
273
|
+
* - `ok(value)` creates a `Result<T, never>` containing the specified value.
|
|
415
274
|
*
|
|
416
275
|
* ### Example
|
|
417
276
|
*
|
|
@@ -423,30 +282,31 @@ export type InferErr<R extends Result<any, any>> = R extends Err<infer E> ? E :
|
|
|
423
282
|
* console.log(success); // { ok: true, value: 42 }
|
|
424
283
|
* ```
|
|
425
284
|
*/
|
|
426
|
-
export declare function ok():
|
|
285
|
+
export declare function ok(): Result<void>;
|
|
427
286
|
/** Creates an {@link Ok} result with a specified value. */
|
|
428
|
-
export declare function ok<T>(value: T):
|
|
287
|
+
export declare function ok<T>(value: T): Result<T>;
|
|
288
|
+
/** Creates an {@link Err} result. */
|
|
289
|
+
export declare const err: <E>(error: E) => Result<never, E>;
|
|
290
|
+
/** Type guard for {@link Ok} results. */
|
|
291
|
+
export declare const isOk: <T, E>(result: Result<T, E>) => result is Ok<T>;
|
|
292
|
+
/** Type guard for {@link Err} results. */
|
|
293
|
+
export declare const isErr: <T, E>(result: Result<T, E>) => result is Err<E>;
|
|
429
294
|
/**
|
|
430
|
-
*
|
|
295
|
+
* Returns the value from an `Ok` {@link Result}, or throws if it is an `Err`.
|
|
431
296
|
*
|
|
432
|
-
*
|
|
297
|
+
* Use this where failure should crash the current flow instead of being handled
|
|
298
|
+
* locally.
|
|
433
299
|
*
|
|
434
|
-
*
|
|
435
|
-
* const failure = err("Something went wrong");
|
|
436
|
-
* console.log(failure); // { ok: false, error: "Something went wrong" }
|
|
437
|
-
* ```
|
|
438
|
-
*/
|
|
439
|
-
export declare const err: <E>(error: E) => Err<E>;
|
|
440
|
-
/**
|
|
441
|
-
* Extracts the value from a {@link Result} if it is an `Ok`, or throws an error
|
|
442
|
-
* if it is an `Err`.
|
|
300
|
+
* **When to use:**
|
|
443
301
|
*
|
|
444
|
-
*
|
|
302
|
+
* - Application startup or composition-root setup where errors must stop the
|
|
303
|
+
* program immediately. In Evolu apps, errors are handled by platform-specific
|
|
304
|
+
* `createRun` adapters at the app boundary.
|
|
305
|
+
* - Module-level constants
|
|
306
|
+
* - Test setup with values that are expected to be valid
|
|
445
307
|
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
* - Not recommended for general error handling in application logic—prefer
|
|
449
|
-
* explicit checks.
|
|
308
|
+
* Prefer an explicit `if (!result.ok)` check in ordinary application logic
|
|
309
|
+
* where the caller can recover, retry, or choose a different flow.
|
|
450
310
|
*
|
|
451
311
|
* ### Example
|
|
452
312
|
*
|
|
@@ -472,87 +332,243 @@ export declare const getOrThrow: <T, E>(result: Result<T, E>) => T;
|
|
|
472
332
|
* ### Example
|
|
473
333
|
*
|
|
474
334
|
* ```ts
|
|
475
|
-
*
|
|
476
|
-
* const
|
|
477
|
-
* // value is unknown | null
|
|
478
|
-
*
|
|
479
|
-
* if (value != null) {
|
|
480
|
-
* console.log("Parsed value:", value);
|
|
481
|
-
* }
|
|
335
|
+
* // For APIs that expect T | null
|
|
336
|
+
* const user = getOrNull(findUser(id));
|
|
482
337
|
* ```
|
|
483
338
|
*/
|
|
484
339
|
export declare const getOrNull: <T, E>(result: Result<T, E>) => T | null;
|
|
485
340
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
341
|
+
* Extracts the value from a {@link Result} whose error type is `never`.
|
|
342
|
+
*
|
|
343
|
+
* This is useful when the type system guarantees the result cannot fail (for
|
|
344
|
+
* example `Result<T, never>`), avoiding impossible `if (!result.ok)` branches
|
|
345
|
+
* at call sites.
|
|
346
|
+
*/
|
|
347
|
+
export declare const getOk: <T>(result: Result<T>) => T;
|
|
348
|
+
/**
|
|
349
|
+
* Wraps a synchronous function that may throw, returning a {@link Result}.
|
|
488
350
|
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
* custom type and returns an `Err` result.
|
|
351
|
+
* Use this when the thrown value should become a typed, recoverable error for
|
|
352
|
+
* the caller. Do not use it for failures that should terminate the current flow
|
|
353
|
+
* and propagate to a global handler.
|
|
493
354
|
*
|
|
494
355
|
* ### Example
|
|
495
356
|
*
|
|
496
357
|
* ```ts
|
|
497
|
-
* interface ParseJsonError {
|
|
498
|
-
* readonly type: "ParseJsonError";
|
|
499
|
-
* readonly message: string;
|
|
500
|
-
* }
|
|
501
|
-
*
|
|
502
358
|
* const parseJson = (value: string): Result<unknown, ParseJsonError> =>
|
|
503
359
|
* trySync(
|
|
504
360
|
* () => JSON.parse(value) as unknown,
|
|
505
|
-
* (error): ParseJsonError
|
|
506
|
-
* type: "ParseJsonError",
|
|
507
|
-
* message: String(error),
|
|
508
|
-
* }),
|
|
361
|
+
* (error) => ({ type: "ParseJsonError", message: String(error) }),
|
|
509
362
|
* );
|
|
510
363
|
* ```
|
|
511
364
|
*/
|
|
512
365
|
export declare const trySync: <T, E>(fn: () => T, mapError: (error: unknown) => E) => Result<T, E>;
|
|
513
366
|
/**
|
|
514
|
-
* Wraps async
|
|
515
|
-
* {@link Result}.
|
|
367
|
+
* Wraps an async function that may throw, returning a {@link Result}.
|
|
516
368
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* the error and returns an `Err` result.
|
|
369
|
+
* Use this when the rejection should become a typed, recoverable error for the
|
|
370
|
+
* caller. Do not use it for failures that should terminate the current flow and
|
|
371
|
+
* propagate to a global handler.
|
|
521
372
|
*
|
|
522
373
|
* ### Example
|
|
523
374
|
*
|
|
524
375
|
* ```ts
|
|
525
|
-
*
|
|
526
|
-
* readonly type: "FetchError";
|
|
527
|
-
* readonly message: string;
|
|
528
|
-
* }
|
|
529
|
-
*
|
|
530
|
-
* const tryFetch = async (
|
|
531
|
-
* url: string,
|
|
532
|
-
* ): Promise<Result<unknown, FetchError>> =>
|
|
376
|
+
* const fetchJson = (url: string): Promise<Result<unknown, FetchError>> =>
|
|
533
377
|
* tryAsync(
|
|
534
378
|
* async () => {
|
|
535
379
|
* const response = await fetch(url);
|
|
536
|
-
* if (!response.ok) {
|
|
537
|
-
* throw new Error(`Request failed with status ${response.status}`);
|
|
538
|
-
* }
|
|
380
|
+
* if (!response.ok) throw new Error(`Status ${response.status}`);
|
|
539
381
|
* return response.json();
|
|
540
382
|
* },
|
|
541
|
-
* (error) => ({
|
|
542
|
-
* type: "FetchError",
|
|
543
|
-
* message: String(error),
|
|
544
|
-
* }),
|
|
383
|
+
* (error) => ({ type: "FetchError", message: String(error) }),
|
|
545
384
|
* );
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
export declare const tryAsync: <T, E>(lazyPromise: Lazy<Promise<T>>, mapError: (error: unknown) => E) => Promise<Result<T, E>>;
|
|
388
|
+
/**
|
|
389
|
+
* A result for a pull-based protocol with three outcomes.
|
|
390
|
+
*
|
|
391
|
+
* The consumer requests the next value (e.g. via `next()`), and the producer
|
|
392
|
+
* responds with one of:
|
|
393
|
+
*
|
|
394
|
+
* - `Ok<A>` — produced a value
|
|
395
|
+
* - `Err<Done<D>>` — completed normally with a done value
|
|
396
|
+
* - `Err<E>` — failed with an error
|
|
397
|
+
*
|
|
398
|
+
* Inspired by JavaScript's `Iterator.next()`, which returns `{ value, done }`.
|
|
399
|
+
*/
|
|
400
|
+
export type NextResult<A, E = never, D = void> = Result<A, E | Done<D>>;
|
|
401
|
+
/**
|
|
402
|
+
* A signal indicating normal completion of a pull-based protocol.
|
|
403
|
+
*
|
|
404
|
+
* This is not a failure — it is a control signal that carries an optional
|
|
405
|
+
* "done" value (often `void`, but can be a final summary or leftover).
|
|
406
|
+
*
|
|
407
|
+
* Inspired by JavaScript's `IteratorResult` where `{ done: true }` signals
|
|
408
|
+
* completion.
|
|
409
|
+
*/
|
|
410
|
+
export interface Done<out D = unknown> extends Typed<"Done"> {
|
|
411
|
+
readonly done: D;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Constructs a {@link Done} value.
|
|
415
|
+
*
|
|
416
|
+
* - `done()` creates a `Done<void>` for protocols that don't need a done value.
|
|
417
|
+
* - `done(value)` creates a `Done<D>` containing the specified value.
|
|
418
|
+
*/
|
|
419
|
+
export declare function done(): Done<void>;
|
|
420
|
+
/** With a done value. */
|
|
421
|
+
export declare function done<D>(value: D): Done<D>;
|
|
422
|
+
/**
|
|
423
|
+
* Removes {@link Done} from an error union.
|
|
424
|
+
*
|
|
425
|
+
* Useful for pull-based protocols where completion is encoded in the error
|
|
426
|
+
* channel (for example {@link NextResult}).
|
|
427
|
+
*
|
|
428
|
+
* @group Utilities
|
|
429
|
+
*/
|
|
430
|
+
export type ExcludeDone<E> = Exclude<E, Done<any>>;
|
|
431
|
+
/**
|
|
432
|
+
* Extracts only {@link Done} from an error union.
|
|
433
|
+
*
|
|
434
|
+
* Useful for pull-based protocols where completion is encoded in the error
|
|
435
|
+
* channel (for example {@link NextResult}).
|
|
436
|
+
*
|
|
437
|
+
* @group Utilities
|
|
438
|
+
*/
|
|
439
|
+
export type OnlyDone<E> = Extract<E, Done<any>>;
|
|
440
|
+
/**
|
|
441
|
+
* Extracts the done value type from a {@link NextResult}.
|
|
442
|
+
*
|
|
443
|
+
* @group Utilities
|
|
444
|
+
*/
|
|
445
|
+
export type InferDone<R extends Result<any, any>> = InferErr<R> extends infer Errors ? Errors extends Done<infer D> ? D : never : never;
|
|
446
|
+
/**
|
|
447
|
+
* Extracts all values from an array of {@link Result}s.
|
|
448
|
+
*
|
|
449
|
+
* Returns the first error if any result fails.
|
|
450
|
+
*
|
|
451
|
+
* ### Example
|
|
452
|
+
*
|
|
453
|
+
* ```ts
|
|
454
|
+
* const results = [ok(1), ok(2), ok(3)];
|
|
455
|
+
* const all = allResult(results);
|
|
456
|
+
* // ok([1, 2, 3])
|
|
546
457
|
*
|
|
547
|
-
* const
|
|
548
|
-
*
|
|
549
|
-
* )
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
|
|
458
|
+
* const withError = [ok(1), err("fail"), ok(3)];
|
|
459
|
+
* const failed = allResult(withError);
|
|
460
|
+
* // err("fail")
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* @group Composition
|
|
464
|
+
*/
|
|
465
|
+
export declare function allResult<const T extends readonly [AnyResult, ...ReadonlyArray<AnyResult>]>(results: T): Result<{
|
|
466
|
+
[K in keyof T]: InferOk<T[K]>;
|
|
467
|
+
}, InferErr<T[number]>>;
|
|
468
|
+
/**
|
|
469
|
+
* Returns object with same keys.
|
|
470
|
+
*
|
|
471
|
+
* ```ts
|
|
472
|
+
* const result = allResult({ a: ok(1), b: ok(2) });
|
|
473
|
+
* // ok({ a: 1, b: 2 })
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
export declare function allResult<T extends Readonly<Record<string, AnyResult>>>(results: T): Result<{
|
|
477
|
+
[P in keyof T]: InferOk<T[P]>;
|
|
478
|
+
}, [
|
|
479
|
+
keyof T
|
|
480
|
+
] extends [never] ? never : InferErr<T[keyof T]>>;
|
|
481
|
+
/**
|
|
482
|
+
* For dynamic or generated result lists.
|
|
483
|
+
*
|
|
484
|
+
* ```ts
|
|
485
|
+
* const results: ReadonlyArray<Result<number, Error>> = getResults();
|
|
486
|
+
* const all = allResult(results);
|
|
487
|
+
* // Result<ReadonlyArray<number>, Error>
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
export declare function allResult<T, E>(results: Iterable<Result<T, E>>): Result<ReadonlyArray<T>, E>;
|
|
491
|
+
/**
|
|
492
|
+
* Guarantees non-empty result.
|
|
493
|
+
*
|
|
494
|
+
* ```ts
|
|
495
|
+
* const results: NonEmptyReadonlyArray<Result<number, Error>> = [
|
|
496
|
+
* ok(1),
|
|
497
|
+
* ok(2),
|
|
498
|
+
* ];
|
|
499
|
+
* const all = allResult(results);
|
|
500
|
+
* // Result<NonEmptyReadonlyArray<number>, Error>
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
export declare function allResult<T, E>(results: NonEmptyReadonlyArray<Result<T, E>>): Result<NonEmptyReadonlyArray<T>, E>;
|
|
504
|
+
/**
|
|
505
|
+
* Maps items to {@link Result}s and extracts all values.
|
|
506
|
+
*
|
|
507
|
+
* Returns the first error if any result fails.
|
|
508
|
+
*
|
|
509
|
+
* ### Example
|
|
510
|
+
*
|
|
511
|
+
* ```ts
|
|
512
|
+
* const users = [{ id: 1 }, { id: 2 }];
|
|
513
|
+
* const result = mapResult(users, validateUser);
|
|
514
|
+
* // Result<ReadonlyArray<ValidUser>, ValidateUserError>
|
|
515
|
+
* ```
|
|
516
|
+
*
|
|
517
|
+
* @group Composition
|
|
518
|
+
*/
|
|
519
|
+
export declare function mapResult<const A extends readonly [unknown, ...Array<unknown>], T, E>(items: A, fn: (a: A[number]) => Result<T, E>): Result<{
|
|
520
|
+
[K in keyof A]: T;
|
|
521
|
+
}, E>;
|
|
522
|
+
/**
|
|
523
|
+
* For dynamic or generated item lists.
|
|
524
|
+
*
|
|
525
|
+
* ```ts
|
|
526
|
+
* const users = [{ id: 1 }, { id: 2 }];
|
|
527
|
+
* const result = mapResult(users, validateUser);
|
|
528
|
+
* // Result<ReadonlyArray<ValidUser>, ValidateUserError>
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
export declare function mapResult<A, T, E>(items: Iterable<A>, fn: (a: A) => Result<T, E>): Result<ReadonlyArray<T>, E>;
|
|
532
|
+
/**
|
|
533
|
+
* Returns object with same keys.
|
|
534
|
+
*
|
|
535
|
+
* ```ts
|
|
536
|
+
* const result = mapResult({ a: 1, b: 2 }, double);
|
|
537
|
+
* // Result<{ a: number, b: number }, DoubleError>
|
|
538
|
+
* ```
|
|
539
|
+
*/
|
|
540
|
+
export declare function mapResult<A, T, E, K extends string>(items: Readonly<Record<K, A>>, fn: (a: A) => Result<T, E>): Result<Readonly<Record<K, T>>, E>;
|
|
541
|
+
/**
|
|
542
|
+
* Returns the first successful {@link Result}.
|
|
543
|
+
*
|
|
544
|
+
* If all results fail, returns the last error.
|
|
545
|
+
*
|
|
546
|
+
* Requires a non-empty array — there's no "first success" with zero
|
|
547
|
+
* participants. Use {@link isNonEmptyArray} to guard:
|
|
548
|
+
*
|
|
549
|
+
* ```ts
|
|
550
|
+
* if (isNonEmptyArray(results)) {
|
|
551
|
+
* const result = anyResult(results);
|
|
554
552
|
* }
|
|
555
553
|
* ```
|
|
554
|
+
*
|
|
555
|
+
* ### Example
|
|
556
|
+
*
|
|
557
|
+
* ```ts
|
|
558
|
+
* const results = [err("fail1"), ok(42), err("fail2")];
|
|
559
|
+
* if (isNonEmptyArray(results)) {
|
|
560
|
+
* const result = anyResult(results);
|
|
561
|
+
* // ok(42)
|
|
562
|
+
* }
|
|
563
|
+
*
|
|
564
|
+
* const allFailed = [err("a"), err("b"), err("c")];
|
|
565
|
+
* if (isNonEmptyArray(allFailed)) {
|
|
566
|
+
* const result = anyResult(allFailed);
|
|
567
|
+
* // err("c") — last error
|
|
568
|
+
* }
|
|
569
|
+
* ```
|
|
570
|
+
*
|
|
571
|
+
* @group Composition
|
|
556
572
|
*/
|
|
557
|
-
export declare const
|
|
573
|
+
export declare function anyResult<const T extends readonly [AnyResult, ...ReadonlyArray<AnyResult>]>(results: T): Result<InferOk<T[number]>, InferErr<T[number]>>;
|
|
558
574
|
//# sourceMappingURL=Result.d.ts.map
|