resultar 3.9.1 → 3.10.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
@@ -405,6 +405,25 @@ Choose the wrapper that matches the external boundary:
405
405
 
406
406
  Prefer a factory with `tryResultAsync` when creating the promise can also throw synchronously.
407
407
 
408
+ ## Reusable function composition
409
+
410
+ Import `pipe`, `flow`, `identity`, and `constant` from the root package:
411
+
412
+ ```ts
413
+ import { constant, flow, identity, ok, pipe } from 'resultar'
414
+
415
+ const normalize = flow((input: string) => input.trim(), (input) => input.toLowerCase())
416
+ const name = pipe(' ADA ', normalize, (value) => ok(value))
417
+ const label = name.match({ ok: identity, error: constant('Unavailable') })
418
+ ```
419
+
420
+ `pipe` applies zero through eight transformations; `value.pipe()` also accepts zero
421
+ transformations. `flow` composes one through nine functions and preserves the first
422
+ function's optional/rest parameters and explicit `this`. Later functions receive one
423
+ value. Both compose synchronously: exceptions propagate, Promises pass through unchanged,
424
+ and tasks execute only at a run boundary. `identity` preserves its argument's reference;
425
+ `constant(value)` captures and returns that same value without copying it.
426
+
408
427
  ## Lazy Workflows With ResultTask
409
428
 
410
429
  `ResultTask<T, E, R>` is the lazy workflow primitive in Resultar. Creating one does not start the
@@ -421,10 +440,10 @@ operation. Mapping, chaining, recovery, service provision, and generator composi
421
440
  | Defer synchronous work | `sync`, `try` |
422
441
  | Defer promise-producing work | `tryPromise` |
423
442
  | Transform, observe, or chain | `map`, `mapError`, `flatMap`, `andThen`, `tap`, `tapError`, `as`, `match` |
424
- | Recover typed failures | `catchAll` |
425
- | Write a linear lazy workflow | `gen` with `yield*` |
443
+ | Recover typed failures | `catchAll`, `catchTag`, `catchTags` |
444
+ | Write a linear lazy workflow | `gen` with `yield*`, `fn` for parameterized functions |
426
445
  | Declare and provide dependencies | `service`, `provideService`, `provideServices`, `provideServiceResolver` |
427
- | Own resources and await cleanup | `acquireRelease`, `scoped` |
446
+ | Own resources and await cleanup | `acquireRelease`, `acquireDisposable`, `scoped` |
428
447
  | Execute at the application boundary | `runExit`, `runResult`, `runPromise` |
429
448
  | Bridge to started promises | `fromResultAsync`, `toResultAsync`, `ResultAsync.fromTask` |
430
449
 
@@ -571,6 +590,38 @@ scopes below when both the body and cleanup failure must be preserved.
571
590
  Generators also accept `yield* result` for plain `Result` values: `Ok` unwraps its value and
572
591
  `Err` short-circuits into `E`.
573
592
 
593
+ ### Parameterized tasks and tagged recovery
594
+
595
+ `ResultTask.fn` wraps a synchronous generator function. Calling the wrapper captures
596
+ its arguments and receiver without executing the body. Each run creates a fresh iterator,
597
+ with the same success/error/requirements inference and cleanup policy as `gen`:
598
+
599
+ ```ts
600
+ import { createTaggedError, pipe, ResultTask } from 'resultar'
601
+
602
+ class NotFound extends createTaggedError({ name: 'NotFound', message: 'User $id was not found' }) {}
603
+ const Users = ResultTask.service<{
604
+ find(id: string): ResultTask<string, NotFound>
605
+ }>()('Users')
606
+
607
+ const loadUser = ResultTask.fn(function* (id: string) {
608
+ const users = yield* Users
609
+ return yield* users.find(id)
610
+ })
611
+
612
+ const recovered = pipe(loadUser('user_123'),
613
+ ResultTask.catchTag('NotFound', (error: NotFound) => ResultTask.succeed(`Guest ${error.id}`)),
614
+ )
615
+ ```
616
+
617
+ `task.catchTag(tag, handler)` narrows the handler's error to that tag. `task.catchTags(handlers)`
618
+ allows partial recovery. Both also support `ResultTask.catchTag(task, tag, handler)` /
619
+ `ResultTask.catchTags(task, handlers)` and curried forms for `pipe`. Unhandled tags remain in
620
+ `E`; handler errors and requirements join the source channels. Scope requirements remain
621
+ until closure. Handlers run only for simple `Fail` causes: defects and interruptions pass
622
+ through, and composites retain the existing `catchAll` policy of `Die(ResultTaskCauseError)`.
623
+ A throwing handler becomes a defect.
624
+
574
625
  ### Resource scopes and application lifecycle
575
626
 
576
627
  ```ts
@@ -596,6 +647,13 @@ const program = ResultTask.scoped(ResultTask.gen(function* () {
596
647
  const exit = await ResultTask.runExit(program)
597
648
  ```
598
649
 
650
+ For native `Disposable` or `AsyncDisposable` resources, use
651
+ `ResultTask.acquireDisposable(acquireTask)` inside the same scopes. It prefers
652
+ `Symbol.asyncDispose` when both protocols exist, awaits disposal once in LIFO order,
653
+ and registers nothing when acquisition fails. Disposal throws/rejections become `Die`;
654
+ body and disposal failures retain their sequential cause tree. Cancellation does not
655
+ interrupt cleanup. This helper adds a scope requirement to `R`, just like `acquireRelease`.
656
+
599
657
  Every run owns a root scope. `scoped` closes a child scope before the next task continues. Successful
