@shirudo/result 0.0.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +137 -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,210 @@
|
|
|
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://codecov.io/gh/shi-rudo/result-ts)
|
|
9
|
+
[](https://www.npmjs.com/package/@shirudo/result)
|
|
10
|
+
[](./LICENSE)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://nodejs.org/)
|
|
13
|
+

|
|
8
14
|
|
|
9
|
-
|
|
10
|
-

|
|
11
|
-

|
|
12
|
-

|
|
15
|
+
## Installation
|
|
13
16
|
|
|
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
|
|
17
|
+
```sh
|
|
28
18
|
npm install @shirudo/result
|
|
29
|
-
# or
|
|
30
19
|
pnpm add @shirudo/result
|
|
31
|
-
# or
|
|
32
20
|
yarn add @shirudo/result
|
|
33
21
|
```
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## 🚀 Quick Start
|
|
38
|
-
|
|
39
|
-
### Basic Usage
|
|
40
|
-
|
|
41
|
-
Instead of throwing errors, return a `Result`.
|
|
23
|
+
## Quick Start
|
|
42
24
|
|
|
43
25
|
```ts
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
26
|
+
import { Result } from '@shirudo/result';
|
|
27
|
+
|
|
28
|
+
type User = { id: string; email: string; active: boolean };
|
|
29
|
+
type UserError =
|
|
30
|
+
| { type: 'not-found'; id: string }
|
|
31
|
+
| { type: 'inactive'; id: string };
|
|
32
|
+
|
|
33
|
+
const users = new Map<string, User>([
|
|
34
|
+
['1', { id: '1', email: 'ada@example.com', active: true }],
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
function loadUser(id: string): Result<User, UserError> {
|
|
38
|
+
const user = users.get(id);
|
|
39
|
+
if (!user) return Result.err({ type: 'not-found', id });
|
|
40
|
+
if (!user.active) return Result.err({ type: 'inactive', id });
|
|
41
|
+
return Result.ok(user);
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
const result =
|
|
44
|
+
const result = loadUser('1');
|
|
54
45
|
|
|
55
46
|
if (result.isOk()) {
|
|
56
|
-
|
|
57
|
-
console.log("Success:", result.value); // 5
|
|
47
|
+
console.log(result.value.email);
|
|
58
48
|
} else {
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
switch (result.error.type) {
|
|
50
|
+
case 'not-found':
|
|
51
|
+
console.error(`Missing user ${result.error.id}`);
|
|
52
|
+
break;
|
|
53
|
+
case 'inactive':
|
|
54
|
+
console.error(`Inactive user ${result.error.id}`);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
61
57
|
}
|
|
62
58
|
```
|
|
63
59
|
|
|
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
|
-
---
|
|
60
|
+
## Why This Library
|
|
85
61
|
|
|
86
|
-
|
|
62
|
+
- `Ok` does not expose `error`, and `Err` does not expose `value`; accidental reads require proper narrowing.
|
|
63
|
+
- `isResult` uses a stable internal brand plus payload validation instead of accepting random lookalike objects.
|
|
64
|
+
- `Result.fromPromise()` maps promise rejections but rethrows bugs inside the error mapper.
|
|
65
|
+
- Sync, async, generator, collection, and matching workflows are first-class.
|
|
66
|
+
- README and docs TypeScript snippets are compile-checked.
|
|
67
|
+
- Package exports are tested for ESM, CJS, and TypeScript consumers.
|
|
87
68
|
|
|
88
|
-
|
|
69
|
+
## Common Workflows
|
|
89
70
|
|
|
90
|
-
|
|
71
|
+
### Catch throwing APIs
|
|
91
72
|
|
|
92
73
|
```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");
|
|
74
|
+
import { Result } from '@shirudo/result';
|
|
104
75
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### Async Pipelines
|
|
76
|
+
const parseJson = Result.fromThrowable(
|
|
77
|
+
JSON.parse,
|
|
78
|
+
error => ({ type: 'parse' as const, cause: error }),
|
|
79
|
+
);
|
|
110
80
|
|
|
111
|
-
|
|
81
|
+
const parsed = parseJson('{"valid": true}');
|
|
112
82
|
|
|
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
|
-
})
|
|
83
|
+
const response = await Result.fromPromise(
|
|
84
|
+
Promise.resolve({ ok: true }),
|
|
85
|
+
error => ({ type: 'network' as const, cause: error }),
|
|
125
86
|
);
|
|
126
87
|
```
|
|
127
88
|
|
|
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.
|
|
89
|
+
### Compose with pipe operators
|
|
131
90
|
|
|
132
91
|
```ts
|
|
133
|
-
import {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// calculate is a Promise<Result<number, Error>>
|
|
92
|
+
import { Result } from '@shirudo/result';
|
|
93
|
+
import { filter, map, mapErr } from '@shirudo/result/operators';
|
|
94
|
+
|
|
95
|
+
const processed = Result.ok<number, string>(10).pipe(
|
|
96
|
+
map(value => value * 2),
|
|
97
|
+
filter(
|
|
98
|
+
value => value > 25,
|
|
99
|
+
() => 'too small',
|
|
100
|
+
),
|
|
101
|
+
mapErr(error => `Validation failed: ${error}`),
|
|
102
|
+
);
|
|
147
103
|
```
|
|
148
104
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
The `task()` function accepts an optional `onThrow` callback for custom error mapping:
|
|
105
|
+
### Compose async work
|
|
152
106
|
|
|
153
107
|
```ts
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
108
|
+
import { Result } from '@shirudo/result';
|
|
109
|
+
import { mapAsync, tryMapAsync } from '@shirudo/result/operators';
|
|
110
|
+
|
|
111
|
+
const db = {
|
|
112
|
+
async getUser(id: number) {
|
|
113
|
+
return { id, email: 'ada@example.com' };
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const normalizedEmail = await Result.ok<number, string>(1).pipeAsync(
|
|
118
|
+
mapAsync(async id => db.getUser(id)),
|
|
119
|
+
tryMapAsync(async user => user.email.toLowerCase()),
|
|
160
120
|
);
|
|
161
|
-
// Returns Result<ProcessedData, CustomError>
|
|
162
121
|
```
|
|
163
122
|
|
|
164
|
-
###
|
|
165
|
-
|
|
166
|
-
The simplest way to handle both `Ok` and `Err` cases and return a single value:
|
|
123
|
+
### Use task notation for sequential flows
|
|
167
124
|
|
|
168
125
|
```ts
|
|
169
|
-
import {
|
|
126
|
+
import { Result, task } from '@shirudo/result';
|
|
170
127
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
(val) => `Success: ${val}`,
|
|
175
|
-
(err) => `Error: ${err}`
|
|
176
|
-
);
|
|
177
|
-
// message = "Success: 42"
|
|
128
|
+
function findUser(id: string) {
|
|
129
|
+
return Result.ok({ id, email: 'ada@example.com' });
|
|
130
|
+
}
|
|
178
131
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
132
|
+
function ensureEmail(user: { id: string; email?: string }) {
|
|
133
|
+
return user.email
|
|
134
|
+
? Result.ok(user.email)
|
|
135
|
+
: Result.err({ type: 'missing-email' as const, id: user.id });
|
|
136
|
+
}
|
|
184
137
|
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
138
|
+
const email = task(function* () {
|
|
139
|
+
const user = yield* findUser('1');
|
|
140
|
+
return yield* ensureEmail(user);
|
|
141
|
+
});
|
|
190
142
|
```
|
|
191
143
|
|
|
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`).
|
|
144
|
+
### Match discriminated-union errors
|
|
195
145
|
|
|
196
146
|
```ts
|
|
197
|
-
import { Result } from
|
|
147
|
+
import { Result, matchTag } from '@shirudo/result';
|
|
198
148
|
|
|
199
|
-
|
|
200
|
-
|
|
149
|
+
type DomainError =
|
|
150
|
+
| { type: 'network'; retryAfter: number }
|
|
151
|
+
| { type: 'validation'; field: string };
|
|
201
152
|
|
|
202
|
-
const
|
|
153
|
+
const failed = Result.err<DomainError>({ type: 'network', retryAfter: 30 });
|
|
203
154
|
|
|
204
|
-
const message =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
.errVal("TIMEOUT_CODE", () => "Operation timed out") // Match primitive values
|
|
209
|
-
.ok((val) => `Success: ${val}`)
|
|
210
|
-
.run();
|
|
155
|
+
const message = matchTag(failed, 'type', {
|
|
156
|
+
network: error => `Retry in ${error.retryAfter}s`,
|
|
157
|
+
validation: error => `Invalid field: ${error.field}`,
|
|
158
|
+
});
|
|
211
159
|
```
|
|
212
160
|
|
|
213
|
-
|
|
161
|
+
## Imports
|
|
214
162
|
|
|
215
|
-
|
|
163
|
+
Everything is available from the package root:
|
|
216
164
|
|
|
217
165
|
```ts
|
|
218
|
-
import {
|
|
219
|
-
|
|
220
|
-
const result = await matchAsync({
|
|
221
|
-
ok: async (val) => `Success: ${val}`,
|
|
222
|
-
err: async (e) => `Error: ${e}`
|
|
223
|
-
})(someResult);
|
|
166
|
+
import { Result, err, ok, task } from '@shirudo/result';
|
|
224
167
|
```
|
|
225
168
|
|
|
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`).
|
|
169
|
+
Focused subpath exports are available for clearer imports and package-level checks:
|
|
276
170
|
|
|
277
171
|
```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}'));
|
|
172
|
+
import { UnwrapOnErrError } from '@shirudo/result/errors';
|
|
173
|
+
import { flatMapAsync, map } from '@shirudo/result/operators';
|
|
174
|
+
import { sequence, sequenceRecord } from '@shirudo/result/collections';
|
|
291
175
|
```
|
|
292
176
|
|
|
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. |
|
|
177
|
+
## Documentation
|
|
309
178
|
|
|
310
|
-
|
|
179
|
+
The full documentation lives in `docs/` and is built with VitePress.
|
|
311
180
|
|
|
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";
|
|
181
|
+
- [Getting Started](docs/guide/getting-started.md)
|
|
182
|
+
- [Pipelines](docs/guide/pipelines.md)
|
|
183
|
+
- [Task Notation](docs/guide/task.md)
|
|
184
|
+
- [Pattern Matching](docs/guide/matching.md)
|
|
185
|
+
- [Result API](docs/api/result.md)
|
|
186
|
+
- [Operators](docs/api/operators.md)
|
|
187
|
+
- [Collections](docs/api/collections.md)
|
|
188
|
+
- [Error Classes](docs/api/errors.md)
|
|
189
|
+
- [Version 1 Migration](docs/migration/v1.md)
|
|
190
|
+
- [Design Decisions](docs/decisions/lazy-async-abstraction.md)
|
|
327
191
|
|
|
328
|
-
|
|
329
|
-
const b = ok(10);
|
|
192
|
+
## Development
|
|
330
193
|
|
|
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")
|
|
194
|
+
```sh
|
|
195
|
+
pnpm install
|
|
196
|
+
pnpm check
|
|
197
|
+
pnpm check:clean
|
|
347
198
|
```
|
|
348
199
|
|
|
349
|
-
|
|
200
|
+
Useful focused commands:
|
|
350
201
|
|
|
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.
|
|
202
|
+
```sh
|
|
203
|
+
pnpm docs:dev
|
|
204
|
+
pnpm docs:check
|
|
205
|
+
pnpm test:exports
|
|
206
|
+
```
|
|
366
207
|
|
|
367
|
-
##
|
|
208
|
+
## License
|
|
368
209
|
|
|
369
|
-
|
|
210
|
+
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
|