@nlozgachev/pipelined 0.65.0 → 0.67.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
@@ -19,8 +19,8 @@ propagation, unhandled async rejections, race conditions, and verbose nested spr
19
19
 
20
20
  `pipelined` provides discriminated unions and pure combinators to model these conditions explicitly:
21
21
  `Maybe` for absence, `Result` for typed failures, `Validation` for multi-error accumulation,
22
- `Task.Result` for lazy infallible async, `Op` for declarative request concurrency, and `Stream` for
23
- typed event sequences.
22
+ `Task.Result` for lazy infallible async, `Op` for declarative request concurrency, and `EventBus`
23
+ for typed event dispatching and sequence funnels.
24
24
 
25
25
  The library has zero external dependencies, <11 KB core gzipped (<17 KB total), and compiles to dual
26
26
  ESM and CommonJS distributions.
@@ -205,6 +205,19 @@ if (Validation.is.failed(outcome)) {
205
205
  console.log(outcome.errors);
206
206
  // ["Username must be at least 3 characters", "Email must contain an @ symbol"]
207
207
  }
208
+
209
+ // Or keep errors partitioned per field for UI forms:
210
+ const validateForm = Validation.keyed.make({
211
+ username: validateUsername,
212
+ email: validateEmail,
213
+ });
214
+
215
+ const formState = validateForm({ username: "a", email: "invalid" });
216
+
217
+ if (Validation.keyed.is.failed(formState)) {
218
+ const errors = Validation.keyed.getErrors(formState);
219
+ // Some({ username: ["Username must be at least 3 characters"], email: ["Email must contain an @ symbol"] })
220
+ }
208
221
  ```
209
222
 
210
223
  ### Eliminating impossible UI states
@@ -287,7 +300,7 @@ const fetchUser = Op.interpret(
287
300
  Op.create(
288
301
  (signal) => (id: string) =>
289
302
  fetch(`/users/${id}`, { signal }).then((r) => r.json() as Promise<User>),
290
- (e) => new ApiError(e),
303
+ { onError: (e) => new ApiError(e) },
291
304
  ),
292
305
  {
293
306
  strategy: "restartable",
@@ -334,7 +347,7 @@ const searchOp = Op.create(
334
347
  fetch(`/search?q=${query}`, { signal }).then((r) =>
335
348
  r.json() as Promise<SearchResult[]>
336
349
  ),
337
- (e) => new SearchError(e),
350
+ { onError: (e) => new SearchError(e) },
338
351
  );
339
352
 
340
353
  const search = Op.interpret(searchOp, {
@@ -360,7 +373,7 @@ const submitOp = Op.create(
360
373
  fetch("/orders", { method: "POST", body: data, signal }).then((r) =>
361
374
  r.json()
362
375
  ),
363
- (e) => new ApiError(e),
376
+ { onError: (e) => new ApiError(e) },
364
377
  );
365
378
 
366
379
  const submit = Op.interpret(submitOp, {
@@ -382,14 +395,14 @@ form.addEventListener("submit", (e) => {
382
395
  The system supports a variety of built-in strategies — `restartable`, `exclusive`, `debounced`,
383
396
  `throttled`, `queue`, `buffered`, `concurrent`, `keyed`, and `once`.
384
397
 
385
- ### Event streaming, sequence funnels, and queue safety
398
+ ### Typed event dispatching, sequence funnels, and queue safety
386
399
 
387
400
  Decoupling event producers from stateful event consumers often leads to untyped event emitters or
388
- recursive call stack crashes during event cascades. `Stream` models in-memory event pipelines with
389
- typed message schemas, multi-step sequence pattern matching, and causal FIFO queue dispatching:
401
+ recursive call stack crashes during event cascades. `EventBus` models in-memory event dispatching
402
+ with typed message schemas, multi-step sequence pattern matching, and causal FIFO queue dispatching:
390
403
 
391
404
  ```ts
392
- import { Stream } from "@nlozgachev/pipelined/core";
405
+ import { EventBus } from "@nlozgachev/pipelined/core";
393
406
 