600
658
  acquisitions register finalizers in LIFO order; every finalizer is awaited once, even after a body
601
659
  failure, defect, or interruption. Failed acquisition does not register its release. Earlier resources
@@ -74,6 +74,12 @@ remains available. Check installed versions before using these overloads.
74
74
  - Keep `_unsafeUnwrap()` and `_unsafeUnwrapErr()` in tests. Use `unwrapOrThrow()` only when throwing
75
75
  is explicitly the intended boundary behavior.
76
76
 
77
+ For reusable synchronous transformations, use root `pipe` / `flow`. `identity` preserves
78
+ its callback input; `constant(value)` captures a fallback. These utilities do not await or
79
+ execute tasks. Use `ResultTask.fn` for parameterized lazy generators and `catchTag` / partial
80
+ `catchTags` for typed task recovery. Native disposables use `acquireDisposable` in a scope;
81
+ async disposal is awaited and disposal failures become defects.
82
+
77
83
  ## Choose The Composition Operator
78
84
 
79
85
  | Need | Prefer |
@@ -187,7 +193,8 @@ Read `references/integrations.md` before implementing request adapters or diagno
187
193
  1. Run the `resultar-check` CLI as the authoritative project and CI gate. It runs TypeScript and the
188
194
  full Resultar rule set against the project.
189
195
  2. Configure `resultar-check lsp` as the editor's stdio language server for inline diagnostics and
190
- quick fixes. It reads the same project configuration as the CLI.
196
+ quick fixes. It analyzes unsaved buffers and dependencies with versioned edits; hover shows
197
+ `T`, `E`, and `R`. It reads the same project configuration as the CLI.
191
198
  3. Use JSONL, SARIF, or JUnit output when another CI or review system needs structured diagnostics;
192
199
  do not present those consumers as replacements for the native checker.
193
200
  4. Run the repository's native formatting, build, tests, analysis, package smoke, and example
@@ -197,6 +204,15 @@ JSONL fixes distinguish `correction` from `intentional-discard`; LSP actions car
197
204
  classification in `data.kind`. Do not apply a discard just to silence the checker: `void task`
198
205
  does not execute a lazy task or handle its errors. Corrections still need compilation and tests.
199
206
 
