@nlozgachev/pipelined 0.17.0 → 0.18.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/README.md CHANGED
@@ -21,13 +21,13 @@ compose with `pipe` and `flow`.
21
21
 
22
22
  ### pipelined/core
23
23
 
24
- - **`Option<A>`** — a value that may not exist; propagates absence without null checks.
24
+ - **`Maybe<A>`** — a value that may not exist; propagates absence without null checks.
25
25
  - **`Result<E, A>`** — an operation that succeeds or fails with a typed error.
26
26
  - **`Validation<E, A>`** — like `Result`, but accumulates every failure instead of stopping at the
27
27
  first.
28
28
  - **`Task<A>`** — a lazy, infallible async operation; nothing runs until called.
29
29
  - **`TaskResult<E, A>`** — a lazy async operation that can fail with a typed error.
30
- - **`TaskOption<A>`** — a lazy async operation that may produce nothing.
30
+ - **`TaskMaybe<A>`** — a lazy async operation that may produce nothing.
31
31
  - **`TaskValidation<E, A>`** — a lazy async operation that accumulates validation errors.
32
32
  - **`These<E, A>`** — an inclusive OR: holds an error, a value, or both at once.
33
33
  - **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
@@ -43,8 +43,8 @@ compose with `pipe` and `flow`.
43
43
 
44
44
  Everyday utilities for built-in JS types.
45
45
 
46
- - **`Arr`** — array utilities, data-last, returning `Option` instead of `undefined`.
47
- - **`Rec`** — record/object utilities, data-last, with `Option`-returning key lookup.
46
+ - **`Arr`** — array utilities, data-last, returning `Maybe` instead of `undefined`.
47
+ - **`Rec`** — record/object utilities, data-last, with `Maybe`-returning key lookup.
48
48
  - **`Num`** — number utilities: `range`, `clamp`, `between`, safe `parse`, and curried arithmetic.
49
49
  - **`Str`** — string utilities: `split`, `trim`, `words`, `lines`, and safe `parse.int` / `parse.float`.
50
50
 
@@ -61,25 +61,43 @@ Everyday utilities for built-in JS types.
61
61
  ## Example
62
62
 
63
63
  ```ts
64
- import { Option, Result } from "@nlozgachev/pipelined/core";
64
+ import { TaskResult } from "@nlozgachev/pipelined/core";
65
65
  import { pipe } from "@nlozgachev/pipelined/composition";
66
66
 
67
- // Chain nullable lookups without nested null checks
68
- const city = pipe(
69
- getUser(userId), // User | null
70
- Option.fromNullable, // Option<User>
71
- Option.chain((u) => Option.fromNullable(u.address)), // Option<Address>
72
- Option.chain((a) => Option.fromNullable(a.city)), // Option<string>
73
- Option.map((c) => c.toUpperCase()), // Option<string>
74
- Option.getOrElse("UNKNOWN"), // string
75
- );
76
-
77
- // Parse input and look up a record — both steps can fail
78
- const record = pipe(
79
- parseId(rawInput), // Result<ParseError, number>
80
- Result.chain((id) => db.find(id)), // Result<ParseError | NotFoundError, Record>
81
- Result.map((r) => r.name), // Result<ParseError | NotFoundError, string>
82
- );
67
+ // Typed errors. Lazy. Composable.
68
+ const getUser = (id: string): TaskResult<string, User> =>
69
+ pipe(
70
+ TaskResult.tryCatch(
71
+ () => fetch(`/users/${id}`).then((r) => r.json() as Promise<User>),
72
+ (e) => `fetch failed: ${e}`,
73
+ ),
74
+ TaskResult.timeout(5_000, () => "request timed out"),
75
+ TaskResult.retry({ attempts: 3, backoff: (n) => n * 1_000 }),
76
+ );
77
+
78
+ // Poll until a background job finishes — stop immediately on failure
79
+ const waitForExport = (jobId: string): TaskResult<string, ExportResult> =>
80
+ pipe(
81
+ TaskResult.tryCatch(
82
+ () => fetch(`/exports/${jobId}`).then((r) => r.json() as Promise<Job>),
83
+ String,
84
+ ),
85
+ TaskResult.pollUntil({
86
+ when: (job) => job.status === "done",
87
+ delay: (n) => n * 500, // 500 ms, 1 s, 1.5 s, ...
88
+ }),
89
+ TaskResult.map((job) => job.result),
90
+ );
91
+
92
+ // Compose the two — nothing runs until the final call
93
+ const message = await pipe(
94
+ getUser("abc"),
95
+ TaskResult.chain((user) => waitForExport(user.exportId)),
96
+ TaskResult.match({
97
+ ok: (r) => `Export ready: ${r.url}`,
98
+ err: (e) => `Failed: ${e}`,
99
+ }),
100
+ )();
83
101
  ```
