@pencroff-lab/kore 0.1.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 +201 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/src/types/err.d.ts +1116 -0
- package/dist/cjs/src/types/err.d.ts.map +1 -0
- package/dist/cjs/src/types/err.js +1324 -0
- package/dist/cjs/src/types/err.js.map +1 -0
- package/dist/cjs/src/types/index.d.ts +3 -0
- package/dist/cjs/src/types/index.d.ts.map +1 -0
- package/dist/cjs/src/types/index.js +19 -0
- package/dist/cjs/src/types/index.js.map +1 -0
- package/dist/cjs/src/types/outcome.d.ts +1002 -0
- package/dist/cjs/src/types/outcome.d.ts.map +1 -0
- package/dist/cjs/src/types/outcome.js +958 -0
- package/dist/cjs/src/types/outcome.js.map +1 -0
- package/dist/cjs/src/utils/format_dt.d.ts +9 -0
- package/dist/cjs/src/utils/format_dt.d.ts.map +1 -0
- package/dist/cjs/src/utils/format_dt.js +29 -0
- package/dist/cjs/src/utils/format_dt.js.map +1 -0
- package/dist/cjs/src/utils/index.d.ts +2 -0
- package/dist/cjs/src/utils/index.d.ts.map +1 -0
- package/dist/cjs/src/utils/index.js +18 -0
- package/dist/cjs/src/utils/index.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/src/types/err.d.ts +1116 -0
- package/dist/esm/src/types/err.d.ts.map +1 -0
- package/dist/esm/src/types/err.js +1320 -0
- package/dist/esm/src/types/err.js.map +1 -0
- package/dist/esm/src/types/index.d.ts +3 -0
- package/dist/esm/src/types/index.d.ts.map +1 -0
- package/dist/esm/src/types/index.js +3 -0
- package/dist/esm/src/types/index.js.map +1 -0
- package/dist/esm/src/types/outcome.d.ts +1002 -0
- package/dist/esm/src/types/outcome.d.ts.map +1 -0
- package/dist/esm/src/types/outcome.js +954 -0
- package/dist/esm/src/types/outcome.js.map +1 -0
- package/dist/esm/src/utils/format_dt.d.ts +9 -0
- package/dist/esm/src/utils/format_dt.d.ts.map +1 -0
- package/dist/esm/src/utils/format_dt.js +26 -0
- package/dist/esm/src/utils/format_dt.js.map +1 -0
- package/dist/esm/src/utils/index.d.ts +2 -0
- package/dist/esm/src/utils/index.d.ts.map +1 -0
- package/dist/esm/src/utils/index.js +2 -0
- package/dist/esm/src/utils/index.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview
|
|
4
|
+
* Monadic container for handling success and error states using tuple-first API design.
|
|
5
|
+
*
|
|
6
|
+
* This module provides the `Outcome<T>` class and related types for implementing
|
|
7
|
+
* type-safe error handling without exceptions. All operations favor immutability.
|
|
8
|
+
*
|
|
9
|
+
* @example Basic usage
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Outcome } from './outcome';
|
|
12
|
+
*
|
|
13
|
+
* const [val, err] = Outcome.from(() => [42, null]).toTuple();
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @example Migration from throwing functions
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Before (throwing):
|
|
19
|
+
* function getUser(id: string): User {
|
|
20
|
+
* const user = db.find(id);
|
|
21
|
+
* if (!user) throw new Error("Not found");
|
|
22
|
+
* return user;
|
|
23
|
+
* }
|
|
24
|
+
* try {
|
|
25
|
+
* const user = getUser("123");
|
|
26
|
+
* console.log(user.name);
|
|
27
|
+
* } catch (e) {
|
|
28
|
+
* console.error(e.message);
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* // After (Outcome):
|
|
32
|
+
* function getUser(id: string): Outcome<User> {
|
|
33
|
+
* return Outcome.from(() => {
|
|
34
|
+
* const user = db.find(id);
|
|
35
|
+
* if (!user) return Err.from("Not found", "NOT_FOUND");
|
|
36
|
+
* return [user, null];
|
|
37
|
+
* });
|
|
38
|
+
* }
|
|
39
|
+
* const [user, err] = getUser("123").toTuple();
|
|
40
|
+
* if (err) {
|
|
41
|
+
* console.error(err.message);
|
|
42
|
+
* return;
|
|
43
|
+
* }
|
|
44
|
+
* console.log(user.name);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.Outcome = void 0;
|
|
49
|
+
const err_1 = require("./err");
|
|
50
|
+
/**
|
|
51
|
+
* A monadic container for handling success and error states.
|
|
52
|
+
*
|
|
53
|
+
* `Outcome<T>` provides a type-safe way to handle operations that can fail,
|
|
54
|
+
* using tuples as the primary interface. All instances are immutable.
|
|
55
|
+
*
|
|
56
|
+
* ## Core Patterns
|
|
57
|
+
*
|
|
58
|
+
* - **Construction**: Use static methods `ok()`, `err()`, `from()`, `fromAsync()`
|
|
59
|
+
* - **Inspection**: Use `isOk`, `isErr`, `value`, `error` properties
|
|
60
|
+
* - **Transformation**: Use `map()`, `mapErr()` for chained operations
|
|
61
|
+
* - **Extraction**: Use `toTuple()` for final value extraction
|
|
62
|
+
*
|
|
63
|
+
* @example Basic usage
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const outcome = Outcome.from(() => {
|
|
66
|
+
* if (Math.random() > 0.5) return [42, null];
|
|
67
|
+
* return Err.from('Bad luck');
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* const [value, err] = outcome.toTuple();
|
|
71
|
+
* if (err) {
|
|
72
|
+
* console.error('Failed:', err.message);
|
|
73
|
+
* } else {
|
|
74
|
+
* console.log('Success:', value);
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example Chaining transformations
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const result = Outcome.ok(5)
|
|
81
|
+
* .map(n => [n * 2, null])
|
|
82
|
+
* .map(n => [n.toString(), null])
|
|
83
|
+
* .toTuple();
|
|
84
|
+
* // result: ['10', null]
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @typeParam T - The type of the success value
|
|
88
|
+
*/
|
|
89
|
+
class Outcome {
|
|
90
|
+
/**
|
|
91
|
+
* Discriminator property for type narrowing.
|
|
92
|
+
* `true` for success outcomes, `false` for error outcomes.
|
|
93
|
+
*/
|
|
94
|
+
isOk;
|
|
95
|
+
/** Internal tuple storage */
|
|
96
|
+
_tuple;
|
|
97
|
+
/**
|
|
98
|
+
* Private constructor - use static factory methods.
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
constructor(tuple) {
|
|
102
|
+
this._tuple = tuple;
|
|
103
|
+
this.isOk = tuple[1] === null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Process a CallbackReturn value into an Outcome.
|
|
107
|
+
* Handles discrimination: Err → null (void) → tuple destructure.
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
static _processCallbackReturn(result) {
|
|
111
|
+
// Case 1: Direct Err return (shorthand)
|
|
112
|
+
if (err_1.Err.isErr(result)) {
|
|
113
|
+
return new Outcome([null, result]);
|
|
114
|
+
}
|
|
115
|
+
// Case 2: null = void success
|
|
116
|
+
if (result === null) {
|
|
117
|
+
return new Outcome([null, null]);
|
|
118
|
+
}
|
|
119
|
+
// Case 3: Tuple [T, null] | [null, Err]
|
|
120
|
+
const [value, error] = result;
|
|
121
|
+
if (err_1.Err.isErr(error)) {
|
|
122
|
+
return new Outcome([null, error]);
|
|
123
|
+
}
|
|
124
|
+
return new Outcome([value, null]);
|
|
125
|
+
}
|
|
126
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
127
|
+
// Static Constructors
|
|
128
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
129
|
+
/**
|
|
130
|
+
* Create a success Outcome with the given value.
|
|
131
|
+
*
|
|
132
|
+
* @param value - The success value
|
|
133
|
+
* @returns Outcome containing the success value
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const outcome = Outcome.ok(42);
|
|
138
|
+
* console.log(outcome.isOk); // true
|
|
139
|
+
* console.log(outcome.value); // 42
|
|
140
|
+
*
|
|
141
|
+
* const [val, err] = outcome.toTuple();
|
|
142
|
+
* // val: 42, err: null
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
static ok(value) {
|
|
146
|
+
return new Outcome([value, null]);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Implementation signature for err().
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
static err(messageOrErr, codeOrOptionsOrErr, options) {
|
|
153
|
+
// If first arg is already an Err, use it directly
|
|
154
|
+
if (err_1.Err.isErr(messageOrErr)) {
|
|
155
|
+
return new Outcome([null, messageOrErr]);
|
|
156
|
+
}
|
|
157
|
+
const message = messageOrErr;
|
|
158
|
+
// If second arg is Err or Error, wrap it
|
|
159
|
+
if (err_1.Err.isErr(codeOrOptionsOrErr) || codeOrOptionsOrErr instanceof Error) {
|
|
160
|
+
const wrapped = err_1.Err.wrap(message, codeOrOptionsOrErr, options);
|
|
161
|
+
return new Outcome([null, wrapped]);
|
|
162
|
+
}
|
|
163
|
+
// Otherwise, create new Err with message and options/code
|
|
164
|
+
// biome-ignore lint/suspicious/noExplicitAny: overloaded argument handling
|
|
165
|
+
const err = err_1.Err.from(message, codeOrOptionsOrErr);
|
|
166
|
+
return new Outcome([null, err]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create a success Outcome with null value (void success).
|
|
170
|
+
*
|
|
171
|
+
* Use for operations that succeed but have no meaningful return value.
|
|
172
|
+
*
|
|
173
|
+
* @returns Outcome<null> representing void success
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* Returns `Outcome<null>` (not `Outcome<undefined>` or `Outcome<void>`).
|
|
177
|
+
* This is intentional for consistency with the tuple pattern where `null`
|
|
178
|
+
* indicates absence of error in `[value, null]`.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* function logMessage(msg: string): Outcome<null> {
|
|
183
|
+
* console.log(msg);
|
|
184
|
+
* return Outcome.unit();
|
|
185
|
+
* }
|
|
186
|
+
*
|
|
187
|
+
* const outcome = logMessage('Hello');
|
|
188
|
+
* console.log(outcome.isOk); // true
|
|
189
|
+
* console.log(outcome.value); // null
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
static unit() {
|
|
193
|
+
return new Outcome([null, null]);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Create an Outcome from a callback that returns `CallbackReturn<T>`.
|
|
197
|
+
*
|
|
198
|
+
* The callback can return:
|
|
199
|
+
* - `[value, null]` - success with value
|
|
200
|
+
* - `[null, Err]` - error as tuple
|
|
201
|
+
* - `null` - void success
|
|
202
|
+
* - `Err` - error directly
|
|
203
|
+
*
|
|
204
|
+
* If the callback throws, the exception is caught and wrapped in an error Outcome.
|
|
205
|
+
*
|
|
206
|
+
* @param fn - Callback returning CallbackReturn<T>
|
|
207
|
+
* @returns Outcome<T>
|
|
208
|
+
*
|
|
209
|
+
* @see {@link fromAsync} for the async version
|
|
210
|
+
*
|
|
211
|
+
* @example Success with value
|
|
212
|
+
* ```typescript
|
|
213
|
+
* const outcome = Outcome.from(() => {
|
|
214
|
+
* return [42, null];
|
|
215
|
+
* });
|
|
216
|
+
* console.log(outcome.value); // 42
|
|
217
|
+
* ```
|
|
218
|
+
*
|
|
219
|
+
* @example Error shorthand
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const outcome = Outcome.from(() => {
|
|
222
|
+
* if (invalid) return Err.from('Invalid input');
|
|
223
|
+
* return [result, null];
|
|
224
|
+
* });
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* @example Catching throws from external libraries
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const outcome = Outcome.from(() => {
|
|
230
|
+
* const data = JSON.parse(untrustedInput); // may throw
|
|
231
|
+
* return [data, null];
|
|
232
|
+
* });
|
|
233
|
+
* // If JSON.parse throws, outcome.isErr === true
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
static from(fn) {
|
|
237
|
+
try {
|
|
238
|
+
const result = fn();
|
|
239
|
+
return Outcome._processCallbackReturn(result);
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Create an Outcome from an async callback that returns `Promise<CallbackReturn<T>>`.
|
|
247
|
+
*
|
|
248
|
+
* Async version of `from()` with identical semantics.
|
|
249
|
+
*
|
|
250
|
+
* @param fn - Async callback returning Promise<CallbackReturn<T>>
|
|
251
|
+
* @returns Promise<Outcome<T>>
|
|
252
|
+
*
|
|
253
|
+
* @see {@link from} for the synchronous version
|
|
254
|
+
*
|
|
255
|
+
* @example Async operation
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const outcome = await Outcome.fromAsync(async () => {
|
|
258
|
+
* const response = await fetch('/api/data');
|
|
259
|
+
* if (!response.ok) {
|
|
260
|
+
* return Err.from('Request failed', { code: 'HTTP_ERROR' });
|
|
261
|
+
* }
|
|
262
|
+
* const data = await response.json();
|
|
263
|
+
* return [data, null];
|
|
264
|
+
* });
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* @example With error aggregation
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const outcome = await Outcome.fromAsync(async () => {
|
|
270
|
+
* let errors = Err.aggregate('Batch failed');
|
|
271
|
+
*
|
|
272
|
+
* const [a, errA] = await taskA().toTuple();
|
|
273
|
+
* if (errA) errors = errors.add(errA);
|
|
274
|
+
*
|
|
275
|
+
* const [b, errB] = await taskB().toTuple();
|
|
276
|
+
* if (errB) errors = errors.add(errB);
|
|
277
|
+
*
|
|
278
|
+
* if (errors.count > 0) return errors;
|
|
279
|
+
* return [{ a, b }, null];
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
static async fromAsync(fn) {
|
|
284
|
+
try {
|
|
285
|
+
const result = await fn();
|
|
286
|
+
return Outcome._processCallbackReturn(result);
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Create an Outcome from an existing ResultTuple.
|
|
294
|
+
*
|
|
295
|
+
* Useful for deserializing Outcomes or converting from external tuple sources.
|
|
296
|
+
*
|
|
297
|
+
* @param tuple - A ResultTuple<T>
|
|
298
|
+
* @returns Outcome<T>
|
|
299
|
+
*
|
|
300
|
+
* @see {@link toTuple} for extracting the tuple from an Outcome
|
|
301
|
+
*
|
|
302
|
+
* @example Deserializing from JSON
|
|
303
|
+
* ```typescript
|
|
304
|
+
* const json = '["hello", null]';
|
|
305
|
+
* const tuple = JSON.parse(json) as ResultTuple<string>;
|
|
306
|
+
* const outcome = Outcome.fromTuple(tuple);
|
|
307
|
+
* console.log(outcome.value); // 'hello'
|
|
308
|
+
* ```
|
|
309
|
+
*
|
|
310
|
+
* @example Round-trip serialization
|
|
311
|
+
* ```typescript
|
|
312
|
+
* const original = Outcome.ok(42);
|
|
313
|
+
* const json = JSON.stringify(original.toJSON());
|
|
314
|
+
* const restored = Outcome.fromTuple(JSON.parse(json));
|
|
315
|
+
* console.log(restored.value); // 42
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
static fromTuple(tuple) {
|
|
319
|
+
return new Outcome(tuple);
|
|
320
|
+
}
|
|
321
|
+
static fromJSON(payload) {
|
|
322
|
+
return Outcome.from(() => {
|
|
323
|
+
if (!Array.isArray(payload) || payload.length !== 2) {
|
|
324
|
+
return err_1.Err.from("Invalid Outcome JSON");
|
|
325
|
+
}
|
|
326
|
+
const [value, error] = payload;
|
|
327
|
+
if (error === null) {
|
|
328
|
+
return [value, null];
|
|
329
|
+
}
|
|
330
|
+
return [null, err_1.Err.fromJSON(error)];
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
334
|
+
// Combinators
|
|
335
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
336
|
+
/**
|
|
337
|
+
* Combines multiple Outcomes, succeeding if all succeed with an array of values.
|
|
338
|
+
* If any Outcome fails, returns an Err containing all failures aggregated via Err.aggregate().
|
|
339
|
+
*
|
|
340
|
+
* This is useful for validation scenarios where you need to collect all errors.
|
|
341
|
+
*
|
|
342
|
+
* For empty arrays, returns `Outcome.ok([])` (vacuous truth).
|
|
343
|
+
*
|
|
344
|
+
* @param outcomes - Array of Outcomes to combine
|
|
345
|
+
* @returns Outcome containing array of all success values, or aggregate error
|
|
346
|
+
*
|
|
347
|
+
* @remarks
|
|
348
|
+
* Time complexity: O(n) where n is the number of outcomes.
|
|
349
|
+
* All outcomes are evaluated (non-short-circuiting) to collect all errors.
|
|
350
|
+
*
|
|
351
|
+
* @example All succeed
|
|
352
|
+
* ```typescript
|
|
353
|
+
* const outcomes = [Outcome.ok(1), Outcome.ok(2), Outcome.ok(3)];
|
|
354
|
+
* const combined = Outcome.all(outcomes);
|
|
355
|
+
* console.log(combined.value); // [1, 2, 3]
|
|
356
|
+
* ```
|
|
357
|
+
*
|
|
358
|
+
* @example One fails
|
|
359
|
+
* ```typescript
|
|
360
|
+
* const outcomes = [
|
|
361
|
+
* Outcome.ok(1),
|
|
362
|
+
* Outcome.err('Failed'),
|
|
363
|
+
* Outcome.ok(3)
|
|
364
|
+
* ];
|
|
365
|
+
* const combined = Outcome.all(outcomes);
|
|
366
|
+
* console.log(combined.isErr); // true
|
|
367
|
+
* console.log(combined.error?.message); // 'Failed'
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @example Many fails
|
|
371
|
+
* ```typescript
|
|
372
|
+
* const mixed = [
|
|
373
|
+
* Outcome.ok(1),
|
|
374
|
+
* Outcome.err("Error A"),
|
|
375
|
+
* Outcome.err("Error B")
|
|
376
|
+
* ];
|
|
377
|
+
* const failed = Outcome.all(mixed);
|
|
378
|
+
* console.log(failed.isErr); // true
|
|
379
|
+
* // Error contains both "Error A" and "Error B"
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
* @example Empty array
|
|
383
|
+
* ```typescript
|
|
384
|
+
* const combined = Outcome.all([]);
|
|
385
|
+
* console.log(combined.value); // []
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
static all(outcomes) {
|
|
389
|
+
const values = [];
|
|
390
|
+
const errors = [];
|
|
391
|
+
for (const outcome of outcomes) {
|
|
392
|
+
if (outcome.isErr) {
|
|
393
|
+
//
|
|
394
|
+
errors.push(outcome._tuple[1]);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
values.push(outcome._tuple[0]);
|
|
398
|
+
}
|
|
399
|
+
if (errors.length === 1) {
|
|
400
|
+
return new Outcome([null, errors[0]]);
|
|
401
|
+
}
|
|
402
|
+
if (errors.length > 0) {
|
|
403
|
+
return Outcome.err(err_1.Err.aggregate("Multiple failed", errors));
|
|
404
|
+
}
|
|
405
|
+
return new Outcome([values, null]);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Return the first successful Outcome from an array.
|
|
409
|
+
*
|
|
410
|
+
* Returns the first success encountered.
|
|
411
|
+
* Returns an aggregate error if ALL outcomes fail.
|
|
412
|
+
*
|
|
413
|
+
* For empty arrays, returns an error (no value to return).
|
|
414
|
+
*
|
|
415
|
+
* @param outcomes - Array of Outcomes to check
|
|
416
|
+
* @returns First successful Outcome, or aggregate of all errors
|
|
417
|
+
*
|
|
418
|
+
* @remarks
|
|
419
|
+
* Time complexity: O(n) worst case, but short-circuits on first success.
|
|
420
|
+
* Best case: O(1) if first outcome is successful.
|
|
421
|
+
*
|
|
422
|
+
* @example First success wins
|
|
423
|
+
* ```typescript
|
|
424
|
+
* const outcomes = [
|
|
425
|
+
* Outcome.err('First failed'),
|
|
426
|
+
* Outcome.ok(42),
|
|
427
|
+
* Outcome.ok(100)
|
|
428
|
+
* ];
|
|
429
|
+
* const result = Outcome.any(outcomes);
|
|
430
|
+
* console.log(result.value); // 42
|
|
431
|
+
* ```
|
|
432
|
+
*
|
|
433
|
+
* @example All fail
|
|
434
|
+
* ```typescript
|
|
435
|
+
* const outcomes = [
|
|
436
|
+
* Outcome.err('Error 1'),
|
|
437
|
+
* Outcome.err('Error 2')
|
|
438
|
+
* ];
|
|
439
|
+
* const result = Outcome.any(outcomes);
|
|
440
|
+
* console.log(result.isErr); // true
|
|
441
|
+
* console.log(result.error?.isAggregate); // true
|
|
442
|
+
* ```
|
|
443
|
+
*
|
|
444
|
+
* @example Empty array
|
|
445
|
+
* ```typescript
|
|
446
|
+
* const result = Outcome.any([]);
|
|
447
|
+
* console.log(result.isErr); // true
|
|
448
|
+
* console.log(result.error?.message); // 'No outcomes provided'
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
static any(outcomes) {
|
|
452
|
+
if (outcomes.length === 0) {
|
|
453
|
+
return Outcome.err("No outcomes provided", "EMPTY_INPUT");
|
|
454
|
+
}
|
|
455
|
+
const errors = [];
|
|
456
|
+
for (const outcome of outcomes) {
|
|
457
|
+
if (outcome.isOk) {
|
|
458
|
+
return outcome;
|
|
459
|
+
}
|
|
460
|
+
errors.push(outcome._tuple[1]);
|
|
461
|
+
}
|
|
462
|
+
const aggregate = err_1.Err.aggregate("All failed", errors);
|
|
463
|
+
return new Outcome([null, aggregate]);
|
|
464
|
+
}
|
|
465
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
466
|
+
// Instance Accessors
|
|
467
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
468
|
+
/**
|
|
469
|
+
* Whether this Outcome is in error state.
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```typescript
|
|
473
|
+
* const success = Outcome.ok(42);
|
|
474
|
+
* const failure = Outcome.err('Failed');
|
|
475
|
+
*
|
|
476
|
+
* console.log(success.isErr); // false
|
|
477
|
+
* console.log(failure.isErr); // true
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
get isErr() {
|
|
481
|
+
return !this.isOk;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* The success value, or null if in error state.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```typescript
|
|
488
|
+
* const success = Outcome.ok(42);
|
|
489
|
+
* const failure = Outcome.err('Failed');
|
|
490
|
+
*
|
|
491
|
+
* console.log(success.value); // 42
|
|
492
|
+
* console.log(failure.value); // null
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
get value() {
|
|
496
|
+
return this._tuple[0];
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* The error, or null if in success state.
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* const success = Outcome.ok(42);
|
|
504
|
+
* const failure = Outcome.err('Failed');
|
|
505
|
+
*
|
|
506
|
+
* console.log(success.error); // null
|
|
507
|
+
* console.log(failure.error?.message); // 'Failed'
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
get error() {
|
|
511
|
+
return this._tuple[1];
|
|
512
|
+
}
|
|
513
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
514
|
+
// Transformation
|
|
515
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
516
|
+
/**
|
|
517
|
+
* Transform the success value using a callback.
|
|
518
|
+
*
|
|
519
|
+
* Only called if this Outcome is successful. Errors pass through unchanged.
|
|
520
|
+
* The callback can return any `CallbackReturn<U>` pattern.
|
|
521
|
+
* If the callback throws, the exception is caught and wrapped.
|
|
522
|
+
*
|
|
523
|
+
* @param fn - Transformation function receiving the success value
|
|
524
|
+
* @returns New Outcome with transformed value or original/new error
|
|
525
|
+
*
|
|
526
|
+
* @see {@link mapAsync} for the async version
|
|
527
|
+
* @see {@link mapErr} for transforming or recovering from errors
|
|
528
|
+
*
|
|
529
|
+
* @example Simple transformation
|
|
530
|
+
* ```typescript
|
|
531
|
+
* const outcome = Outcome.ok(5)
|
|
532
|
+
* .map(n => [n * 2, null])
|
|
533
|
+
* .map(n => [n.toString(), null]);
|
|
534
|
+
*
|
|
535
|
+
* console.log(outcome.value); // '10'
|
|
536
|
+
* ```
|
|
537
|
+
*
|
|
538
|
+
* @example Transformation that can fail
|
|
539
|
+
* ```typescript
|
|
540
|
+
* const outcome = Outcome.ok('{"name":"John"}')
|
|
541
|
+
* .map(json => {
|
|
542
|
+
* try {
|
|
543
|
+
* return [JSON.parse(json), null];
|
|
544
|
+
* } catch {
|
|
545
|
+
* return Err.from('Invalid JSON');
|
|
546
|
+
* }
|
|
547
|
+
* });
|
|
548
|
+
* ```
|
|
549
|
+
*
|
|
550
|
+
* @example Error passes through
|
|
551
|
+
* ```typescript
|
|
552
|
+
* const outcome = Outcome.err('Original error')
|
|
553
|
+
* .map(v => [v * 2, null]); // Never called
|
|
554
|
+
*
|
|
555
|
+
* console.log(outcome.error?.message); // 'Original error'
|
|
556
|
+
* ```
|
|
557
|
+
*/
|
|
558
|
+
map(fn) {
|
|
559
|
+
if (this.isErr) {
|
|
560
|
+
return new Outcome([null, this._tuple[1]]);
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
const result = fn(this._tuple[0]);
|
|
564
|
+
return Outcome._processCallbackReturn(result);
|
|
565
|
+
}
|
|
566
|
+
catch (e) {
|
|
567
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Async version of `map()`.
|
|
572
|
+
*
|
|
573
|
+
* @param fn - Async transformation function
|
|
574
|
+
* @returns Promise of new Outcome
|
|
575
|
+
*
|
|
576
|
+
* @see {@link map} for the synchronous version
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* const outcome = await Outcome.ok(userId)
|
|
581
|
+
* .mapAsync(async id => {
|
|
582
|
+
* const user = await fetchUser(id);
|
|
583
|
+
* return [user, null];
|
|
584
|
+
* });
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
async mapAsync(fn) {
|
|
588
|
+
if (this.isErr) {
|
|
589
|
+
return new Outcome([null, this._tuple[1]]);
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
const result = await fn(this._tuple[0]);
|
|
593
|
+
return Outcome._processCallbackReturn(result);
|
|
594
|
+
}
|
|
595
|
+
catch (e) {
|
|
596
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Transform or recover from an error using a callback.
|
|
601
|
+
*
|
|
602
|
+
* Only called if this Outcome is in error state. Success passes through unchanged.
|
|
603
|
+
* The callback can return any `CallbackReturn<U>` pattern, allowing:
|
|
604
|
+
* - Recovery: return `[value, null]` to convert error to success
|
|
605
|
+
* - Transform: return `Err` or `[null, Err]` to change the error
|
|
606
|
+
*
|
|
607
|
+
* @param fn - Function receiving the error
|
|
608
|
+
* @returns New Outcome with transformed error or recovered value
|
|
609
|
+
*
|
|
610
|
+
* @see {@link mapErrAsync} for the async version
|
|
611
|
+
* @see {@link map} for transforming success values
|
|
612
|
+
*
|
|
613
|
+
* @example Recovery
|
|
614
|
+
* ```typescript
|
|
615
|
+
* const outcome = Outcome.err('Not found')
|
|
616
|
+
* .mapErr(err => {
|
|
617
|
+
* if (err.hasCode('NOT_FOUND')) {
|
|
618
|
+
* return [defaultValue, null]; // recover with default
|
|
619
|
+
* }
|
|
620
|
+
* return err; // pass through other errors
|
|
621
|
+
* });
|
|
622
|
+
* ```
|
|
623
|
+
*
|
|
624
|
+
* @example Error transformation
|
|
625
|
+
* ```typescript
|
|
626
|
+
* const outcome = Outcome.err('Low-level error')
|
|
627
|
+
* .mapErr(err => err.wrap('High-level context'));
|
|
628
|
+
* ```
|
|
629
|
+
*
|
|
630
|
+
* @example Logging and pass-through
|
|
631
|
+
* ```typescript
|
|
632
|
+
* const outcome = Outcome.err('Something failed')
|
|
633
|
+
* .mapErr(err => {
|
|
634
|
+
* console.error('Error occurred:', err.message);
|
|
635
|
+
* return err; // pass through unchanged
|
|
636
|
+
* });
|
|
637
|
+
* ```
|
|
638
|
+
*/
|
|
639
|
+
mapErr(fn) {
|
|
640
|
+
if (this.isOk) {
|
|
641
|
+
return this;
|
|
642
|
+
}
|
|
643
|
+
try {
|
|
644
|
+
const result = fn(this._tuple[1]);
|
|
645
|
+
return Outcome._processCallbackReturn(result);
|
|
646
|
+
}
|
|
647
|
+
catch (e) {
|
|
648
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Async version of `mapErr()`.
|
|
653
|
+
*
|
|
654
|
+
* @param fn - Async function receiving the error
|
|
655
|
+
* @returns Promise of new Outcome
|
|
656
|
+
*
|
|
657
|
+
* @see {@link mapErr} for the synchronous version
|
|
658
|
+
*
|
|
659
|
+
* @example Async recovery with fallback fetch
|
|
660
|
+
* ```typescript
|
|
661
|
+
* const outcome = await Outcome.err('Primary failed')
|
|
662
|
+
* .mapErrAsync(async err => {
|
|
663
|
+
* const fallback = await fetchFromBackup();
|
|
664
|
+
* if (fallback) return [fallback, null];
|
|
665
|
+
* return err.wrap('Backup also failed');
|
|
666
|
+
* });
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
async mapErrAsync(fn) {
|
|
670
|
+
if (this.isOk) {
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
try {
|
|
674
|
+
const result = await fn(this._tuple[1]);
|
|
675
|
+
return Outcome._processCallbackReturn(result);
|
|
676
|
+
}
|
|
677
|
+
catch (e) {
|
|
678
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
682
|
+
// Side Effects
|
|
683
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
684
|
+
/**
|
|
685
|
+
* Execute a side effect with access to the full tuple.
|
|
686
|
+
*
|
|
687
|
+
* The callback receives the tuple `[value, error]` regardless of state.
|
|
688
|
+
* Returns `this` unchanged for chaining.
|
|
689
|
+
* If the callback throws, the exception is caught and the Outcome becomes an error.
|
|
690
|
+
*
|
|
691
|
+
* @param fn - Side effect function receiving the tuple
|
|
692
|
+
* @returns This Outcome (for chaining), or error Outcome if callback throws
|
|
693
|
+
*
|
|
694
|
+
* @see {@link effectAsync} for the async version
|
|
695
|
+
*
|
|
696
|
+
* @example Logging
|
|
697
|
+
* ```typescript
|
|
698
|
+
* const outcome = Outcome.ok(42)
|
|
699
|
+
* .effect(([val, err]) => {
|
|
700
|
+
* if (err) console.error('Failed:', err.message);
|
|
701
|
+
* else console.log('Success:', val);
|
|
702
|
+
* })
|
|
703
|
+
* .map(v => [v * 2, null]);
|
|
704
|
+
* ```
|
|
705
|
+
*
|
|
706
|
+
* @example Metrics
|
|
707
|
+
* ```typescript
|
|
708
|
+
* outcome.effect(([val, err]) => {
|
|
709
|
+
* metrics.record({
|
|
710
|
+
* success: !err,
|
|
711
|
+
* value: val,
|
|
712
|
+
* errorCode: err?.code
|
|
713
|
+
* });
|
|
714
|
+
* });
|
|
715
|
+
* ```
|
|
716
|
+
*/
|
|
717
|
+
effect(fn) {
|
|
718
|
+
try {
|
|
719
|
+
const t = this.toTuple();
|
|
720
|
+
fn(t);
|
|
721
|
+
return this;
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Async version of `effect()`.
|
|
729
|
+
*
|
|
730
|
+
* @param fn - Async side effect function
|
|
731
|
+
* @returns Promise of this Outcome
|
|
732
|
+
*
|
|
733
|
+
* @see {@link effect} for the synchronous version
|
|
734
|
+
*
|
|
735
|
+
* @example Async logging
|
|
736
|
+
* ```typescript
|
|
737
|
+
* const outcome = await Outcome.ok(data)
|
|
738
|
+
* .effectAsync(async ([val, err]) => {
|
|
739
|
+
* await logger.log({ value: val, error: err?.toJSON() });
|
|
740
|
+
* });
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
743
|
+
async effectAsync(fn) {
|
|
744
|
+
try {
|
|
745
|
+
const t = this.toTuple();
|
|
746
|
+
await fn(t);
|
|
747
|
+
return this;
|
|
748
|
+
}
|
|
749
|
+
catch (e) {
|
|
750
|
+
return new Outcome([null, err_1.Err.from(e)]);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Implementation for defaultTo overloads.
|
|
755
|
+
* @internal
|
|
756
|
+
*/
|
|
757
|
+
defaultTo(fallbackOrHandler, asValue) {
|
|
758
|
+
if (this.isOk) {
|
|
759
|
+
return this._tuple[0];
|
|
760
|
+
}
|
|
761
|
+
if (asValue === true) {
|
|
762
|
+
return fallbackOrHandler;
|
|
763
|
+
}
|
|
764
|
+
if (typeof fallbackOrHandler === "function") {
|
|
765
|
+
return fallbackOrHandler(this._tuple[1]);
|
|
766
|
+
}
|
|
767
|
+
return fallbackOrHandler;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Transform the Outcome into a final value by handling both cases.
|
|
771
|
+
*
|
|
772
|
+
* This is a terminal operation that exits the Outcome chain, similar to
|
|
773
|
+
* `toTuple()` but with transformation logic applied.
|
|
774
|
+
*
|
|
775
|
+
* Each handler receives only its relevant type with full type safety:
|
|
776
|
+
* - `onOk` receives `T` (guaranteed non-null value)
|
|
777
|
+
* - `onErr` receives `Err` (guaranteed error)
|
|
778
|
+
*
|
|
779
|
+
* @param onOk - Function to transform success value into final result
|
|
780
|
+
* @param onErr - Function to transform error into final result
|
|
781
|
+
* @returns The transformed value (not wrapped in Outcome)
|
|
782
|
+
* @throws If either callback throws, the exception propagates to the caller
|
|
783
|
+
*
|
|
784
|
+
* @see {@link defaultTo} for simple value extraction with fallback
|
|
785
|
+
* @see {@link toTuple} for raw tuple extraction
|
|
786
|
+
* @see {@link toJSON} for JSON serialization
|
|
787
|
+
*
|
|
788
|
+
* @example Basic transformation
|
|
789
|
+
* ```typescript
|
|
790
|
+
* const message = fetchUser(id).either(
|
|
791
|
+
* user => `Welcome, ${user.name}!`,
|
|
792
|
+
* err => `Error: ${err.message}`
|
|
793
|
+
* );
|
|
794
|
+
* // message is string, not Outcome<string>
|
|
795
|
+
* ```
|
|
796
|
+
*
|
|
797
|
+
* @example HTTP response building
|
|
798
|
+
* ```typescript
|
|
799
|
+
* const response = processOrder(orderId).either(
|
|
800
|
+
* order => ({ status: 200, body: { id: order.id, total: order.total } }),
|
|
801
|
+
* err => ({
|
|
802
|
+
* status: err.hasCode('NOT_FOUND') ? 404 : 500,
|
|
803
|
+
* body: { error: err.message }
|
|
804
|
+
* })
|
|
805
|
+
* );
|
|
806
|
+
* ```
|
|
807
|
+
*
|
|
808
|
+
* @example Default value on error
|
|
809
|
+
* ```typescript
|
|
810
|
+
* const count = parseNumber(input).either(n => n, () => 0);
|
|
811
|
+
* ```
|
|
812
|
+
*
|
|
813
|
+
* @example Type transformation
|
|
814
|
+
* ```typescript
|
|
815
|
+
* const status: 'success' | 'error' = outcomeEntity.either(
|
|
816
|
+
* () => 'success',
|
|
817
|
+
* () => 'error'
|
|
818
|
+
* );
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
either(onOk, onErr) {
|
|
822
|
+
if (this.isOk) {
|
|
823
|
+
return onOk(this._tuple[0]);
|
|
824
|
+
}
|
|
825
|
+
return onErr(this._tuple[1]);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Implementation for pipe overloads.
|
|
829
|
+
* @internal
|
|
830
|
+
*/
|
|
831
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
832
|
+
pipe(...fns) {
|
|
833
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
834
|
+
let current = this;
|
|
835
|
+
for (const fn of fns) {
|
|
836
|
+
try {
|
|
837
|
+
const result = fn(current.toTuple());
|
|
838
|
+
current = Outcome._processCallbackReturn(result);
|
|
839
|
+
}
|
|
840
|
+
catch (e) {
|
|
841
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
842
|
+
current = new Outcome([null, err_1.Err.from(e)]);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return current;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Implementation for pipeAsync overloads.
|
|
849
|
+
* @internal
|
|
850
|
+
*/
|
|
851
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
852
|
+
async pipeAsync(...fns) {
|
|
853
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
854
|
+
let current = this;
|
|
855
|
+
for (const fn of fns) {
|
|
856
|
+
try {
|
|
857
|
+
const result = await fn(current.toTuple());
|
|
858
|
+
current = Outcome._processCallbackReturn(result);
|
|
859
|
+
}
|
|
860
|
+
catch (e) {
|
|
861
|
+
// biome-ignore lint/suspicious/noExplicitAny: implementation signature needs any
|
|
862
|
+
current = new Outcome([null, err_1.Err.from(e)]);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return current;
|
|
866
|
+
}
|
|
867
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
868
|
+
// Conversion
|
|
869
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
870
|
+
/**
|
|
871
|
+
* Extract the internal tuple.
|
|
872
|
+
*
|
|
873
|
+
* Primary method for extracting values from an Outcome.
|
|
874
|
+
* Use destructuring for ergonomic access.
|
|
875
|
+
*
|
|
876
|
+
* @returns The internal ResultTuple<T>
|
|
877
|
+
*
|
|
878
|
+
* @see {@link fromTuple} for creating an Outcome from a tuple
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* const outcome = Outcome.ok(42);
|
|
883
|
+
* const [value, error] = outcome.toTuple();
|
|
884
|
+
*
|
|
885
|
+
* if (error) {
|
|
886
|
+
* console.error('Failed:', error.message);
|
|
887
|
+
* return;
|
|
888
|
+
* }
|
|
889
|
+
* console.log('Value:', value); // 42
|
|
890
|
+
* ```
|
|
891
|
+
*/
|
|
892
|
+
toTuple() {
|
|
893
|
+
const [v, e] = this._tuple;
|
|
894
|
+
return [v, e];
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Convert to JSON-serializable tuple.
|
|
898
|
+
*
|
|
899
|
+
* For success: returns `[value, null]`
|
|
900
|
+
* For error: returns `[null, errJSON]` where errJSON is from `Err.toJSON()`
|
|
901
|
+
*
|
|
902
|
+
* @returns JSON-serializable representation
|
|
903
|
+
*
|
|
904
|
+
* @see {@link fromJSON} for deserializing an Outcome from JSON
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```typescript
|
|
908
|
+
* const outcome = Outcome.ok({ name: 'John' });
|
|
909
|
+
* const json = JSON.stringify(outcome.toJSON());
|
|
910
|
+
* // '[{"name":"John"},null]'
|
|
911
|
+
*
|
|
912
|
+
* // Deserialize
|
|
913
|
+
* const restored = Outcome.fromJSON(JSON.parse(json));
|
|
914
|
+
* ```
|
|
915
|
+
*/
|
|
916
|
+
toJSON() {
|
|
917
|
+
if (this.isOk) {
|
|
918
|
+
return [this._tuple[0], null];
|
|
919
|
+
}
|
|
920
|
+
return [null, this._tuple[1].toJSON()];
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Convert to a human-readable string.
|
|
924
|
+
*
|
|
925
|
+
* @returns String representation
|
|
926
|
+
*
|
|
927
|
+
* @example
|
|
928
|
+
* ```typescript
|
|
929
|
+
* console.log(Outcome.ok(42).toString());
|
|
930
|
+
* // 'Outcome.ok(42)'
|
|
931
|
+
*
|
|
932
|
+
* console.log(Outcome.err('Failed').toString());
|
|
933
|
+
* // 'Outcome.err([ERROR] Failed)'
|
|
934
|
+
* ```
|
|
935
|
+
*/
|
|
936
|
+
toString() {
|
|
937
|
+
if (this.isOk) {
|
|
938
|
+
return `Outcome.ok(${fmt(this._tuple[0])})`;
|
|
939
|
+
}
|
|
940
|
+
return `Outcome.err(${this._tuple[1].toString()})`;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
exports.Outcome = Outcome;
|
|
944
|
+
function fmt(v) {
|
|
945
|
+
if (v === null)
|
|
946
|
+
return "null";
|
|
947
|
+
if (v === undefined)
|
|
948
|
+
return "undefined";
|
|
949
|
+
if (typeof v === "string")
|
|
950
|
+
return JSON.stringify(v);
|
|
951
|
+
try {
|
|
952
|
+
return JSON.stringify(v);
|
|
953
|
+
}
|
|
954
|
+
catch {
|
|
955
|
+
return String(v);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
//# sourceMappingURL=outcome.js.map
|