207
+ Use `resultar-check overview --json` to inventory exported workflows/errors/services and
208
+ `resultar-check quickfixes --json` to inspect proposed edits without writing files. Preserve
209
+ concrete task requirements: assertions must not hide services or pending scope errors.
210
+ Remove unused suppression entries; disabled rules are excluded from usage checks.
211
+
212
+ The checker schema combines standard `tsconfig` options and Resultar rules for editor
213
+ completion/validation. Schema resolution and compiler compatibility are separate checks;
214
+ read [checker configuration](references/integrations.md#configure-the-native-checker) for details.
215
+
200
216
  Treat stable `resultar/*` rule IDs as architecture feedback. Fix the typed boundary or composition
201
217
  problem instead of suppressing diagnostics broadly. Use narrow ignore patterns only for deliberate
202
218
  test, script, generated, or process-boundary exceptions.
@@ -207,6 +207,13 @@ const workflow = safeTry(async function* () {
207
207
  Use `yield*` for `Result` and `ResultAsync`. Wrap raw promises before yielding. Avoid raw `await`,
208
208
  `try/catch`, and legacy `safeUnwrap()` inside the generator.
209
209
 
210
+ ## Function Utilities
211
+
212
+ Root `pipe` supports zero through eight transforms; `flow` supports one through nine
213
+ functions and preserves the first function's arguments/receiver. Both compose synchronously.
214
+ `identity` returns the original value; `constant(value)` returns a callback capturing that value.
215
+ Use `result.match({ ok: identity, error: constant(fallback) })` for fixed boundary fallbacks.
216
+
210
217
  ## ResultTask (Lazy Workflows)
211
218
 
212
219
  `ResultTask<A, E, R>` is a lazy, reusable description of work: building one starts nothing, and
@@ -215,12 +222,19 @@ laziness matters.
215
222
 
216
223
  - Build: `succeed`, `fail`, `fromResult`, `sync`, `try`, `tryPromise` (`try`/`tryPromise` map
217
224
  external throws into `E`; `sync` treats them as defects).
218
- - Compose lazily: `map`, `flatMap`, `catchAll`, `tap`, `gen` with `yield*` (also accepts
225
+ - Compose lazily: `map`, `flatMap`, `catchAll`, `catchTag`, partial `catchTags`, `tap`, `gen` with `yield*` (also accepts
219
226
  `yield* result`: `Ok` unwraps, `Err` short-circuits into `E`).
227
+ - `fn(function* (args) { ... })` creates a lazy parameterized function, capturing arguments/
228
+ receiver and creating fresh generators on each execution; infer all three task channels.
229
+ - Tagged task recovery has instance/direct/curried forms. Handler errors and requirements
230
+ join the source; unhandled tags and scope markers remain. Defects/interruptions pass through;
231
+ composites follow `catchAll`'s `ResultTaskCauseError` policy.
232
+ - `acquireDisposable` prefers async disposal, awaits it once in LIFO order, and treats
233
+ disposal failures as defects while preserving sequential body/cleanup causes.
220
234
  - Provide services: `yield* Tag`, then `provideService` / `provideServices` /
221
235
  `provideServiceResolver` (data-first and curried); missing or incompatible providers are
222
236
  compile-time errors, never runtime surprises.
223
- - Resources: `acquireRelease` + `scoped`; LIFO awaited finalizers run on every exit; deferred
237
+ - Resources: `acquireRelease` / `acquireDisposable` + `scoped`; LIFO awaited finalizers run on every exit; deferred
224
238
  release errors live in `R` until `scoped` folds them into `E`; `catchAll` recovers a release
225
239
  failure only after `scoped`.
226
240
  - Boundaries: `runExit` preserves `Success`/`Fail`/`Die`/`Interrupt`/`Sequential`; `runResult`
@@ -222,6 +222,19 @@ extension. Start `resultar-check lsp` from the editor's language-server configur
222
222
  rule IDs such as `resultar/no-discard`; pass `--format json` (or `--json`) when machine-readable JSON
223
223
  Lines output is needed.
224
224
 
225
+ The `$schema` composes the standard SchemaStore `tsconfig` schema with Resultar rule options.
226
+ Preserve it when configuring the checker so editors can complete and validate both sets of options.
227
+ Resolving the standard reference requires SchemaStore access or an editor cache. The schema is
228
+ independent of the installed TypeScript version; validate compiler compatibility with the native
229
+ checker and, when relevant, the consuming project's installed compiler. The `plugins` entry remains
230
+ configuration for the native checker, including when the project uses TypeScript 7.
231
+
232
+ The LSP overlays unsaved project buffers and dependencies, debounces changes, and uses
233
+ versioned UTF-16 edits. Hover separates task success/errors/requirements and service contracts.
234
+ Use `resultar-check overview --json` for a sorted exported inventory and
235
+ `resultar-check quickfixes --json` for read-only fix previews. The latter emits diagnostic
236
+ JSONL; inventory output is one document with `schemaVersion: 1`.
237
+
225
238
  Read `packages/check/README.md` for all rules, severities, modes, and command-line flags. Use narrow
226
239
  `ignoreFilePatterns` only for deliberate tests, scripts, generated files, or terminal process boundaries.
227
240
 
@@ -241,7 +254,8 @@ When an agent encounters a diagnostic, fix the architecture first:
241
254
  - replace project-wide `try/catch` with `tryResult` or `tryResultAsync` when `noTryCatch` is enabled;
242
255
  - wrap raw promise awaits in Resultar contexts;
243
256
  - use `yield*` and avoid `try/catch` inside `Result.gen` / `safeTry`;
244
- - preserve the inferred error channel instead of asserting it away.
257
+ - preserve inferred error and task requirements channels instead of asserting them away;
258
+ - remove obsolete suppression entries without suppressing their diagnostic broadly.
245
259
 
246
260
  ## Discover Repository Commands
247
261
 
@@ -12,7 +12,7 @@ The former `/advanced` subpath has been removed; migrate existing imports to `re
12
12
  ({ ... }) })`. The readonly dependency object is inferred; `make` can return the service
13
13
  object directly or a ResultTask. Factories remain lazy and synchronous values add no typed
14
14
  initialization errors. Promises and thenables are rejected. Use `ResultTask.gen` for
15
- initialization with typed failures, additional requirements or owned resources. Without `requires`, pass a task directly as `make` and yield requirements inline or by token.
15
+ initialization with typed failures, additional requirements or owned resources. Without `requires`, pass a task or a zero-argument factory returning a synchronous value or ResultTask. Tasks can yield requirements inline or by token.
16
16
  Both forms support `Service<Contract>()('name', definition)`.
17
17
  - Keep provider registration explicit. `requires` does not acquire or register dependencies.
18
18
  Inline tokens resolve by name in DI or `provideServices`; core `provideService` supplies an exact
@@ -1,10 +1,10 @@
1
1
  {
2
- "resultar-check": "3.2.0",
3
- "resultar-di": "0.4.0",
4
- "resultar-fastify": "0.4.0",
5
- "resultar-hono": "0.4.0",
2
+ "resultar-check": "3.3.0",
3
+ "resultar-di": "0.4.1",
4
+ "resultar-fastify": "0.4.1",
5
+ "resultar-hono": "0.4.1",
6
6
  "resultar-request": "0.1.2",
7
7
  "resultar-request-typebox": "0.1.2",
8
8
  "resultar-request-zod": "0.1.2",
9
- "resultar": "3.9.1"
9
+ "resultar": "3.10.0"
10
10
  }
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ declare abstract class Pipeable {
12
12
  * Use `pipe` to package reusable Result or ResultAsync combinators without hiding the underlying
13
13
  * type.
14
14
  */
15
+ pipe(): this;
15
16
  pipe<A>(ab: PipeFn<this, A>): A;
16
17
  pipe<A, B>(ab: PipeFn<this, A>, bc: PipeFn<A, B>): B;
17
18
  pipe<A, B, C>(ab: PipeFn<this, A>, bc: PipeFn<A, B>, cd: PipeFn<B, C>): C;
@@ -309,9 +310,15 @@ interface ResultOperations<T, E> {
309
310
  * Pipes this Result through reusable transformation functions.
310
311
  */
311
312
  pipe: {
312
- <A>(ab: PipeFn<Result<T, E>, A>): A;
313
- <A, B>(ab: PipeFn<Result<T, E>, A>, bc: PipeFn<A, B>): B;
314
- <A, B, C>(ab: PipeFn<Result<T, E>, A>, bc: PipeFn<A, B>, cd: PipeFn<B, C>): C;
313
+ <Self>(this: Self): Self;
314
+ <P1>(f0: PipeFn<Result<T, E>, P1>): P1;
315
+ <P1, P2>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>): P2;
316
+ <P1, P2, P3>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>): P3;
317
+ <P1, P2, P3, P4>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>, f3: PipeFn<P3, P4>): P4;
318
+ <P1, P2, P3, P4, P5>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>, f3: PipeFn<P3, P4>, f4: PipeFn<P4, P5>): P5;
319
+ <P1, P2, P3, P4, P5, P6>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>, f3: PipeFn<P3, P4>, f4: PipeFn<P4, P5>, f5: PipeFn<P5, P6>): P6;
320
+ <P1, P2, P3, P4, P5, P6, P7>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>, f3: PipeFn<P3, P4>, f4: PipeFn<P4, P5>, f5: PipeFn<P5, P6>, f6: PipeFn<P6, P7>): P7;
321
+ <P1, P2, P3, P4, P5, P6, P7, P8>(f0: PipeFn<Result<T, E>, P1>, f1: PipeFn<P1, P2>, f2: PipeFn<P2, P3>, f3: PipeFn<P3, P4>, f4: PipeFn<P4, P5>, f5: PipeFn<P5, P6>, f6: PipeFn<P6, P7>, f7: PipeFn<P7, P8>): P8;
315
322
  };