84
102
 
85
103
  ## Documentation
@@ -224,16 +224,16 @@ declare namespace Result {
224
224
  */
225
225
  const recoverUnless: <E, A, B>(blockedErr: E, fallback: () => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
226
226
  /**
227
- * Converts a Result to an Option.
227
+ * Converts a Result to an Maybe.
228
228
  * Ok becomes Some, Err becomes None (the error is discarded).
229
229
  *
230
230
  * @example
231
231
  * ```ts
232
- * Result.toOption(Result.ok(42)); // Some(42)
233
- * Result.toOption(Result.err("oops")); // None
232
+ * Result.toMaybe(Result.ok(42)); // Some(42)
233
+ * Result.toMaybe(Result.err("oops")); // None
234
234
  * ```
235
235
  */
236
- const toOption: <E, A>(data: Result<E, A>) => Option<A>;
236
+ const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
237
237
  /**
238
238
  * Applies a function wrapped in an Result to a value wrapped in an Result.
239
239
  *
@@ -253,141 +253,141 @@ declare namespace Result {
253
253
  type Some<A> = WithKind<"Some"> & WithValue<A>;
254
254
  type None = WithKind<"None">;
255
255
  /**
256
- * Option represents an optional value: every Option is either Some (contains a value) or None (empty).
257
- * Use Option instead of null/undefined to make optionality explicit and composable.
256
+ * Maybe represents an optional value: every Maybe is either Some (contains a value) or None (empty).
257
+ * Use Maybe instead of null/undefined to make optionality explicit and composable.
258
258
  *
259
259
  * @example
260
260
  * ```ts
261
- * const findUser = (id: string): Option<User> =>
262
- * users.has(id) ? Option.some(users.get(id)!) : Option.none();
261
+ * const findUser = (id: string): Maybe<User> =>
262
+ * users.has(id) ? Maybe.some(users.get(id)!) : Maybe.none();
263
263
  *
264
264
  * pipe(
265
265
  * findUser("123"),
266
- * Option.map(user => user.name),
267
- * Option.getOrElse(() => "Unknown")
266
+ * Maybe.map(user => user.name),
267
+ * Maybe.getOrElse(() => "Unknown")
268
268
  * );
269
269
  * ```
270
270
  */
271
- type Option<T> = Some<T> | None;
272
- declare namespace Option {
271
+ type Maybe<T> = Some<T> | None;
272
+ declare namespace Maybe {
273
273
  /**
274
274
  * Creates a Some containing the given value.
275
275
  */
276
276
  const some: <A>(value: A) => Some<A>;
277
277
  /**
278
- * Type guard that checks if a Option is Some.
278
+ * Type guard that checks if a Maybe is Some.
279
279
  */
280
- const isSome: <A>(data: Option<A>) => data is Some<A>;
280
+ const isSome: <A>(data: Maybe<A>) => data is Some<A>;
281
281
  /**
282
- * Creates a None (empty Option).
282
+ * Creates a None (empty Maybe).
283
283
  */
284
284
  const none: () => None;
285
285
  /**
286
- * Type guard that checks if a Option is None.
286
+ * Type guard that checks if a Maybe is None.
287
287
  */
288
- const isNone: <A>(data: Option<A>) => data is None;
288
+ const isNone: <A>(data: Maybe<A>) => data is None;
289
289
  /**
290
- * Creates a Option from a nullable value.
290
+ * Creates a Maybe from a nullable value.
291
291
  * Returns None if the value is null or undefined, Some otherwise.
292
292
  *
293
293
  * @example
294
294
  * ```ts
295
- * Option.fromNullable(null); // None
296
- * Option.fromNullable(42); // Some(42)
295
+ * Maybe.fromNullable(null); // None
296
+ * Maybe.fromNullable(42); // Some(42)
297
297
  * ```
298
298
  */
299
- const fromNullable: <A>(value: A | null | undefined) => Option<A>;
299
+ const fromNullable: <A>(value: A | null | undefined) => Maybe<A>;
300
300
  /**
301
- * Extracts the value from a Option, returning null if None.
301
+ * Extracts the value from a Maybe, returning null if None.
302
302
  */
303
- const toNullable: <A>(data: Option<A>) => A | null;
303
+ const toNullable: <A>(data: Maybe<A>) => A | null;
304
304
  /**
305
- * Extracts the value from a Option, returning undefined if None.
305
+ * Extracts the value from a Maybe, returning undefined if None.
306
306
  */
307
- const toUndefined: <A>(data: Option<A>) => A | undefined;
307
+ const toUndefined: <A>(data: Maybe<A>) => A | undefined;
308
308
  /**
309
- * Creates a Option from a possibly undefined value.
309
+ * Creates a Maybe from a possibly undefined value.
310
310
  * Returns None if undefined, Some otherwise.
311
311
  */
312
- const fromUndefined: <A>(value: A | undefined) => Option<A>;
312
+ const fromUndefined: <A>(value: A | undefined) => Maybe<A>;
313
313
  /**
314
- * Converts an Option to a Result.
314
+ * Converts an Maybe to a Result.
315
315
  * Some becomes Ok, None becomes Err with the provided error.
316
316
  *
317
317
  * @example
318
318
  * ```ts
319
319
  * pipe(
320
- * Option.some(42),
321
- * Option.toResult(() => "Value was missing")
320
+ * Maybe.some(42),
321
+ * Maybe.toResult(() => "Value was missing")
322
322
  * ); // Ok(42)
323
323
  *
324
324
  * pipe(
325
- * Option.none(),
326
- * Option.toResult(() => "Value was missing")
325
+ * Maybe.none(),
326
+ * Maybe.toResult(() => "Value was missing")
327
327
  * ); // Err("Value was missing")
328
328
  * ```
329
329
  */
330
- const toResult: <E>(onNone: () => E) => <A>(data: Option<A>) => Result<E, A>;
330
+ const toResult: <E>(onNone: () => E) => <A>(data: Maybe<A>) => Result<E, A>;
331
331
  /**
332
- * Creates an Option from a Result.
332
+ * Creates an Maybe from a Result.
333
333
  * Ok becomes Some, Err becomes None (the error is discarded).
334
334
  *
335
335
  * @example
336
336
  * ```ts
337
- * Option.fromResult(Result.ok(42)); // Some(42)
338
- * Option.fromResult(Result.err("oops")); // None
337
+ * Maybe.fromResult(Result.ok(42)); // Some(42)
338
+ * Maybe.fromResult(Result.err("oops")); // None
339
339
  * ```
340
340
  */
341
- const fromResult: <E, A>(data: Result<E, A>) => Option<A>;
341
+ const fromResult: <E, A>(data: Result<E, A>) => Maybe<A>;
342
342
  /**
343
- * Transforms the value inside a Option if it exists.
343
+ * Transforms the value inside a Maybe if it exists.
344
344
  *
345
345
  * @example
346
346
  * ```ts
347
- * pipe(Option.some(5), Option.map(n => n * 2)); // Some(10)
348
- * pipe(Option.none(), Option.map(n => n * 2)); // None
347
+ * pipe(Maybe.some(5), Maybe.map(n => n * 2)); // Some(10)
348
+ * pipe(Maybe.none(), Maybe.map(n => n * 2)); // None
349
349
  * ```
350
350
  */
351
- const map: <A, B>(f: (a: A) => B) => (data: Option<A>) => Option<B>;
351
+ const map: <A, B>(f: (a: A) => B) => (data: Maybe<A>) => Maybe<B>;
352
352
  /**
353
- * Chains Option computations. If the first is Some, passes the value to f.
353
+ * Chains Maybe computations. If the first is Some, passes the value to f.
354
354
  * If the first is None, propagates None.
355
355
  *
356
356
  * @example
357
357
  * ```ts
358
- * const parseNumber = (s: string): Option<number> => {
358
+ * const parseNumber = (s: string): Maybe<number> => {
359
359
  * const n = parseInt(s, 10);
360
- * return isNaN(n) ? Option.none() : Option.some(n);
360
+ * return isNaN(n) ? Maybe.none() : Maybe.some(n);
361
361
  * };
362
362
  *
363
- * pipe(Option.some("42"), Option.chain(parseNumber)); // Some(42)
364
- * pipe(Option.some("abc"), Option.chain(parseNumber)); // None
363
+ * pipe(Maybe.some("42"), Maybe.chain(parseNumber)); // Some(42)
364
+ * pipe(Maybe.some("abc"), Maybe.chain(parseNumber)); // None
365
365
  * ```
366
366
  */
367
- const chain: <A, B>(f: (a: A) => Option<B>) => (data: Option<A>) => Option<B>;
367
+ const chain: <A, B>(f: (a: A) => Maybe<B>) => (data: Maybe<A>) => Maybe<B>;
368
368
  /**
369
- * Extracts the value from a Option by providing handlers for both cases.
369
+ * Extracts the value from a Maybe by providing handlers for both cases.
370
370
  *
371
371
  * @example
372
372
  * ```ts
373
373
  * pipe(
374
- * Option.some(5),
375
- * Option.fold(
374
+ * Maybe.some(5),
375
+ * Maybe.fold(
376
376
  * () => "No value",
377
377
  * n => `Value: ${n}`
378
378
  * )
379
379
  * ); // "Value: 5"
380
380
  * ```
381
381
  */
382
- const fold: <A, B>(onNone: () => B, onSome: (a: A) => B) => (data: Option<A>) => B;
382
+ const fold: <A, B>(onNone: () => B, onSome: (a: A) => B) => (data: Maybe<A>) => B;
383
383
  /**
384
- * Pattern matches on a Option, returning the result of the matching case.
384
+ * Pattern matches on a Maybe, returning the result of the matching case.
385
385
  *
386
386
  * @example
387
387
  * ```ts
388
388
  * pipe(
389
389
  * optionUser,
390
- * Option.match({
390
+ * Maybe.match({
391
391
  * some: user => `Hello, ${user.name}`,
392
392
  * none: () => "Hello, stranger"
393
393
  * })
@@ -397,64 +397,64 @@ declare namespace Option {
397
397
  const match: <A, B>(cases: {
398
398
  none: () => B;
399
399
  some: (a: A) => B;
400
- }) => (data: Option<A>) => B;
400
+ }) => (data: Maybe<A>) => B;
401
401
  /**
402
- * Returns the value inside an Option, or a default value if None.
403
- * The default is a thunk `() => B` — evaluated only when the Option is None.
402
+ * Returns the value inside an Maybe, or a default value if None.
403
+ * The default is a thunk `() => B` — evaluated only when the Maybe is None.
404
404
  * The default can be a different type, widening the result to `A | B`.
405
405
  *
406
406
  * @example
407
407
  * ```ts
408
- * pipe(Option.some(5), Option.getOrElse(() => 0)); // 5
409
- * pipe(Option.none(), Option.getOrElse(() => 0)); // 0
410
- * pipe(Option.none<string>(), Option.getOrElse(() => null)); // null — typed as string | null
408
+ * pipe(Maybe.some(5), Maybe.getOrElse(() => 0)); // 5
409
+ * pipe(Maybe.none(), Maybe.getOrElse(() => 0)); // 0
410
+ * pipe(Maybe.none<string>(), Maybe.getOrElse(() => null)); // null — typed as string | null
411
411
  * ```
412
412
  */
413
- const getOrElse: <A, B>(defaultValue: () => B) => (data: Option<A>) => A | B;
413
+ const getOrElse: <A, B>(defaultValue: () => B) => (data: Maybe<A>) => A | B;
414
414
  /**
415
- * Executes a side effect on the value without changing the Option.
415
+ * Executes a side effect on the value without changing the Maybe.
416
416
  * Useful for logging or debugging.
417
417
  *
418
418
  * @example
419
419
  * ```ts
420
420
  * pipe(
421
- * Option.some(5),
422
- * Option.tap(n => console.log("Value:", n)),
423
- * Option.map(n => n * 2)
421
+ * Maybe.some(5),
422
+ * Maybe.tap(n => console.log("Value:", n)),
423
+ * Maybe.map(n => n * 2)
424
424
  * );
425
425
  * ```
426
426
  */
427
- const tap: <A>(f: (a: A) => void) => (data: Option<A>) => Option<A>;
427
+ const tap: <A>(f: (a: A) => void) => (data: Maybe<A>) => Maybe<A>;
428
428
  /**
429
- * Filters a Option based on a predicate.
430
- * Returns None if the predicate returns false or if the Option is already None.
429
+ * Filters a Maybe based on a predicate.
430
+ * Returns None if the predicate returns false or if the Maybe is already None.
431
431
  *
432
432
  * @example
433
433
  * ```ts
434
- * pipe(Option.some(5), Option.filter(n => n > 3)); // Some(5)
435
- * pipe(Option.some(2), Option.filter(n => n > 3)); // None
434
+ * pipe(Maybe.some(5), Maybe.filter(n => n > 3)); // Some(5)
435
+ * pipe(Maybe.some(2), Maybe.filter(n => n > 3)); // None
436
436
  * ```
437
437
  */
438
- const filter: <A>(predicate: (a: A) => boolean) => (data: Option<A>) => Option<A>;
438
+ const filter: <A>(predicate: (a: A) => boolean) => (data: Maybe<A>) => Maybe<A>;
439
439
  /**
440
- * Recovers from a None by providing a fallback Option.
441
- * The fallback can produce a different type, widening the result to `Option<A | B>`.
440
+ * Recovers from a None by providing a fallback Maybe.
441
+ * The fallback can produce a different type, widening the result to `Maybe<A | B>`.
442
442
  */
443
- const recover: <A, B>(fallback: () => Option<B>) => (data: Option<A>) => Option<A | B>;
443
+ const recover: <A, B>(fallback: () => Maybe<B>) => (data: Maybe<A>) => Maybe<A | B>;
444
444
  /**
445
- * Applies a function wrapped in a Option to a value wrapped in a Option.
445
+ * Applies a function wrapped in a Maybe to a value wrapped in a Maybe.
446
446
  *
447
447
  * @example
448
448
  * ```ts
449
449
  * const add = (a: number) => (b: number) => a + b;
450
450
  * pipe(
451
- * Option.some(add),
452
- * Option.ap(Option.some(5)),
453
- * Option.ap(Option.some(3))
451
+ * Maybe.some(add),
452
+ * Maybe.ap(Maybe.some(5)),
453
+ * Maybe.ap(Maybe.some(3))
454
454
  * ); // Some(8)
455
455
  * ```
456
456
  */
457
- const ap: <A>(arg: Option<A>) => <B>(data: Option<(a: A) => B>) => Option<B>;
457
+ const ap: <A>(arg: Maybe<A>) => <B>(data: Maybe<(a: A) => B>) => Maybe<B>;
458
458
  }
459
459
 
460
460
  /**
@@ -674,4 +674,4 @@ declare namespace Task {
674
674
  const timeout: <E>(ms: number, onTimeout: () => E) => <A>(task: Task<A>) => Task<Result<E, A>>;
675
675
  }
676
676
 
677
- export { Deferred as D, type Err as E, type None as N, type Ok as O, Result as R, type Some as S, Task as T, type WithValue as W, Option as a, type WithLog as b, type WithKind as c, type WithError as d, type WithErrors as e, type WithFirst as f, type WithSecond as g };
677
+ export { Deferred as D, type Err as E, Maybe as M, type None as N, type Ok as O, Result as R, type Some as S, Task as T, type WithValue as W, type WithLog as a, type WithKind as b, type WithError as c, type WithErrors as d, type WithFirst as e, type WithSecond as f };