@shirudo/result 0.0.6 → 1.0.1

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -296
  3. package/dist/collections.cjs +12 -0
  4. package/dist/collections.d.cts +3 -0
  5. package/dist/collections.d.mts +3 -0
  6. package/dist/collections.mjs +4 -0
  7. package/dist/errors-2WOswg7r.mjs +95 -0
  8. package/dist/errors-2WOswg7r.mjs.map +1 -0
  9. package/dist/errors-BFjY06EV.d.cts +55 -0
  10. package/dist/errors-BFjY06EV.d.cts.map +1 -0
  11. package/dist/errors-C5qGMRiU.d.mts +55 -0
  12. package/dist/errors-C5qGMRiU.d.mts.map +1 -0
  13. package/dist/errors-D2EMzJQl.cjs +209 -0
  14. package/dist/errors-D2EMzJQl.cjs.map +1 -0
  15. package/dist/errors.cjs +21 -0
  16. package/dist/errors.d.cts +2 -0
  17. package/dist/errors.d.mts +2 -0
  18. package/dist/errors.mjs +3 -0
  19. package/dist/flatten-B8bN6fiI.d.mts +71 -0
  20. package/dist/flatten-B8bN6fiI.d.mts.map +1 -0
  21. package/dist/flatten-C6Y9hx79.mjs +147 -0
  22. package/dist/flatten-C6Y9hx79.mjs.map +1 -0
  23. package/dist/flatten-DK7eJKPx.d.cts +71 -0
  24. package/dist/flatten-DK7eJKPx.d.cts.map +1 -0
  25. package/dist/flatten-Df9U40nO.cjs +182 -0
  26. package/dist/flatten-Df9U40nO.cjs.map +1 -0
  27. package/dist/index.cjs +113 -1018
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +12 -546
  30. package/dist/index.d.cts.map +1 -1
  31. package/dist/index.d.mts +12 -546
  32. package/dist/index.d.mts.map +1 -1
  33. package/dist/index.mjs +25 -935
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/mapOrElse-B0_4r6Jn.mjs +96 -0
  36. package/dist/mapOrElse-B0_4r6Jn.mjs.map +1 -0
  37. package/dist/mapOrElse-B5gx9x2E.d.mts +64 -0
  38. package/dist/mapOrElse-B5gx9x2E.d.mts.map +1 -0
  39. package/dist/mapOrElse-DIe7LkYV.d.cts +64 -0
  40. package/dist/mapOrElse-DIe7LkYV.d.cts.map +1 -0
  41. package/dist/mapOrElse-H-GvAUka.cjs +137 -0
  42. package/dist/mapOrElse-H-GvAUka.cjs.map +1 -0
  43. package/dist/operators.cjs +33 -0
  44. package/dist/operators.d.cts +3 -0
  45. package/dist/operators.d.mts +3 -0
  46. package/dist/operators.mjs +4 -0
  47. package/dist/result-CY-KIivk.cjs +1100 -0
  48. package/dist/result-CY-KIivk.cjs.map +1 -0
  49. package/dist/result-DKKaNdOx.mjs +861 -0
  50. package/dist/result-DKKaNdOx.mjs.map +1 -0
  51. package/dist/sequence-Br6tAIIu.d.cts +419 -0
  52. package/dist/sequence-Br6tAIIu.d.cts.map +1 -0
  53. package/dist/sequence-C0jlR3AY.d.mts +419 -0
  54. package/dist/sequence-C0jlR3AY.d.mts.map +1 -0
  55. package/package.json +44 -10
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shirudo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,369 +1,209 @@
1
1
  # @shirudo/result
2
2
 
3
- **Robust, type-safe error handling for TypeScript.**
3
+ Robust, type-safe error handling for TypeScript.
4
4
 
5
- > ⚠️ **Beta Notice**: This library is currently in beta. The API may change before the stable release. Use with caution in production environments.
5
+ `@shirudo/result` models expected failures as values instead of hidden exceptions. Functions return `Result<T, E>`, callers must handle both states, and TypeScript narrows access to `value` and `error` only when the state is known.
6
6
 