316
323
  /**
317
324
  * Runs a side effect with the Ok or Err value and returns this same Result.
@@ -847,6 +854,11 @@ type GeneratorRequirements<Yield> = Yield extends {
847
854
  readonly _tag: "Service";
848
855
  readonly tag: infer Tag;
849
856
  } ? Tag : never;
857
+ type FunctionTask<Body extends (...args: never[]) => unknown> = ReturnType<Body> extends Generator<infer Yield, infer Return, infer _Next> ? ResultTask<Return, GeneratorError<Yield>, GeneratorRequirements<Yield>> : never;
858
+ type TaskSuccess<T> = T extends ResultTask<infer A, infer _E, infer _R> ? A : never;
859
+ type TaskError<T> = T extends ResultTask<infer _A, infer E, infer _R> ? E : never;
860
+ type TaskRequirements<T> = T extends ResultTask<infer _A, infer _E, infer R> ? R : never;
861
+ type TaskHandlers<E, H> = CatchTagHandlers<E, H, ResultTask<unknown, unknown, unknown>>;
850
862
  type ResultTaskRunArguments<R> = [WithoutScope<R>] extends [never] ? [options?: ResultTaskRunOptions<R>] : [options: ResultTaskRunOptions<R>];
851
863
  /** Returns true if a value is a nominal `ServiceTag`. */
852
864
  export declare const isServiceTag: (value: unknown) => value is ServiceTag<string, unknown>;
@@ -894,12 +906,16 @@ export declare class ResultTask<out A, out E = never, out R = never> extends Pip
894
906
  * scope requirement until `scoped` or a run boundary closes it; catchAll cannot erase them early.
895
907
  */
896
908
  static acquireRelease<A, E, R, ReleaseError = never, ReleaseR = never>(options: ResultTaskAcquireReleaseOptions<A, E, R, ReleaseError, ReleaseR>): ResultTask<A, E, R | WithoutScope<ReleaseR> | ResultTaskScope<ReleaseError | ScopeError<ReleaseR>>>;
909
+ /** Acquires a native disposable lazily and releases it in the owning scope. */
910
+ static acquireDisposable<A extends Disposable | AsyncDisposable, E, R>(acquire: ResultTask<A, E, R>): ResultTask<A, E, R | ResultTaskScope<never>>;
897
911
  /** Closes a child scope before continuing, adding its deferred release errors to E. */
898
912
  static scoped<A, E, R>(task: ResultTask<A, E, R>): ResultTask<A, E | ScopeError<R>, WithoutScope<R>>;
899
913
  /** Creates a service tag that can be requested with `yield*` inside `ResultTask.gen`. */
900
914
  static service<Service, const Identifier extends string>(identifier: Identifier): ServiceTag<Identifier, Service>;
901
915
  /** Infers the literal identifier after specifying the service contract. */
902
916
  static service<Service>(): <const Identifier extends string>(identifier: Identifier) => ServiceTag<Identifier, Service>;
917
+ /** Wraps a generator function without executing it until each task run. */
918
+ static fn<Body extends (...args: never[]) => Generator<unknown, unknown, never>>(body: Body): (this: ThisParameterType<Body>, ...args: Parameters<Body>) => FunctionTask<Body>;
903
919
  /** Builds a task from a generator and short-circuits on the first failed task. */
904
920
  static gen<Yield, Return, Next>(body: () => Generator<Yield, Return, Next>): ResultTask<Return, GeneratorError<Yield>, GeneratorRequirements<Yield>>;
905
921
  /** Maps the success value without executing the task. */
@@ -913,6 +929,10 @@ export declare class ResultTask<out A, out E = never, out R = never> extends Pip
913
929
  * removed E cannot escape through runExit; the original tree remains available on error.cause.
914
930
  */
915
931
  catchAll<B, E2, R2>(f: (error: E) => ResultTask<B, E2, R2>): ResultTask<A | B, E2, R | R2>;
932
+ /** Recovers a selected simple tagged failure, preserving other errors and scope requirements. */
933
+ catchTag<const Tag extends TagsOf<E>, B, E2, R2>(tag: Tag, handler: (error: ErrorForTag$1<E, Tag>) => ResultTask<B, E2, R2>): ResultTask<A | B, ExcludeTag<E, Tag> | E2, R | R2>;
934
+ /** Recovers any supplied tags; omitted tags remain in the error channel. */
935
+ catchTags<const H extends object>(handlers: H & TaskHandlers<E, H>): ResultTask<A | TaskSuccess<CatchTagHandlerResult<H>>, ExcludeTag<E, keyof H & string> | TaskError<CatchTagHandlerResult<H>>, R | TaskRequirements<CatchTagHandlerResult<H>>>;
916
936
  /** Maps the error value without executing the task. */
