@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.
Files changed (251) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +622 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +36 -26
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +415 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +465 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. package/src/local-first/Sync.ts +0 -960
@@ -1,23 +1,67 @@
1
1
  /**
2
- * The problem with throwing an exception in JavaScript is that the caught error
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
- * Languages like Rust or Haskell use a type-safe approach to error handling,
8
- * where errors are explicitly represented as part of the return type, such as
9
- * Result or Either, allowing the developer to handle errors safely. TypeScript
10
- * can have this too via the `Result` type.
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
- * The `Result` type can be either {@link Ok} (success) or {@link Err} (error).
13
- * Use {@link ok} to create a successful result and {@link err} to create an error
14
- * result.
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
- * Now let's look at how `Result` can be used for safe JSON parsing:
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
- * interface ParseJsonError {
20
- * readonly type: "ParseJsonError";
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
- * // Result<unknown, ParseJsonError>
33
- * const json = parseJson('{"key": "value"}');
76
+ * const json = parseJson('{"name": "Alice"}');
77
+ * if (!json.ok) return json; // short-circuit on error
34
78
  *
35
- * // Fail fast to handle errors early.
36
- * if (!json.ok) return json; // Err<ParseJsonError>
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
- * Note how we didn't have to use the try/catch, just `if (!json.ok)`, and how
43
- * the error isn't unknown but has a type.
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
- * But we had to use `try/catch` in the `parseJson` function. For such a case,
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
- * {@link trySync} helper makes unsafe (can throw) synchronous code safe; for
57
- * unsafe asynchronous code, use {@link tryAsync}.
100
+ * `trySync` and {@link tryAsync} are for intentionally converting thrown errors
101
+ * into typed, recoverable {@link Result} values.
58
102
  *
59
- * Let's summarize it:
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
- * - For safe code, use `ok` and `err`.
62
- * - For unsafe code, use `trySync` or `tryAsync`.
107
+ * Since `Result` is a plain object, imperative code works naturally:
63
108
  *
64
- * Safe asynchronous code (using Result with a Promise):
109
+ * ### Stop on error, map on success
65
110
  *
66
111
  * ```ts
67
- * const fetchUser = async (
68
- * userId: string,
69
- * ): Promise<Result<User, FetchUserError>> => {
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
- * For lazy, cancellable async operations, see {@link Task}.
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 processUser = () => {
98
- * const user = getUser();
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
- * ### Examples
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
- * Here's an example of a database reset operation that drops tables, restores a
130
- * schema, and initializes the database, stopping on the first error:
128
+ * Imperative code is the preferred way to compose sequential {@link Result}
129
+ * operations.
131
130
  *
132
131
  * ```ts
133
- * const result = deps.sqlite.transaction(() => {
134
- * const result = dropAllTables(deps);
135
- * if (!result.ok) return result;
132
+ * const user = getUser();
133
+ * if (!user.ok) return user;
136
134
  *
137
- * if (message.restore) {
138
- * const dbSchema = getDbSchema(deps)();
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
- * if (!result.ok) {
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
- * In this pattern:
163
- *
164
- * - Each operation returns a `Result` (e.g., `Result<void, E>` or `Result<T,
165
- * E>`).
166
- * - After each operation, check `if (!result.ok)` and return the `Err` to
167
- * short-circuit.
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
- * This approach ensures type-safe error handling, avoids nested try/catch
173
- * blocks, and clearly communicates the control flow.
148
+ * ## Composition
174
149
  *
175
- * #### A function with two different errors:
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 example = (value: string): Result<number, FooError | BarError> => {
179
- * const foo = getFoo(value);
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
- * ### Handling unexpected errors
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
- * // Global error handler for unexpected errors
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
- * #### In Node.js environments
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
- * // Handle uncaught exceptions - log and fail fast
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
- * These global handlers serve as a safety net to log and report unexpected
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
- * - Return `Result<User, UserNotFoundError>` - can fail with a known error
250
- * - Return `User` - cannot fail with a known error
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
- * // Safe to return void - unsafe code is wrapped and error is handled
263
- * const processData = (data: string): void => {
264
- * const result = trySync(
265
- * () => JSON.parse(data),
266
- * (error) => ({ type: "ParseError", message: String(error) }),
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
- * if (!result.ok) {
270
- * logError(result.error);
271
- * return;
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
- * // Continue with safe operations...
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
- * #### How do I handle an array of operations and short-circuit on the first error?
198
+ * ## Unrecoverable errors
305
199
  *
306
- * If you have an array of operations (not results), you should make them
307
- * _lazy_—that is, represent each operation as a function. This way, you only
308
- * execute each operation as needed, and can stop on the first error:
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
- * ```ts
311
- * import type { LazyValue } from "./Function";
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
- * const operations: LazyValue<Result<void, MyError>>[] = [
314
- * () => doSomething(),
315
- * () => doSomethingElse(),
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
- * for (const op of operations) {
319
- * const result = op();
320
- * if (!result.ok) return result;
321
- * }
322
- * // All operations succeeded
323
- * ```
212
+ * ## FAQ
324
213
  *
325
- * If you already have an array of `Result`s, the processing has already
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
- * ### Why doesn't Evolu provide "handy helpers"?
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
- * Evolu intentionally favors imperative patterns (like the `for...of` loop
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 Result<T, E> = Ok<T> | Err<E>;
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 normal
353
- * domain logic, use a plain object. This allows us to structure errors with
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
- * If you need a stack trace for debugging, use an `Error` instance or a custom
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
- * class MyCustomError extends Error {
382
- * constructor(
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
- * const failure = err(new MyCustomError(404, "bad-input"));
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
- * @category Utilities
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
- * @category Utilities
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 an `Ok<void>` for operations that succeed without producing a
413
- * value.
414
- * - `ok(value)` creates an `Ok<T>` containing the specified value.
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(): Ok<void>;
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): Ok<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
- * Creates an {@link Err} result.
295
+ * Returns the value from an `Ok` {@link Result}, or throws if it is an `Err`.
431
296
  *
432
- * ### Example
297
+ * Use this where failure should crash the current flow instead of being handled
298
+ * locally.
433
299
  *
434
- * ```ts
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
- * **Intended usage:**
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
- * - For critical code paths (e.g., app startup, config values) where failure
447
- * should crash the app.
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
- * const parseResult = parseJson('{"key": "value"}');
476
- * const value = getOrNull(parseResult);
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
- * Wraps synchronous functions that may throw exceptions, returning a
487
- * {@link Result}.
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
- * The `trySync` function is designed to handle synchronous code safely by
490
- * wrapping the execution in a try-catch block. If the function succeeds, it
491
- * returns an `Ok` result. If an exception is thrown, it maps the error to a
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 functions or any operation returning a promise, returning a
515
- * {@link Result}.
367
+ * Wraps an async function that may throw, returning a {@link Result}.
516
368
  *
517
- * The `tryAsync` function provides a way to handle asynchronous code safely by
518
- * catching any rejected promises and mapping errors to a custom type. If the
519
- * promise resolves, it returns an `Ok` result. If the promise rejects, it maps
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
- * interface FetchError {
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 result = await tryFetch(
548
- * "https://jsonplaceholder.typicode.com/posts/1",
549
- * );
550
- * if (result.ok) {
551
- * console.log("Data:", result.value);
552
- * } else {
553
- * console.error("Error:", result.error);
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 tryAsync: <T, E>(promiseFn: () => Promise<T>, mapError: (error: unknown) => E) => Promise<Result<T, E>>;
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