7
- `@shirudo/result` brings the power of the Result pattern (Monad) to TypeScript. It helps you write safer, more predictable code by treating errors as values rather than exceptions. Stop guessing if a function will throw—let the type system guide you.
7
+ [![CI](https://github.com/shi-rudo/result-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/shi-rudo/result-ts/actions/workflows/ci.yml)
8
+ [![npm version](https://img.shields.io/npm/v/@shirudo/result.svg)](https://www.npmjs.com/package/@shirudo/result)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7%2B-blue.svg)](https://www.typescriptlang.org/)
11
+ [![Node](https://img.shields.io/badge/Node-%3E%3D20-green.svg)](https://nodejs.org/)
12
+ ![Edge Runtime](https://img.shields.io/badge/Edge_Runtime-compatible-brightgreen.svg)
8
13
 
9
- ![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)
10
- ![Runtime](https://img.shields.io/badge/Runtime-Node%20%7C%20Browser-green)
11
- ![License](https://img.shields.io/badge/License-MIT-yellow.svg)
12
- ![Beta](https://img.shields.io/badge/Status-Beta-orange.svg)
14
+ ## Installation
13
15
 
14
- ## 🌟 Key Features
15
-
16
- - **Type-Safe:** generic `Result<T, E>` type discriminates between Success (`Ok`) and Failure (`Err`).
17
- - **Pipeable Architecture:** Functional, tree-shakeable operators via `.pipe()` and `.pipeAsync()`.
18
- - **Async Support:** First-class support for Promises and async transformations.
19
- - **Do-Notation:** A `task` generator utility to write sequential code without callback hell (similar to Rust's `?` operator).
20
- - **Rich Pattern Matching:** Fluent builders for exhaustive matching and error handling.
21
- - **Comprehensive Utilities:** Helpers for collections, conversion from/to Promises, Nullables, and try/catch blocks.
22
-
23
- ---
24
-
25
- ## 📦 Installation
26
-
27
- ```bash
16
+ ```sh
28
17
  npm install @shirudo/result
29
- # or
30
18
  pnpm add @shirudo/result
31
- # or
32
19
  yarn add @shirudo/result
33
20
  ```
34
21
 
35
- ---
36
-
37
- ## 🚀 Quick Start
38
-
39
- ### Basic Usage
40
-
41
- Instead of throwing errors, return a `Result`.
22
+ ## Quick Start
42
23
 
43
24
  ```ts
44
- import { ok, err, Result } from "@shirudo/result";
45
-
46
- function divide(a: number, b: number): Result<number, string> {
47
- if (b === 0) {
48
- return err("Division by zero");
49
- }
50
- return ok(a / b);
25
+ import { Result } from '@shirudo/result';
26
+
27
+ type User = { id: string; email: string; active: boolean };
28
+ type UserError =
29
+ | { type: 'not-found'; id: string }
30
+ | { type: 'inactive'; id: string };
31
+
32
+ const users = new Map<string, User>([
33
+ ['1', { id: '1', email: 'ada@example.com', active: true }],
34
+ ]);
35
+
36
+ function loadUser(id: string): Result<User, UserError> {
37
+ const user = users.get(id);
38
+ if (!user) return Result.err({ type: 'not-found', id });
39
+ if (!user.active) return Result.err({ type: 'inactive', id });
40
+ return Result.ok(user);
51
41
  }
52
42
 
53
- const result = divide(10, 2);
43
+ const result = loadUser('1');
54
44
 
55
45
  if (result.isOk()) {
56
- // TypeScript narrows 'result' to Ok<number>
57
- console.log("Success:", result.value); // 5
46
+ console.log(result.value.email);
58
47
  } else {
59
- // TypeScript narrows 'result' to Err<string>
60
- console.error("Error:", result.error);
48
+ switch (result.error.type) {
49
+ case 'not-found':
50
+ console.error(`Missing user ${result.error.id}`);
51
+ break;
52
+ case 'inactive':
53
+ console.error(`Inactive user ${result.error.id}`);
54
+ break;
55
+ }
61
56
  }
62
57
  ```
63
58
 
64
- ### Functional Pipelines
65
-
66
- Use `.pipe()` to chain operations. If an error occurs at any step, the chain short-circuits and returns the `Err`.
67
-
68
- ```ts
69
- import { ok, map, filter, mapErr } from "@shirudo/result";
70
-
71
- const processed = ok(10).pipe(
72
- map((n) => n * 2), // 20
73
- filter(
74
- (n) => n > 50,
75
- () => "Too small"
76
- ), // Returns Err('Too small')
77
- mapErr((e) => `Error: ${e}`) // Transforms the error message
78
- );
79
-
80
- console.log(processed.isErr()); // true
81
- console.log(processed.unwrapOr(0)); // 0 (fallback)
82
- ```
83
-
84
- ---
59
+ ## Why This Library
85
60
 
86
- ## 💡 Core Concepts
61
+ - `Ok` does not expose `error`, and `Err` does not expose `value`; accidental reads require proper narrowing.
62
+ - `isResult` uses a stable internal brand plus payload validation instead of accepting random lookalike objects.
63
+ - `Result.fromPromise()` maps promise rejections but rethrows bugs inside the error mapper.
64
+ - Sync, async, generator, collection, and matching workflows are first-class.
65
+ - README and docs TypeScript snippets are compile-checked.
66
+ - Package exports are tested for ESM, CJS, and TypeScript consumers.
87
67
 
88
- ### Creating Results
68
+ ## Common Workflows
89
69
 
90
- There are several static factories to help you wrap existing code or values.
70
+ ### Catch throwing APIs
91
71
 
92
72
  ```ts
93
- import { Result } from "@shirudo/result";
94
-
95
- // Standard
96
- const a = Result.ok(42);
97
- const b = Result.err("Something went wrong");
98
-
99
- // From a function that might throw
100
- const json = Result.try(() => JSON.parse('{"valid": true}'));
101
-
102
- // From a potentially null/undefined value
103
- const user = Result.fromNullable(maybeUser, "User not found");
73
+ import { Result } from '@shirudo/result';
104
74
 
105
- // From a Promise (catches rejections)
106
- const asyncRes = await Result.fromPromise(fetch("/api/data"));
107
- ```
108
-
109
- ### Async Pipelines
75
+ const parseJson = Result.fromThrowable(
76
+ JSON.parse,
77
+ error => ({ type: 'parse' as const, cause: error }),
78
+ );
110
79
 
111
- Transforming async results is seamless with `.pipeAsync()`.
80
+ const parsed = parseJson('{"valid": true}');
112
81
 
113
- ```ts
114
- import { ok, mapAsync, tryCatchAsync } from "@shirudo/result";
115
-
116
- const result = await ok(1).pipeAsync(
117
- mapAsync(async (id) => {
118
- const user = await db.getUser(id);
119
- return user.name;
120
- }),
121
- tryCatchAsync(async (name) => {
122
- // If this throws, it becomes an Err
123
- return await externalService.validate(name);
124
- })
82
+ const response = await Result.fromPromise(
83
+ Promise.resolve({ ok: true }),
84
+ error => ({ type: 'network' as const, cause: error }),
125
85
  );
126
86
  ```
127
87
 
128
- ### Generator "Do-Notation" (`task`)
129
-
130
- The `task` (or `gen`) utility allows you to write code that looks imperative but handles `Result` flow control automatically. Use `yield*` to unwrap `Ok` values; if an `Err` is yielded, the function returns early with that error.
88
+ ### Compose with pipe operators
131
89
 
132
90
  ```ts
133
- import { task, ok, err } from "@shirudo/result";
134
-
135
- const calculate = task(function* () {
136
- // yield* automatically unwraps the value if Ok
137
- const x = yield* ok(10);
138
- const y = yield* ok(20);
139
-
140
- // If this were err(), execution would stop here and return that err
141
- const z = yield* validate(x + y);
142
-
143
- return z; // Returns Ok(z)
144
- });
145
-
146
- // calculate is a Promise<Result<number, Error>>
91
+ import { Result } from '@shirudo/result';
92
+ import { filter, map, mapErr } from '@shirudo/result/operators';
93
+
94
+ const processed = Result.ok<number, string>(10).pipe(
95
+ map(value => value * 2),
96
+ filter(
97
+ value => value > 25,
98
+ () => 'too small',
99
+ ),
100
+ mapErr(error => `Validation failed: ${error}`),
101
+ );
147
102
  ```
148
103
 
149
- #### Error Handling with `onThrow`
150
-
151
- The `task()` function accepts an optional `onThrow` callback for custom error mapping:
104
+ ### Compose async work
152
105
 
153
106
  ```ts
154
- const result = await task(
155
- function* () {
156
- const data = yield* fetchData();
157
- return process(data);
158
- },
159
- (error) => new CustomError(`Failed: ${error}`) // custom error mapping
107
+ import { Result } from '@shirudo/result';
108
+ import { mapAsync, tryMapAsync } from '@shirudo/result/operators';
109
+
110
+ const db = {
111
+ async getUser(id: number) {
112
+ return { id, email: 'ada@example.com' };
113
+ },
114
+ };
115
+
116
+ const normalizedEmail = await Result.ok<number, string>(1).pipeAsync(
117
+ mapAsync(async id => db.getUser(id)),
118
+ tryMapAsync(async user => user.email.toLowerCase()),
160
119
  );
161
- // Returns Result<ProcessedData, CustomError>
162
120
  ```
163
121
 
164
- ### Folding Results
165
-
166
- The simplest way to handle both `Ok` and `Err` cases and return a single value:
122
+ ### Use task notation for sequential flows
167
123
 
168
124
  ```ts
169
- import { ok, err } from "@shirudo/result";
125
+ import { Result, task } from '@shirudo/result';
170
126
 
171
- const result = ok(42);
172
-
173
- const message = result.fold(
174
- (val) => `Success: ${val}`,
175
- (err) => `Error: ${err}`
176
- );
177
- // message = "Success: 42"
127
+ function findUser(id: string) {
128
+ return Result.ok({ id, email: 'ada@example.com' });
129
+ }
178
130
 
179
- // Useful for side effects
180
- result.fold(
181
- (val) => console.log("Yay:", val),
182
- (err) => console.error("Nay:", err)
183
- );
131
+ function ensureEmail(user: { id: string; email?: string }) {
132
+ return user.email
133
+ ? Result.ok(user.email)
134
+ : Result.err({ type: 'missing-email' as const, id: user.id });
135
+ }
184
136
 
185
- // Convert to HTTP response
186
- const response = result.fold(
187
- (data) => ({ status: 200, body: data }),
188
- (error) => ({ status: 500, body: { error } })
189
- );
137
+ const email = task(function* () {
138
+ const user = yield* findUser('1');
139
+ return yield* ensureEmail(user);
140
+ });
190
141
  ```
191
142
 
192
- ### Pattern Matching
193
-
194
- Handle errors exhaustively using the fluent matching API for complex error types. You can match by Error class (`.err`) or by primitive value (`.errVal`).
143
+ ### Match discriminated-union errors
195
144
 
196
145
  ```ts
197
- import { Result } from "@shirudo/result";
146
+ import { Result, matchTag } from '@shirudo/result';
198
147
 
199
- class NetworkError extends Error {}
200
- class ValidationError extends Error {}
148
+ type DomainError =
149
+ | { type: 'network'; retryAfter: number }
150
+ | { type: 'validation'; field: string };
201
151
 
202
- const result = Result.err(new NetworkError("Timeout"));
152
+ const failed = Result.err<DomainError>({ type: 'network', retryAfter: 30 });
203
153
 
204
- const message = result
205
- .match()
206
- .err(NetworkError, (e) => `Retry later: ${e.message}`)
207
- .err(ValidationError, (e) => `Invalid input: ${e.message}`)
208
- .errVal("TIMEOUT_CODE", () => "Operation timed out") // Match primitive values
209
- .ok((val) => `Success: ${val}`)
210
- .run();
154
+ const message = matchTag(failed, 'type', {
155
+ network: error => `Retry in ${error.retryAfter}s`,
156
+ validation: error => `Invalid field: ${error.field}`,
157
+ });
211
158
  ```
212
159
 
213
- #### Async Pattern Matching
160
+ ## Imports
214
161
 
215
- For async handlers, use `matchAsync`:
162
+ Everything is available from the package root:
216
163
 
217
164
  ```ts
218
- import { matchAsync } from "@shirudo/result";
219
-
220
- const result = await matchAsync({
221
- ok: async (val) => `Success: ${val}`,
222
- err: async (e) => `Error: ${e}`
223
- })(someResult);
165
+ import { Result, err, ok, task } from '@shirudo/result';
224
166
  ```
225
167
 
226
- **When to use what:**
227
-
228
- - Use `.fold()` for simple cases where you handle both Ok and Err
229
- - Use `.match()` for complex pattern matching on multiple error types
230
- - Use `fold()` pipe operator for functional composition in pipelines
231
-
232
- ---
233
-
234
- ## 📚 API Reference
235
-
236
- ### Creation & Conversions
237
-
238
- - `ok(value)` / `err(error)`: Create basic instances.
239
- - `okIf(condition, okValue, errValue)`: Conditionally create `Ok` or `Err`.
240
- - `okIfLazy(condition, okFn, errFn)`: Lazy conditional creation.
241
- - `Result.try(fn)`: Execute a sync function; catches exceptions as `Err`.
242
- - `Result.fromNullable(val, fallback)`: Convert `null | undefined` to `Err`.
243
- - `Result.fromPromise(promise)`: Convert a Promise to `Promise<Result>`.
244
- - `.toPromise()`: Convert `Ok` to resolved Promise, `Err` to rejected.
245
- - `.toNullable()`: Convert `Ok` to value, `Err` to `null`.
246
-
247
- ### Instance Methods
248
-
249
- - `.isOk()`: Type guard for success.
250
- - `.isErr()`: Type guard for failure.
251
- - `.unwrap()`: Get value or throw (use carefully).
252
- - `.unwrapErr()`: Get error or throw (use carefully).
253
- - `.unwrapOr(default)`: Get value or return default.
254
- - `.unwrapOrElse(fn)`: Get value or generate default from error.
255
- - `.unwrapOrThrow()`: Get value or throw original error (preserves stack trace).
256
- - `.expect(msg)`: Get value or throw with specific message.
257
- - `.expectErr(msg)`: Get error or throw with specific message.
258
- - `.fold(onOk, onErr)`: Handle both cases and return a single value.
259
- - `.pipe(...)`: Chain operators synchronously.
260
- - `.pipeAsync(...)`: Chain operators asynchronously.
261
- - `.match()`: Start a fluent pattern matching builder (Err only).
262
- - `.matchErr()`: Pattern matching builder for Err cases.
263
- - `.serialize()`: Convert to `{ isSuccess, data?, error? }`.
264
- - `.toUserFriendly()`: User-friendly serialization with error messages.
265
-
266
- **Error types:** Methods that throw (e.g., `.unwrap()`, `.unwrapErr()`, `.expect()`, `.expectErr()`, and invalid-state checks) now use custom error classes with stable `code` values like `ERR_UNWRAP_ON_ERR` and `ERR_INVALID_STATE`. These classes and constants are exported from the package for programmatic handling.
267
-
268
- ### Utilities
269
-
270
- Type guards and helper functions:
271
-
272
- - `isResult(value)`: Type guard to check if a value is a `Result`.
273
- - `contains(result, value)`: Check if `Ok` contains a specific value.
274
- - `containsErr(result, error)`: Check if `Err` contains a specific error.
275
- - `fromResult(fn)`: Execute a function, catching exceptions (Rust `Result::from`).
168
+ Focused subpath exports are available for clearer imports and package-level checks:
276
169
 
277
170
  ```ts
278
- import { isResult, contains, containsErr, fromResult, ok, err } from "@shirudo/result";
279
-
280
- isResult(ok(5)); // true
281
- isResult("not a result"); // false
282
-
283
- const result = ok(42);
284
- contains(result, 42); // true
285
- contains(result, 100); // false
286
-
287
- const errResult = err("not found");
288
- containsErr(errResult, "not found"); // true
289
-
290
- const wrapped = fromResult(() => JSON.parse('{"valid": true}'));
171
+ import { UnwrapOnErrError } from '@shirudo/result/errors';
172
+ import { flatMapAsync, map } from '@shirudo/result/operators';
173
+ import { sequence, sequenceRecord } from '@shirudo/result/collections';
291
174
  ```
292
175
 
293
- ### Pipeable Operators
294
-
295
- Import these from the root package to use inside `.pipe()`.
296
-
297
- | Operator | Description |
298
- | :--------------------- | :------------------------------------------------------- |
299
- | `map(fn)` | Transform the `Ok` value. |
300
- | `mapErr(fn)` | Transform the `Err` value. |
301
- | `mapBoth(fnOk, fnErr)` | Transform both sides. |
302
- | `flatMap(fn)` | Chain a function that returns a `Result` (monadic bind). |
303
- | `filter(pred, errFn)` | Turn `Ok` into `Err` if predicate fails. |
304
- | `tap(observer)` | Run side effects (logging) without changing the result. |
305
- | `recover(val)` | Convert `Err` to `Ok` with a default value. |
306
- | `tryCatch(fn)` | Run a function, catching exceptions into `Err`. |
307
- | `tryMap(fn)` | Like `map`, but catches exceptions. |
308
- | `fold({ ok, err })` | Terminate the pipe and return a value based on state. |
176
+ ## Documentation
309
177
 
310
- **Async Variants:** `mapAsync`, `mapErrAsync`, `flatMapAsync`, `filterAsync`, `tapAsync`, `tryCatchAsync`, `tryMapAsync`, `foldAsync`.
178
+ The full documentation lives in `docs/` and is built with VitePress.
311
179
 
312
- ### Combinators
313
-
314
- Combinators (inspired by Rust) for composing and transforming Results:
315
-
316
- | Combinator | Description |
317
- | :--------- | :---------- |
318
- | `and(r1, r2)` | Short-circuit AND: returns `r2` only if `r1` is `Ok` |
319
- | `or(r1, r2)` | Returns `r1` if `Ok`, otherwise `r2` |
320
- | `orElse(r, fn)` | Returns `r` if `Ok`, otherwise calls `fn(error)` |
321
- | `mapOr(r, default, fn)` | Maps `Ok` value or returns `default` |
322
- | `mapOrElse(r, defaultFn, fn)` | Maps `Ok` value or computes `default` from error |
323
- | `swap(r)` | Swaps Ok and Err: `Result<T, E>` → `Result<E, T>` |
324
-
325
- ```ts
326
- import { and, or, orElse, mapOr, mapOrElse, swap, ok, err } from "@shirudo/result";
180
+ - [Getting Started](docs/guide/getting-started.md)
181
+ - [Pipelines](docs/guide/pipelines.md)
182
+ - [Task Notation](docs/guide/task.md)
183
+ - [Pattern Matching](docs/guide/matching.md)
184
+ - [Result API](docs/api/result.md)
185
+ - [Operators](docs/api/operators.md)
186
+ - [Collections](docs/api/collections.md)
187
+ - [Error Classes](docs/api/errors.md)
188
+ - [Version 1 Migration](docs/migration/v1.md)
189
+ - [Design Decisions](docs/decisions/lazy-async-abstraction.md)
327
190
 
328
- const a = ok(5);
329
- const b = ok(10);
191
+ ## Development
330
192
 
331
- // and: returns b only if a is Ok
332
- and(a, b); // Ok(10)
333
-
334
- // or: returns first Ok, otherwise fallback
335
- or(err("fallback"), ok("success")); // Ok("success")
336
-
337
- // orElse: lazy fallback with error context
338
- orElse(err("error"), (e) => ok(`recovered: ${e}`)); // Ok("recovered: error")
339
-
340
- // mapOr: map or use default
341
- mapOr(ok(5), 0, (n) => n * 2); // 10
342
- mapOr(err("x"), 0, (n) => n * 2); // 0
343
-
344
- // swap: interchange Ok and Err
345
- swap(ok("value")); // Err("value")
346
- swap(err("error")); // Ok("error")
193
+ ```sh
194
+ pnpm install
195
+ pnpm check
196
+ pnpm check:clean
347
197
  ```
348
198
 
349
- ### Collections
199
+ Useful focused commands:
350
200
 
351
- - `sequence(results)`: Turn `Result[]` into `Result<T[]>`. First error stops the process.
352
- - `sequenceRecord(record)`: Like `sequence`, but for objects (`{ a: Result, b: Result }` → `Result<{ a, b }>`).
353
- - `collectFirstOk(results)`: Find the first success, or return all errors.
354
- - `collectFirstOkAsync(results)`: Async version - find the first success.
355
- - `collectFirstOkParallelAsync(results)`: Parallel variant - first success wins, all rejections continue.
356
- - `collectAllErrors(results)`: Returns `Ok(values)` only if all are Ok, otherwise collects _all_ errors.
357
- - `partition(results)`: Separate a list into arrays of `[oks, errs]`.
358
- - `flatten(result)`: Flattens a nested `Result<Result<T, E>, E>` into `Result<T, E>`.
359
- - `zip(r1, r2)`: Combine two results into a tuple.
360
-
361
- ---
362
-
363
- ## 🤝 Contributing
364
-
365
- We welcome contributions! Please follow the standard pull request process. Ensure usage of TypeScript and Vitest for testing.
201
+ ```sh
202
+ pnpm docs:dev
203
+ pnpm docs:check
204
+ pnpm test:exports
205
+ ```
366
206
 
367
- ## 📄 License
207
+ ## License
368
208
 
369
- This project is licensed under the MIT License.
209
+ MIT
@@ -0,0 +1,12 @@
1
+ const require_result = require('./result-CY-KIivk.cjs');
2
+ const require_flatten = require('./flatten-Df9U40nO.cjs');
3
+
4
+ exports.all = require_result.all;
5
+ exports.collectAllErrors = require_flatten.collectAllErrors;
6
+ exports.collectFirstOk = require_result.collectFirstOk;
7
+ exports.collectFirstOkAsync = require_flatten.collectFirstOkAsync;
8
+ exports.collectFirstOkParallelAsync = require_flatten.collectFirstOkParallelAsync;
9
+ exports.flatten = require_flatten.flatten;
10
+ exports.partition = require_flatten.partition;
11
+ exports.sequence = require_result.sequence;
12
+ exports.sequenceRecord = require_flatten.sequenceRecord;
@@ -0,0 +1,3 @@
1
+ import { E as collectFirstOk, n as sequence, t as all } from "./sequence-Br6tAIIu.cjs";
2
+ import { a as collectFirstOkAsync, i as collectFirstOkParallelAsync, n as partition, o as sequenceRecord, r as collectAllErrors, t as flatten } from "./flatten-DK7eJKPx.cjs";
3
+ export { all, collectAllErrors, collectFirstOk, collectFirstOkAsync, collectFirstOkParallelAsync, flatten, partition, sequence, sequenceRecord };
@@ -0,0 +1,3 @@
1
+ import { E as collectFirstOk, n as sequence, t as all } from "./sequence-C0jlR3AY.mjs";
2
+ import { a as collectFirstOkAsync, i as collectFirstOkParallelAsync, n as partition, o as sequenceRecord, r as collectAllErrors, t as flatten } from "./flatten-B8bN6fiI.mjs";
3
+ export { all, collectAllErrors, collectFirstOk, collectFirstOkAsync, collectFirstOkParallelAsync, flatten, partition, sequence, sequenceRecord };
@@ -0,0 +1,4 @@
1
+ import { L as all, R as sequence, x as collectFirstOk } from "./result-DKKaNdOx.mjs";
2
+ import { a as collectFirstOkAsync, i as collectFirstOkParallelAsync, n as partition, o as sequenceRecord, r as collectAllErrors, t as flatten } from "./flatten-C6Y9hx79.mjs";
3
+
4
+ export { all, collectAllErrors, collectFirstOk, collectFirstOkAsync, collectFirstOkParallelAsync, flatten, partition, sequence, sequenceRecord };
@@ -0,0 +1,95 @@
1
+ //#region src/errors.ts
2
+ const ERR_INVALID_RESULT_STATE = "ERR_INVALID_RESULT_STATE";
3
+ const ERR_INVALID_STATE = ERR_INVALID_RESULT_STATE;
4
+ const ERR_TASK_YIELD_NOT_RESULT = "ERR_TASK_YIELD_NOT_RESULT";
5
+ const ERR_MATCH_ON_OK = "ERR_MATCH_ON_OK";
6
+ const ERR_MATCH_ERR_HANDLER_NOT_RESULT = "ERR_MATCH_ERR_HANDLER_NOT_RESULT";
7
+ const ERR_UNWRAP_ON_ERR = "ERR_UNWRAP_ON_ERR";
8
+ const ERR_UNWRAP_ERR_ON_OK = "ERR_UNWRAP_ERR_ON_OK";
9
+ const ERR_EXPECT_OK = "ERR_EXPECT_OK";
10
+ const ERR_EXPECT_ERR = "ERR_EXPECT_ERR";
11
+ const formatResultErrorMessage = (code, message, context) => {
12
+ if (context) return `${code}: ${message} (context: ${context})`;
13
+ return `${code}: ${message}`;
14
+ };
15
+ var ResultError = class extends Error {
16
+ code;
17
+ context;
18
+ constructor(message, code, context) {
19
+ super(formatResultErrorMessage(code, message, context));
20
+ this.code = code;
21
+ this.context = context;
22
+ this.name = new.target.name;
23
+ Object.setPrototypeOf(this, new.target.prototype);
24
+ }
25
+ };
26
+ var ResultTypeError = class extends TypeError {
27
+ code;
28
+ context;
29
+ constructor(message, code, context) {
30
+ super(formatResultErrorMessage(code, message, context));
31
+ this.code = code;
32
+ this.context = context;
33
+ this.name = new.target.name;
34
+ Object.setPrototypeOf(this, new.target.prototype);
35
+ }
36
+ };
37
+ const INVALID_RESULT_STATE_MESSAGE = "Unreachable: Result is neither Ok nor Err";
38
+ var InvalidResultStateError = class extends ResultError {
39
+ constructor(context) {
40
+ super(INVALID_RESULT_STATE_MESSAGE, ERR_INVALID_RESULT_STATE, context);
41
+ }
42
+ };
43
+ var TaskYieldNotResultError = class extends ResultTypeError {
44
+ yieldedValue;
45
+ constructor(yieldedValue) {
46
+ super("task() expected yielded values to be Result. Use `yield*` on a Result.", ERR_TASK_YIELD_NOT_RESULT);
47
+ this.yieldedValue = yieldedValue;
48
+ }
49
+ };
50
+ var MatchOnOkError = class extends ResultTypeError {
51
+ constructor(methodName = "match") {
52
+ super(`${methodName}() can only be called on Err results. Use \`if (result.isErr()) { ... }\` first.`, ERR_MATCH_ON_OK);
53
+ }
54
+ };
55
+ var MatchErrHandlerNotResultError = class extends ResultTypeError {
56
+ handlerName;
57
+ returnedValue;
58
+ constructor(handlerName, returnedValue) {
59
+ super(`matchErr().${handlerName}() handlers must return a Result. Wrap values with ok(...) or err(...).`, ERR_MATCH_ERR_HANDLER_NOT_RESULT);
60
+ this.handlerName = handlerName;
61
+ this.returnedValue = returnedValue;
62
+ }
63
+ };
64
+ var UnwrapOnErrError = class extends ResultTypeError {
65
+ errorValue;
66
+ constructor(errorValue) {
67
+ super(`Called unwrap() on Err: ${String(errorValue)}`, ERR_UNWRAP_ON_ERR);
68
+ this.errorValue = errorValue;
69
+ }
70
+ };
71
+ var UnwrapErrOnOkError = class extends ResultTypeError {
72
+ okValue;
73
+ constructor(okValue) {
74
+ super(`Called unwrapErr() on Ok: ${String(okValue)}`, ERR_UNWRAP_ERR_ON_OK);
75
+ this.okValue = okValue;
76
+ }
77
+ };
78
+ var ExpectOkError = class extends ResultError {
79
+ expectedMessage;
80
+ constructor(expectedMessage) {
81
+ super(expectedMessage, ERR_EXPECT_OK);
82
+ this.expectedMessage = expectedMessage;
83
+ }
84
+ };
85
+ var ExpectErrError = class extends ResultError {
86
+ expectedMessage;
87
+ constructor(expectedMessage) {
88
+ super(expectedMessage, ERR_EXPECT_ERR);
89
+ this.expectedMessage = expectedMessage;
90
+ }
91
+ };
92
+
93
+ //#endregion
94
+ export { TaskYieldNotResultError as _, ERR_MATCH_ERR_HANDLER_NOT_RESULT as a, ERR_UNWRAP_ERR_ON_OK as c, ExpectOkError as d, InvalidResultStateError as f, ResultTypeError as g, ResultError as h, ERR_INVALID_STATE as i, ERR_UNWRAP_ON_ERR as l, MatchOnOkError as m, ERR_EXPECT_OK as n, ERR_MATCH_ON_OK as o, MatchErrHandlerNotResultError as p, ERR_INVALID_RESULT_STATE as r, ERR_TASK_YIELD_NOT_RESULT as s, ERR_EXPECT_ERR as t, ExpectErrError as u, UnwrapErrOnOkError as v, UnwrapOnErrError as y };
95
+ //# sourceMappingURL=errors-2WOswg7r.mjs.map