917
937
  mapError<E2>(f: (error: E) => E2): ResultTask<A, E2, R>;
918
938
  /**
@@ -969,6 +989,14 @@ export declare class ResultTask<out A, out E = never, out R = never> extends Pip
969
989
  /** Recovers from a typed failure using the canonical functional form or curried for `pipe`. */
970
990
  static catchAll<A, E, R, B, E2, R2>(task: ResultTask<A, E, R>, f: (error: E) => ResultTask<B, E2, R2>): ResultTask<A | B, E2, R | R2>;
971
991
  static catchAll<E, B, E2, R2>(f: (error: E) => ResultTask<B, E2, R2>): <A, R>(task: ResultTask<A, E, R>) => ResultTask<A | B, E2, R | R2>;
992
+ /** Recovers a tagged failure in direct or pipe-friendly form. */
993
+ static catchTag<A, E, R, const Tag extends TagsOf<E>, B, E2, R2>(task: ResultTask<A, E, R>, tag: Tag, handler: (error: ErrorForTag$1<E, Tag>) => ResultTask<B, E2, R2>): ResultTask<A | B, ExcludeTag<E, Tag> | E2, R | R2>;
994
+ static catchTag<const Tag extends string, Handled extends {
995
+ readonly _tag: Tag;
996
+ }, B, E2, R2>(tag: Tag, handler: (error: Handled) => ResultTask<B, E2, R2>): <A, E, R>(task: ResultTask<A, E, R> & (Tag extends TagsOf<E> ? (ErrorForTag$1<E, Tag> extends Handled ? unknown : never) : never)) => ResultTask<A | B, ExcludeTag<E, Tag> | E2, R | R2>;
997
+ /** Recovers supplied tags in direct or pipe-friendly form. */
998
+ static catchTags<A, E, R, const H extends object>(task: ResultTask<A, E, R>, handlers: H & TaskHandlers<E, H>): ResultTask<A | TaskSuccess<CatchTagHandlerResult<H>>, ExcludeTag<E, keyof H & string> | TaskError<CatchTagHandlerResult<H>>, R | TaskRequirements<CatchTagHandlerResult<H>>>;
999
+ static catchTags<const H extends object>(handlers: H): <A, E, R>(task: ResultTask<A, E, R> & (H extends TaskHandlers<E, H> ? unknown : never)) => ResultTask<A | TaskSuccess<CatchTagHandlerResult<H>>, ExcludeTag<E, keyof H & string> | TaskError<CatchTagHandlerResult<H>>, R | TaskRequirements<CatchTagHandlerResult<H>>>;
972
1000
  /** Executes a side effect on the success value using the canonical functional form or curried for `pipe`. */
973
1001
  static tap<A, E, R, E2 = never, R2 = never>(task: ResultTask<A, E, R>, f: (value: A) => ResultTask<unknown, E2, R2> | Promise<unknown> | void): ResultTask<A, E | E2, R | R2>;
974
1002
  static tap<A, E2 = never, R2 = never>(f: (value: A) => ResultTask<unknown, E2, R2> | Promise<unknown> | void): <E, R>(task: ResultTask<A, E, R>) => ResultTask<A, E | E2, R | R2>;
