@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.
Files changed (46) hide show
  1. package/README.md +242 -0
  2. package/dist/array-utils.d.mts +317 -0
  3. package/dist/array-utils.d.mts.map +1 -0
  4. package/dist/array-utils.mjs +370 -0
  5. package/dist/array-utils.mjs.map +1 -0
  6. package/dist/composition.d.mts +603 -0
  7. package/dist/composition.d.mts.map +1 -0
  8. package/dist/composition.mjs +516 -0
  9. package/dist/composition.mjs.map +1 -0
  10. package/dist/object-utils.d.mts +267 -0
  11. package/dist/object-utils.d.mts.map +1 -0
  12. package/dist/object-utils.mjs +258 -0
  13. package/dist/object-utils.mjs.map +1 -0
  14. package/dist/option.d.mts +622 -0
  15. package/dist/option.d.mts.map +1 -0
  16. package/dist/option.mjs +637 -0
  17. package/dist/option.mjs.map +1 -0
  18. package/dist/performance.d.mts +265 -0
  19. package/dist/performance.d.mts.map +1 -0
  20. package/dist/performance.mjs +453 -0
  21. package/dist/performance.mjs.map +1 -0
  22. package/dist/pipeline.d.mts +431 -0
  23. package/dist/pipeline.d.mts.map +1 -0
  24. package/dist/pipeline.mjs +460 -0
  25. package/dist/pipeline.mjs.map +1 -0
  26. package/dist/predicates.d.mts +722 -0
  27. package/dist/predicates.d.mts.map +1 -0
  28. package/dist/predicates.mjs +802 -0
  29. package/dist/predicates.mjs.map +1 -0
  30. package/dist/reader-result.d.mts +422 -0
  31. package/dist/reader-result.d.mts.map +1 -0
  32. package/dist/reader-result.mjs +758 -0
  33. package/dist/reader-result.mjs.map +1 -0
  34. package/dist/result.d.mts +684 -0
  35. package/dist/result.d.mts.map +1 -0
  36. package/dist/result.mjs +814 -0
  37. package/dist/result.mjs.map +1 -0
  38. package/dist/types.d.mts +439 -0
  39. package/dist/types.d.mts.map +1 -0
  40. package/dist/types.mjs +191 -0
  41. package/dist/types.mjs.map +1 -0
  42. package/dist/validation.d.mts +622 -0
  43. package/dist/validation.d.mts.map +1 -0
  44. package/dist/validation.mjs +852 -0
  45. package/dist/validation.mjs.map +1 -0
  46. 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