@satoshibits/functional 1.0.2
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/README.md +242 -0
- package/dist/array-utils.d.mts +317 -0
- package/dist/array-utils.d.mts.map +1 -0
- package/dist/array-utils.mjs +370 -0
- package/dist/array-utils.mjs.map +1 -0
- package/dist/composition.d.mts +603 -0
- package/dist/composition.d.mts.map +1 -0
- package/dist/composition.mjs +516 -0
- package/dist/composition.mjs.map +1 -0
- package/dist/object-utils.d.mts +267 -0
- package/dist/object-utils.d.mts.map +1 -0
- package/dist/object-utils.mjs +258 -0
- package/dist/object-utils.mjs.map +1 -0
- package/dist/option.d.mts +622 -0
- package/dist/option.d.mts.map +1 -0
- package/dist/option.mjs +637 -0
- package/dist/option.mjs.map +1 -0
- package/dist/performance.d.mts +265 -0
- package/dist/performance.d.mts.map +1 -0
- package/dist/performance.mjs +453 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/pipeline.d.mts +431 -0
- package/dist/pipeline.d.mts.map +1 -0
- package/dist/pipeline.mjs +460 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/predicates.d.mts +722 -0
- package/dist/predicates.d.mts.map +1 -0
- package/dist/predicates.mjs +802 -0
- package/dist/predicates.mjs.map +1 -0
- package/dist/reader-result.d.mts +422 -0
- package/dist/reader-result.d.mts.map +1 -0
- package/dist/reader-result.mjs +758 -0
- package/dist/reader-result.mjs.map +1 -0
- package/dist/result.d.mts +684 -0
- package/dist/result.d.mts.map +1 -0
- package/dist/result.mjs +814 -0
- package/dist/result.mjs.map +1 -0
- package/dist/types.d.mts +439 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +191 -0
- package/dist/types.mjs.map +1 -0
- package/dist/validation.d.mts +622 -0
- package/dist/validation.d.mts.map +1 -0
- package/dist/validation.mjs +852 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module reader-result
|
|
4
|
+
* @description Combines Reader (dependency injection) with Result (error handling)
|
|
5
|
+
* for asynchronous computations. This provides a powerful abstraction for
|
|
6
|
+
* building composable, testable applications with explicit error handling
|
|
7
|
+
* and dependency injection. Similar to fp-ts's ReaderTaskEither but tailored
|
|
8
|
+
* for our Result type and async/await patterns.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { ReaderResult, liftAsync } from './reader-result.mts';
|
|
13
|
+
*
|
|
14
|
+
* // define dependencies
|
|
15
|
+
* interface Deps {
|
|
16
|
+
* db: Database;
|
|
17
|
+
* logger: Logger;
|
|
18
|
+
* config: Config;
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* // create reusable operations
|
|
22
|
+
* const getUser = (id: string): ReaderResult<Deps, string, User> =>
|
|
23
|
+
* ReaderResult.tryCatch(
|
|
24
|
+
* async (deps) => deps.db.users.findById(id),
|
|
25
|
+
* (error) => `Failed to fetch user: ${error}`
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* // compose operations
|
|
29
|
+
* const program = ReaderResult.Do()
|
|
30
|
+
* .pipe(ReaderResult.bind('user', () => getUser('123')))
|
|
31
|
+
* .pipe(ReaderResult.bind('posts', ({ user }) => getUserPosts(user.id)))
|
|
32
|
+
* .pipe(ReaderResult.map(({ user, posts }) => ({ ...user, posts })));
|
|
33
|
+
*
|
|
34
|
+
* // run with dependencies
|
|
35
|
+
* const result = await ReaderResult.run(dependencies)(program);
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @category Core
|
|
39
|
+
* @since 2025-07-03
|
|
40
|
+
*/
|
|
41
|
+
var __assign = (this && this.__assign) || function () {
|
|
42
|
+
__assign = Object.assign || function(t) {
|
|
43
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
44
|
+
s = arguments[i];
|
|
45
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
46
|
+
t[p] = s[p];
|
|
47
|
+
}
|
|
48
|
+
return t;
|
|
49
|
+
};
|
|
50
|
+
return __assign.apply(this, arguments);
|
|
51
|
+
};
|
|
52
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
53
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
54
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
55
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
56
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
57
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
58
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
62
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
63
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
64
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
65
|
+
function step(op) {
|
|
66
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
67
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
68
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
69
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
70
|
+
switch (op[0]) {
|
|
71
|
+
case 0: case 1: t = op; break;
|
|
72
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
73
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
74
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
75
|
+
default:
|
|
76
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
77
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
78
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
79
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
80
|
+
if (t[2]) _.ops.pop();
|
|
81
|
+
_.trys.pop(); continue;
|
|
82
|
+
}
|
|
83
|
+
op = body.call(thisArg, _);
|
|
84
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
85
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
89
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
90
|
+
if (ar || !(i in from)) {
|
|
91
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
92
|
+
ar[i] = from[i];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
96
|
+
};
|
|
97
|
+
import { Result as ResultUtils } from './result.mjs';
|
|
98
|
+
/**
|
|
99
|
+
* Constructors and combinators for ReaderResult
|
|
100
|
+
*/
|
|
101
|
+
export var ReaderResult = {
|
|
102
|
+
/**
|
|
103
|
+
* Lift a pure value into ReaderResult context.
|
|
104
|
+
* @description Creates a ReaderResult that always succeeds with the given value,
|
|
105
|
+
* ignoring the dependencies.
|
|
106
|
+
*
|
|
107
|
+
* @template R - The type of dependencies
|
|
108
|
+
* @template E - The error type
|
|
109
|
+
* @template A - The type of the value
|
|
110
|
+
* @param {A} value - The value to wrap
|
|
111
|
+
* @returns {ReaderResult<R, E, A>} A ReaderResult that always succeeds with the value
|
|
112
|
+
*
|
|
113
|
+
* @category Constructors
|
|
114
|
+
* @example
|
|
115
|
+
* const always42 = ReaderResult.of<Deps, string, number>(42);
|
|
116
|
+
* const result = await ReaderResult.run(deps)(always42);
|
|
117
|
+
* // => { success: true, data: 42 }
|
|
118
|
+
*
|
|
119
|
+
* @since 2025-07-03
|
|
120
|
+
*/
|
|
121
|
+
of: function (value) {
|
|
122
|
+
return function () { return Promise.resolve(ResultUtils.ok(value)); };
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Lift an error into ReaderResult context.
|
|
126
|
+
* @description Creates a ReaderResult that always fails with the given error,
|
|
127
|
+
* ignoring the dependencies.
|
|
128
|
+
*
|
|
129
|
+
* @template R - The type of dependencies
|
|
130
|
+
* @template E - The error type
|
|
131
|
+
* @template A - The success type
|
|
132
|
+
* @param {E} error - The error to wrap
|
|
133
|
+
* @returns {ReaderResult<R, E, A>} A ReaderResult that always fails with the error
|
|
134
|
+
*
|
|
135
|
+
* @category Constructors
|
|
136
|
+
* @example
|
|
137
|
+
* const alwaysFails = ReaderResult.fail<Deps, string, User>('User not found');
|
|
138
|
+
* const result = await ReaderResult.run(deps)(alwaysFails);
|
|
139
|
+
* // => { success: false, error: 'User not found' }
|
|
140
|
+
*
|
|
141
|
+
* @since 2025-07-03
|
|
142
|
+
*/
|
|
143
|
+
fail: function (error) {
|
|
144
|
+
return function () { return Promise.resolve(ResultUtils.err(error)); };
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Sequential composition - the heart of ReaderResult.
|
|
148
|
+
* @description If first computation fails, short-circuit. Otherwise, feed result to next computation.
|
|
149
|
+
* This is the monadic bind operation that enables chaining ReaderResult computations.
|
|
150
|
+
*
|
|
151
|
+
* @template R - The type of dependencies
|
|
152
|
+
* @template E - The error type
|
|
153
|
+
* @template A - The input value type
|
|
154
|
+
* @template B - The output value type
|
|
155
|
+
* @param {(a: A) => ReaderResult<R, E, B>} f - Function that takes the success value and returns a new ReaderResult
|
|
156
|
+
* @returns {(ma: ReaderResult<R, E, A>) => ReaderResult<R, E, B>} A function that chains ReaderResults
|
|
157
|
+
*
|
|
158
|
+
* @category Combinators
|
|
159
|
+
* @example
|
|
160
|
+
* const getUser = (id: string): ReaderResult<Deps, string, User> => ...
|
|
161
|
+
* const getUserPosts = (userId: string): ReaderResult<Deps, string, Post[]> => ...
|
|
162
|
+
*
|
|
163
|
+
* const getUserWithPosts = ReaderResult.chain((user: User) =>
|
|
164
|
+
* ReaderResult.map((posts: Post[]) => ({ ...user, posts }))(getUserPosts(user.id))
|
|
165
|
+
* )(getUser('123'));
|
|
166
|
+
*
|
|
167
|
+
* @since 2025-07-03
|
|
168
|
+
*/
|
|
169
|
+
chain: function (f) { return function (ma) {
|
|
170
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
171
|
+
var resultA;
|
|
172
|
+
return __generator(this, function (_a) {
|
|
173
|
+
switch (_a.label) {
|
|
174
|
+
case 0: return [4 /*yield*/, ma(deps)];
|
|
175
|
+
case 1:
|
|
176
|
+
resultA = _a.sent();
|
|
177
|
+
if (!resultA.success) {
|
|
178
|
+
return [2 /*return*/, resultA]; // Type assertion for proper inference
|
|
179
|
+
}
|
|
180
|
+
return [2 /*return*/, f(resultA.data)(deps)];
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}); };
|
|
184
|
+
}; },
|
|
185
|
+
/**
|
|
186
|
+
* Map a pure function over the success value.
|
|
187
|
+
* @description Transforms the success value using a pure function. If the computation
|
|
188
|
+
* fails, the error is propagated unchanged.
|
|
189
|
+
*
|
|
190
|
+
* @template R - The type of dependencies
|
|
191
|
+
* @template E - The error type
|
|
192
|
+
* @template A - The input value type
|
|
193
|
+
* @template B - The output value type
|
|
194
|
+
* @param {(a: A) => B} f - Function to transform the success value
|
|
195
|
+
* @returns {(ma: ReaderResult<R, E, A>) => ReaderResult<R, E, B>} A function that maps over ReaderResult
|
|
196
|
+
*
|
|
197
|
+
* @category Transformations
|
|
198
|
+
* @example
|
|
199
|
+
* const double = ReaderResult.map((n: number) => n * 2);
|
|
200
|
+
* const program = double(ReaderResult.of<Deps, string, number>(21));
|
|
201
|
+
* const result = await ReaderResult.run(deps)(program);
|
|
202
|
+
* // => { success: true, data: 42 }
|
|
203
|
+
*
|
|
204
|
+
* @since 2025-07-03
|
|
205
|
+
*/
|
|
206
|
+
map: function (f) { return function (ma) {
|
|
207
|
+
return ReaderResult.chain(function (a) { return ReaderResult.of(f(a)); })(ma);
|
|
208
|
+
}; },
|
|
209
|
+
/**
|
|
210
|
+
* Map a function over the error value.
|
|
211
|
+
* @description Transforms the error value using a pure function. If the computation
|
|
212
|
+
* succeeds, the success value is propagated unchanged.
|
|
213
|
+
*
|
|
214
|
+
* @template R - The type of dependencies
|
|
215
|
+
* @template E - The input error type
|
|
216
|
+
* @template F - The output error type
|
|
217
|
+
* @template A - The value type
|
|
218
|
+
* @param {(e: E) => F} f - Function to transform the error
|
|
219
|
+
* @returns {(ma: ReaderResult<R, E, A>) => ReaderResult<R, F, A>} A function that maps over errors
|
|
220
|
+
*
|
|
221
|
+
* @category Transformations
|
|
222
|
+
* @example
|
|
223
|
+
* const enrichError = ReaderResult.mapError((e: string) => ({
|
|
224
|
+
* message: e,
|
|
225
|
+
* timestamp: new Date()
|
|
226
|
+
* }));
|
|
227
|
+
*
|
|
228
|
+
* @since 2025-07-03
|
|
229
|
+
*/
|
|
230
|
+
mapError: function (f) { return function (ma) {
|
|
231
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
232
|
+
var result;
|
|
233
|
+
return __generator(this, function (_a) {
|
|
234
|
+
switch (_a.label) {
|
|
235
|
+
case 0: return [4 /*yield*/, ma(deps)];
|
|
236
|
+
case 1:
|
|
237
|
+
result = _a.sent();
|
|
238
|
+
return [2 /*return*/, result.success
|
|
239
|
+
? result
|
|
240
|
+
: ResultUtils.err(f(result.error))];
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}); };
|
|
244
|
+
}; },
|
|
245
|
+
/**
|
|
246
|
+
* Access the dependencies.
|
|
247
|
+
* @description Returns a ReaderResult that succeeds with the current dependencies.
|
|
248
|
+
* Useful when you need to access the dependencies within a computation chain.
|
|
249
|
+
*
|
|
250
|
+
* @template R - The type of dependencies
|
|
251
|
+
* @template E - The error type
|
|
252
|
+
* @returns {ReaderResult<R, E, R>} A ReaderResult that returns the dependencies
|
|
253
|
+
*
|
|
254
|
+
* @category Dependencies
|
|
255
|
+
* @example
|
|
256
|
+
* const program = ReaderResult.Do()
|
|
257
|
+
* .pipe(ReaderResult.bind('deps', () => ReaderResult.ask<Deps, string>()))
|
|
258
|
+
* .pipe(ReaderResult.bind('config', ({ deps }) =>
|
|
259
|
+
* ReaderResult.of(deps.config)
|
|
260
|
+
* ));
|
|
261
|
+
*
|
|
262
|
+
* @since 2025-07-03
|
|
263
|
+
*/
|
|
264
|
+
ask: function () {
|
|
265
|
+
return function (deps) { return Promise.resolve(ResultUtils.ok(deps)); };
|
|
266
|
+
},
|
|
267
|
+
/**
|
|
268
|
+
* Access a part of the dependencies.
|
|
269
|
+
* @description Returns a ReaderResult that succeeds with a projection of the dependencies.
|
|
270
|
+
* Useful for extracting specific values from the dependency container.
|
|
271
|
+
*
|
|
272
|
+
* @template R - The type of dependencies
|
|
273
|
+
* @template E - The error type
|
|
274
|
+
* @template A - The type of the extracted value
|
|
275
|
+
* @param {(deps: R) => A} f - Function to extract a value from dependencies
|
|
276
|
+
* @returns {ReaderResult<R, E, A>} A ReaderResult that returns the extracted value
|
|
277
|
+
*
|
|
278
|
+
* @category Dependencies
|
|
279
|
+
* @example
|
|
280
|
+
* const getConfig = ReaderResult.asks<Deps, string, Config>(deps => deps.config);
|
|
281
|
+
* const getLogger = ReaderResult.asks<Deps, string, Logger>(deps => deps.logger);
|
|
282
|
+
*
|
|
283
|
+
* @since 2025-07-03
|
|
284
|
+
*/
|
|
285
|
+
asks: function (f) {
|
|
286
|
+
return function (deps) { return Promise.resolve(ResultUtils.ok(f(deps))); };
|
|
287
|
+
},
|
|
288
|
+
/**
|
|
289
|
+
* Lift a Result into ReaderResult
|
|
290
|
+
*/
|
|
291
|
+
fromResult: function (result) {
|
|
292
|
+
return function () { return Promise.resolve(result); };
|
|
293
|
+
},
|
|
294
|
+
/**
|
|
295
|
+
* Lift an async operation that might throw into ReaderResult
|
|
296
|
+
*/
|
|
297
|
+
tryCatch: function (f, onError) {
|
|
298
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
299
|
+
var result, error_1;
|
|
300
|
+
return __generator(this, function (_a) {
|
|
301
|
+
switch (_a.label) {
|
|
302
|
+
case 0:
|
|
303
|
+
_a.trys.push([0, 2, , 3]);
|
|
304
|
+
return [4 /*yield*/, f(deps)];
|
|
305
|
+
case 1:
|
|
306
|
+
result = _a.sent();
|
|
307
|
+
return [2 /*return*/, ResultUtils.ok(result)];
|
|
308
|
+
case 2:
|
|
309
|
+
error_1 = _a.sent();
|
|
310
|
+
return [2 /*return*/, ResultUtils.err(onError(error_1))];
|
|
311
|
+
case 3: return [2 /*return*/];
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}); };
|
|
315
|
+
},
|
|
316
|
+
/**
|
|
317
|
+
* Execute ReaderResult with dependencies
|
|
318
|
+
*/
|
|
319
|
+
run: function (deps) { return function (ma) {
|
|
320
|
+
return ma(deps);
|
|
321
|
+
}; },
|
|
322
|
+
/**
|
|
323
|
+
* Combine two ReaderResults in parallel.
|
|
324
|
+
* @description Runs two ReaderResult computations in parallel and combines their results
|
|
325
|
+
* into a tuple. If either fails, returns the first failure.
|
|
326
|
+
*
|
|
327
|
+
* @template R - The type of dependencies
|
|
328
|
+
* @template E - The error type
|
|
329
|
+
* @template A - The first value type
|
|
330
|
+
* @template B - The second value type
|
|
331
|
+
* @param {ReaderResult<R, E, A>} ma - First computation
|
|
332
|
+
* @param {ReaderResult<R, E, B>} mb - Second computation
|
|
333
|
+
* @returns {ReaderResult<R, E, [A, B]>} A ReaderResult containing a tuple of both results
|
|
334
|
+
*
|
|
335
|
+
* @category Combinations
|
|
336
|
+
* @example
|
|
337
|
+
* const userAndPosts = ReaderResult.zip(
|
|
338
|
+
* getUser('123'),
|
|
339
|
+
* getUserPosts('123')
|
|
340
|
+
* );
|
|
341
|
+
*
|
|
342
|
+
* @since 2025-07-03
|
|
343
|
+
*/
|
|
344
|
+
zip: function (ma, mb) {
|
|
345
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
346
|
+
var _a, resultA, resultB;
|
|
347
|
+
return __generator(this, function (_b) {
|
|
348
|
+
switch (_b.label) {
|
|
349
|
+
case 0: return [4 /*yield*/, Promise.all([
|
|
350
|
+
ma(deps),
|
|
351
|
+
mb(deps)
|
|
352
|
+
])];
|
|
353
|
+
case 1:
|
|
354
|
+
_a = _b.sent(), resultA = _a[0], resultB = _a[1];
|
|
355
|
+
if (!resultA.success)
|
|
356
|
+
return [2 /*return*/, resultA];
|
|
357
|
+
if (!resultB.success)
|
|
358
|
+
return [2 /*return*/, resultB];
|
|
359
|
+
return [2 /*return*/, ResultUtils.ok([resultA.data, resultB.data])];
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}); };
|
|
363
|
+
},
|
|
364
|
+
/**
|
|
365
|
+
* Sequence an array of ReaderResults.
|
|
366
|
+
* @description Transforms an array of ReaderResults into a ReaderResult of an array.
|
|
367
|
+
* Executes each computation sequentially. If any fails, returns the first failure.
|
|
368
|
+
*
|
|
369
|
+
* @template R - The type of dependencies
|
|
370
|
+
* @template E - The error type
|
|
371
|
+
* @template A - The value type
|
|
372
|
+
* @param {readonly ReaderResult<R, E, A>[]} rrs - Array of ReaderResult computations
|
|
373
|
+
* @returns {ReaderResult<R, E, readonly A[]>} A ReaderResult containing an array of all results
|
|
374
|
+
*
|
|
375
|
+
* @category Combinations
|
|
376
|
+
* @example
|
|
377
|
+
* const userIds = ['123', '456', '789'];
|
|
378
|
+
* const getUsers = ReaderResult.sequence(
|
|
379
|
+
* userIds.map(id => getUser(id))
|
|
380
|
+
* );
|
|
381
|
+
*
|
|
382
|
+
* @since 2025-07-03
|
|
383
|
+
*/
|
|
384
|
+
sequence: function (rrs) {
|
|
385
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
386
|
+
var results, _i, rrs_1, rr, result;
|
|
387
|
+
return __generator(this, function (_a) {
|
|
388
|
+
switch (_a.label) {
|
|
389
|
+
case 0:
|
|
390
|
+
results = [];
|
|
391
|
+
_i = 0, rrs_1 = rrs;
|
|
392
|
+
_a.label = 1;
|
|
393
|
+
case 1:
|
|
394
|
+
if (!(_i < rrs_1.length)) return [3 /*break*/, 4];
|
|
395
|
+
rr = rrs_1[_i];
|
|
396
|
+
return [4 /*yield*/, rr(deps)];
|
|
397
|
+
case 2:
|
|
398
|
+
result = _a.sent();
|
|
399
|
+
if (!result.success) {
|
|
400
|
+
return [2 /*return*/, result];
|
|
401
|
+
}
|
|
402
|
+
results.push(result.data);
|
|
403
|
+
_a.label = 3;
|
|
404
|
+
case 3:
|
|
405
|
+
_i++;
|
|
406
|
+
return [3 /*break*/, 1];
|
|
407
|
+
case 4: return [2 /*return*/, ResultUtils.ok(results)];
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}); };
|
|
411
|
+
},
|
|
412
|
+
/**
|
|
413
|
+
* Do notation for building up computations.
|
|
414
|
+
* @description Starts a Do notation chain for building complex computations
|
|
415
|
+
* in a more imperative style. Use with bind and let methods.
|
|
416
|
+
*
|
|
417
|
+
* @template R - The type of dependencies
|
|
418
|
+
* @template E - The error type
|
|
419
|
+
* @returns {ReaderResult<R, E, Record<string, never>>} An empty ReaderResult to start the chain
|
|
420
|
+
*
|
|
421
|
+
* @category Do Notation
|
|
422
|
+
* @example
|
|
423
|
+
* const program = ReaderResult.Do<Deps, string>()
|
|
424
|
+
* .pipe(ReaderResult.bind('user', () => getUser('123')))
|
|
425
|
+
* .pipe(ReaderResult.bind('posts', ({ user }) => getUserPosts(user.id)))
|
|
426
|
+
* .pipe(ReaderResult.let('postCount', ({ posts }) => posts.length))
|
|
427
|
+
* .pipe(ReaderResult.map(({ user, posts, postCount }) => ({
|
|
428
|
+
* ...user,
|
|
429
|
+
* posts,
|
|
430
|
+
* stats: { postCount }
|
|
431
|
+
* })));
|
|
432
|
+
*
|
|
433
|
+
* @since 2025-07-03
|
|
434
|
+
*/
|
|
435
|
+
Do: function () {
|
|
436
|
+
return ReaderResult.of({});
|
|
437
|
+
},
|
|
438
|
+
/**
|
|
439
|
+
* Bind a computation result to a name (for Do notation)
|
|
440
|
+
*/
|
|
441
|
+
bind: function (name, f) { return function (fa) {
|
|
442
|
+
return ReaderResult.chain(function (a) {
|
|
443
|
+
return ReaderResult.map(function (b) {
|
|
444
|
+
var _a;
|
|
445
|
+
return (__assign(__assign({}, a), (_a = {}, _a[name] = b, _a)));
|
|
446
|
+
})(f(a));
|
|
447
|
+
})(fa);
|
|
448
|
+
}; },
|
|
449
|
+
/**
|
|
450
|
+
* Bind a value to a name (for Do notation)
|
|
451
|
+
*/
|
|
452
|
+
let: function (name, f) { return function (fa) {
|
|
453
|
+
return ReaderResult.map(function (a) {
|
|
454
|
+
var _a;
|
|
455
|
+
return (__assign(__assign({}, a), (_a = {}, _a[name] = f(a), _a)));
|
|
456
|
+
})(fa);
|
|
457
|
+
}; },
|
|
458
|
+
/**
|
|
459
|
+
* Provides a fallback ReaderResult if the original fails.
|
|
460
|
+
* @description The fallback function receives the error and returns a new ReaderResult.
|
|
461
|
+
* Useful for error recovery or providing default values.
|
|
462
|
+
*
|
|
463
|
+
* @template R - The type of dependencies
|
|
464
|
+
* @template E - The input error type
|
|
465
|
+
* @template F - The output error type
|
|
466
|
+
* @template A - The value type
|
|
467
|
+
* @param {(error: E) => ReaderResult<R, F, A>} onError - Function to handle the error
|
|
468
|
+
* @returns {(ma: ReaderResult<R, E, A>) => ReaderResult<R, F, A>} A function that adds fallback behavior
|
|
469
|
+
*
|
|
470
|
+
* @category Error Handling
|
|
471
|
+
* @example
|
|
472
|
+
* const getUserWithFallback = ReaderResult.orElse(
|
|
473
|
+
* (error: string) => getDefaultUser()
|
|
474
|
+
* )(getUser('123'));
|
|
475
|
+
*
|
|
476
|
+
* @since 2025-07-03
|
|
477
|
+
*/
|
|
478
|
+
orElse: function (onError) { return function (ma) {
|
|
479
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
480
|
+
var result;
|
|
481
|
+
return __generator(this, function (_a) {
|
|
482
|
+
switch (_a.label) {
|
|
483
|
+
case 0: return [4 /*yield*/, ma(deps)];
|
|
484
|
+
case 1:
|
|
485
|
+
result = _a.sent();
|
|
486
|
+
if (result.success) {
|
|
487
|
+
return [2 /*return*/, result];
|
|
488
|
+
}
|
|
489
|
+
return [2 /*return*/, onError(result.error)(deps)];
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}); };
|
|
493
|
+
}; },
|
|
494
|
+
/**
|
|
495
|
+
* Timeout for ReaderResult computations.
|
|
496
|
+
* If the computation doesn't complete within the specified time, it fails with the timeout error.
|
|
497
|
+
*/
|
|
498
|
+
timeout: function (ms, timeoutError) { return function (ma) {
|
|
499
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
500
|
+
var timeoutPromise;
|
|
501
|
+
return __generator(this, function (_a) {
|
|
502
|
+
timeoutPromise = new Promise(function (resolve) {
|
|
503
|
+
setTimeout(function () { return resolve(ResultUtils.err(timeoutError)); }, ms);
|
|
504
|
+
});
|
|
505
|
+
return [2 /*return*/, Promise.race([ma(deps), timeoutPromise])];
|
|
506
|
+
});
|
|
507
|
+
}); };
|
|
508
|
+
}; },
|
|
509
|
+
/**
|
|
510
|
+
* Retry a ReaderResult computation with exponential backoff.
|
|
511
|
+
* Only retries on failures, not on successful results.
|
|
512
|
+
*/
|
|
513
|
+
retry: function (maxAttempts, baseDelay, shouldRetry) {
|
|
514
|
+
if (baseDelay === void 0) { baseDelay = 1000; }
|
|
515
|
+
if (shouldRetry === void 0) { shouldRetry = function () { return true; }; }
|
|
516
|
+
return function (ma) {
|
|
517
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
518
|
+
var _loop_1, attempt, state_1;
|
|
519
|
+
return __generator(this, function (_a) {
|
|
520
|
+
switch (_a.label) {
|
|
521
|
+
case 0:
|
|
522
|
+
// If maxAttempts is not at least 1, just run the operation once.
|
|
523
|
+
if (maxAttempts < 1) {
|
|
524
|
+
return [2 /*return*/, ma(deps)];
|
|
525
|
+
}
|
|
526
|
+
_loop_1 = function (attempt) {
|
|
527
|
+
var result, delay;
|
|
528
|
+
return __generator(this, function (_b) {
|
|
529
|
+
switch (_b.label) {
|
|
530
|
+
case 0: return [4 /*yield*/, ma(deps)];
|
|
531
|
+
case 1:
|
|
532
|
+
result = _b.sent();
|
|
533
|
+
if (result.success) {
|
|
534
|
+
return [2 /*return*/, { value: result }];
|
|
535
|
+
}
|
|
536
|
+
// If it's the last attempt or shouldRetry returns false, return the error.
|
|
537
|
+
if (attempt === maxAttempts || !shouldRetry(result.error, attempt)) {
|
|
538
|
+
return [2 /*return*/, { value: result }];
|
|
539
|
+
}
|
|
540
|
+
delay = baseDelay * Math.pow(2, attempt - 1);
|
|
541
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, delay); })];
|
|
542
|
+
case 2:
|
|
543
|
+
_b.sent();
|
|
544
|
+
return [2 /*return*/];
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
attempt = 1;
|
|
549
|
+
_a.label = 1;
|
|
550
|
+
case 1:
|
|
551
|
+
if (!(attempt <= maxAttempts)) return [3 /*break*/, 4];
|
|
552
|
+
return [5 /*yield**/, _loop_1(attempt)];
|
|
553
|
+
case 2:
|
|
554
|
+
state_1 = _a.sent();
|
|
555
|
+
if (typeof state_1 === "object")
|
|
556
|
+
return [2 /*return*/, state_1.value];
|
|
557
|
+
_a.label = 3;
|
|
558
|
+
case 3:
|
|
559
|
+
attempt++;
|
|
560
|
+
return [3 /*break*/, 1];
|
|
561
|
+
case 4:
|
|
562
|
+
// This part is now unreachable due to the logic above, but satisfies TS.
|
|
563
|
+
// A more robust way to signal this is to throw, as it should never happen.
|
|
564
|
+
throw new Error('Retry logic reached an impossible state.');
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}); };
|
|
568
|
+
};
|
|
569
|
+
},
|
|
570
|
+
/**
|
|
571
|
+
* Execute multiple ReaderResults in parallel and collect all results.
|
|
572
|
+
* @description Similar to sequence but runs in parallel rather than sequentially.
|
|
573
|
+
* More efficient for independent computations but still fails fast on first error.
|
|
574
|
+
*
|
|
575
|
+
* @template R - The type of dependencies
|
|
576
|
+
* @template E - The error type
|
|
577
|
+
* @template A - The value type
|
|
578
|
+
* @param {readonly ReaderResult<R, E, A>[]} rrs - Array of ReaderResult computations
|
|
579
|
+
* @returns {ReaderResult<R, E, readonly A[]>} A ReaderResult containing an array of all results
|
|
580
|
+
*
|
|
581
|
+
* @category Combinations
|
|
582
|
+
* @example
|
|
583
|
+
* // Fetch multiple users in parallel
|
|
584
|
+
* const userIds = ['123', '456', '789'];
|
|
585
|
+
* const getUsers = ReaderResult.sequencePar(
|
|
586
|
+
* userIds.map(id => getUser(id))
|
|
587
|
+
* );
|
|
588
|
+
*
|
|
589
|
+
* @since 2025-07-03
|
|
590
|
+
*/
|
|
591
|
+
sequencePar: function (rrs) {
|
|
592
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
593
|
+
var results, data, _i, results_1, result;
|
|
594
|
+
return __generator(this, function (_a) {
|
|
595
|
+
switch (_a.label) {
|
|
596
|
+
case 0: return [4 /*yield*/, Promise.all(rrs.map(function (rr) { return rr(deps); }))];
|
|
597
|
+
case 1:
|
|
598
|
+
results = _a.sent();
|
|
599
|
+
data = [];
|
|
600
|
+
for (_i = 0, results_1 = results; _i < results_1.length; _i++) {
|
|
601
|
+
result = results_1[_i];
|
|
602
|
+
if (!result.success) {
|
|
603
|
+
return [2 /*return*/, result];
|
|
604
|
+
}
|
|
605
|
+
data.push(result.data);
|
|
606
|
+
}
|
|
607
|
+
return [2 /*return*/, ResultUtils.ok(data)];
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}); };
|
|
611
|
+
},
|
|
612
|
+
/**
|
|
613
|
+
* Combine multiple ReaderResults in parallel into a tuple.
|
|
614
|
+
* Extends zip to work with any number of ReaderResults.
|
|
615
|
+
*/
|
|
616
|
+
zipAll: function () {
|
|
617
|
+
var rrs = [];
|
|
618
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
619
|
+
rrs[_i] = arguments[_i];
|
|
620
|
+
}
|
|
621
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
622
|
+
var results, data, _i, results_2, result;
|
|
623
|
+
return __generator(this, function (_a) {
|
|
624
|
+
switch (_a.label) {
|
|
625
|
+
case 0: return [4 /*yield*/, Promise.all(rrs.map(function (rr) { return rr(deps); }))];
|
|
626
|
+
case 1:
|
|
627
|
+
results = _a.sent();
|
|
628
|
+
data = [];
|
|
629
|
+
for (_i = 0, results_2 = results; _i < results_2.length; _i++) {
|
|
630
|
+
result = results_2[_i];
|
|
631
|
+
if (!result.success) {
|
|
632
|
+
return [2 /*return*/, result];
|
|
633
|
+
}
|
|
634
|
+
data.push(result.data);
|
|
635
|
+
}
|
|
636
|
+
return [2 /*return*/, ResultUtils.ok(data)];
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
}); };
|
|
640
|
+
},
|
|
641
|
+
/**
|
|
642
|
+
* Run multiple ReaderResults in parallel and collect all results or all errors.
|
|
643
|
+
* @description Unlike sequencePar, this doesn't short-circuit on the first error.
|
|
644
|
+
* Collects all errors if any computations fail, making it useful for validation scenarios.
|
|
645
|
+
*
|
|
646
|
+
* @template R - The type of dependencies
|
|
647
|
+
* @template E - The error type
|
|
648
|
+
* @template T - The record type mapping keys to ReaderResults
|
|
649
|
+
*
|
|
650
|
+
* @category Combinations
|
|
651
|
+
* @example
|
|
652
|
+
* const validation = ReaderResult.parallel({
|
|
653
|
+
* name: validateName(input.name),
|
|
654
|
+
* email: validateEmail(input.email),
|
|
655
|
+
* age: validateAge(input.age)
|
|
656
|
+
* });
|
|
657
|
+
* // If any fail, returns all failures
|
|
658
|
+
*
|
|
659
|
+
* @since 2025-07-03
|
|
660
|
+
*/
|
|
661
|
+
parallel: function (rrs) {
|
|
662
|
+
return function (deps) { return __awaiter(void 0, void 0, void 0, function () {
|
|
663
|
+
var entries, results, errors, data, _i, results_3, _a, key, result;
|
|
664
|
+
return __generator(this, function (_b) {
|
|
665
|
+
switch (_b.label) {
|
|
666
|
+
case 0:
|
|
667
|
+
entries = Object.entries(rrs);
|
|
668
|
+
return [4 /*yield*/, Promise.all(entries.map(function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
|
669
|
+
var _c;
|
|
670
|
+
var key = _b[0], rr = _b[1];
|
|
671
|
+
return __generator(this, function (_d) {
|
|
672
|
+
switch (_d.label) {
|
|
673
|
+
case 0:
|
|
674
|
+
_c = [key];
|
|
675
|
+
return [4 /*yield*/, rr(deps)];
|
|
676
|
+
case 1: return [2 /*return*/, _c.concat([_d.sent()])];
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}); }))];
|
|
680
|
+
case 1:
|
|
681
|
+
results = _b.sent();
|
|
682
|
+
errors = [];
|
|
683
|
+
data = {};
|
|
684
|
+
for (_i = 0, results_3 = results; _i < results_3.length; _i++) {
|
|
685
|
+
_a = results_3[_i], key = _a[0], result = _a[1];
|
|
686
|
+
if (result.success) {
|
|
687
|
+
data[key] = result.data;
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
errors.push({ key: key, error: result.error });
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return [2 /*return*/, errors.length > 0
|
|
694
|
+
? ResultUtils.err(errors)
|
|
695
|
+
: ResultUtils.ok(data)];
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
}); };
|
|
699
|
+
},
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
702
|
+
* Helper to create ReaderResult from a domain function that returns Result.
|
|
703
|
+
* @description Lifts a pure function that returns a Result into the ReaderResult context.
|
|
704
|
+
* Useful for integrating existing Result-based functions.
|
|
705
|
+
*
|
|
706
|
+
* @template R - The type of dependencies
|
|
707
|
+
* @template E - The error type
|
|
708
|
+
* @template A - The value type
|
|
709
|
+
* @template Args - The argument types
|
|
710
|
+
* @param {(...args: Args) => Result<A, E>} f - Function that returns a Result
|
|
711
|
+
* @returns {(...args: Args) => ReaderResult<R, E, A>} A function that returns a ReaderResult
|
|
712
|
+
*
|
|
713
|
+
* @category Helpers
|
|
714
|
+
* @example
|
|
715
|
+
* const validateAge = (age: number): Result<number, string> =>
|
|
716
|
+
* age >= 18 ? Result.ok(age) : Result.err('Must be 18 or older');
|
|
717
|
+
*
|
|
718
|
+
* const validateAgeRR = liftDomain<Deps, string, number, [number]>(validateAge);
|
|
719
|
+
*
|
|
720
|
+
* @since 2025-07-03
|
|
721
|
+
*/
|
|
722
|
+
export var liftDomain = function (f) { return function () {
|
|
723
|
+
var args = [];
|
|
724
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
725
|
+
args[_i] = arguments[_i];
|
|
726
|
+
}
|
|
727
|
+
return ReaderResult.fromResult(f.apply(void 0, args));
|
|
728
|
+
}; };
|
|
729
|
+
/**
|
|
730
|
+
* Helper to create ReaderResult from an async function that might throw.
|
|
731
|
+
* @description Lifts an async function that might throw into the ReaderResult context,
|
|
732
|
+
* converting exceptions to typed errors.
|
|
733
|
+
*
|
|
734
|
+
* @template R - The type of dependencies
|
|
735
|
+
* @template E - The error type
|
|
736
|
+
* @template A - The value type
|
|
737
|
+
* @template Args - The argument types
|
|
738
|
+
* @param {(deps: R, ...args: Args) => Promise<A>} f - Async function that might throw
|
|
739
|
+
* @param {(error: unknown) => E} onError - Function to convert exceptions to errors
|
|
740
|
+
* @returns {(...args: Args) => ReaderResult<R, E, A>} A function that returns a ReaderResult
|
|
741
|
+
*
|
|
742
|
+
* @category Helpers
|
|
743
|
+
* @example
|
|
744
|
+
* const fetchUser = liftAsync<Deps, string, User, [string]>(
|
|
745
|
+
* async (deps, id) => deps.api.getUser(id),
|
|
746
|
+
* (error) => `Failed to fetch user: ${error}`
|
|
747
|
+
* );
|
|
748
|
+
*
|
|
749
|
+
* @since 2025-07-03
|
|
750
|
+
*/
|
|
751
|
+
export var liftAsync = function (f, onError) { return function () {
|
|
752
|
+
var args = [];
|
|
753
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
754
|
+
args[_i] = arguments[_i];
|
|
755
|
+
}
|
|
756
|
+
return ReaderResult.tryCatch(function (deps) { return f.apply(void 0, __spreadArray([deps], args, false)); }, onError);
|
|
757
|
+
}; };
|
|
758
|
+
//# sourceMappingURL=reader-result.mjs.map
|