@@ -1990,4 +2018,30 @@ export declare const matchError: <ErrorType extends Error, ReturnType>(error: Er
1990
2018
  */
1991
2019
  export declare const matchErrorPartial: <ErrorType extends Error, ReturnType>(error: ErrorType, handlers: PartialMatchHandlers<ErrorType, ReturnType>, fallback: (error: ErrorType) => ReturnType) => ReturnType;
1992
2020
  //#endregion
2021
+ //#region src/function.d.ts
2022
+ /** Returns its input, preserving its type and reference. */
2023
+ export declare const identity: <A>(value: A) => A;
2024
+ /** Captures a value and returns it on every call without copying or recomputing it. */
2025
+ export declare const constant: <A>(value: A) => (() => A);
2026
+ /** Applies zero through eight synchronous transformations from left to right. */
2027
+ export declare function pipe<A>(value: A): A;
2028
+ export declare function pipe<A, B>(value: A, f0: PipeFn<A, B>): B;
2029
+ export declare function pipe<A, B, C>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>): C;
2030
+ export declare function pipe<A, B, C, D>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>): D;
2031
+ export declare function pipe<A, B, C, D, E>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>, f3: PipeFn<D, E>): E;
2032
+ export declare function pipe<A, B, C, D, E, F>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>, f3: PipeFn<D, E>, f4: PipeFn<E, F>): F;
2033
+ export declare function pipe<A, B, C, D, E, F, G>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>, f3: PipeFn<D, E>, f4: PipeFn<E, F>, f5: PipeFn<F, G>): G;
2034
+ export declare function pipe<A, B, C, D, E, F, G, H>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>, f3: PipeFn<D, E>, f4: PipeFn<E, F>, f5: PipeFn<F, G>, f6: PipeFn<G, H>): H;
2035
+ export declare function pipe<A, B, C, D, E, F, G, H, I>(value: A, f0: PipeFn<A, B>, f1: PipeFn<B, C>, f2: PipeFn<C, D>, f3: PipeFn<D, E>, f4: PipeFn<E, F>, f5: PipeFn<F, G>, f6: PipeFn<G, H>, f7: PipeFn<H, I>): I;
2036
+ /** Composes functions, preserving the arguments and receiver of the first function. */
2037
+ export declare function flow<First extends (...args: never[]) => unknown>(first: First): (this: ThisParameterType<First>, ...args: Parameters<First>) => ReturnType<First>;
2038
+ export declare function flow<First extends (...args: never[]) => unknown, B>(first: First, f1: PipeFn<ReturnType<First>, B>): (this: ThisParameterType<First>, ...args: Parameters<First>) => B;
2039
+ export declare function flow<First extends (...args: never[]) => unknown, B, C>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>): (this: ThisParameterType<First>, ...args: Parameters<First>) => C;
2040
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>): (this: ThisParameterType<First>, ...args: Parameters<First>) => D;
2041
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D, E>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>, f4: PipeFn<D, E>): (this: ThisParameterType<First>, ...args: Parameters<First>) => E;
2042
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D, E, F>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>, f4: PipeFn<D, E>, f5: PipeFn<E, F>): (this: ThisParameterType<First>, ...args: Parameters<First>) => F;
2043
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D, E, F, G>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>, f4: PipeFn<D, E>, f5: PipeFn<E, F>, f6: PipeFn<F, G>): (this: ThisParameterType<First>, ...args: Parameters<First>) => G;
2044
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D, E, F, G, H>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>, f4: PipeFn<D, E>, f5: PipeFn<E, F>, f6: PipeFn<F, G>, f7: PipeFn<G, H>): (this: ThisParameterType<First>, ...args: Parameters<First>) => H;
2045
+ export declare function flow<First extends (...args: never[]) => unknown, B, C, D, E, F, G, H, I>(first: First, f1: PipeFn<ReturnType<First>, B>, f2: PipeFn<B, C>, f3: PipeFn<C, D>, f4: PipeFn<D, E>, f5: PipeFn<E, F>, f6: PipeFn<F, G>, f7: PipeFn<G, H>, f8: PipeFn<H, I>): (this: ThisParameterType<First>, ...args: Parameters<First>) => I;
2046
+ //#endregion
1993
2047
  export { type Cause, type ErrResult, type Exit, type OkResult, type Redacted, type ResultAsyncAbortSignal, type ResultAsyncCallbackCleanup, type ResultAsyncCallbackContext, type ResultAsyncConcurrency, type ResultAsyncFromCallbackOptions, type ResultAsyncRaceHandle, type ResultAsyncRaceTask, type ResultAsyncResourceAcquire, type ResultAsyncResourceEffect, type ResultAsyncResourceRelease, type ResultAsyncResourceReleaseContext, type ResultAsyncResourceUse, type ResultAsyncRetryContext, type ResultAsyncRetryOptions, type ResultAsyncRetryOrElseOptions, type ResultAsyncRetryTask, type ResultAsyncTimeoutOptions, type ResultAsyncWithResourceOptions, type ResultOperations, type ResultTaskAcquireReleaseOptions, type ResultTaskMatchOptions, type ResultTaskRunOptions, type ResultTaskScope, type ResultTaskScopeOwner, type ResultTaskServiceResolver, type ResultTaskServices, type ResultTaskTryOptions, type ResultTaskTryPromiseOptions, type SafeTryAsyncOptions, type SafeTryOptions, type ServiceTag, type StrictResult, type StrictResultAsync, type TaggedEnum, type TaggedEnumFactory, type TaggedErrorClass, type TaggedErrorInstance, type TaggedErrorOptions, type TryResultAsyncOptions, type TryResultOptions, tryResult as default, tryResult as try, tryResultAsync as tryAsync };
