@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.
- package/LICENSE +21 -0
- package/README.md +136 -296
- package/dist/collections.cjs +12 -0
- package/dist/collections.d.cts +3 -0
- package/dist/collections.d.mts +3 -0
- package/dist/collections.mjs +4 -0
- package/dist/errors-2WOswg7r.mjs +95 -0
- package/dist/errors-2WOswg7r.mjs.map +1 -0
- package/dist/errors-BFjY06EV.d.cts +55 -0
- package/dist/errors-BFjY06EV.d.cts.map +1 -0
- package/dist/errors-C5qGMRiU.d.mts +55 -0
- package/dist/errors-C5qGMRiU.d.mts.map +1 -0
- package/dist/errors-D2EMzJQl.cjs +209 -0
- package/dist/errors-D2EMzJQl.cjs.map +1 -0
- package/dist/errors.cjs +21 -0
- package/dist/errors.d.cts +2 -0
- package/dist/errors.d.mts +2 -0
- package/dist/errors.mjs +3 -0
- package/dist/flatten-B8bN6fiI.d.mts +71 -0
- package/dist/flatten-B8bN6fiI.d.mts.map +1 -0
- package/dist/flatten-C6Y9hx79.mjs +147 -0
- package/dist/flatten-C6Y9hx79.mjs.map +1 -0
- package/dist/flatten-DK7eJKPx.d.cts +71 -0
- package/dist/flatten-DK7eJKPx.d.cts.map +1 -0
- package/dist/flatten-Df9U40nO.cjs +182 -0
- package/dist/flatten-Df9U40nO.cjs.map +1 -0
- package/dist/index.cjs +113 -1018
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -546
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +12 -546
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +25 -935
- package/dist/index.mjs.map +1 -1
- package/dist/mapOrElse-B0_4r6Jn.mjs +96 -0
- package/dist/mapOrElse-B0_4r6Jn.mjs.map +1 -0
- package/dist/mapOrElse-B5gx9x2E.d.mts +64 -0
- package/dist/mapOrElse-B5gx9x2E.d.mts.map +1 -0
- package/dist/mapOrElse-DIe7LkYV.d.cts +64 -0
- package/dist/mapOrElse-DIe7LkYV.d.cts.map +1 -0
- package/dist/mapOrElse-H-GvAUka.cjs +137 -0
- package/dist/mapOrElse-H-GvAUka.cjs.map +1 -0
- package/dist/operators.cjs +33 -0
- package/dist/operators.d.cts +3 -0
- package/dist/operators.d.mts +3 -0
- package/dist/operators.mjs +4 -0
- package/dist/result-CY-KIivk.cjs +1100 -0
- package/dist/result-CY-KIivk.cjs.map +1 -0
- package/dist/result-DKKaNdOx.mjs +861 -0
- package/dist/result-DKKaNdOx.mjs.map +1 -0
- package/dist/sequence-Br6tAIIu.d.cts +419 -0
- package/dist/sequence-Br6tAIIu.d.cts.map +1 -0
- package/dist/sequence-C0jlR3AY.d.mts +419 -0
- package/dist/sequence-C0jlR3AY.d.mts.map +1 -0
- 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
|
-
|
|
3
|
+
Robust, type-safe error handling for TypeScript.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
7
|
+
[](https://github.com/shi-rudo/result-ts/actions/workflows/ci.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/@shirudo/result)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://nodejs.org/)
|
|
12
|
+

|
|
8
13
|
|
|
9
|
-
|
|
10
|
-

|
|
11
|
-

|
|
12
|
-

|
|
14
|
+
## Installation
|
|
13
15
|
|
|
14
|
-
|
|
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 {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 =
|
|
43
|
+
const result = loadUser('1');
|
|
54
44
|
|
|
55
45
|
if (result.isOk()) {
|
|
56
|
-
|
|
57
|
-
console.log("Success:", result.value); // 5
|
|
46
|
+
console.log(result.value.email);
|
|
58
47
|
} else {
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
+
## Common Workflows
|
|
89
69
|
|
|
90
|
-
|
|
70
|
+
### Catch throwing APIs
|
|
91
71
|
|
|
92
72
|
```ts
|
|
93
|
-
import { Result } from
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
80
|
+
const parsed = parseJson('{"valid": true}');
|
|
112
81
|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
###
|
|
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 {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
The `task()` function accepts an optional `onThrow` callback for custom error mapping:
|
|
104
|
+
### Compose async work
|
|
152
105
|
|
|
153
106
|
```ts
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
###
|
|
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 {
|
|
125
|
+
import { Result, task } from '@shirudo/result';
|
|
170
126
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
137
|
+
const email = task(function* () {
|
|
138
|
+
const user = yield* findUser('1');
|
|
139
|
+
return yield* ensureEmail(user);
|
|
140
|
+
});
|
|
190
141
|
```
|
|
191
142
|
|
|
192
|
-
###
|
|
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
|
|
146
|
+
import { Result, matchTag } from '@shirudo/result';
|
|
198
147
|
|
|
199
|
-
|
|
200
|
-
|
|
148
|
+
type DomainError =
|
|
149
|
+
| { type: 'network'; retryAfter: number }
|
|
150
|
+
| { type: 'validation'; field: string };
|
|
201
151
|
|
|
202
|
-
const
|
|
152
|
+
const failed = Result.err<DomainError>({ type: 'network', retryAfter: 30 });
|
|
203
153
|
|
|
204
|
-
const message =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
160
|
+
## Imports
|
|
214
161
|
|
|
215
|
-
|
|
162
|
+
Everything is available from the package root:
|
|
216
163
|
|
|
217
164
|
```ts
|
|
218
|
-
import {
|
|
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
|
-
|
|
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 {
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
-
|
|
178
|
+
The full documentation lives in `docs/` and is built with VitePress.
|
|
311
179
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
329
|
-
const b = ok(10);
|
|
191
|
+
## Development
|
|
330
192
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
199
|
+
Useful focused commands:
|
|
350
200
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
##
|
|
207
|
+
## License
|
|
368
208
|
|
|
369
|
-
|
|
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
|