394
407
  type UserFlowMessages = {
395
408
  sessionStarted: { sessionId: string };
@@ -398,11 +411,11 @@ type UserFlowMessages = {
398
411
  flowCancelled: { reason: string };
399
412
  };
400
413
 
401
- const flowStream = Stream.make<UserFlowMessages>();
414
+ const flowBus = EventBus.make<UserFlowMessages>();
402
415
 
403
416
  // Pattern-match the complete multi-step funnel
404
- const sub = Stream.listen(
405
- flowStream,
417
+ const sub = EventBus.listen(
418
+ flowBus,
406
419
  ["sessionStarted", "stepCompleted", "flowFinished"],
407
420
  { ordered: true, reset: "flowCancelled" },
408
421
  ).reduce(
@@ -415,18 +428,18 @@ const sub = Stream.listen(
415
428
  { completedFlows: 0 },
416
429
  );
417
430
 
418
- // Emit typed messages to the stream
419
- Stream.emit(flowStream, {
431
+ // Emit typed messages to the event bus
432
+ EventBus.emit(flowBus, {
420
433
  kind: "sessionStarted",
421
434
  value: { sessionId: "sess-101" },
422
435
  });
423
436
 
424
- Stream.emit(flowStream, {
437
+ EventBus.emit(flowBus, {
425
438
  kind: "stepCompleted",
426
439
  value: { stepName: "onboarding" },
427
440
  });
428
441
 
429
- Stream.emit(flowStream, {
442
+ EventBus.emit(flowBus, {
430
443
  kind: "flowFinished",
431
444
  value: { totalTimeMs: 4200 },
432
445
  });
@@ -434,7 +447,7 @@ Stream.emit(flowStream, {
434
447
  sub.getState(); // { completedFlows: 1 }
435
448
  ```
436
449
 
437
- `Stream` executes cascading events iteratively using an internal queue with O(1) stack overhead,
450
+ `EventBus` executes cascading events iteratively using an internal queue with O(1) stack overhead,
438
451
  completely preventing stack overflow crashes and out-of-order re-entrant execution.
439
452
 
440
453
  ### Deep immutable updates without spread boilerplate
@@ -504,44 +517,44 @@ if (Result.is.ok(email)) {
504
517
 
505
518
  ## Quick Reference
506
519
 
507
- | Problem to Solve | Module | Import Path |
508
- | ------------------------------------------------------------- | ----------------------------- | ----------------------------------- |
509
- | Optional values without `null` / `undefined` checks | `Maybe` | `@nlozgachev/pipelined/core` |
510
- | Synchronous typed errors without `try`/`catch` | `Result` | `@nlozgachev/pipelined/core` |
511
- | Multi-field form and batch validation error accumulation | `Validation` | `@nlozgachev/pipelined/core` |
512
- | Lazy async workflows with automatic `AbortSignal` | `Task.Result`, `Task` | `@nlozgachev/pipelined/core` |
513
- | Infallible async return container for tasks | `Deferred` | `@nlozgachev/pipelined/core` |
514
- | Eliminating impossible UI loading/error/data states | `RemoteData` | `@nlozgachev/pipelined/core` |
515
- | Managing request race conditions, retries, and locks | `Op` | `@nlozgachev/pipelined/core` |
516
- | In-memory event streaming, sequence funnels & queue safety | `Stream` | `@nlozgachev/pipelined/core` |
517
- | Deep nested immutable updates without spread boilerplate | `Lens`, `Optional` | `@nlozgachev/pipelined/core` |
518
- | Implicit dependency injection without prop drilling | `Reader` | `@nlozgachev/pipelined/core` |
519
- | Pure state transitions, tokenizers, and parsers | `State` | `@nlozgachev/pipelined/core` |
520
- | Deterministic bracket cleanup (DB pools, file locks) | `Resource` | `@nlozgachev/pipelined/core` |
521
- | Deferred computation memoized on first access | `Lazy` | `@nlozgachev/pipelined/core` |
522
- | Pure calculation audit trails and decision logging | `Logged` | `@nlozgachev/pipelined/core` |
523
- | Inclusive-OR data modeling and two-way sync diffs | `These` | `@nlozgachev/pipelined/core` |
524
- | Deep structural equality & React component memoization | `Equality` | `@nlozgachev/pipelined/core` |
525
- | Multi-column table sorting with tiebreakers | `Ordering` | `@nlozgachev/pipelined/core` |
526
- | Composable boolean filter pipelines & authorization policies | `Predicate` | `@nlozgachev/pipelined/core` |
527
- | Runtime type narrowing & custom type guard composition | `Refinement` | `@nlozgachev/pipelined/core` |
528
- | Merging configurations & metric structures (Monoids) | `Combinable` | `@nlozgachev/pipelined/core` |
529
- | Strongly-typed immutable pair manipulation | `Tuple` | `@nlozgachev/pipelined/core` |
530
- | Point-free, bounds-safe array transformations | `Arr`, `Arr.NonEmpty` | `@nlozgachev/pipelined/data` |
531
- | Type-safe object manipulation & key migration | `Rec`, `Rec.NonEmpty` | `@nlozgachev/pipelined/data` |
532
- | Insertion-ordered maps with non-string keys | `Dict`, `Dict.NonEmpty` | `@nlozgachev/pipelined/data` |
533
- | Immutable sets & role/permission algebra | `Uniq` | `@nlozgachev/pipelined/data` |
534
- | String sanitization, numeric conversion & slug parsing | `Str` | `@nlozgachev/pipelined/data` |
535
- | Boundary clamping & division-by-zero protection | `Num` | `@nlozgachev/pipelined/data` |
536
- | Financial ledger arithmetic without float precision drift | `BigNum` | `@nlozgachev/pipelined/data` |
537
- | Safe JSON parsing & circular reference protection | `Json` | `@nlozgachev/pipelined/data` |
538
- | Nominal typing & security boundary gates | `Brand` | `@nlozgachev/pipelined/types` |
539
- | Explicit, unit-safe time spans & timeout policies | `Duration`, `RetryPolicy` | `@nlozgachev/pipelined/types` |
540
- | Left-to-right value pipeline execution | `pipe` | `@nlozgachev/pipelined/composition` |
541
- | Left-to-right and right-to-left function composition | `flow`, `compose` | `@nlozgachev/pipelined/composition` |
542
- | Currying, uncurrying, and argument flipping | `curry`, `uncurry`, `flip` | `@nlozgachev/pipelined/composition` |
543
- | Multi-branch argument routing and combining | `converge`, `juxt`, `on` | `@nlozgachev/pipelined/composition` |
544
- | Pure function memoization, predicates & pipeline side-effects | `memoize`, `tap`, `not`, `fn` | `@nlozgachev/pipelined/composition` |
520
+ | Problem to Solve | Module | Import Path |
521
+ | ------------------------------------------------------------------ | ----------------------------- | ----------------------------------- |
522
+ | Optional values without `null` / `undefined` checks | `Maybe` | `@nlozgachev/pipelined/core` |
523
+ | Synchronous typed errors without `try`/`catch` | `Result` | `@nlozgachev/pipelined/core` |
524
+ | Multi-field form and batch validation error accumulation | `Validation` | `@nlozgachev/pipelined/core` |
525
+ | Lazy async workflows with automatic `AbortSignal` | `Task.Result`, `Task` | `@nlozgachev/pipelined/core` |
526
+ | Infallible async return container for tasks | `Deferred` | `@nlozgachev/pipelined/core` |
527
+ | Eliminating impossible UI loading/error/data states | `RemoteData` | `@nlozgachev/pipelined/core` |
528
+ | Managing request race conditions, retries, and locks | `Op` | `@nlozgachev/pipelined/core` |
529
+ | In-memory typed event dispatching, sequence funnels & queue safety | `EventBus` | `@nlozgachev/pipelined/core` |
530
+ | Deep nested immutable updates without spread boilerplate | `Lens`, `Optional` | `@nlozgachev/pipelined/core` |
531
+ | Implicit dependency injection without prop drilling | `Reader` | `@nlozgachev/pipelined/core` |
532
+ | Pure state transitions, tokenizers, and parsers | `State` | `@nlozgachev/pipelined/core` |
533
+ | Deterministic bracket cleanup (DB pools, file locks) | `Resource` | `@nlozgachev/pipelined/core` |
534
+ | Deferred computation memoized on first access | `Lazy` | `@nlozgachev/pipelined/core` |
535
+ | Pure calculation audit trails and decision logging | `Logged` | `@nlozgachev/pipelined/core` |
536
+ | Inclusive-OR data modeling and two-way sync diffs | `These` | `@nlozgachev/pipelined/core` |
537
+ | Deep structural equality & React component memoization | `Equality` | `@nlozgachev/pipelined/core` |
538
+ | Multi-column table sorting with tiebreakers | `Ordering` | `@nlozgachev/pipelined/core` |
539
+ | Composable boolean filter pipelines & authorization policies | `Predicate` | `@nlozgachev/pipelined/core` |
540
+ | Runtime type narrowing & custom type guard composition | `Refinement` | `@nlozgachev/pipelined/core` |
541
+ | Merging configurations & metric structures (Monoids) | `Combinable` | `@nlozgachev/pipelined/core` |
542
+ | Strongly-typed immutable pair manipulation | `Pair` | `@nlozgachev/pipelined/core` |
543
+ | Point-free, bounds-safe array transformations | `Arr`, `Arr.NonEmpty` | `@nlozgachev/pipelined/data` |
544
+ | Type-safe object manipulation & key migration | `Rec`, `Rec.NonEmpty` | `@nlozgachev/pipelined/data` |
545
+ | Insertion-ordered maps with non-string keys | `Dict`, `Dict.NonEmpty` | `@nlozgachev/pipelined/data` |
546
+ | Immutable sets & role/permission algebra | `Uniq` | `@nlozgachev/pipelined/data` |
547
+ | String sanitization, numeric conversion & slug parsing | `Str` | `@nlozgachev/pipelined/data` |
548
+ | Boundary clamping & division-by-zero protection | `Num` | `@nlozgachev/pipelined/data` |
549
+ | Financial ledger arithmetic without float precision drift | `BigNum` | `@nlozgachev/pipelined/data` |
550
+ | Safe JSON parsing & circular reference protection | `Json` | `@nlozgachev/pipelined/data` |
551
+ | Nominal typing & security boundary gates | `Brand` | `@nlozgachev/pipelined/types` |
552
+ | Explicit, unit-safe time spans & timeout policies | `Duration`, `RetryPolicy` | `@nlozgachev/pipelined/types` |
553
+ | Left-to-right value pipeline execution | `pipe` | `@nlozgachev/pipelined/composition` |
554
+ | Left-to-right and right-to-left function composition | `flow`, `compose` | `@nlozgachev/pipelined/composition` |
555
+ | Currying, uncurrying, and argument flipping | `curry`, `uncurry`, `flip` | `@nlozgachev/pipelined/composition` |
556
+ | Multi-branch argument routing and combining | `converge`, `juxt`, `on` | `@nlozgachev/pipelined/composition` |
557
+ | Pure function memoization, predicates & pipeline side-effects | `memoize`, `tap`, `not`, `fn` | `@nlozgachev/pipelined/composition` |
545
558
 
546
559
  ---
547
560
 
@@ -552,11 +565,11 @@ if (Result.is.ok(email)) {
552
565
  - **`@nlozgachev/pipelined/core`**: Core context containers, async runtimes, optics, and logic
553
566
  abstractions (<11 KB gzipped).
554
567
  - **`@nlozgachev/pipelined/data`**: Curried, data-last utilities for collections, numbers, strings,
555
- and JSON (<7 KB gzipped).
568
+ and JSON (<10 KB gzipped).
556
569
  - **`@nlozgachev/pipelined/composition`**: Pure higher-order function combinators (`pipe`, `flow`,
557
570
  `compose`, `curry`, `uncurry`, `converge`, `juxt`, `memoize`, `tap`, `on`, `not`, `flip`, `fn`)
558
571
  (<2 KB gzipped).
559
- - **`@nlozgachev/pipelined/types`**: Type-level utilities (`Brand`, `Duration`, `RetryPolicy`) (<400
572
+ - **`@nlozgachev/pipelined/types`**: Type-level utilities (`Brand`, `Duration`, `RetryPolicy`) (<450
560
573
  B gzipped).
561
574
 
562
575
  Every utility in the library is benchmarked against its native equivalent. While currying introduces