package/dist/index.js CHANGED
@@ -20,10 +20,13 @@ const isAbortError = (value) => value instanceof AbortError || hasAbortName(valu
20
20
  //#endregion
21
21
  //#region src/pipe.ts
22
22
  const runPipe = (self, fns) => {
23
- const first = fns[0];
24
- if (!first) return self;
25
- let input = first(self);
26
- for (const fn of fns.slice(1)) input = fn(input);
23
+ let input = self;
24
+ const length = fns.length;
25
+ for (let index = 0; index < length; index += 1) {
26
+ const transform = fns[index];
27
+ if (transform === void 0) throw new TypeError("pipe requires transformation functions");
28
+ input = transform(input);
29
+ }
27
30
  return input;
28
31
  };
29
32
  var Pipeable = class {
@@ -42,24 +45,9 @@ const createResultAsync = (promise) => {
42
45
  return resultAsyncFactory(promise);
43
46
  };
44
47
  //#endregion
45
- //#region src/error.ts
46
- const defaultErrorConfig = { withStackTrace: false };
47
- const createResultarError = (message, result, config = defaultErrorConfig) => {
48
- const data = result.isOk() ? {
49
- type: "Ok",
50
- value: result.value
51
- } : {
52
- type: "Err",
53
- value: result.error
54
- };
55
- const maybeStack = config.withStackTrace ? (/* @__PURE__ */ new Error()).stack : void 0;
56
- const error = {
57
- name: "ResultarError",
58
- data,
59
- message
60
- };
61
- return maybeStack === void 0 ? error : Object.assign(error, { stack: maybeStack });
62
- };
48
+ //#region src/dual.ts
49
+ /** Internal dispatcher; callers retain explicit overloads and argument validation. */
50
+ const dual = (isDataFirst, body) => (...args) => isDataFirst(args) ? body(...args) : (self) => body(...[self, ...args]);
63
51
  //#endregion
64
52
  //#region src/tagged-match.ts
65
53
  const noTaggedHandlerMatch = Symbol("noTaggedHandlerMatch");
@@ -87,6 +75,25 @@ const matchTaggedOr = (error, handlers, fallback) => {
87
75
  return isTaggedHandlerMatch(handled) ? handled.value : fallback(error);
88
76
  };
89
77
  //#endregion
78
+ //#region src/error.ts
79
+ const defaultErrorConfig = { withStackTrace: false };
80
+ const createResultarError = (message, result, config = defaultErrorConfig) => {
81
+ const data = result.isOk() ? {
82
+ type: "Ok",
83
+ value: result.value
84
+ } : {
85
+ type: "Err",
86
+ value: result.error
87
+ };
88
+ const maybeStack = config.withStackTrace ? (/* @__PURE__ */ new Error()).stack : void 0;
89
+ const error = {
90
+ name: "ResultarError",
91
+ data,
92
+ message
93
+ };
94
+ return maybeStack === void 0 ? error : Object.assign(error, { stack: maybeStack });
95
+ };
96
+ //#endregion
90
97
  //#region src/result.ts
91
98
  const combineResultList = (resultList) => {
92
99
  const values = [];
@@ -1214,6 +1221,17 @@ var ResultTask = class ResultTask extends Pipeable {
1214
1221
  return acquired;
1215
1222
  });
1216
1223
  }
1224
+ /** Acquires a native disposable lazily and releases it in the owning scope. */
1225
+ static acquireDisposable(acquire) {
1226
+ return ResultTask.acquireRelease({
1227
+ acquire,
1228
+ release: (resource) => new ResultTask(async () => {
1229
+ if (Symbol.asyncDispose in resource) await resource[Symbol.asyncDispose]();
1230
+ else resource[Symbol.dispose]();
1231
+ return success(void 0);
1232
+ })
1233
+ });
1234
+ }
1217
1235
  /** Closes a child scope before continuing, adding its deferred release errors to E. */
1218
1236
  static scoped(task) {
1219
1237
  return new ResultTask(async (context) => {
@@ -1226,6 +1244,12 @@ var ResultTask = class ResultTask extends Pipeable {
1226
1244
  if (identifier === void 0) return (name) => new ServiceTagValue(name);
1227
1245
  return new ServiceTagValue(identifier);
1228
1246
  }
1247
+ /** Wraps a generator function without executing it until each task run. */
1248
+ static fn(body) {
1249
+ return function(...args) {
1250
+ return ResultTask.gen(() => body.call(this, ...args));
1251
+ };
1252
+ }
1229
1253
  /** Builds a task from a generator and short-circuits on the first failed task. */
1230
1254
  static gen(body) {
1231
1255
  return new ResultTask(async (context) => {
@@ -1286,6 +1310,17 @@ var ResultTask = class ResultTask extends Pipeable {
1286
1310
  f
1287
1311
  });
1288
1312
  }
1313
+ /** Recovers a selected simple tagged failure, preserving other errors and scope requirements. */
1314
+ catchTag(tag, handler) {
1315
+ return this.catchAll((error) => hasTag(error, tag) ? handler(error) : ResultTask.fail(error));
1316
+ }
1317
+ /** Recovers any supplied tags; omitted tags remain in the error channel. */
1318
+ catchTags(handlers) {
1319
+ return this.catchAll((error) => {
1320
+ const handled = callTaggedHandler(error, handlers);
1321
+ return isTaggedHandlerMatch(handled) ? handled.value : ResultTask.fail(error);
1322
+ });
1323
+ }
1289
1324
  /** Maps the error value without executing the task. */
1290
1325
  mapError(f) {
1291
1326
  return new ResultTask({
@@ -1465,39 +1500,54 @@ var ResultTask = class ResultTask extends Pipeable {
1465
1500
  throw new TypeError("ResultTask.provideServices requires a services object");
1466
1501
  }
1467
1502
  static map(taskOrF, f) {
1468
- if (typeof taskOrF === "function") return (task) => task.map(taskOrF);
1469
- if (f === void 0) throw new TypeError("ResultTask.map requires a mapping function");
1470
- return taskOrF.map(f);
1503
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1504
+ if (callback === void 0) throw new TypeError("ResultTask.map requires a mapping function");
1505
+ return task.map(callback);
1506
+ })(taskOrF, f);
1471
1507
  }
1472
1508
  static mapError(taskOrF, f) {
1473
- if (typeof taskOrF === "function") return (task) => task.mapError(taskOrF);
1474
- if (f === void 0) throw new TypeError("ResultTask.mapError requires an error mapping function");
1475
- return taskOrF.mapError(f);
1509
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1510
+ if (callback === void 0) throw new TypeError("ResultTask.mapError requires an error mapping function");
1511
+ return task.mapError(callback);
1512
+ })(taskOrF, f);
1476
1513
  }
1477
1514
  static flatMap(taskOrF, f) {
1478
- if (typeof taskOrF === "function") return (task) => task.flatMap(taskOrF);
1479
- if (f === void 0) throw new TypeError("ResultTask.flatMap requires a continuation function");
1480
- return taskOrF.flatMap(f);
1515
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1516
+ if (callback === void 0) throw new TypeError("ResultTask.flatMap requires a continuation function");
1517
+ return task.flatMap(callback);
1518
+ })(taskOrF, f);
1481
1519
  }
1482
1520
  static andThen(taskOrF, f) {
1483
- if (typeof taskOrF === "function") return (task) => task.flatMap(taskOrF);
1484
- if (f === void 0) throw new TypeError("ResultTask.andThen requires a continuation function");
1485
- return taskOrF.flatMap(f);
1521
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1522
+ if (callback === void 0) throw new TypeError("ResultTask.andThen requires a continuation function");
1523
+ return task.flatMap(callback);
1524
+ })(taskOrF, f);
1486
1525
  }
1487
1526
  static catchAll(taskOrF, f) {
1488
- if (typeof taskOrF === "function") return (task) => task.catchAll(taskOrF);
1489
- if (f === void 0) throw new TypeError("ResultTask.catchAll requires a recovery function");
1490
- return taskOrF.catchAll(f);
1527
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1528
+ if (callback === void 0) throw new TypeError("ResultTask.catchAll requires a recovery function");
1529
+ return task.catchAll(callback);
1530
+ })(taskOrF, f);
1531
+ }
1532
+ static catchTag(...args) {
1533
+ if (typeof args[0] === "string") return (task) => ResultTask.catchTag(task, args[0], args[1]);
1534
+ return args[0].catchTag(args[1], args[2]);
1535
+ }
1536
+ static catchTags(...args) {
1537
+ if (args.length === 1) return (task) => task.catchTags(args[0]);
1538
+ return args[0].catchTags(args[1]);
1491
1539
  }
1492
1540
  static tap(taskOrF, f) {
1493
- if (typeof taskOrF === "function") return (task) => task.tap(taskOrF);
1494
- if (f === void 0) throw new TypeError("ResultTask.tap requires a side-effect function");
1495
- return taskOrF.tap(f);
1541
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1542
+ if (callback === void 0) throw new TypeError("ResultTask.tap requires a side-effect function");
1543
+ return task.tap(callback);
1544
+ })(taskOrF, f);
1496
1545
  }
1497
1546
  static tapError(taskOrF, f) {
1498
- if (typeof taskOrF === "function") return (task) => task.tapError(taskOrF);
1499
- if (f === void 0) throw new TypeError("ResultTask.tapError requires an error side-effect function");
1500
- return taskOrF.tapError(f);
1547
+ return dual((args) => typeof args[0] !== "function", (task, callback) => {
1548
+ if (callback === void 0) throw new TypeError("ResultTask.tapError requires an error side-effect function");
1549
+ return task.tapError(callback);
1550
+ })(taskOrF, f);
1501
1551
  }
1502
1552
  static as(taskOrValue, value) {
1503
1553
  if (taskOrValue instanceof ResultTask) {
@@ -2928,4 +2978,18 @@ const runPromise = (result) => result.unwrapOrThrow();
2928
2978
  /** Compatibility alias. Prefer `tryResultAsync` in new code. */
2929
2979
  const tryCatchAsync = tryResultAsync;
2930
2980
  //#endregion
2931
- export { AbortError, DisposableResult, DisposableResultAsync, MissingServiceError, Result, ResultAsync, ResultTask, ResultTaskCauseError, ResultTaskTypeId, ResultTaskYieldTypeId, ServiceTagTypeId, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromCallback, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isAbortError, isError, isRedacted, isResultTask, isServiceTag, matchError, matchErrorPartial, ok, okAsync, redact, revealRedacted, runPromise, runSync, safeTry, serviceTag, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
2981
+ //#region src/function.ts
2982
+ /** Returns its input, preserving its type and reference. */
2983
+ const identity = (value) => value;
2984
+ /** Captures a value and returns it on every call without copying or recomputing it. */
2985
+ const constant = (value) => () => value;
2986
+ function pipe(value, ...functions) {
2987
+ return runPipe(value, functions);
2988
+ }
2989
+ function flow(first, ...functions) {
2990
+ return function(...args) {
2991
+ return runPipe(first.apply(this, args), functions);
2992
+ };
2993
+ }
2994
+ //#endregion
2995
+ export { AbortError, DisposableResult, DisposableResultAsync, MissingServiceError, Result, ResultAsync, ResultTask, ResultTaskCauseError, ResultTaskTypeId, ResultTaskYieldTypeId, ServiceTagTypeId, constant, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, flow, fromCallback, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, identity, isAbortError, isError, isRedacted, isResultTask, isServiceTag, matchError, matchErrorPartial, ok, okAsync, pipe, redact, revealRedacted, runPromise, runSync, safeTry, serviceTag, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
package/dist/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # Resultar 3.9.1
1
+ # Resultar 3.10.0
2
2
 
3
3
  Documentation bundled with this package version. Installed declarations remain authoritative.
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resultar",
3
- "version": "3.9.1",
3
+ "version": "3.10.0",
4
4
  "description": "Result pattern for TypeScript",
5
5
  "keywords": [
6
6
  "error-handling",
@@ -43,14 +43,14 @@
43
43
  "devDependencies": {
44
44
  "@stryker-mutator/core": "10.0.0",
45
45
  "@stryker-mutator/vitest-runner": "10.0.0",
46
- "@types/node": "26.5.1",
47
- "@vitest/coverage-v8": "4.1.11",
48
- "@vitest/ui": "4.1.11",
49
- "fallow": "3.25.0",
50
- "tsx": "4.23.13",
46
+ "@types/node": "26.6.4",
47
+ "@vitest/coverage-v8": "5.0.3",
48
+ "@vitest/ui": "5.0.3",
49
+ "fallow": "3.32.0",
50
+ "tsx": "4.23.15",
51
51
  "typescript": "7.0.2",
52
- "vite-plus": "0.3.2",
53
- "vitest": "4.1.11"
52
+ "vite-plus": "1.1.0",
53
+ "vitest": "5.0.3"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "typescript": ">=7.0.0"