unthrown 5.9.0 → 5.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/dist/index.d.cts CHANGED
@@ -63,26 +63,53 @@ type UniversalPattern = PatternMatcher<unknown> & {
63
63
  * @internal
64
64
  */
65
65
  type MatchedOf<Pt> = Pt extends PatternMatcher<infer M> ? M : Pt extends object ? { [K in keyof Pt]: MatchedOf<Pt[K]>; } : Pt;
66
+ /**
67
+ * Reject a **keyless object pattern** (`{}`) where it is written.
68
+ *
69
+ * @remarks
70
+ * At runtime an empty object pattern matches *every* object (no key to
71
+ * disagree on), and at the type level `MatchedOf<{}>` is `{}`, so `Exclude`
72
+ * removes every non-nullish case — an unflagged catch-all, invisible to
73
+ * `@unthrown/oxlint`'s `no-catch-all-pattern`. Mapping it to a branded string
74
+ * makes the argument unassignable, with the reason in the error. `null` /
75
+ * `undefined` (also keyless) stay legal literal patterns, and every `P.*`
76
+ * pattern carries a symbol key. The check recurses into structural patterns:
77
+ * a nested `{ data: {} }` is rejected too, since the type level treats `{}` as
78
+ * matching a primitive `data` while the runtime rejects a non-object there — a
79
+ * match that compiles as exhaustive and then throws.
80
+ *
81
+ * @internal
82
+ */
83
+ type NoEmptyPattern<Pt> = [Pt] extends [null | undefined] ? Pt : [Pt] extends [PatternMatcher<unknown>] ? Pt : [keyof Pt] extends [never] ? "unthrown: an empty object pattern `{}` matches every object — name the case (a key to match on), or use P._ deliberately" : Pt extends object ? { [K in keyof Pt]: NoEmptyPattern<Pt[K]>; } : Pt;
66
84
  /**
67
85
  * The diagnostic type of `.exhaustive` on a builder that has NOT covered every
68
86
  * case: not callable (so it fails the `ExhaustiveMatch` constraint at the call
69
87
  * site), and it names the remaining cases so the error reads as a to-do list.
88
+ * The alias NAME is the diagnostic: the compiler prints "Type
89
+ * 'UnhandledCases<NotFound>' provides no match for the signature …", so it
90
+ * says what is wrong and lists what is left (it was `NonExhaustive<…>`).
70
91
  *
71
92
  * @internal
72
93
  */
73
- type NonExhaustive<Remaining> = {
94
+ type UnhandledCases<Remaining> = {
74
95
  readonly "unthrown: this match is not exhaustive — add a `.with(…)` for the remaining cases": Remaining;
75
96
  };
76
97
  /**
77
98
  * The "no output type declared" sentinel for a builder's `Declared` parameter.
78
- * A `unique symbol` so no user type can collide with it. Declaration-only —
79
- * `tsc` emits it into the `.d.ts` without it needing to be exported.
99
+ *
100
+ * @remarks
101
+ * A string-keyed brand, **not** a `unique symbol`: every unpinned builder's
102
+ * type carries it (`Matcher<E, R, O, Unset>`), so a consumer's
103
+ * `export const m = match(x).with(…)` has to be able to print it under
104
+ * declaration emit. A non-exported `unique symbol` cannot be named there
105
+ * (TS2527), while an object literal type can always be written out. No user
106
+ * type collides with it in practice — the key is the explanation.
80
107
  *
81
108
  * @internal
82
109
  */
83
- declare const UNSET: unique symbol;
84
- /** @internal */
85
- type Unset = typeof UNSET;
110
+ type Unset = {
111
+ readonly "unthrown: no output type declared — call .returnType<R>() to pin one": true;
112
+ };
86
113
  /**
87
114
  * A branch handler's return position: free inference (`O2`) while the builder
88
115
  * is unpinned — today's behaviour, unchanged — or the declared type once
@@ -148,7 +175,7 @@ type Matcher<E, Remaining, O, Declared = Unset> = {
148
175
  * `Remaining`, so cases already handled by earlier arms are excluded); the
149
176
  * matched cases are subtracted from `Remaining`.
150
177
  */
151
- with<const Pts extends readonly [unknown, ...unknown[]], O2>(...args: [...patterns: Pts, handler: (value: Extract<Remaining, MatchedOf<Pts[number]>>) => BranchReturn<Declared, O2>]): Matcher<E, Exclude<Remaining, MatchedOf<Pts[number]>>, O | O2, Declared>;
178
+ with<const Pts extends readonly [unknown, ...unknown[]], O2>(...args: [...patterns: { [I in keyof Pts]: NoEmptyPattern<Pts[I]>; }, handler: (value: Extract<Remaining, MatchedOf<Pts[number]>>) => BranchReturn<Declared, O2>]): Matcher<E, Exclude<Remaining, MatchedOf<Pts[number]>>, O | O2, Declared>;
152
179
  /**
153
180
  * Declare the match's output type up front: every subsequent branch handler
154
181
  * is checked against `R`, and the match evaluates to `R` instead of the
@@ -183,7 +210,7 @@ type Matcher<E, Remaining, O, Declared = Unset> = {
183
210
  * naming the remaining cases, and the builder fails the `ExhaustiveMatch`
184
211
  * constraint at the combinator call site.
185
212
  */
186
- exhaustive: [Remaining] extends [never] ? () => PinnedOut<Declared, O> : NonExhaustive<Remaining>;
213
+ exhaustive: [Remaining] extends [never] ? () => PinnedOut<Declared, O> : UnhandledCases<Remaining>;
187
214
  /**
188
215
  * Execute the match (the combinators call this; it runs `.exhaustive()`).
189
216
  * A value with no matching arm throws {@link NonExhaustiveError} —
@@ -200,8 +227,25 @@ type Matcher<E, Remaining, O, Declared = Unset> = {
200
227
  * is a bug).
201
228
  *
202
229
  * @category Errors
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * import { match, NonExhaustiveError } from "unthrown";
234
+ *
235
+ * // A value typed "a" | "b" that is really "c" (a cast, a raw-JS caller):
236
+ * const rogue = "c" as "a" | "b";
237
+ * try {
238
+ * match(rogue)
239
+ * .with("a", () => 1)
240
+ * .with("b", () => 2)
241
+ * .exhaustive();
242
+ * } catch (error) {
243
+ * error instanceof NonExhaustiveError; // => true
244
+ * (error as NonExhaustiveError).input; // => "c"
245
+ * }
246
+ * ```
203
247
  */
204
- declare class NonExhaustiveError extends Error {
248
+ export declare class NonExhaustiveError extends Error {
205
249
  /** The value no arm matched. */
206
250
  readonly input: unknown;
207
251
  constructor(input: unknown);
@@ -220,8 +264,26 @@ declare class NonExhaustiveError extends Error {
220
264
  * {@link P}).
221
265
  *
222
266
  * @category Constructors
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * import { match, type Result } from "unthrown";
271
+ *
272
+ * // Matching a whole Result natively — every variant named, `.exhaustive()` last:
273
+ * declare const r: Result<number, "odd" | "negative">;
274
+ * const label = match(r)
275
+ * .with({ tag: "Ok" }, (ok) => `got ${ok.value}`)
276
+ * .with({ tag: "Err" }, (err) => `failed: ${err.error}`)
277
+ * .with({ tag: "Defect" }, () => "bug")
278
+ * .exhaustive();
279
+ *
280
+ * // Inside a combinator, return the un-terminated builder — it runs `.exhaustive()`:
281
+ * const reason = r.mapErrCases((matcher) =>
282
+ * matcher.with("odd", () => "not even" as const).with("negative", () => "below zero" as const),
283
+ * ); // Result<number, "not even" | "below zero">
284
+ * ```
223
285
  */
224
- declare function match<const E>(value: E): Matcher<E, E, never>;
286
+ export declare function match<const E>(value: E): Matcher<E, E, never>;
225
287
  /**
226
288
  * The pattern namespace (unthrown's own; the former ts-pattern `P`):
227
289
  *
@@ -246,1932 +308,2014 @@ declare function match<const E>(value: E): Matcher<E, E, never>;
246
308
  * (`.with(P.tag("A"), P.tag("B"), handler)`).
247
309
  * - `P.instanceOf(Cls)` — an `instanceof` check, narrowing to the class
248
310
  * instance type (for union members that are not tagged, e.g. a third-party
249
- * error class).
311
+ * error class). **Exhaustiveness here is structural, the check is not:**
312
+ * two classes with the same shape (`class A extends Error {}`,
313
+ * `class B extends Error {}`) are one type to the compiler, so a match
314
+ * naming only `A` compiles as exhaustive while a `B` fails `instanceof A`
315
+ * at runtime and becomes a `Defect`. Give each class a distinguishing field
316
+ * (a `readonly kind = "A"` literal) or use `TaggedError`, and the missing
317
+ * arm is a compile error again.
250
318
  * - `P.when(guard)` — an arbitrary type-guard predicate. Also the way to match
251
319
  * a primitive shape (`P.when((v): v is string => typeof v === "string")`),
252
320
  * and grouping patterns under one handler is what a `.with(a, b, handler)`
253
321
  * arm already does.
254
322
  *
255
323
  * @category Constructors
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * import { P, TaggedError, type Result } from "unthrown";
328
+ *
329
+ * class NotFound extends TaggedError("NotFound")<{ id: string }> {}
330
+ * class Conflict extends TaggedError("Conflict") {}
331
+ * class VendorTimeout extends Error {
332
+ * readonly afterMs = 30_000;
333
+ * }
334
+ *
335
+ * declare const r: Result<string, NotFound | Conflict | VendorTimeout | "rate_limited">;
336
+ * const status = r.match({
337
+ * ok: () => 200,
338
+ * errCases: (matcher) =>
339
+ * matcher
340
+ * .with(P.tag("NotFound"), () => 404) // a TaggedError, narrowed with its payload
341
+ * .with(P.tag("Conflict"), () => 409)
342
+ * .with(P.instanceOf(VendorTimeout), (e) => (e.afterMs > 10_000 ? 504 : 503))
343
+ * .with(
344
+ * P.when((v): v is "rate_limited" => v === "rate_limited"),
345
+ * () => 429,
346
+ * ),
347
+ * defect: () => 500,
348
+ * });
349
+ * ```
256
350
  */
257
- declare const P: Readonly<{
351
+ export declare const P: Readonly<{
258
352
  _: UniversalPattern;
259
353
  tag: <const Tag extends string>(value: Tag) => {
260
- _tag: Tag;
354
+ readonly _tag: Tag;
261
355
  };
262
356
  instanceOf: <C extends abstract new (...args: never[]) => unknown>(cls: C) => PatternMatcher<InstanceType<C>>;
263
357
  when: <G>(guard: (value: unknown) => value is G) => PatternMatcher<G>;
264
358
  }>;
265
359
  //#endregion
266
- //#region src/types.d.ts
360
+ //#region src/core.d.ts
267
361
  /**
268
- * Flatten an intersection into a single object literal so accumulated `bind` /
269
- * `let` scopes display cleanly (`{ a; b }` rather than `{ a } & { b }`).
362
+ * Thrown by a {@link Result}'s `get` / `getErr` when the assertion is
363
+ * wrong on a *modeled* result — `get()` on an `Err`, or `getErr()` on an
364
+ * `Ok`.
270
365
  *
271
- * @internal
272
- */
273
- type Prettify<T> = { [K in keyof T]: T[K]; } & {};
274
- /**
275
- * The scope produced by a `bind` / `let` step: `T` with `K` added (as a readonly
276
- * property of type `U`). `Omit<T, K>` first drops any existing `K`, so re-binding
277
- * a name **overwrites** it — matching the runtime spread — rather than producing
278
- * an unsound `T[K] & U` intersection.
366
+ * @remarks
367
+ * The offending value is exposed two ways: the typed {@link GetError.error}
368
+ * property for programmatic access, and the standard `Error.cause` for the
369
+ * runtime and devtools to chain — when `E` is an `Error` (e.g. a `TaggedError`)
370
+ * its original stack is printed under "caused by".
279
371
  *
280
- * @internal
372
+ * A `Defect` is never wrapped in a `GetError`: its original cause is
373
+ * re-thrown (with its original stack) instead.
374
+ *
375
+ * `get()` and `getErr()` are type-gated (`this: Result<T, never>` /
376
+ * `Result<never, E>`), so the wrong-variant branch that throws this is
377
+ * unreachable through well-typed code — it remains only as a defensive guard
378
+ * against unsound runtime misuse (e.g. an `as` cast past the gate).
379
+ *
380
+ * @typeParam E - the type of the {@link GetError.error} it carries.
381
+ *
382
+ * @category Errors
281
383
  */
282
- type Bound<T, K extends string, U> = Prettify<Omit<T, K> & { readonly [P in K]: U; }>;
384
+ export declare class GetError<E = unknown> extends Error {
385
+ /**
386
+ * The offending value: the `Err` error for `get()`, or the `Ok` value for
387
+ * `getErr()`.
388
+ */
389
+ readonly error: E;
390
+ constructor(error: E);
391
+ }
283
392
  /**
284
- * Compile-time rejection of a thenable callback result — the type-level
285
- * enforcement of "combinator callbacks are synchronous" (see the
286
- * {@link AsyncResult} remarks).
393
+ * Type guard: is `x` a {@link Result} (any of `Ok` / `Err` / `Defect`)?
287
394
  *
288
395
  * @remarks
289
- * Resolves to `unknown` (a no-op in an intersection) for any non-thenable `R`,
290
- * and to an explanatory string-literal type when `R` is a `PromiseLike` — so an
291
- * `async` callback fails to compile with the explanation in the error. Without
292
- * this, `async () => …` would be assignable to `() => void`, and its rejection
293
- * would escape the pipeline as an unhandled rejection instead of a `Defect`.
294
- * Lift async work with {@link fromPromise} and compose it with `flatMap`.
396
+ * Unlike {@link isOk} / {@link isErr} / {@link isDefect}, which narrow a value
397
+ * already known to be a `Result`, this narrows from `unknown` — useful at an
398
+ * untyped boundary. It checks the value carries the `Result` prototype
399
+ * (`instanceof` first, falling back to the `Symbol.for("unthrown.Result")`
400
+ * brand the prototype carries — so a `Result` built by **another copy** of
401
+ * unthrown, e.g. the CJS and ESM builds loaded side by side, is still
402
+ * recognised). A look-alike plain object (`{ tag: "Ok" }`) carries neither and
403
+ * is **not** matched; nor is a forgery built on the real prototype whose `tag` or
404
+ * payload is a getter (both must be own data properties). An `AsyncResult` is not a `Result` and returns `false`.
295
405
  *
296
- * Spelled with `Extract`, not `[R] extends [PromiseLike<…>]`, so the ban also
297
- * fires when only SOME arms of a union return are thenable — a *sometimes*-async
298
- * callback (`flag ? 1 : work()`) is still an unawaited effect whose rejection
299
- * the pipeline never sees. The tuple-wrapped form is false for a partial union
300
- * and let exactly that through. This is the same reasoning `fromPromise`'s
301
- * async-qualify guard already used.
406
+ * @returns `true` when `x` is a `Result` produced by this library.
302
407
  *
303
- * @typeParam R - the callback's inferred return type.
304
- * @category Types
408
+ * @example
409
+ * ```ts
410
+ * import { isResult, Ok, P } from "unthrown";
411
+ *
412
+ * isResult(Ok(1)); // => true
413
+ * isResult({ tag: "Ok" }); // => false (look-alike, wrong prototype)
414
+ * isResult(Ok(1).toAsync()); // => false (an AsyncResult is not a Result)
415
+ *
416
+ * const x: unknown = Ok(1);
417
+ * if (isResult(x))
418
+ * // `E` is `unknown` here — an untyped boundary has no cases to enumerate,
419
+ * // so the `P._` escape hatch is the only arm that can terminate the match:
420
+ * // oxlint-disable-next-line unthrown/no-catch-all-pattern -- untyped boundary: `E` is `unknown`
421
+ * x.match({
422
+ * ok: () => 1,
423
+ * errCases: (m) => m.with(P._, () => 0),
424
+ * defect: () => -1,
425
+ * });
426
+ * ```
427
+ *
428
+ * @category Guards
305
429
  */
306
- type NotThenable<R> = [Extract<R, PromiseLike<unknown>>] extends [never] ? unknown : "unthrown: combinator callbacks are synchronous — lift async work with fromPromise and compose with flatMap";
430
+ export declare function isResult(x: unknown): x is Result<unknown, unknown>;
431
+ //#endregion
432
+ //#region src/do.d.ts
307
433
  /**
308
- * The built-in match builder over an error union `E`, as produced by
309
- * `match(error)`. This is what an error combinator's callback receives — chain
310
- * `.with(pattern, handler)` on it; the combinator itself calls `.exhaustive()`,
311
- * so the callback returns the **un-terminated** builder.
434
+ * Start a do-notation chain with an empty object scope, grown step by step with
435
+ * `bind` (for `Result`-returning steps) and `let` (for pure values).
312
436
  *
313
437
  * @remarks
314
- * Named via `ReturnType<typeof match<E>>` (i.e. `Matcher<E, E, never>`),
315
- * keeping this alias stable however the builder evolves.
438
+ * Capitalised because `do` is a reserved word. Each step receives the scope
439
+ * accumulated so far; the error types union across `bind`s, and a throw in any
440
+ * step becomes a `Defect`. To go asynchronous, lift the chain with `toAsync()`
441
+ * (then a `bind` may return an `AsyncResult`).
316
442
  *
317
- * @typeParam E - the error union being matched.
318
- * @category Types
319
- */
320
- type ErrMatcher<E> = ReturnType<typeof match<E>>;
321
- /**
322
- * The shape an error-combinator callback must return: an **exhaustive**
323
- * match builder. `exhaustive` is required to be *callable* — on a builder
324
- * that hasn't covered every case the matcher types it as a branded diagnostic
325
- * (not a function), so a non-exhaustive chain fails to satisfy this and errors
326
- * at the call site. `run` carries the output type.
443
+ * @example
444
+ * ```ts
445
+ * import { Do, Ok } from "unthrown";
327
446
  *
328
- * @typeParam O - the union of the branch return types (the builder's output).
329
- * @internal
447
+ * const result = Do()
448
+ * .bind("user", () => findUser(id)) // Result<User, NotFound>
449
+ * .bind("org", ({ user }) => findOrg(user.orgId)) // Result<Org, NotFound>
450
+ * .let("label", ({ user, org }) => `${user.name} @ ${org.name}`)
451
+ * .map(({ user, org, label }) => render(user, org, label));
452
+ * // Result<View, NotFound>
453
+ * ```
454
+ *
455
+ * @example
456
+ * ```ts
457
+ * import { Do, Ok, Err } from "unthrown";
458
+ *
459
+ * // Ok path — the scope accumulates:
460
+ * Do()
461
+ * .bind("a", () => Ok(2))
462
+ * .let("b", ({ a }) => a * 10)
463
+ * .map(({ a, b }) => a + b); // => Ok(22)
464
+ *
465
+ * // Err path — the first Err short-circuits the rest:
466
+ * Do()
467
+ * .bind("a", () => Err("boom"))
468
+ * .let("b", ({ a }) => a); // => Err("boom")
469
+ * ```
470
+ *
471
+ * @category Do-notation
330
472
  */
331
- type ExhaustiveMatch<O> = {
332
- exhaustive: (...args: never[]) => unknown;
333
- run: () => O;
334
- };
473
+ export declare function Do(): Result<{}, never>;
335
474
  /**
336
- * The output of an `ExhaustiveMatch` — the union of its branch returns.
475
+ * Start an **asynchronous** do-notation chain with an empty object scope — the
476
+ * pre-lifted form of {@link Do}, sparing you `Do().toAsync()`.
337
477
  *
338
- * @internal
478
+ * @remarks
479
+ * From here a `bind` may return a `Result` **or** an `AsyncResult`; the scope
480
+ * accumulates exactly as in a sync {@link Do} chain, and a throw in any step
481
+ * becomes a `Defect`. Named with the `Async` suffix the async free functions
482
+ * carry (`OkAsync`, `allAsync`); the {@link AsyncResult} companion aliases it as
483
+ * `AsyncResult.Do` (the namespace already says "async", so the suffix drops).
484
+ *
485
+ * @example
486
+ * ```ts
487
+ * import { DoAsync, Ok } from "unthrown";
488
+ *
489
+ * const result = await DoAsync()
490
+ * .bind("user", () => findUser(id)) // AsyncResult<User, NotFound>
491
+ * .bind("plan", ({ user }) => Ok(user.plan)) // a sync Result is accepted too
492
+ * .let("label", ({ user, plan }) => `${user.name} on ${plan}`);
493
+ * // Result<{ user: User; plan: Plan; label: string }, NotFound>
494
+ * ```
495
+ *
496
+ * @category Do-notation
339
497
  */
340
- type MatchOut<M> = M extends ExhaustiveMatch<infer O> ? O : never;
498
+ export declare function DoAsync(): AsyncResult<{}, never>;
499
+ //#endregion
500
+ //#region src/interop.d.ts
341
501
  /**
342
- * The outgoing modeled-error type a transforming match produces: the builder's
343
- * output with the `Defect` arm **subtracted** (`Exclude<O, Defect>`, the same
344
- * inference as the boundary `qualify`, Thesis #3). A branch that returns
345
- * `defect(cause)` therefore contributes nothing to the modeled channel.
502
+ * Bridge a nullable value into a {@link Result}: absence becomes a **modeled**
503
+ * `Err`. The sanctioned alternative to an `Option` type.
346
504
  *
347
- * @internal
505
+ * @remarks
506
+ * `null` and `undefined` map to `Err(onAbsent())`; any other value (including
507
+ * falsy ones like `0`, `""`, `false`) maps to `Ok`.
508
+ *
509
+ * @typeParam T - the (nullable) value type.
510
+ * @typeParam E - the error produced when the value is absent.
511
+ * @param value - the possibly-absent value.
512
+ * @param onAbsent - lazily produces the error for the absent case.
513
+ *
514
+ * @category Interop
515
+ *
516
+ * @example
517
+ * ```ts
518
+ * import { fromNullable } from "unthrown";
519
+ *
520
+ * const map = new Map([["a", 1]]);
521
+ * fromNullable(map.get("a"), () => "absent").getOr(0); // => 1
522
+ * fromNullable(map.get("z"), () => "absent"); // => Err("absent")
523
+ * fromNullable(0, () => "absent").getOr(-1); // => 0 (falsy but present)
524
+ * ```
348
525
  */
349
- type MatchErrOut<M> = Exclude<MatchOut<M>, Defect>;
526
+ export declare function fromNullable<T, E>(value: T | null | undefined, onAbsent: () => E): Result<NonNullable<T>, E>;
350
527
  /**
351
- * The fluent method surface every {@link Result} variant carries — the
352
- * combinators (`map`, `flatMap`, `mapErrCases`, `match`, `get`, …), documented one
353
- * per entry below. Factored out so the three variants ({@link OkView},
354
- * {@link ErrView}, {@link DefectView}) can each intersect it; {@link AsyncResult}
355
- * mirrors this surface with async signatures.
528
+ * Wrap a throwing synchronous function so it returns a {@link Result} instead of
529
+ * throwing.
356
530
  *
357
531
  * @remarks
358
- * This type exists to **document** the surface and to power narrowing — not to be
359
- * authored against. You obtain it by holding a `Result` (or `AsyncResult`), never
360
- * by implementing your own `Result`-like; treat it as read-only reference.
532
+ * `qualify` **must** triage every thrown cause into a modeled error `E` or a
533
+ * `Defect` (via the injected `defect` helper, its second argument) — there is no
534
+ * path that leaves `unknown` in `E`. A throw inside `qualify` itself is treated
535
+ * as a `Defect`. `qualify` is **synchronous**: an `async` qualify is rejected at
536
+ * compile time ({@link NotThenable}) — its `Promise` would land in `E` un-triaged
537
+ * — and a thenable slipped past the types at runtime becomes a `Defect` (never
538
+ * an `Err(Promise)`), its orphaned rejection silenced.
361
539
  *
362
- * @typeParam T - the success value type.
363
- * @typeParam E - the modeled error type.
364
- * @category Methods
365
- */
366
- type ResultMethods<out T, out E> = {
367
- /**
368
- * Transform the success value with `f`.
369
- *
370
- * Runs `f` only on `Ok`; `Err` and `Defect` pass through untouched. If `f`
371
- * throws, the thrown value is captured as a `Defect`.
372
- *
373
- * An async callback is rejected at compile time ({@link NotThenable}).
374
- *
375
- * @typeParam U - the mapped success type.
376
- * @param f - maps the current success value to a new one.
377
- */
378
- map<U>(f: (value: T) => U & NotThenable<U>): Result$1<U, E>;
379
- /**
380
- * Sequence a dependent, `Result`-returning step (monadic bind).
381
- *
382
- * Runs `f` only on `Ok`; `Err` and `Defect` pass through. The error channels
383
- * combine, widening to `E | E2`. If `f` throws, the throw becomes a `Defect`.
384
- *
385
- * @typeParam U - the success type of the next step.
386
- * @typeParam E2 - the error type the next step may introduce.
387
- * @param f - produces the next `Result` from the current success value.
388
- */
389
- flatMap<U, E2>(f: (value: T) => Result$1<U, E2>): Result$1<U, E | E2>;
390
- /**
391
- * Run a side effect on the success value and pass the `Result` through
392
- * unchanged.
393
- *
394
- * Runs only on `Ok`. If `f` throws, the throw becomes a `Defect`. An async
395
- * callback is rejected at compile time ({@link NotThenable}).
396
- *
397
- * @remarks
398
- * `f`'s return value is **ignored** — a `Result` returned by the effect
399
- * compiles but is discarded, `Err` and all. If the effect can fail, sequence
400
- * it instead of tapping it: a `Result`-returning effect goes in
401
- * {@link ResultMethods.flatTap | flatTap}; an `AsyncResult`-returning effect
402
- * cannot be sequenced from the sync surface — lift the chain with
403
- * {@link ResultMethods.toAsync | toAsync} and use the async
404
- * {@link AsyncResultMethods.flatTap | flatTap} (which accepts both).
405
- *
406
- * @param f - the side effect (its return value is ignored).
407
- */
408
- tap<R>(f: (value: T) => R & NotThenable<R>): Result$1<T, E>;
409
- /**
410
- * Run a **failable** side effect on the success value, keeping the original
411
- * value but threading the effect's error.
412
- *
413
- * @remarks
414
- * This is to {@link ResultMethods.tap | tap} what
415
- * {@link ResultMethods.flatMap | flatMap} is to {@link ResultMethods.map | map}:
416
- * `f` returns a `Result`, but its **success value is discarded** — on success
417
- * the original value flows through (`Result<T, E | E2>`), while an `Err` (or
418
- * `Defect`) from `f` short-circuits. Runs only on `Ok`; `Err` and `Defect` pass
419
- * through. If `f` throws, the throw becomes a `Defect`. Use it for a validation
420
- * or write whose _result_ matters but whose _value_ you don't need.
421
- *
422
- * @typeParam E2 - the error type the effect may introduce.
423
- * @param f - the failable side effect; its `Ok` value is ignored.
424
- */
425
- flatTap<E2>(f: (value: T) => Result$1<unknown, E2>): Result$1<T, E | E2>;
426
- /**
427
- * Do-notation: run `f` for a `Result` and **bind its value** under `name` in
428
- * an accumulating object scope.
429
- *
430
- * @remarks
431
- * Begin a chain with {@link Do} (an empty object scope) and grow it step by
432
- * step. `f` receives the scope accumulated so far and returns a `Result`; on
433
- * `Ok` the value is added as `{ ...scope, [name]: value }`, on `Err`/`Defect`
434
- * the chain short-circuits. Errors union (`E | E2`). A throw becomes a
435
- * `Defect` — as does calling `bind` on a non-object scope (e.g. `Ok(5).bind`),
436
- * which is misuse: the scope is always an object inside a real `Do()` chain.
437
- * (`let` is the pure-value counterpart.)
438
- *
439
- * @typeParam K - the key the bound value is stored under.
440
- * @typeParam U - the bound value type.
441
- * @typeParam E2 - the error type `f` may introduce.
442
- * @param name - the scope key.
443
- * @param f - produces a `Result` from the accumulated scope.
444
- */
445
- bind<K extends string, U, E2>(name: K, f: (scope: T) => Result$1<U, E2>): Result$1<Bound<T, K, U>, E | E2>;
446
- /**
447
- * Do-notation: run `f` for a **plain value** and bind it under `name` in the
448
- * accumulating object scope. The pure-value counterpart of {@link ResultMethods.bind | bind}.
449
- *
450
- * @remarks
451
- * `f` receives the scope and returns a value (not a `Result`); it is added as
452
- * `{ ...scope, [name]: value }`. Runs only on `Ok`; `Err`/`Defect` pass
453
- * through. A throw becomes a `Defect`. An async callback is rejected at
454
- * compile time ({@link NotThenable}).
455
- *
456
- * @typeParam K - the key the value is stored under.
457
- * @typeParam U - the value type.
458
- * @param name - the scope key.
459
- * @param f - computes a value from the accumulated scope.
460
- */
461
- let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): Result$1<Bound<T, K, U>, E>;
462
- /**
463
- * Replace the success value with a constant `value`.
464
- *
465
- * Runs only on `Ok`; `Err` and `Defect` pass through.
466
- *
467
- * @typeParam U - the replacement value type.
468
- */
469
- as<U>(value: U): Result$1<U, E>;
470
- /**
471
- * Drop the success value, collapsing the success type to `void`.
472
- *
473
- * The named form of `map(() => undefined)`. Runs only on `Ok` (the value is
474
- * replaced with `undefined`); `Err` and `Defect` pass through. Unlike
475
- * `as(undefined)` — which produces `Result<undefined, E>` — the success type
476
- * is `void`: the value's story ends here.
477
- */
478
- discard(): Result$1<void, E>;
479
- /**
480
- * Validate the success value — keep the `Ok` when `predicate` holds,
481
- * otherwise fail into the **modeled** channel with `Err(onFail(value))`.
482
- *
483
- * @remarks
484
- * The named form of `flatMap((v) => (p(v) ? Ok(v) : Err(e)))`. With a
485
- * **type-guard** predicate (`(v): v is U`) the success type is **refined** to
486
- * `U` on the way through (this overload). Runs only on `Ok` — a passing value
487
- * flows through as the *same* `Ok`; `Err` and `Defect` pass through
488
- * untouched. A throw in `predicate` or `onFail` becomes a `Defect`.
489
- *
490
- * Both callbacks are synchronous: an async `onFail` is rejected at compile
491
- * time ({@link NotThenable}), and an async predicate does not type-check
492
- * either — its `Promise<boolean>` is not a `boolean` (and, being truthy,
493
- * would have silently always passed).
494
- *
495
- * @typeParam U - the refined success type (type-guard form).
496
- * @typeParam E2 - the error type `onFail` produces.
497
- * @param predicate - the check; a type guard refines `T` to `U`.
498
- * @param onFail - maps the failing value to the modeled error.
499
- *
500
- * @example
501
- * ```ts
502
- * // boolean form: gate a value
503
- * Ok(-1).ensure((n) => n > 0, (n) => `negative: ${n}`); // Err("negative: -1")
504
- *
505
- * // type-guard form: refine the success type
506
- * declare const r: Result<string | number, "e">;
507
- * const s = r.ensure(
508
- * (v): v is string => typeof v === "string",
509
- * () => "not_a_string" as const,
510
- * ); // Result<string, "e" | "not_a_string">
511
- * ```
512
- */
513
- ensure<U extends T, E2>(predicate: (value: T) => value is U, onFail: (value: T) => E2 & NotThenable<E2>): Result$1<U, E | E2>;
514
- /**
515
- * Boolean form of {@link ResultMethods.ensure | ensure} — validates without
516
- * refining, keeping the success type `T`.
517
- */
518
- ensure<E2>(predicate: (value: T) => boolean, onFail: (value: T) => E2 & NotThenable<E2>): Result$1<T, E | E2>;
519
- /**
520
- * Transform the modeled error by **matching it exhaustively**.
521
- *
522
- * @remarks
523
- * The callback receives `match(error)` (an {@link ErrMatcher}) and the
524
- * injected `defect` helper. Chain `.with(pattern, handler)` and **return the
525
- * un-terminated builder** — `mapErrCases` calls `.exhaustive()` itself, so a
526
- * missing case is a compile error at the call site (there is no `.exhaustive()`
527
- * to forget, and no way to slip in `.otherwise()`). The outgoing error type is
528
- * the union of the branch returns with the `Defect` arm subtracted
529
- * (`Exclude<O, Defect>`) — a branch returning `defect(cause)` converts that case
530
- * to a `Defect` and drops it from `E`. Runs only on `Err`; `Ok` and `Defect`
531
- * pass through. A branch that throws also becomes a `Defect`.
532
- *
533
- * **Name every case.** Match on anything the matcher supports — `_tag`,
534
- * `code`, structural shape, guards — and group the cases that share a handler
535
- * with `.with(a, b, handler)`. `.with(P._, …)` is the wildcard **escape
536
- * hatch**, not the default: it makes any match exhaustive, so it also absorbs
537
- * every case `E` grows later. Two uses are sanctioned — a helper generic in
538
- * `E`, where no arm list can prove exhaustiveness against an unresolved type
539
- * parameter, and an `E` that is a single type rather than a union of cases
540
- * (see {@link P} for both). `@unthrown/oxlint`'s `no-catch-all-pattern` (in
541
- * its `recommended` preset) flags the rest.
542
- *
543
- * @typeParam M - the exhaustive builder the callback returns.
544
- * @param f - builds the match over the error (returns the un-terminated builder).
545
- */
546
- mapErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): Result$1<T, MatchErrOut<M>>;
547
- /**
548
- * Sequence from an `Err` by producing another `Result` — the error-channel
549
- * mirror of {@link ResultMethods.flatMap | flatMap}, **matching the error
550
- * exhaustively** ({@link ErrMatcher}; the combinator calls `.exhaustive()`).
551
- *
552
- * Each branch returns a `Result`; the outgoing channels are the unions of the
553
- * branch-returned `Result`s' channels. A branch may return `defect(cause)`.
554
- * Runs only on `Err`; `Ok` and `Defect` pass through.
555
- *
556
- * @typeParam M - the exhaustive builder the callback returns.
557
- * @param f - builds the match; each branch produces a fallback `Result`.
558
- */
559
- flatMapErrCases<M extends ExhaustiveMatch<Result$1<unknown, unknown> | Defect>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): Result$1<T | OkOf<MatchOut<M>>, ErrOf<MatchOut<M>>>;
560
- /**
561
- * Recover from an `Err` by producing a success value, emptying the error
562
- * channel — **matching the error exhaustively** ({@link ErrMatcher}). Pairs
563
- * with {@link ResultMethods.recoverDefect | recoverDefect}.
564
- *
565
- * @remarks
566
- * The result type is `Result<T | U, never>`, but `never` describes only the
567
- * **error** channel — a `Defect` can still be present at runtime. A branch may
568
- * return `defect(cause)` (which stays a `Defect`, not a recovery). Runs only on
569
- * `Err`; `Ok` and `Defect` pass through.
570
- *
571
- * @typeParam M - the exhaustive builder the callback returns.
572
- * @param f - builds the match; each branch produces a success value.
573
- */
574
- recoverErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): Result$1<T | MatchErrOut<M>, never>;
575
- /**
576
- * Run a side effect on the error — **matched exhaustively** ({@link ErrMatcher})
577
- * — and pass the `Result` through unchanged.
578
- *
579
- * @remarks
580
- * The callback builds a match whose branches run side effects; their return
581
- * values are ignored and the original `Err` flows through. Exhaustive like the
582
- * transformers, and like them it wants every case named — `.with(P._, …)`
583
- * remains the wildcard escape hatch. If a branch throws, the
584
- * result is a `Defect` whose cause is an `AggregateError` of `[thrown, original
585
- * failure]` — observing a failure never destroys it. An **async branch is
586
- * rejected at compile time** ({@link NotThenable} on the builder output):
587
- * because the branch results are discarded, a returned `Promise` would float
588
- * unobserved and its rejection would vanish. The one branch return that is
589
- * **not** discarded is the injected `defect(cause)` marker: it is the
590
- * lint-clean, expression-position form of a `throw`, so it follows the throw
591
- * rule above (an `AggregateError` of `[the branch's cause, original
592
- * failure]`), never a silent no-op. A failable
593
- * `Result`-returning effect belongs in
594
- * {@link ResultMethods.flatTapErrCases | flatTapErrCases}.
595
- *
596
- * @param f - builds the match; branch returns are ignored, bar `defect(cause)`.
597
- */
598
- tapErrCases<R>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<R & NotThenable<R>>): Result$1<T, E>;
599
- /**
600
- * Run a **failable** side effect on the error, keeping the original error but
601
- * threading the effect's own error — **matched exhaustively**
602
- * ({@link ErrMatcher}).
603
- *
604
- * @remarks
605
- * The error-channel mirror of {@link ResultMethods.flatTap | flatTap}: each
606
- * branch returns a `Result` whose **success value is discarded** — on the
607
- * effect's `Ok` the original `Err` flows through, while an `Err`/`Defect` from a
608
- * branch short-circuits and threads its error. Note the asymmetry with a
609
- * *throw*: a branch that **returns** a Defect-state `Result` **replaces** the
610
- * original `Err` (Defect-dominance, the short-circuit rule — it is not
611
- * aggregated), whereas a branch that **throws** produces a `Defect`
612
- * aggregating `[thrown, original failure]` (observing a failure by throwing
613
- * never destroys it). A branch returning the injected `defect(cause)` marker —
614
- * reachable under a `returnType` pin — follows the *throw* rule, since it is
615
- * the lint-clean, expression-position form of one.
616
- *
617
- * @typeParam M - the exhaustive builder the callback returns.
618
- * @param f - builds the match; each branch is a failable effect (its `Ok` is ignored).
619
- */
620
- flatTapErrCases<E2>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<Result$1<unknown, E2>>): Result$1<T, E | E2>;
621
- /**
622
- * Recover from a `Defect` — the **only** combinator that can touch one.
623
- *
624
- * @remarks
625
- * Runs `f` only when a `Defect` is present, re-entering the modeled world by
626
- * returning a `Result` (an `Ok` or a fresh `Err`). `Ok` and `Err` pass
627
- * through. Recovering a Defect should be rare: usually you let it bubble to
628
- * the edge. If `f` throws, the throw becomes a new `Defect`.
629
- *
630
- * @typeParam U - a success type the recovery may produce.
631
- * @typeParam E2 - an error type the recovery may produce.
632
- * @param f - maps the Defect's unknown cause to a recovering `Result`.
633
- */
634
- recoverDefect<U, E2>(f: (cause: unknown) => Result$1<U, E2>): Result$1<T | U, E | E2>;
635
- /**
636
- * Run a side effect on a present `Defect`'s cause (e.g. logging) and pass the
637
- * `Defect` through unchanged. If `f` throws, the result is a `Defect` whose
638
- * cause is an `AggregateError` of `[thrown, original failure]` — observing a
639
- * failure never destroys it. An async callback is rejected at compile time
640
- * ({@link NotThenable}).
641
- *
642
- * @param f - the side effect over the unknown cause.
643
- */
644
- tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): Result$1<T, E>;
645
- /**
646
- * Run a side effect on **any failure** — `Err` or `Defect` — and pass the
647
- * `Result` through unchanged. The one cross-channel observer, for the shared
648
- * "it went KO" concern (logging, metrics, rollback) that would otherwise be
649
- * duplicated across {@link ResultMethods.tapErrCases | tapErrCases} and
650
- * {@link ResultMethods.tapDefect | tapDefect}.
651
- *
652
- * @remarks
653
- * `f` receives the narrowed **failure variant** ({@link FailureView}), not a
654
- * payload — the payload union `E | unknown` would collapse to `unknown` and
655
- * lose `E`'s typing. Branch on `failure.tag` to reach the typed payload
656
- * (`"Err"` → `failure.error: E`, `"Defect"` → `failure.cause: unknown`), or
657
- * treat it opaquely for a shared logger. Runs on `Err` and `Defect`; `Ok`
658
- * passes through. It **observes without consuming**: the failure flows on
659
- * unchanged — to also recover, use
660
- * {@link ResultMethods.recoverErrCases | recoverErrCases} /
661
- * {@link ResultMethods.recoverDefect | recoverDefect} (deliberately separate
662
- * acts) or {@link ResultMethods.match | match} at the edge. If `f` throws, the
663
- * result is a `Defect` whose cause is an `AggregateError` of `[thrown,
664
- * original failure]` — observing a failure never destroys it. An async
665
- * callback is rejected at compile time ({@link NotThenable}).
666
- *
667
- * @param f - the side effect over the failure variant (its return value is ignored).
668
- */
669
- tapFailure<R>(f: (failure: FailureView<E, T>) => R & NotThenable<R>): Result$1<T, E>;
670
- /**
671
- * Exhaustively fold all three runtime states into a single value.
672
- *
673
- * @remarks
674
- * Exactly one handler runs. Together with the throw-to-Defect guarantee, this
675
- * is typically the single place a pipeline is handled at the edge — mapping
676
- * `Ok`/`Err`/`Defect` to (for example) 2xx / 4xx / 5xx with no `try`/`catch`.
677
- *
678
- * The `errCases` handler does not take a single blanket callback: it receives
679
- * `match(error)` (an {@link ErrMatcher}) and **matches the error exhaustively**,
680
- * exactly like the error combinators — which is why the key carries the same
681
- * `…Cases` suffix. Chain `.with(pattern, handler)` and **return the
682
- * un-terminated builder** — `match` calls `.exhaustive()` itself, so a missing
683
- * case is a compile error at the call site (no `.exhaustive()` to forget).
684
- * Folding at the edge names every case too — `.with(P._, …)` is the wildcard
685
- * escape hatch, not the default. Unlike the combinators the branches
686
- * receive **no `defect` helper** — `match` is total elimination to a value,
687
- * with no `Defect` output channel; the `defect` case handles a `Result` that
688
- * already carries one. (A `Result` is also a discriminated union — for richer
689
- * whole-`Result` matching, `match(result).with(…)`.)
690
- *
691
- * @typeParam ROk - the `ok` handler return type.
692
- * @typeParam RDefect - the `defect` handler return type.
693
- * @typeParam M - the exhaustive builder the `errCases` handler returns.
694
- * @param cases - the `ok`/`defect` handlers plus the `errCases` matcher builder.
695
- */
696
- match<ROk, RDefect, M extends ExhaustiveMatch<unknown>>(cases: {
697
- ok: (value: T) => ROk;
698
- errCases: (matcher: ErrMatcher<E>) => M;
699
- defect: (cause: unknown) => RDefect;
700
- }): ROk | RDefect | MatchOut<M>;
701
- /**
702
- * Extract the success value.
703
- *
704
- * @remarks
705
- * Compiles only when the error channel is empty (`E = never`) — eliminate
706
- * modeled errors first (`match` / `recoverErrCases` / `flatMapErrCases`), or reach for the
707
- * `getOr` / `getOrElse` / `getOrNull` / `getOrUndefined` family (which
708
- * recover an `Err`). If you get a `'this' context` type error here, that is
709
- * the gate: the receiver still has a non-`never` error channel.
710
- *
711
- * `E = never` empties only the **modeled** error channel — a `Defect` can
712
- * still be present, and `get()` **rethrows its original cause** (it
713
- * _panics_); `Result<T, never>` does not mean `get()` cannot throw.
714
- *
715
- * @returns the `Ok` value.
716
- */
717
- get(this: Result$1<T, never>): T;
718
- /**
719
- * Extract the modeled error.
720
- *
721
- * @remarks
722
- * Compiles only when the success channel is empty (`T = never`) — eliminate
723
- * the success case first. `T = never` is rarely the case in practice (a
724
- * `Result` you hold usually still has a success type), so to inspect an
725
- * error prefer an `isErr()` guard or, in tests, `@unthrown/vitest`'s
726
- * `toBeErrWith`. A `Defect` still **rethrows its original cause** (a defect is
727
- * a bug, not an absent value), so this does not mean `getErr()` can't throw.
728
- *
729
- * @returns the `Err` value.
730
- */
731
- getErr(this: Result$1<never, E>): E;
732
- /**
733
- * The success value, or `fallback` on `Err`.
734
- *
735
- * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
736
- * @param fallback - returned when the result is an `Err` (may be a different type; the return widens to `T | U`).
737
- * @throws Re-throws on a `Defect` — a Defect is a bug, not an absent value, so
738
- * it is never silently replaced.
739
- */
740
- getOr<U>(fallback: U): T | U;
741
- /**
742
- * The success value, or `f(error)` on `Err`.
743
- *
744
- * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
745
- * @param f - lazily computes the fallback from the error (may return a different type; the return widens to `T | U`).
746
- * @throws Re-throws on a `Defect`.
747
- */
748
- getOrElse<U>(f: (error: E) => U): T | U;
749
- /**
750
- * The success value, or `null` on `Err`.
751
- *
752
- * @throws Re-throws on a `Defect`.
753
- */
754
- getOrNull(): T | null;
755
- /**
756
- * The success value, or `undefined` on `Err`.
757
- *
758
- * @throws Re-throws on a `Defect`.
759
- */
760
- getOrUndefined(): T | undefined;
761
- /**
762
- * The success value, or **throw** the modeled error on `Err`.
763
- *
764
- * @remarks
765
- * A deliberate escape hatch off the errors-as-values model — it **throws the
766
- * `Err` value as-is** at the call site, so a caller of the enclosing function
767
- * sees a throw rather than a channel. Its home is **tests and scripts**,
768
- * where "this `Result` had better be `Ok`" is the assertion and a throw is
769
- * the correct failure mode.
770
- *
771
- * In production code, fold the error channel instead:
772
- * {@link ResultMethods.recoverErrCases | recoverErrCases} empties `E`, so
773
- * {@link ResultMethods.get | get} compiles and a case routed to the injected
774
- * `defect(...)` panics with its original cause — with every case still named.
775
- * {@link ResultMethods.match | match} and
776
- * {@link ResultMethods.flatMapErrCases | flatMapErrCases} are the other two
777
- * ways to keep the error a value. `@unthrown/oxlint`'s opt-in
778
- * `no-get-or-throw` rule enforces this, exempting test files through an
779
- * oxlint `overrides` entry.
780
- *
781
- * Type-gated as the **complement** of {@link ResultMethods.get | get}: it
782
- * compiles only when the error channel is **non-empty** (`E` is not `never`) —
783
- * there must be a modeled error for it to throw. On a `Result<T, never>` there
784
- * is nothing to throw, so `getOrThrow` does not compile; use `get()` (which
785
- * gates the other way). Together they partition extraction by the error
786
- * channel's state, with no overlap.
787
- *
788
- * @returns the `Ok` value.
789
- * @throws the modeled `error` on `Err`; re-throws the original `cause` on a
790
- * `Defect` (a panic, like the rest of the `getOr…` family).
791
- */
792
- getOrThrow(this: [E] extends [never] ? "unthrown: getOrThrow is unnecessary here — the Err channel is empty (E = never), so there is nothing to throw. Use get() instead." : Result$1<T, E>): T;
793
- /** Whether this result is `Ok` — narrows `this` to its {@link OkView} on `true`. */
794
- isOk(): this is OkView<T, E>;
795
- /** Whether this result is `Err` — narrows `this` to its {@link ErrView} on `true`. */
796
- isErr(): this is ErrView<E, T>;
797
- /** Whether this result is a `Defect` — narrows `this` to its {@link DefectView} on `true`. */
798
- isDefect(): this is DefectView<T, E>;
799
- /** Lift this synchronous `Result` into an {@link AsyncResult}. */
800
- toAsync(): AsyncResult$1<T, E>;
801
- };
540
+ * `fn` is **synchronous** too. An `async` `fn` rejects *after* this boundary has
541
+ * already returned, so its rejection could never reach `qualify`: it becomes a
542
+ * `Defect` (never `Ok(<Promise>)`) and the orphaned rejection is silenced rather
543
+ * than left to float. Reach for {@link fromPromise} to wrap async work.
544
+ *
545
+ * The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
546
+ * `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
547
+ * `qualify` that returns *only* `defect(cause)` yields `E = never` (a Defect is
548
+ * out-of-band and must not pollute the error channel); reach for
549
+ * {@link fromSafeThrowable} when every throw is a Defect.
550
+ *
551
+ * @typeParam A - the wrapped function's argument tuple.
552
+ * @typeParam T - the wrapped function's return type.
553
+ * @typeParam R - `qualify`'s return type; the modeled error `E` is
554
+ * `Exclude<R, Defect>` (its `Defect` arm, if any, is subtracted).
555
+ * @param fn - the throwing function to wrap.
556
+ * @param qualify - triages a thrown `cause` into a modeled `E`, or marks it
557
+ * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
558
+ * @returns a function with the same arguments returning `Result<T, E>`.
559
+ *
560
+ * @category Interop
561
+ *
562
+ * @example
563
+ * ```ts
564
+ * import { fromThrowable } from "unthrown";
565
+ *
566
+ * // Model the parse failure as an `Err`, everything unexpected as a `Defect`.
567
+ * const parse = fromThrowable(
568
+ * (text: string) => JSON.parse(text) as unknown,
569
+ * (cause, defect) =>
570
+ * cause instanceof SyntaxError ? ("invalid_json" as const) : defect(cause),
571
+ * );
572
+ *
573
+ * parse('{"ok":true}').getOr(null); // => { ok: true }
574
+ * parse("nope"); // => Err("invalid_json")
575
+ * ```
576
+ */
577
+ export declare function fromThrowable<A extends unknown[], T, R>(fn: (...args: A) => T, qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R & NotThenable<R>): (...args: A) => Result<T, Exclude<R, Defect>>;
578
+ /**
579
+ * Wrap a throwing synchronous function asserted **not** to fail in any modeled
580
+ * way: any throw becomes a `Defect`.
581
+ *
582
+ * @remarks
583
+ * The synchronous counterpart of {@link fromSafePromise}. Use it only when a
584
+ * throw genuinely indicates a bug rather than an anticipated outcome — the
585
+ * error channel is `never`, so there is nothing to triage; there is no
586
+ * `qualify`. When some throws *are* anticipated, reach for
587
+ * {@link fromThrowable} and triage them.
588
+ *
589
+ * `fn` is **synchronous**: an `async` `fn` becomes a `Defect` (never
590
+ * `Ok(<Promise>)`), with its orphaned rejection silenced rather than left to
591
+ * float. Reach for {@link fromSafePromise} to wrap async work.
592
+ *
593
+ * @typeParam A - the wrapped function's argument tuple.
594
+ * @typeParam T - the wrapped function's return type.
595
+ * @param fn - the throwing function to wrap.
596
+ * @returns a function with the same arguments returning `Result<T, never>`.
597
+ *
598
+ * @category Interop
599
+ *
600
+ * @example
601
+ * ```ts
602
+ * import { fromSafeThrowable } from "unthrown";
603
+ *
604
+ * // A decode failure here is a bug (the row came from our own schema), so
605
+ * // every throw is a defect — no throwaway `(cause, defect) => defect(cause)`.
606
+ * const decode = fromSafeThrowable((row: Row) => userSchema.parse(row));
607
+ *
608
+ * decode(row); // => Result<User, never> — a throw becomes a Defect
609
+ * ```
610
+ */
611
+ export declare function fromSafeThrowable<A extends unknown[], T>(fn: (...args: A) => T): (...args: A) => Result<T, never>;
802
612
  /**
803
- * The `Ok` variant of a {@link Result}: a success carrying a `value`. This is
804
- * what a successful `isOk` guard narrows to, making `.value` reachable. It also
805
- * carries the shared fluent surface ({@link ResultMethods}).
613
+ * Wrap a `Promise` (or a thunk producing one) as an {@link AsyncResult}, forcing
614
+ * every rejection to be triaged.
615
+ *
616
+ * @remarks
617
+ * `qualify` **must** map each rejection cause into a modeled error `E` or a
618
+ * `Defect` (via the injected `defect` helper, its second argument). The returned
619
+ * `AsyncResult`'s internal promise never rejects; `await`-ing it always yields a
620
+ * `Result`. A throw inside `qualify` is itself a `Defect`. `qualify` is
621
+ * **synchronous**: an `async` qualify is rejected at compile time
622
+ * ({@link NotThenable}), and a thenable slipped past the types at runtime
623
+ * becomes a `Defect` (never an `Err(Promise)`), its orphaned rejection silenced.
624
+ *
625
+ * The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
626
+ * `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
627
+ * `qualify` that returns *only* `defect(cause)` yields `E = never`; when every
628
+ * rejection is a Defect, prefer {@link fromSafePromise}.
629
+ *
630
+ * @typeParam T - the resolved value type.
631
+ * @typeParam R - `qualify`'s return type; the modeled error `E` is
632
+ * `Exclude<R, Defect>` (its `Defect` arm, if any, is subtracted).
633
+ * @param promise - the promise, or a thunk returning one.
634
+ * @param qualify - triages a rejection `cause` into a modeled `E`, or marks it
635
+ * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
636
+ * @param _guard - compile-time only; never pass it. The phantom rest-tuple that
637
+ * enforces "qualify is synchronous": an `async` qualify makes this demand an
638
+ * impossible extra argument (whose type spells out the error), while a
639
+ * synchronous one leaves it empty. Encoded here — not on `qualify`'s return
640
+ * type — so `T`'s inference from `promise` is undisturbed.
641
+ *
642
+ * @category Interop
806
643
  *
807
644
  * @example
808
645
  * ```ts
809
- * if (r.isOk()) r.value; // r: OkView<T, E> here — .value is a T
810
- * ```
646
+ * import { fromPromise } from "unthrown";
811
647
  *
812
- * @category Types
648
+ * // A rejection with a NotFoundError becomes a modeled `Err`; anything else a Defect.
649
+ * const user = await fromPromise(fetchUser(id), (cause, defect) =>
650
+ * cause instanceof NotFoundError ? ("not_found" as const) : defect(cause),
651
+ * );
652
+ *
653
+ * if (user.isOk()) user.value; // => the fetched user
654
+ * // when fetchUser rejects with NotFoundError: user is Err("not_found")
655
+ * ```
813
656
  */
814
- interface OkView<out T, out E = never> extends ResultMethods<T, E> {
815
- readonly tag: "Ok";
816
- readonly value: T;
817
- }
657
+ export declare function fromPromise<T, R>(promise: Promise<T> | (() => Promise<T>), qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R, ..._guard: [Extract<R, PromiseLike<unknown>>] extends [never] ? [] : ["unthrown: qualify must be synchronous — its Promise would land in E un-triaged"]): AsyncResult<T, Exclude<R, Defect>>;
818
658
  /**
819
- * The `Err` variant of a {@link Result}: a modeled failure carrying an `error`.
820
- * This is what a successful `isErr` guard narrows to, exposing `.error`. It also
821
- * carries the shared fluent surface ({@link ResultMethods}).
659
+ * Wrap a `Promise` asserted **not** to fail in any modeled way: any rejection
660
+ * becomes a `Defect`.
822
661
  *
823
662
  * @remarks
824
- * **Note the parameter order: `ErrView<E, T>` puts the error type _first_** — the
825
- * reverse of the `<T, E>` order used by {@link OkView}, {@link DefectView}, and
826
- * {@link Result} — because `Result<T, E>` narrows to `ErrView<E, T>` (the error is
827
- * the payload the guard makes reachable). You rarely write it by hand (a failed
828
- * `isErr()` narrows to it for you); if you do, mind the flip — `ErrView<MyError,
829
- * MyValue>`, not `ErrView<MyValue, MyError>`.
663
+ * Use this only when a rejection genuinely indicates a bug rather than an
664
+ * anticipated outcome — the error channel is `never`, so there is nothing to
665
+ * triage. (`await`-ing still yields a `Result`; it never throws.) The
666
+ * synchronous counterpart is {@link fromSafeThrowable}.
667
+ *
668
+ * @typeParam T - the resolved value type.
669
+ * @param promise - the promise, or a thunk returning one.
670
+ *
671
+ * @category Interop
830
672
  *
831
673
  * @example
832
674
  * ```ts
833
- * if (r.isErr()) r.error; // r: ErrView<E, T> here — .error is an E
675
+ * import { fromSafePromise } from "unthrown";
676
+ *
677
+ * (await fromSafePromise(Promise.resolve(3))).get(); // => 3
678
+ * // a rejection becomes a Defect (never a modeled Err):
679
+ * await fromSafePromise(Promise.reject(new Error("boom"))); // => Defect(Error("boom"))
834
680
  * ```
681
+ */
682
+ export declare function fromSafePromise<T>(promise: Promise<T> | (() => Promise<T>)): AsyncResult<T, never>;
683
+ /**
684
+ * The settler a {@link fromExecutor} executor receives. Settles the pending
685
+ * `AsyncResult` **once** — later calls are no-ops, exactly as `resolve` is on a
686
+ * `Promise`.
687
+ *
688
+ * @typeParam T - the success type.
689
+ * @typeParam E - the modeled error type.
835
690
  *
836
691
  * @category Types
837
692
  */
838
- interface ErrView<out E, out T = never> extends ResultMethods<T, E> {
839
- readonly tag: "Err";
840
- readonly error: E;
841
- }
693
+ type Settle<T, E> = (result: Result<T, E> | Defect) => void;
842
694
  /**
843
- * The `Defect` variant of a {@link Result}: an unmodeled failure carrying a
844
- * `cause`. This is what a successful `isDefect` guard narrows to, exposing
845
- * `.cause`. It also carries the shared fluent surface ({@link ResultMethods}).
695
+ * Build an {@link AsyncResult} from a callback-style API — this library's
696
+ * answer to `new Promise((resolve, reject) => …)`.
697
+ *
698
+ * @remarks
699
+ * The settler takes a **`Result`**, not a value-or-reason pair: the caller names
700
+ * the variant, so no `unknown` can enter `E` and there is no `qualify` to pass.
701
+ * For a failure that is *not* modeled, settle the injected `defect` helper's
702
+ * marker — the same injection `qualify` receives, and the only way to reach the
703
+ * defect channel from inside an asynchronous callback (a `throw` there runs in
704
+ * its own turn, long after the executor body returned).
705
+ *
706
+ * `T` and `E` cannot be inferred from the body, since `settle` is a parameter.
707
+ * Supply them explicitly, or let them flow from an annotated target. Absent
708
+ * either, both default to `never` (Thesis #3: no path may produce `unknown` in
709
+ * `E`) — so an unannotated call is a compile error at the `settle(...)` call
710
+ * site, not a silently-`unknown` channel.
711
+ *
712
+ * An executor that never settles yields an `AsyncResult` that never resolves —
713
+ * the one hazard {@link fromPromise} does not have, and identical to
714
+ * `new Promise`.
715
+ *
716
+ * @typeParam T - the success type.
717
+ * @typeParam E - the modeled error type.
718
+ * @param executor - runs immediately; receives the settler and the `defect` helper.
719
+ *
720
+ * @category Interop
846
721
  *
847
722
  * @example
848
723
  * ```ts
849
- * if (r.isDefect()) r.cause; // r: DefectView<T, E> here — .cause is `unknown`
724
+ * import { fromExecutor, Err, Ok } from "unthrown";
725
+ *
726
+ * const listen = (port: number) =>
727
+ * fromExecutor<Server, PortInUse>((settle, defect) => {
728
+ * server.once("error", (cause) =>
729
+ * isAddrInUse(cause) ? settle(Err(new PortInUse(port))) : settle(defect(cause)),
730
+ * );
731
+ * server.listen(port, () => settle(Ok(server)));
732
+ * });
850
733
  * ```
734
+ */
735
+ export declare function fromExecutor<T = never, E = never>(executor: (settle: Settle<T, E>, defect: (cause: unknown) => Defect) => void): AsyncResult<T, E>;
736
+ /**
737
+ * The success channel of {@link all} / {@link allAsync}: a **positional tuple**
738
+ * for a fixed-length input (including the empty tuple), or a homogeneous
739
+ * **array** for a dynamic one.
851
740
  *
852
- * @category Types
741
+ * @remarks
742
+ * The split keys off the input's `length`: a fixed tuple has a literal length
743
+ * (`number extends Rs["length"]` is false → keep the positional `Ts`), while a
744
+ * general array has `length: number` (→ collapse to `Ts[number][]`). Checking
745
+ * length rather than `Rs extends [unknown, ...unknown[]]` keeps `all([])` typed
746
+ * as `Result<[], …>` instead of `Result<never[], …>`.
747
+ *
748
+ * @typeParam Rs - the tuple/array of input `Result` types.
749
+ * @typeParam Ts - per-element extracted success types (`OkOf` for `all`,
750
+ * `AsyncOkOf` for `allAsync`).
751
+ * @internal
853
752
  */
854
- interface DefectView<out T = never, out E = never> extends ResultMethods<T, E> {
855
- readonly tag: "Defect";
856
- readonly cause: unknown;
857
- }
753
+ type AllOk<Rs extends readonly unknown[], Ts extends readonly unknown[]> = number extends Rs["length"] ? Ts[number][] : Ts;
858
754
  /**
859
- * A failure variant of a {@link Result}: an {@link ErrView} **or** a
860
- * {@link DefectView}. This is what a `tapFailure` callback receives — the
861
- * discriminated variant rather than a payload, because the payload union
862
- * `E | unknown` would collapse to `unknown` and lose `E`'s typing. Branch on
863
- * `tag` to narrow (`"Err"` → `.error: E`, `"Defect"` → `.cause: unknown`).
755
+ * A `[key, error]` pair from a record aggregate, correlated per key.
864
756
  *
865
757
  * @remarks
866
- * Like {@link ErrView}, the error type comes **first** (`FailureView<E, T>`) —
867
- * the error is the payload you are usually here for, and a shared observer can
868
- * spell just `FailureView<MyError>`.
758
+ * `-?` because the mapped type is homomorphic: an optional input key would
759
+ * otherwise carry its optionality through and put `undefined` in the entry
760
+ * union, breaking the documented `([key, error]) => …` destructure. An entry
761
+ * whose error channel is `never` (an infallible input — every `@unthrown/drizzle`
762
+ * read) is dropped rather than emitted as an uninhabited `[K, never]` arm, which
763
+ * a `switch` over the key would still have to write a dead case for.
764
+ *
765
+ * @internal
766
+ */
767
+ type DictErrEntry<R> = { [K in keyof R]-?: [ErrOf<R[K]>] extends [never] ? never : readonly [K, ErrOf<R[K]>]; }[keyof R];
768
+ /** The {@link AsyncResult} counterpart of {@link DictErrEntry}. @internal */
769
+ type AsyncDictErrEntry<R> = { [K in keyof R]-?: [AsyncErrOf<R[K]>] extends [never] ? never : readonly [K, AsyncErrOf<R[K]>]; }[keyof R];
770
+ /** A non-empty readonly list — `merge` runs only when an `Err` was collected. @internal */
771
+ type NonEmpty<T> = readonly [T, ...T[]];
772
+ /**
773
+ * A record of `Result`s — the input to {@link allFromDict}. Keyed by any
774
+ * `PropertyKey`: a symbol key is folded like a string one (see
775
+ * {@link ownEntries}), so the constraint checks its value too.
776
+ */
777
+ type ResultRecord = Record<PropertyKey, Result<unknown, unknown>>;
778
+ /** A record of `AsyncResult`s — the input to {@link allFromDictAsync}. */
779
+ type AsyncResultRecord = Record<PropertyKey, AsyncResult<unknown, unknown>>;
780
+ /**
781
+ * Collect a tuple/array of {@link Result}s into a single `Result` of all their
782
+ * success values.
783
+ *
784
+ * @remarks
785
+ * Short-circuits on the **first** `Err` (later entries are not inspected for
786
+ * their error); any `Defect` present **dominates**, winning even over an earlier
787
+ * `Err`. A **fixed tuple** keeps its positional types — `all([Ok(1), Ok("a")])`
788
+ * is `Result<[number, string], …>` — while a **dynamic array** `Result<T, E>[]`
789
+ * collapses to `Result<T[], E>` with no cast. For a **record** keyed by name,
790
+ * use {@link allFromDict}. To report **every** `Err` instead of only the first,
791
+ * use {@link validateAll}.
792
+ *
793
+ * @category Aggregate
869
794
  *
870
795
  * @example
871
796
  * ```ts
872
- * const logKo = (f: FailureView<ApiError>) =>
873
- * f.tag === "Err" ? logger.warn(f.error) : logger.error(f.cause);
874
- * result.tapFailure(logKo);
797
+ * import { all, Ok, Err } from "unthrown";
798
+ *
799
+ * all([Ok(1), Ok("a"), Ok(true)]).get(); // => [1, "a", true] (typed [number, string, boolean])
800
+ * all([Ok(1), Err("e"), Ok(3)]); // => Err("e") (short-circuits on the first Err)
875
801
  * ```
802
+ */
803
+ export declare function all<Rs extends readonly Result<unknown, unknown>[]>(results: readonly [...Rs]): Result<AllOk<Rs, { [K in keyof Rs]: OkOf<Rs[K]>; }>, ErrOf<Rs[number]>>;
804
+ /**
805
+ * Collect a **record** of {@link Result}s into a single `Result` of a record of
806
+ * their success values — `allFromDict({ a: Result<A, E>, b: Result<B, E> })` is
807
+ * `Result<{ a: A; b: B }, E>`. The named counterpart of {@link all}, for
808
+ * parallel work you'd rather not tuple.
876
809
  *
877
- * @typeParam E - the modeled error type.
878
- * @typeParam T - the success value type (phantom here; a failure carries none).
879
- * @category Types
810
+ * @remarks
811
+ * Same folding rules as {@link all}: first `Err` short-circuits, any `Defect`
812
+ * dominates. This is **not** error accumulation — for that, reach for
813
+ * {@link validateAllFromDict}, which accumulates every `Err` and folds them into
814
+ * one modeled error.
815
+ *
816
+ * @category Aggregate
817
+ *
818
+ * @example
819
+ * ```ts
820
+ * import { allFromDict, Ok, Err } from "unthrown";
821
+ *
822
+ * allFromDict({ id: Ok(1), name: Ok("ada") }).get(); // => { id: 1, name: "ada" }
823
+ * allFromDict({ id: Ok(1), name: Err("missing") }); // => Err("missing")
824
+ * ```
880
825
  */
881
- type FailureView<E, T = never> = ErrView<E, T> | DefectView<T, E>;
826
+ export declare function allFromDict<R extends ResultRecord>(results: R): Result<{ [K in keyof R]: OkOf<R[K]>; }, ErrOf<R[keyof R]>>;
882
827
  /**
883
- * The core type of the library: a computation that has either succeeded with a
884
- * value of type `T` or failed with a *modeled* error of type `E`.
828
+ * The asynchronous counterpart of {@link all}: combine a tuple/array of
829
+ * {@link AsyncResult}s into one `AsyncResult` of all their success values.
885
830
  *
886
831
  * @remarks
887
- * A `Result` is a **discriminated union** of three variants, distinguished by a
888
- * `tag` of `"Ok"` | `"Err"` | `"Defect"`:
832
+ * The inputs are resolved **concurrently** (order preserved); the resolved
833
+ * `Result`s are then folded with the same rules as {@link all} — first `Err`
834
+ * short-circuits, any `Defect` dominates. As ever, the returned `AsyncResult`'s
835
+ * internal promise never rejects. For a **record**, use {@link allFromDictAsync};
836
+ * to report **every** `Err`, use {@link validateAllAsync}.
889
837
  *
890
- * - **`Ok`** — a success carrying a `value: T`.
891
- * - **`Err`** — a modeled, anticipated failure carrying an `error: E`.
892
- * - **`Defect`** — an *unmodeled* failure carrying an unknown `cause`. A Defect
893
- * never appears in `E`; it is the library's third, out-of-band channel.
838
+ * @category Aggregate
894
839
  *
895
- * Because it is a real union, you can match it natively (a `switch` on `tag`, or
896
- * the built-in `match(...).with({ tag: "Ok" }, …).exhaustive()`), *and* it
897
- * carries the full method surface ({@link ResultMethods}) for fluent chaining.
898
- * Either way, the payload (`value`/`error`/`cause`) is only reachable after you
899
- * narrow — so "check before you access" still holds.
840
+ * @example
841
+ * ```ts
842
+ * import { allAsync, fromSafePromise } from "unthrown";
843
+ *
844
+ * const both = allAsync([
845
+ * fromSafePromise(Promise.resolve(1)),
846
+ * fromSafePromise(Promise.resolve(2)),
847
+ * ]);
848
+ * (await both).get(); // => [1, 2]
849
+ * ```
850
+ */
851
+ export declare function allAsync<Rs extends readonly AsyncResult<unknown, unknown>[]>(results: readonly [...Rs]): AsyncResult<AllOk<Rs, { [K in keyof Rs]: AsyncOkOf<Rs[K]>; }>, AsyncErrOf<Rs[number]>>;
852
+ /**
853
+ * The asynchronous counterpart of {@link allFromDict}: combine a record of
854
+ * {@link AsyncResult}s into one `AsyncResult` of a record of their values.
900
855
  *
901
- * @typeParam T - the success value type.
902
- * @typeParam E - the modeled error type (only anticipated domain failures).
856
+ * @remarks
857
+ * Resolved concurrently (order preserved), folded with the {@link all} rules,
858
+ * and the internal promise never rejects. To report **every** `Err`, use
859
+ * {@link validateAllFromDictAsync}.
860
+ *
861
+ * @category Aggregate
903
862
  *
904
863
  * @example
905
864
  * ```ts
906
- * import { Ok, Err, type Result } from "unthrown";
907
- *
908
- * function half(n: number): Result<number, "odd"> {
909
- * return n % 2 === 0 ? Ok(n / 2) : Err("odd");
910
- * }
865
+ * import { allFromDictAsync, fromSafePromise } from "unthrown";
911
866
  *
912
- * const message = half(10).match({
913
- * ok: (n) => `got ${n}`,
914
- * // every case of `E` named — here the one literal it holds
915
- * errCases: (matcher) => matcher.with("odd", () => "failed: odd"),
916
- * defect: (cause) => `bug: ${String(cause)}`,
867
+ * const both = allFromDictAsync({
868
+ * a: fromSafePromise(Promise.resolve(1)),
869
+ * b: fromSafePromise(Promise.resolve("x")),
917
870
  * });
871
+ * (await both).get(); // => { a: 1, b: "x" }
918
872
  * ```
919
873
  */
920
- type Result$1<T, E> = OkView<T, E> | ErrView<E, T> | DefectView<T, E>;
874
+ export declare function allFromDictAsync<R extends AsyncResultRecord>(results: R): AsyncResult<{ [K in keyof R]: AsyncOkOf<R[K]>; }, AsyncErrOf<R[keyof R]>>;
921
875
  /**
922
- * A success-only thenable: awaitable, but deliberately **not** a full
923
- * `PromiseLike`.
876
+ * Collect a tuple/array of {@link Result}s, **accumulating every** `Err` and
877
+ * merging them into a single modeled error — the accumulating counterpart of
878
+ * {@link all}.
924
879
  *
925
880
  * @remarks
926
- * An {@link AsyncResult}'s internal promise never rejects, so `await`-ing one
927
- * always yields a {@link Result} and never throws — there is no rejection
928
- * channel to model, and none is advertised. At runtime it is still a thenable
929
- * (the only way `await` can collapse it), and `Promise.all` / `Promise.resolve`
930
- * will still adopt it — harmlessly, since it settles to a `Result` and never
931
- * rejects. What the narrowing prevents is treating it as a full promise:
932
- * `.catch()` / `.finally()` do not type-check, because there is no rejection to
933
- * handle.
881
+ * Same success channel as {@link all}: a **fixed tuple** keeps its positional
882
+ * types, a **dynamic array** collapses to `Result<T[], E2>`. The difference is
883
+ * the error channel — instead of the first `Err` winning, every `Err` is
884
+ * collected in input order and handed to `merge`, whose return becomes the
885
+ * modeled error.
934
886
  *
935
- * @typeParam T - the value `await` resolves to.
887
+ * `merge` receives a **non-empty** list, so it is total: it is called only when
888
+ * at least one `Err` was collected. It is **not** called when every element is
889
+ * `Ok`, nor when a `Defect` is present.
936
890
  *
937
- * @category Types
938
- */
939
- type Awaitable<out T> = {
940
- then<R = T>(onfulfilled?: ((value: T) => R | PromiseLike<R>) | null): PromiseLike<R>;
941
- };
942
- /**
943
- * The async method surface every {@link AsyncResult} carries — the combinators
944
- * (`map`, `flatMap`, `mapErrCases`, `match`, `get`, …) with their asynchronous
945
- * signatures, documented one per entry below. The async mirror of
946
- * {@link ResultMethods}: each entry links its synchronous counterpart and states
947
- * only the async delta.
891
+ * Any `Defect` still **dominates** — it wins over the accumulated errors, which
892
+ * are discarded and never reach `merge`. A defect means something in this batch
893
+ * failed in a way nobody modeled, so the violations computed alongside it are
894
+ * not trustworthy. An out-of-contract non-`Result` element becomes a
895
+ * `TypeError`-caused `Defect` the same way, and a throw inside `merge` becomes
896
+ * a `Defect` too.
948
897
  *
949
- * @remarks
950
- * Like {@link ResultMethods}, this type exists to **document** the surface — not
951
- * to be authored against; you obtain it by holding an `AsyncResult`. Its
952
- * combinator callbacks are **synchronous** (a raw `Promise` may never enter — see
953
- * the {@link AsyncResult} remarks); async work re-enters via {@link fromPromise}
954
- * and composes with `flatMap`. Systematic differences from the sync surface: the
955
- * binds return an `AsyncResult` (and additionally accept one), and the
956
- * eliminators return a `Promise`.
898
+ * `merge` must be **synchronous** — an `async` one is a compile error
899
+ * ({@link NotThenable}), since a `Promise` in `E` is an unqualified rejection.
957
900
  *
958
- * @typeParam T - the success value type.
959
- * @typeParam E - the modeled error type.
960
- * @category Methods
901
+ * For **schema-shaped** input (a request body, a form), reach for
902
+ * `@unthrown/standard-schema`'s `fromSchema` instead — a validator already
903
+ * hands you every issue as the modeled error. `validateAll` is for independent
904
+ * checks you wrote yourself. For a **record** keyed by name, use
905
+ * {@link validateAllFromDict}.
906
+ *
907
+ * @typeParam Rs - the tuple/array of input `Result` types.
908
+ * @typeParam E2 - the merged error type.
909
+ * @param results - the results to collect.
910
+ * @param merge - folds the collected errors into one modeled error.
911
+ *
912
+ * @category Aggregate
913
+ *
914
+ * @example
915
+ * ```ts
916
+ * import { validateAll, Ok, Err } from "unthrown";
917
+ *
918
+ * // every Err is collected, not just the first
919
+ * validateAll([Ok(1), Err("stock"), Err("credit")], (errors) => errors.join(" and "));
920
+ * // => Err("stock and credit")
921
+ *
922
+ * // all-Ok keeps the positional tuple; `merge` never runs
923
+ * validateAll([Ok(1), Ok("a")], (errors) => errors.join());
924
+ * // => Ok([1, "a"]) typed Result<[number, string], string>
925
+ * ```
961
926
  */
962
- type AsyncResultMethods<out T, out E> = {
963
- /**
964
- * Asynchronous {@link ResultMethods.map | map}: transforms the success value
965
- * with `f`. `f` is synchronous; a throw becomes a `Defect`. An async callback
966
- * is rejected at compile time ({@link NotThenable}).
967
- */
968
- map<U>(f: (value: T) => U & NotThenable<U>): AsyncResult$1<U, E>;
969
- /**
970
- * Asynchronous {@link ResultMethods.flatMap | flatMap}. Unlike the sync form,
971
- * `f` may return a `Result` **or** an `AsyncResult` (never a raw `Promise`); a
972
- * throw becomes a `Defect`.
973
- *
974
- * @remarks
975
- * The async branch of `f`'s return type is spelled `Awaitable<Result<U, E2>> &
976
- * { flatMap: unknown }` rather than `AsyncResult<U, E2>`: this is what you get
977
- * by returning an `AsyncResult` (it satisfies both), but inference runs through
978
- * the `Awaitable` then-channel so `U`/`E2` stay precise instead of collapsing
979
- * to `unknown`, while the `{ flatMap: unknown }` marker still rejects a bare
980
- * `Promise` (it has no `flatMap`). Just return a `Result` or an `AsyncResult`.
981
- */
982
- flatMap<U, E2>(f: (value: T) => Result$1<U, E2> | (Awaitable<Result$1<U, E2>> & {
983
- flatMap: unknown;
984
- })): AsyncResult$1<U, E | E2>;
985
- /**
986
- * Asynchronous {@link ResultMethods.tap | tap}. `f` is synchronous; a throw
987
- * becomes a `Defect`. An async callback is rejected at compile time
988
- * ({@link NotThenable}) — and so is a returned `AsyncResult` (it is
989
- * awaitable). Beware the near-miss: _calling_ an `AsyncResult`-returning
990
- * effect inside the callback without returning it compiles and leaves the
991
- * effect floating — fire-and-forget, never awaited, its `Err`/`Defect`
992
- * unobserved. If the effect returns a `Result`/`AsyncResult`, use
993
- * {@link AsyncResultMethods.flatTap | flatTap}.
994
- */
995
- tap<R>(f: (value: T) => R & NotThenable<R>): AsyncResult$1<T, E>;
996
- /**
997
- * Asynchronous {@link ResultMethods.flatTap | flatTap} — a failable tap that
998
- * keeps the original value. `f` may return a `Result` **or** an `AsyncResult`;
999
- * its `Ok` value is discarded, an `Err`/`Defect` short-circuits, and a throw
1000
- * becomes a `Defect`.
1001
- */
1002
- flatTap<E2>(f: (value: T) => Result$1<unknown, E2> | (Awaitable<Result$1<unknown, E2>> & {
1003
- flatMap: unknown;
1004
- })): AsyncResult$1<T, E | E2>;
1005
- /**
1006
- * Asynchronous {@link ResultMethods.bind | bind} (do-notation). `f` may return
1007
- * a `Result` **or** an `AsyncResult`; its value is bound under `name` in the
1008
- * accumulating scope.
1009
- */
1010
- bind<K extends string, U, E2>(name: K, f: (scope: T) => Result$1<U, E2> | (Awaitable<Result$1<U, E2>> & {
1011
- flatMap: unknown;
1012
- })): AsyncResult$1<Bound<T, K, U>, E | E2>;
1013
- /**
1014
- * Asynchronous {@link ResultMethods.let | let} (do-notation). `f` returns a
1015
- * plain value, bound under `name`. An async callback is rejected at compile
1016
- * time ({@link NotThenable}).
1017
- */
1018
- let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): AsyncResult$1<Bound<T, K, U>, E>;
1019
- /** Asynchronous {@link ResultMethods.as | as}: replaces the value with `value`. */
1020
- as<U>(value: U): AsyncResult$1<U, E>;
1021
- /** Asynchronous {@link ResultMethods.discard | discard}: drops the value, collapsing the success type to `void`. */
1022
- discard(): AsyncResult$1<void, E>;
1023
- /**
1024
- * Asynchronous {@link ResultMethods.ensure | ensure}: validate the success
1025
- * value — and, with a type-guard predicate (this overload), **refine** it —
1026
- * failing into the modeled channel with `Err(onFail(value))`. Both callbacks
1027
- * are synchronous (an async `onFail` is rejected at compile time,
1028
- * {@link NotThenable}); a throw in either becomes a `Defect`.
1029
- */
1030
- ensure<U extends T, E2>(predicate: (value: T) => value is U, onFail: (value: T) => E2 & NotThenable<E2>): AsyncResult$1<U, E | E2>;
1031
- /** Boolean form of the asynchronous {@link ResultMethods.ensure | ensure} — validates without refining, keeping `T`. */
1032
- ensure<E2>(predicate: (value: T) => boolean, onFail: (value: T) => E2 & NotThenable<E2>): AsyncResult$1<T, E | E2>;
1033
- /**
1034
- * Asynchronous {@link ResultMethods.mapErrCases | mapErrCases} — the same exhaustive
1035
- * {@link ErrMatcher} form; the combinator calls `.exhaustive()`.
1036
- */
1037
- mapErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): AsyncResult$1<T, MatchErrOut<M>>;
1038
- /**
1039
- * Asynchronous {@link ResultMethods.flatMapErrCases | flatMapErrCases} — the same
1040
- * exhaustive {@link ErrMatcher} form. Unlike the sync form, a branch may
1041
- * return a `Result` **or** an `AsyncResult`.
1042
- */
1043
- flatMapErrCases<M extends ExhaustiveMatch<Result$1<unknown, unknown> | AsyncResult$1<unknown, unknown> | Defect>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): AsyncResult$1<T | OkOf<MatchOut<M>> | AsyncOkOf<MatchOut<M>>, ErrOf<MatchOut<M>> | AsyncErrOf<MatchOut<M>>>;
1044
- /**
1045
- * Asynchronous {@link ResultMethods.recoverErrCases | recoverErrCases} — the same
1046
- * exhaustive {@link ErrMatcher} form. Branches are synchronous; a throw
1047
- * becomes a `Defect`.
1048
- */
1049
- recoverErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): AsyncResult$1<T | MatchErrOut<M>, never>;
1050
- /**
1051
- * Asynchronous {@link ResultMethods.tapErrCases | tapErrCases}. `f` is synchronous; if it
1052
- * throws — or a branch returns the injected `defect(cause)` marker, the
1053
- * expression-position form of a throw — the result is a `Defect` whose cause
1054
- * is an `AggregateError` of `[thrown, original failure]` — observing a failure
1055
- * never destroys it. An
1056
- * async branch is rejected at compile time ({@link NotThenable} on the
1057
- * builder output) — other branch results are discarded, so a rejected
1058
- * `Promise` would float unobserved. The
1059
- * {@link AsyncResultMethods.tap | tap} fire-and-forget caveat applies here
1060
- * too — a failable effect belongs in
1061
- * {@link AsyncResultMethods.flatTapErrCases | flatTapErrCases}.
1062
- */
1063
- tapErrCases<R>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<R & NotThenable<R>>): AsyncResult$1<T, E>;
1064
- /**
1065
- * Asynchronous {@link ResultMethods.flatTapErrCases | flatTapErrCases} — the
1066
- * error-channel mirror of `flatTap`. `f` may return a `Result` **or** an
1067
- * `AsyncResult`; its `Ok` value is discarded, an `Err`/`Defect` from `f`
1068
- * threads through, and if `f` throws — or a branch returns the injected
1069
- * `defect(cause)` marker, the expression-position form of a throw — the result
1070
- * is a `Defect` whose cause is an `AggregateError` of `[thrown, original
1071
- * failure]` — observing a failure never destroys it.
1072
- */
1073
- flatTapErrCases<E2>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<Result$1<unknown, E2> | AsyncResult$1<unknown, E2>>): AsyncResult$1<T, E | E2>;
1074
- /**
1075
- * Asynchronous {@link ResultMethods.recoverDefect | recoverDefect}. `f` may
1076
- * return a `Result` or an `AsyncResult`.
1077
- */
1078
- recoverDefect<U, E2>(f: (cause: unknown) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<T | U, E | E2>;
1079
- /**
1080
- * Asynchronous {@link ResultMethods.tapDefect | tapDefect}. If `f` throws, the
1081
- * result is a `Defect` whose cause is an `AggregateError` of `[thrown,
1082
- * original failure]` — observing a failure never destroys it. An async
1083
- * callback is rejected at compile time ({@link NotThenable}).
1084
- */
1085
- tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): AsyncResult$1<T, E>;
1086
- /**
1087
- * Asynchronous {@link ResultMethods.tapFailure | tapFailure} — the
1088
- * cross-channel observer. `f` receives the narrowed failure variant
1089
- * ({@link FailureView}); if it throws, the result is a `Defect` whose cause
1090
- * is an `AggregateError` of `[thrown, original failure]` — observing a
1091
- * failure never destroys it. An async callback is rejected at compile time
1092
- * ({@link NotThenable}).
1093
- */
1094
- tapFailure<R>(f: (failure: FailureView<E, T>) => R & NotThenable<R>): AsyncResult$1<T, E>;
1095
- /**
1096
- * Asynchronous {@link ResultMethods.match | match}. Handlers are synchronous
1097
- * (the `errCases` handler returns an exhaustive {@link ErrMatcher} builder, no
1098
- * `defect` helper); resolves to a `Promise` of the folded value.
1099
- */
1100
- match<ROk, RDefect, M extends ExhaustiveMatch<unknown>>(cases: {
1101
- ok: (value: T) => ROk;
1102
- errCases: (matcher: ErrMatcher<E>) => M;
1103
- defect: (cause: unknown) => RDefect;
1104
- }): Promise<ROk | RDefect | MatchOut<M>>;
1105
- /**
1106
- * Asynchronous {@link ResultMethods.get | get}. Compiles only when the
1107
- * error channel is empty (`this: AsyncResult<T, never>`); the returned promise
1108
- * rejects on a `Defect` (rethrowing its cause).
1109
- */
1110
- get(this: AsyncResult$1<T, never>): Promise<T>;
1111
- /**
1112
- * Asynchronous {@link ResultMethods.getErr | getErr}. Compiles only when
1113
- * the success channel is empty (`this: AsyncResult<never, E>`); the returned
1114
- * promise rejects on a `Defect` (rethrowing its cause).
1115
- */
1116
- getErr(this: AsyncResult$1<never, E>): Promise<E>;
1117
- /** Asynchronous {@link ResultMethods.getOr | getOr}. */
1118
- getOr<U>(fallback: U): Promise<T | U>;
1119
- /** Asynchronous {@link ResultMethods.getOrElse | getOrElse}. */
1120
- getOrElse<U>(f: (error: E) => U): Promise<T | U>;
1121
- /** Asynchronous {@link ResultMethods.getOrNull | getOrNull}. */
1122
- getOrNull(): Promise<T | null>;
1123
- /** Asynchronous {@link ResultMethods.getOrUndefined | getOrUndefined}. */
1124
- getOrUndefined(): Promise<T | undefined>;
1125
- /**
1126
- * Asynchronous {@link ResultMethods.getOrThrow | getOrThrow} — the returned
1127
- * promise **rejects** with the modeled error on `Err` (or the original cause
1128
- * on a `Defect`), rather than throwing synchronously. Gated the same way: it
1129
- * compiles only when the error channel is non-empty (`E` is not `never`).
1130
- */
1131
- getOrThrow(this: [E] extends [never] ? "unthrown: getOrThrow is unnecessary here — the Err channel is empty (E = never), so there is nothing to throw. Use get() instead." : AsyncResult$1<T, E>): Promise<T>;
1132
- };
927
+ export declare function validateAll<Rs extends readonly Result<unknown, unknown>[], E2>(results: readonly [...Rs], merge: (errors: NonEmpty<ErrOf<Rs[number]>>) => E2 & NotThenable<E2>): Result<AllOk<Rs, { [K in keyof Rs]: OkOf<Rs[K]>; }>, E2>;
1133
928
  /**
1134
- * The asynchronous counterpart of {@link Result}: an awaitable wrapper carrying
1135
- * the {@link AsyncResultMethods} surface, collapsing to a `Result<T, E>` when
1136
- * `await`-ed.
929
+ * Collect a **record** of {@link Result}s, accumulating every `Err` — the
930
+ * accumulating counterpart of {@link allFromDict}, and the named counterpart of
931
+ * {@link validateAll}.
1137
932
  *
1138
933
  * @remarks
1139
- * **Combinator callbacks are synchronous.** A raw `Promise` may never enter an
1140
- * `AsyncResult` method — that would be an un-qualified async boundary, and its
1141
- * rejection would silently become a `Defect`, skipping the triage that
1142
- * {@link fromPromise} forces. To do further async work, re-enter through a
1143
- * qualified boundary and compose it: `ar.flatMap((v) => fromPromise(work(v),
1144
- * qualify))`. The eliminators (`get`, …) return promises; the binds
1145
- * (`flatMap`, `flatTap`, `flatMapErrCases`, `recoverDefect`) additionally accept an
1146
- * `AsyncResult`. Its combinators are documented one per entry on
1147
- * {@link AsyncResultMethods}.
934
+ * `merge` receives a non-empty list of **`[key, error]` entries**, correlated
935
+ * per key: `{ a: Result<A, E1>; b: Result<B, E2> }` yields
936
+ * `["a", E1] | ["b", E2]`, so a `switch` on the key narrows the error and an
937
+ * impossible pairing does not typecheck. That is what keeps two checks sharing
938
+ * one error type distinguishable. Entries come in key order — `Object.keys`
939
+ * order, then enumerable symbol keys (a symbol key is folded like any other).
1148
940
  *
1149
- * To pattern-match an `AsyncResult`, `await` it first: `match(await ar)`.
941
+ * Every other rule matches {@link validateAll}: any `Defect` dominates and
942
+ * discards the accumulated errors, a throw in `merge` becomes a `Defect`, and
943
+ * `merge` must be synchronous.
1150
944
  *
1151
- * @typeParam T - the success value type.
1152
- * @typeParam E - the modeled error type.
1153
- */
1154
- interface AsyncResult$1<out T, out E> extends Awaitable<Result$1<T, E>>, AsyncResultMethods<T, E> {}
1155
- /**
1156
- * Extract the success type `T` from a `Result` type — derive one type from
1157
- * another instead of restating it (e.g. the payload a function returns).
945
+ * @typeParam R - the record of input `Result` types.
946
+ * @typeParam E2 - the merged error type.
947
+ * @param results - the results to collect, keyed by name.
948
+ * @param merge - folds the collected `[key, error]` entries into one error.
1158
949
  *
1159
- * @typeParam R - the `Result` type to inspect.
950
+ * @category Aggregate
1160
951
  *
1161
952
  * @example
1162
953
  * ```ts
1163
- * type R = Result<User, NotFound>;
1164
- * type U = OkOf<R>; // User
1165
- * type E = ErrOf<R>; // NotFound
1166
- * ```
954
+ * import { validateAllFromDict, Ok, Err } from "unthrown";
1167
955
  *
1168
- * @category Types
956
+ * validateAllFromDict(
957
+ * { vatRate: Err("out of range"), currency: Ok("EUR"), dueDate: Err("past") },
958
+ * (entries) => entries.map(([key, error]) => `${key}: ${error}`).join("; "),
959
+ * );
960
+ * // => Err("vatRate: out of range; dueDate: past")
961
+ * ```
1169
962
  */
1170
- type OkOf<R> = R extends {
1171
- readonly tag: "Ok";
1172
- readonly value: infer T;
1173
- } ? T : never;
963
+ export declare function validateAllFromDict<R extends ResultRecord, E2>(results: R, merge: (entries: NonEmpty<DictErrEntry<R>>) => E2 & NotThenable<E2>): Result<{ [K in keyof R]: OkOf<R[K]>; }, E2>;
1174
964
  /**
1175
- * Extract the error type `E` from a `Result` type — the counterpart of
1176
- * {@link OkOf}.
1177
- *
1178
- * @typeParam R - the `Result` type to inspect.
965
+ * The asynchronous counterpart of {@link validateAll}: collect a tuple/array of
966
+ * {@link AsyncResult}s, accumulating every `Err` into one merged error.
1179
967
  *
1180
- * @example
1181
- * ```ts
1182
- * type E = ErrOf<Result<User, NotFound>>; // NotFound
1183
- * ```
968
+ * @remarks
969
+ * Every {@link validateAll} rule holds, with the inputs resolved
970
+ * **concurrently** (order preserved) — as with {@link allAsync}, no work is
971
+ * short-circuited either way; the fail-fast/accumulating split is purely which
972
+ * errors get reported. The internal promise never rejects: an out-of-contract
973
+ * rejecting thenable becomes a dominating `Defect`. `merge` stays synchronous
974
+ * here too — this is exactly where its rejection would land unqualified in `E`.
975
+ * For a **record**, use {@link validateAllFromDictAsync}.
1184
976
  *
1185
- * @category Types
1186
- */
1187
- type ErrOf<R> = R extends {
1188
- readonly tag: "Err";
1189
- readonly error: infer E;
1190
- } ? E : never;
1191
- /**
1192
- * Extract the success type `T` from an {@link AsyncResult} type — the async
1193
- * counterpart of {@link OkOf}.
977
+ * @typeParam Rs - the tuple/array of input `AsyncResult` types.
978
+ * @typeParam E2 - the merged error type.
979
+ * @param results - the async results to collect.
980
+ * @param merge - folds the collected errors into one modeled error.
1194
981
  *
1195
- * @typeParam R - the `AsyncResult` type to inspect.
982
+ * @category Aggregate
1196
983
  *
1197
984
  * @example
1198
985
  * ```ts
1199
- * type T = AsyncOkOf<AsyncResult<User, NotFound>>; // User
1200
- * ```
986
+ * import { validateAllAsync, OkAsync, ErrAsync } from "unthrown";
1201
987
  *
1202
- * @category Types
988
+ * const checked = validateAllAsync(
989
+ * [OkAsync(1), ErrAsync("stock"), ErrAsync("credit")],
990
+ * (errors) => errors.join(" and "),
991
+ * );
992
+ * // (await checked) => Err("stock and credit")
993
+ * ```
1203
994
  */
1204
- type AsyncOkOf<R> = R extends Awaitable<infer Res> ? OkOf<Res> : never;
995
+ export declare function validateAllAsync<Rs extends readonly AsyncResult<unknown, unknown>[], E2>(results: readonly [...Rs], merge: (errors: NonEmpty<AsyncErrOf<Rs[number]>>) => E2 & NotThenable<E2>): AsyncResult<AllOk<Rs, { [K in keyof Rs]: AsyncOkOf<Rs[K]>; }>, E2>;
1205
996
  /**
1206
- * Extract the error type `E` from an {@link AsyncResult} type — the async
1207
- * counterpart of {@link ErrOf}.
997
+ * The asynchronous counterpart of {@link validateAllFromDict}: collect a record
998
+ * of {@link AsyncResult}s, accumulating every `Err` into one merged error.
1208
999
  *
1209
- * @typeParam R - the `AsyncResult` type to inspect.
1000
+ * @remarks
1001
+ * The {@link validateAllFromDict} rules, over inputs resolved concurrently as
1002
+ * in {@link validateAllAsync}.
1003
+ *
1004
+ * @typeParam R - the record of input `AsyncResult` types.
1005
+ * @typeParam E2 - the merged error type.
1006
+ * @param results - the async results to collect, keyed by name.
1007
+ * @param merge - folds the collected `[key, error]` entries into one error.
1008
+ *
1009
+ * @category Aggregate
1210
1010
  *
1211
1011
  * @example
1212
1012
  * ```ts
1213
- * type E = AsyncErrOf<AsyncResult<User, NotFound>>; // NotFound
1214
- * ```
1013
+ * import { validateAllFromDictAsync, OkAsync, ErrAsync } from "unthrown";
1215
1014
  *
1216
- * @category Types
1015
+ * const checked = validateAllFromDictAsync(
1016
+ * { stock: ErrAsync("none left"), credit: OkAsync(500) },
1017
+ * (entries) => entries.map(([key, error]) => `${key}: ${error}`).join("; "),
1018
+ * );
1019
+ * // (await checked) => Err("stock: none left")
1020
+ * ```
1217
1021
  */
1218
- type AsyncErrOf<R> = R extends Awaitable<infer Res> ? ErrOf<Res> : never;
1022
+ export declare function validateAllFromDictAsync<R extends AsyncResultRecord, E2>(results: R, merge: (entries: NonEmpty<AsyncDictErrEntry<R>>) => E2 & NotThenable<E2>): AsyncResult<{ [K in keyof R]: AsyncOkOf<R[K]>; }, E2>;
1219
1023
  //#endregion
1220
- //#region src/constructors.d.ts
1024
+ //#region src/facade.d.ts
1221
1025
  /**
1222
- * Construct a successful `void` {@link Result} — `Result<void, never>` —
1223
- * sparing you `Ok(undefined)` and typing the success channel `void`, not
1224
- * `undefined`.
1026
+ * Companion object grouping the **`Result`-producing** entry points under a
1027
+ * single, discoverable namespace: {@link Result.Ok}, {@link Result.Err},
1028
+ * {@link Result.Do}, {@link Result.fromNullable}, {@link Result.fromThrowable},
1029
+ * {@link Result.fromSafeThrowable}, {@link Result.all},
1030
+ * {@link Result.allFromDict}, {@link Result.validateAll},
1031
+ * {@link Result.validateAllFromDict}, {@link Result.isOk}, {@link Result.isErr},
1032
+ * {@link Result.isDefect}, {@link Result.isResult}.
1033
+ *
1034
+ * @remarks
1035
+ * Purely additive sugar — each member **is** the corresponding free function.
1036
+ * The free functions remain the primary, tree-shakeable API; importing only
1037
+ * `{ Ok }` never pulls this object in. The value `Result` and the type
1038
+ * {@link Result} share one name (the companion-object pattern).
1039
+ *
1040
+ * The **async** entry points live on the sibling {@link AsyncResult} companion
1041
+ * (`AsyncResult.fromPromise`, `AsyncResult.all`, …), grouped by what they
1042
+ * return — a static lives in exactly one namespace.
1043
+ *
1044
+ * @category Facade
1225
1045
  *
1226
1046
  * @example
1227
1047
  * ```ts
1228
- * import { Ok } from "unthrown";
1229
- *
1230
- * Ok(); // => a void success: Result<void, never>
1048
+ * import { Result } from "unthrown";
1049
+ * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).get(); // => 2
1231
1050
  * ```
1232
- *
1233
- * @category Constructors
1234
1051
  */
1235
- declare function Ok(): Result$1<void, never>;
1052
+ export declare const Result: {
1053
+ readonly Ok: typeof Ok;
1054
+ readonly Err: typeof Err;
1055
+ readonly Do: typeof Do;
1056
+ readonly fromNullable: typeof fromNullable;
1057
+ readonly fromThrowable: typeof fromThrowable;
1058
+ readonly fromSafeThrowable: typeof fromSafeThrowable;
1059
+ readonly all: typeof all;
1060
+ readonly allFromDict: typeof allFromDict;
1061
+ readonly validateAll: typeof validateAll;
1062
+ readonly validateAllFromDict: typeof validateAllFromDict;
1063
+ readonly isOk: typeof isOk;
1064
+ readonly isErr: typeof isErr;
1065
+ readonly isDefect: typeof isDefect;
1066
+ readonly isResult: typeof isResult;
1067
+ };
1236
1068
  /**
1237
- * Construct a successful {@link Result}.
1069
+ * The core type of the library: a computation that has either succeeded with a
1070
+ * value of type `T` or failed with a *modeled* error of type `E`. Shares its
1071
+ * name with the {@link Result | companion object} above (the value and type are
1072
+ * one name); this is the type half.
1238
1073
  *
1239
- * @typeParam T - the success value type.
1240
- * @param value - the success value to wrap.
1074
+ * @remarks
1075
+ * A `Result` is a **discriminated union** of three variants, distinguished by a
1076
+ * `tag` of `"Ok"` | `"Err"` | `"Defect"`:
1241
1077
  *
1242
- * @example
1243
- * ```ts
1244
- * import { Ok } from "unthrown";
1078
+ * - **`Ok`** — a success carrying a `value: T`.
1079
+ * - **`Err`** — a modeled, anticipated failure carrying an `error: E`.
1080
+ * - **`Defect`** — an *unmodeled* failure carrying an unknown `cause`. A Defect
1081
+ * never appears in `E`; it is the library's third, out-of-band channel.
1245
1082
  *
1246
- * Ok(2).map((n) => n + 1); // => Ok(3)
1247
- * Ok(42).get(); // => 42
1248
- * ```
1083
+ * Because it is a real union, you can match it natively (a `switch` on `tag`, or
1084
+ * the built-in `match(...).with({ tag: "Ok" }, …).exhaustive()`), *and* it
1085
+ * carries the full method surface for fluent chaining. Either way, the payload
1086
+ * (`value`/`error`/`cause`) is only reachable after you narrow — so "check
1087
+ * before you access" still holds.
1088
+ *
1089
+ * TypeDoc can't list a union's methods on this alias: its fluent combinators
1090
+ * (`map`, `flatMap`, `match`, `get`, …) are documented one per entry on
1091
+ * {@link ResultMethods} — the shared method surface every variant carries. For
1092
+ * "which one do I reach for?", see the
1093
+ * [Choosing a combinator](/reference/combinators) guide.
1249
1094
  *
1250
- * @category Constructors
1251
- */
1252
- declare function Ok<T>(value: T): Result$1<T, never>;
1253
- /**
1254
- * Construct a failed {@link Result} carrying a **modeled** error.
1095
+ * @typeParam T - the success value type.
1096
+ * @typeParam E - the modeled error type (only anticipated domain failures).
1255
1097
  *
1256
- * @typeParam E - the modeled error type.
1257
- * @param error - the domain error to wrap.
1098
+ * @category Facade
1258
1099
  *
1259
1100
  * @example
1260
1101
  * ```ts
1261
- * import { Err } from "unthrown";
1102
+ * import { Ok, Err, type Result } from "unthrown";
1262
1103
  *
1263
- * Err("not_found").map((n) => n + 1); // => Err("not_found") (map skipped)
1264
- * Err("not_found").getErr(); // => "not_found"
1265
- * ```
1104
+ * function half(n: number): Result<number, "odd"> {
1105
+ * return n % 2 === 0 ? Ok(n / 2) : Err("odd");
1106
+ * }
1266
1107
  *
1267
- * @category Constructors
1108
+ * const message = half(10).match({
1109
+ * ok: (n) => `got ${n}`,
1110
+ * // every case of `E` named — here the one literal it holds
1111
+ * errCases: (matcher) => matcher.with("odd", () => "failed: odd"),
1112
+ * defect: (cause) => `bug: ${String(cause)}`,
1113
+ * });
1114
+ * ```
1268
1115
  */
1269
- declare function Err<E>(error: E): Result$1<never, E>;
1116
+ export type Result<T, E> = OkView<T, E> | ErrView<E, T> | DefectView<T, E>;
1270
1117
  /**
1271
- * Construct a successful `void` {@link AsyncResult} — `AsyncResult<void, never>`
1272
- * — the pre-lifted form of the no-arg {@link Ok}, sparing you
1273
- * `Ok(undefined).toAsync()`.
1118
+ * Companion object grouping the **`AsyncResult`-producing** entry points under
1119
+ * the matching namespace: {@link AsyncResult.Ok}, {@link AsyncResult.Err},
1120
+ * {@link AsyncResult.Do}, {@link AsyncResult.fromExecutor},
1121
+ * {@link AsyncResult.fromPromise}, {@link AsyncResult.fromSafePromise},
1122
+ * {@link AsyncResult.all}, {@link AsyncResult.allFromDict},
1123
+ * {@link AsyncResult.validateAll}, {@link AsyncResult.validateAllFromDict}.
1124
+ *
1125
+ * @remarks
1126
+ * The async sibling of {@link Result}. Statics are grouped by what they
1127
+ * **return**, so the pre-lifted constructors, `fromExecutor`,
1128
+ * `fromPromise`/`fromSafePromise`, and the async aggregates sit here rather
1129
+ * than on {@link Result}; the namespace
1130
+ * already conveys "async", so the members drop the `Async` suffix their free
1131
+ * functions carry (`AsyncResult.Ok` is `OkAsync`; `AsyncResult.Err` is
1132
+ * `ErrAsync`; `AsyncResult.Do` is `DoAsync`; `AsyncResult.all` is `allAsync`;
1133
+ * `AsyncResult.allFromDict` is
1134
+ * `allFromDictAsync`; `AsyncResult.validateAll` is `validateAllAsync`). Like
1135
+ * {@link Result}, the free functions remain the
1136
+ * primary, tree-shakeable API; the value `AsyncResult` and the type
1137
+ * {@link AsyncResult} share one name.
1138
+ *
1139
+ * @category Facade
1274
1140
  *
1275
1141
  * @example
1276
1142
  * ```ts
1277
- * import { OkAsync } from "unthrown";
1278
- *
1279
- * OkAsync(); // => a void success: AsyncResult<void, never>
1143
+ * import { AsyncResult } from "unthrown";
1144
+ * const user = await AsyncResult.fromPromise(
1145
+ * fetchUser(id),
1146
+ * (c, defect) => defect(c),
1147
+ * );
1148
+ * user.get(); // => the fetched user (on success)
1280
1149
  * ```
1281
- *
1282
- * @category Constructors
1283
1150
  */
1284
- declare function OkAsync(): AsyncResult$1<void, never>;
1151
+ export declare const AsyncResult: {
1152
+ readonly Ok: typeof OkAsync;
1153
+ readonly Err: typeof ErrAsync;
1154
+ readonly Do: typeof DoAsync;
1155
+ readonly fromExecutor: typeof fromExecutor;
1156
+ readonly fromPromise: typeof fromPromise;
1157
+ readonly fromSafePromise: typeof fromSafePromise;
1158
+ readonly all: typeof allAsync;
1159
+ readonly allFromDict: typeof allFromDictAsync;
1160
+ readonly validateAll: typeof validateAllAsync;
1161
+ readonly validateAllFromDict: typeof validateAllFromDictAsync;
1162
+ };
1285
1163
  /**
1286
- * Construct a successful {@link AsyncResult} from a pure value — the pre-lifted
1287
- * form of {@link Ok}, sparing you `Ok(value).toAsync()`.
1164
+ * The asynchronous counterpart of {@link Result}: an awaitable wrapper carrying
1165
+ * the {@link AsyncResultMethods} surface, collapsing to a `Result<T, E>` when
1166
+ * `await`-ed. Shares its name with the {@link AsyncResult | companion object}
1167
+ * above (value and type are one name); this is the type half.
1168
+ *
1169
+ * @remarks
1170
+ * **Combinator callbacks are synchronous.** A raw `Promise` may never enter an
1171
+ * `AsyncResult` method — that would be an un-qualified async boundary, and its
1172
+ * rejection would silently become a `Defect`, skipping the triage that
1173
+ * {@link fromPromise} forces. To do further async work, re-enter through a
1174
+ * qualified boundary and compose it: `ar.flatMap((v) => fromPromise(work(v),
1175
+ * qualify))`. The eliminators (`get`, …) return promises; the binds
1176
+ * (`flatMap`, `flatTap`, `flatMapErrCases`, `recoverDefect`) additionally accept an
1177
+ * `AsyncResult`. Its combinators are documented one per entry — with their
1178
+ * async signatures — on {@link AsyncResultMethods}. For "which one do I reach
1179
+ * for?", see the [Choosing a combinator](/reference/combinators) guide.
1288
1180
  *
1289
- * @remarks
1290
- * Reach for this on the synchronous/early branch of an `AsyncResult`-returning
1291
- * function, so both branches share one return type without a trailing
1292
- * `.toAsync()`. Named with the `Async` suffix the async free functions carry
1293
- * (`allAsync`, `allFromDictAsync`); the {@link AsyncResult} companion aliases it
1294
- * as `AsyncResult.Ok` (the namespace already says "async", so the suffix drops).
1181
+ * To pattern-match an `AsyncResult`, `await` it first: `match(await ar)`.
1295
1182
  *
1296
1183
  * @typeParam T - the success value type.
1297
- * @param value - the success value to wrap.
1184
+ * @typeParam E - the modeled error type.
1298
1185
  *
1299
- * @example
1300
- * ```ts
1301
- * import { OkAsync, type AsyncResult } from "unthrown";
1186
+ * @category Facade
1187
+ */
1188
+ export interface AsyncResult<out T, out E> extends Awaitable<Result<T, E>>, AsyncResultMethods<T, E> {}
1189
+ //#endregion
1190
+ //#region src/types.d.ts
1191
+ /**
1192
+ * Flatten an intersection into a single object literal so accumulated `bind` /
1193
+ * `let` scopes display cleanly (`{ a; b }` rather than `{ a } & { b }`).
1302
1194
  *
1303
- * function loadItems(ids: string[]): AsyncResult<Item[], never> {
1304
- * if (ids.length === 0) return OkAsync([]); // no more Ok([]).toAsync()
1305
- * return itemRepository.load(ids);
1306
- * }
1307
- * ```
1195
+ * @internal
1196
+ */
1197
+ type Prettify<T> = { [K in keyof T]: T[K]; } & {};
1198
+ /**
1199
+ * The scope produced by a `bind` / `let` step: `T` with `K` added (as a readonly
1200
+ * property of type `U`). `Omit<T, K>` first drops any existing `K`, so re-binding
1201
+ * a name **overwrites** it — matching the runtime spread — rather than producing
1202
+ * an unsound `T[K] & U` intersection.
1308
1203
  *
1309
- * @category Constructors
1204
+ * @internal
1310
1205
  */
1311
- declare function OkAsync<T>(value: T): AsyncResult$1<T, never>;
1206
+ type Bound<T, K extends string, U> = Prettify<Omit<T, K> & { readonly [P in K]: U; }>;
1312
1207
  /**
1313
- * Construct a failed {@link AsyncResult} carrying a **modeled** error — the
1314
- * pre-lifted form of {@link Err}, sparing you `Err(error).toAsync()`.
1208
+ * Compile-time rejection of a thenable callback result — the type-level
1209
+ * enforcement of "combinator callbacks are synchronous" (see the
1210
+ * {@link AsyncResult} remarks).
1315
1211
  *
1316
1212
  * @remarks
1317
- * The error-channel mirror of {@link OkAsync}; see it for the naming and the
1318
- * `AsyncResult.Err` companion alias.
1319
- *
1320
- * @typeParam E - the modeled error type.
1321
- * @param error - the domain error to wrap.
1322
- *
1323
- * @example
1324
- * ```ts
1325
- * import { ErrAsync } from "unthrown";
1213
+ * Resolves to `unknown` (a no-op in an intersection) for any non-thenable `R`,
1214
+ * and to an explanatory string-literal type when `R` is a `PromiseLike` — so an
1215
+ * `async` callback fails to compile with the explanation in the error. Without
1216
+ * this, `async () => …` would be assignable to `() => void`, and its rejection
1217
+ * would escape the pipeline as an unhandled rejection instead of a `Defect`.
1218
+ * Lift async work with {@link fromPromise} and compose it with `flatMap`.
1326
1219
  *
1327
- * ErrAsync("not_found"); // AsyncResult<never, string>
1328
- * ```
1220
+ * Spelled with `Extract`, not `[R] extends [PromiseLike<…>]`, so the ban also
1221
+ * fires when only SOME arms of a union return are thenable — a *sometimes*-async
1222
+ * callback (`flag ? 1 : work()`) is still an unawaited effect whose rejection
1223
+ * the pipeline never sees. The tuple-wrapped form is false for a partial union
1224
+ * and let exactly that through. This is the same reasoning `fromPromise`'s
1225
+ * async-qualify guard already used.
1329
1226
  *
1330
- * @category Constructors
1227
+ * @typeParam R - the callback's inferred return type.
1228
+ * @category Types
1331
1229
  */
1332
- declare function ErrAsync<E>(error: E): AsyncResult$1<never, E>;
1230
+ type NotThenable<R> = [Extract<R, PromiseLike<unknown>>] extends [never] ? unknown : "unthrown: combinator callbacks are synchronous — lift async work with fromPromise and compose with flatMap";
1333
1231
  /**
1334
- * Type guard: narrow a {@link Result} to its `Ok` variant, exposing `.value`.
1335
- *
1336
- * @returns `true` when `r` is `Ok`.
1337
- *
1338
- * @example
1339
- * ```ts
1340
- * import { isOk, Ok, Err, type Result } from "unthrown";
1341
- *
1342
- * isOk(Ok(1)); // => true
1343
- * isOk(Err("boom")); // => false
1232
+ * The built-in match builder over an error union `E`, as produced by
1233
+ * `match(error)`. This is what an error combinator's callback receives — chain
1234
+ * `.with(pattern, handler)` on it; the combinator itself calls `.exhaustive()`,
1235
+ * so the callback returns the **un-terminated** builder.
1344
1236
  *
1345
- * declare const r: Result<number, string>;
1346
- * if (isOk(r)) r.value; // number, narrowed
1347
- * ```
1237
+ * @remarks
1238
+ * Named via `ReturnType<typeof match<E>>` (i.e. `Matcher<E, E, never>`),
1239
+ * keeping this alias stable however the builder evolves.
1348
1240
  *
1349
- * @category Guards
1241
+ * @typeParam E - the error union being matched.
1242
+ * @category Types
1350
1243
  */
1351
- declare function isOk<T, E>(r: Result$1<T, E>): r is OkView<T, E>;
1244
+ type ErrMatcher<E> = ReturnType<typeof match<E>>;
1352
1245
  /**
1353
- * Type guard: narrow a {@link Result} to its `Err` variant, exposing `.error`.
1354
- *
1355
- * @returns `true` when `r` is `Err`.
1356
- *
1357
- * @example
1358
- * ```ts
1359
- * import { isErr, Ok, Err, type Result } from "unthrown";
1246
+ * The shape an error-combinator callback must return: an **exhaustive**
1247
+ * match builder. `exhaustive` is required to be *callable* — on a builder
1248
+ * that hasn't covered every case the matcher types it as a branded diagnostic
1249
+ * (not a function), so a non-exhaustive chain fails to satisfy this and errors
1250
+ * at the call site. `run` carries the output type.
1360
1251
  *
1361
- * isErr(Err("boom")); // => true
1362
- * isErr(Ok(1)); // => false
1252
+ * @typeParam O - the union of the branch return types (the builder's output).
1253
+ * @internal
1254
+ */
1255
+ type ExhaustiveMatch<O> = {
1256
+ exhaustive: (...args: never[]) => unknown;
1257
+ run: () => O;
1258
+ };
1259
+ /**
1260
+ * The output of an `ExhaustiveMatch` — the union of its branch returns.
1363
1261
  *
1364
- * declare const r: Result<number, string>;
1365
- * if (isErr(r)) r.error; // string, narrowed
1366
- * ```
1262
+ * @internal
1263
+ */
1264
+ type MatchOut<M> = M extends ExhaustiveMatch<infer O> ? O : never;
1265
+ /**
1266
+ * The outgoing modeled-error type a transforming match produces: the builder's
1267
+ * output with the `Defect` arm **subtracted** (`Exclude<O, Defect>`, the same
1268
+ * inference as the boundary `qualify`, Thesis #3). A branch that returns
1269
+ * `defect(cause)` therefore contributes nothing to the modeled channel.
1367
1270
  *
1368
- * @category Guards
1271
+ * @internal
1369
1272
  */
1370
- declare function isErr<T, E>(r: Result$1<T, E>): r is ErrView<E, T>;
1273
+ type MatchErrOut<M> = Exclude<MatchOut<M>, Defect>;
1371
1274
  /**
1372
- * Type guard: narrow a {@link Result} to its `Defect` variant, exposing `.cause`.
1275
+ * The phantom rest-tuple guard that bans an **async branch** in the
1276
+ * non-awaiting error transformers (`mapErrCases` / `recoverErrCases`): empty
1277
+ * for a synchronous builder output, an impossible extra argument (labelled
1278
+ * with the explanation) when any branch returns a thenable.
1373
1279
  *
1374
1280
  * @remarks
1375
- * A `Defect` has no public constructor — it only arises at a boundary (e.g. a
1376
- * callback throwing inside a combinator). This guard is how you detect one.
1377
- *
1378
- * @returns `true` when `r` is a `Defect`.
1379
- *
1380
- * @example
1381
- * ```ts
1382
- * import { isDefect, Ok } from "unthrown";
1383
- *
1384
- * // A throw inside a combinator is captured as a Defect:
1385
- * const r = Ok(1).map(() => {
1386
- * throw new Error("boom");
1387
- * });
1388
- * isDefect(r); // => true
1389
- * isDefect(Ok(1)); // => false
1390
- *
1391
- * if (isDefect(r)) r.cause; // unknown, narrowed
1392
- * ```
1281
+ * Those two run the matched branch without awaiting it, so an `async` branch
1282
+ * put `Err(<Promise>)` / `Ok(<Promise>)` in the channel — a `Promise` in `E` is
1283
+ * the un-triaged value Thesis #3 forbids — and its rejection floated
1284
+ * unobserved.
1285
+ *
1286
+ * The first test, `[O] extends [Pass]`, is the **generic escape**: `Pass` is
1287
+ * what the receiver's own channels already hold (`E` for `mapErrCases`,
1288
+ * `T | E` for `recoverErrCases`), so re-emitting the error — the sanctioned
1289
+ * `P._` use inside a helper generic in `E` — resolves to `[]` even while `E` is
1290
+ * an unresolved type parameter. No `NotThenable`-style check can: TypeScript
1291
+ * cannot decide "is not thenable" for an unresolved `E`, so `M &
1292
+ * NotThenable<…>` (the `tapErrCases` spelling) rejected that helper outright.
1293
+ * A thenable already in `E` is no new hole. Encoded as a trailing phantom
1294
+ * (the `fromPromise` shape) rather than on the callback's return, which keeps
1295
+ * the inference-bearing callback free of conditional types. A `this` gate
1296
+ * would print the message more directly but breaks the verified `out T, out E`
1297
+ * variance annotations (`Pass` mentions both). An `any` output (a mocked
1298
+ * branch) is let through, as `U & NotThenable<U>` does.
1393
1299
  *
1394
- * @category Guards
1300
+ * @internal
1395
1301
  */
1396
- declare function isDefect<T, E>(r: Result$1<T, E>): r is DefectView<T, E>;
1397
- //#endregion
1398
- //#region src/core.d.ts
1302
+ type SyncBranches<O, Pass> = [O] extends [Pass | Defect] ? [] : 0 extends 1 & O ? [] : [Extract<O, PromiseLike<unknown>>] extends [never] ? [] : [unthrown_errorMatcherBranchesAreSynchronous: "an async branch would put a Promise in the channel — lift async work with fromPromise and use flatMapErrCases"];
1399
1303
  /**
1400
- * Thrown by a {@link Result}'s `get` / `getErr` when the assertion is
1401
- * wrong on a *modeled* result — `get()` on an `Err`, or `getErr()` on an
1402
- * `Ok`.
1304
+ * The marker that keeps a bare `Promise` out of the async binds (`flatMap` /
1305
+ * `flatTap` / `bind`) and the awaiting error combinators (`flatMapErrCases` /
1306
+ * `flatTapErrCases`): every `AsyncResult` has a `flatMap`, a `Promise` has
1307
+ * none, so a raw rejection cannot bypass qualification.
1403
1308
  *
1404
1309
  * @remarks
1405
- * The offending value is exposed two ways: the typed {@link GetError.error}
1406
- * property for programmatic access, and the standard `Error.cause` for the
1407
- * runtime and devtools to chain — when `E` is an `Error` (e.g. a `TaggedError`)
1408
- * its original stack is printed under "caused by".
1310
+ * Structurally it is just `{ flatMap: unknown }`; the **name** is the
1311
+ * diagnostic. An `async` callback fails with "Property 'flatMap' is missing in
1312
+ * type 'Promise<…>' but required in type 'ReturnAnAsyncResultNotAPromise'",
1313
+ * which says what to do — instead of the anonymous `{ flatMap: unknown }`, or
1314
+ * (in the error combinators, which named `AsyncResult` itself) a list of 25
1315
+ * missing methods. It always sits beside `Awaitable<Result<…>>`, so inference
1316
+ * runs through the then-channel and stays junk-free (see
1317
+ * {@link AsyncResultMethods.flatMap}).
1409
1318
  *
1410
- * A `Defect` is never wrapped in a `GetError`: its original cause is
1411
- * re-thrown (with its original stack) instead.
1412
- *
1413
- * `get()` and `getErr()` are type-gated (`this: Result<T, never>` /
1414
- * `Result<never, E>`), so the wrong-variant branch that throws this is
1415
- * unreachable through well-typed code — it remains only as a defensive guard
1416
- * against unsound runtime misuse (e.g. an `as` cast past the gate).
1319
+ * @internal
1320
+ */
1321
+ type ReturnAnAsyncResultNotAPromise = {
1322
+ flatMap: unknown;
1323
+ };
1324
+ /**
1325
+ * The fluent method surface every {@link Result} variant carries — the
1326
+ * combinators (`map`, `flatMap`, `mapErrCases`, `match`, `get`, …), documented one
1327
+ * per entry below. Factored out so the three variants ({@link OkView},
1328
+ * {@link ErrView}, {@link DefectView}) can each intersect it; {@link AsyncResult}
1329
+ * mirrors this surface with async signatures.
1417
1330
  *
1418
- * @typeParam E - the type of the {@link GetError.error} it carries.
1331
+ * @remarks
1332
+ * This type exists to **document** the surface and to power narrowing — not to be
1333
+ * authored against. You obtain it by holding a `Result` (or `AsyncResult`), never
1334
+ * by implementing your own `Result`-like; treat it as read-only reference.
1419
1335
  *
1420
- * @category Errors
1336
+ * @typeParam T - the success value type.
1337
+ * @typeParam E - the modeled error type.
1338
+ * @category Methods
1421
1339
  */
1422
- declare class GetError<E = unknown> extends Error {
1340
+ type ResultMethods<out T, out E> = {
1341
+ /**
1342
+ * Transform the success value with `f`.
1343
+ *
1344
+ * Runs `f` only on `Ok`; `Err` and `Defect` pass through untouched. If `f`
1345
+ * throws, the thrown value is captured as a `Defect`.
1346
+ *
1347
+ * An async callback is rejected at compile time ({@link NotThenable}).
1348
+ *
1349
+ * @typeParam U - the mapped success type.
1350
+ * @param f - maps the current success value to a new one.
1351
+ */
1352
+ map<U>(f: (value: T) => U & NotThenable<U>): Result<U, E>;
1353
+ /**
1354
+ * Sequence a dependent, `Result`-returning step (monadic bind).
1355
+ *
1356
+ * Runs `f` only on `Ok`; `Err` and `Defect` pass through. The error channels
1357
+ * combine, widening to `E | E2`. If `f` throws, the throw becomes a `Defect`.
1358
+ *
1359
+ * @typeParam U - the success type of the next step.
1360
+ * @typeParam E2 - the error type the next step may introduce.
1361
+ * @param f - produces the next `Result` from the current success value.
1362
+ */
1363
+ flatMap<U, E2>(f: (value: T) => Result<U, E2>): Result<U, E | E2>;
1364
+ /**
1365
+ * Run a side effect on the success value and pass the `Result` through
1366
+ * unchanged.
1367
+ *
1368
+ * Runs only on `Ok`. If `f` throws, the throw becomes a `Defect`. An async
1369
+ * callback is rejected at compile time ({@link NotThenable}).
1370
+ *
1371
+ * @remarks
1372
+ * `f`'s return value is **ignored** — a `Result` returned by the effect
1373
+ * compiles but is discarded, `Err` and all. If the effect can fail, sequence
1374
+ * it instead of tapping it: a `Result`-returning effect goes in
1375
+ * {@link ResultMethods.flatTap | flatTap}; an `AsyncResult`-returning effect
1376
+ * cannot be sequenced from the sync surface — lift the chain with
1377
+ * {@link ResultMethods.toAsync | toAsync} and use the async
1378
+ * {@link AsyncResultMethods.flatTap | flatTap} (which accepts both).
1379
+ *
1380
+ * @param f - the side effect (its return value is ignored).
1381
+ */
1382
+ tap<R>(f: (value: T) => R & NotThenable<R>): Result<T, E>;
1383
+ /**
1384
+ * Run a **failable** side effect on the success value, keeping the original
1385
+ * value but threading the effect's error.
1386
+ *
1387
+ * @remarks
1388
+ * This is to {@link ResultMethods.tap | tap} what
1389
+ * {@link ResultMethods.flatMap | flatMap} is to {@link ResultMethods.map | map}:
1390
+ * `f` returns a `Result`, but its **success value is discarded** — on success
1391
+ * the original value flows through (`Result<T, E | E2>`), while an `Err` (or
1392
+ * `Defect`) from `f` short-circuits. Runs only on `Ok`; `Err` and `Defect` pass
1393
+ * through. If `f` throws, the throw becomes a `Defect`. Use it for a validation
1394
+ * or write whose _result_ matters but whose _value_ you don't need.
1395
+ *
1396
+ * @typeParam E2 - the error type the effect may introduce.
1397
+ * @param f - the failable side effect; its `Ok` value is ignored.
1398
+ */
1399
+ flatTap<E2>(f: (value: T) => Result<unknown, E2>): Result<T, E | E2>;
1400
+ /**
1401
+ * Do-notation: run `f` for a `Result` and **bind its value** under `name` in
1402
+ * an accumulating object scope.
1403
+ *
1404
+ * @remarks
1405
+ * Begin a chain with {@link Do} (an empty object scope) and grow it step by
1406
+ * step. `f` receives the scope accumulated so far and returns a `Result`; on
1407
+ * `Ok` the value is added as `{ ...scope, [name]: value }`, on `Err`/`Defect`
1408
+ * the chain short-circuits. Errors union (`E | E2`). A throw becomes a
1409
+ * `Defect` — as does calling `bind` on a non-object or non-plain scope (e.g.
1410
+ * `Ok(5).bind`, or a class instance whose getters the merge would drop), which
1411
+ * is misuse: the scope is always a plain object inside a real `Do()` chain.
1412
+ * (`let` is the pure-value counterpart.)
1413
+ *
1414
+ * @typeParam K - the key the bound value is stored under.
1415
+ * @typeParam U - the bound value type.
1416
+ * @typeParam E2 - the error type `f` may introduce.
1417
+ * @param name - the scope key.
1418
+ * @param f - produces a `Result` from the accumulated scope.
1419
+ */
1420
+ bind<K extends string, U, E2>(name: K, f: (scope: T) => Result<U, E2>): Result<Bound<T, K, U>, E | E2>;
1421
+ /**
1422
+ * Do-notation: run `f` for a **plain value** and bind it under `name` in the
1423
+ * accumulating object scope. The pure-value counterpart of {@link ResultMethods.bind | bind}.
1424
+ *
1425
+ * @remarks
1426
+ * `f` receives the scope and returns a value (not a `Result`); it is added as
1427
+ * `{ ...scope, [name]: value }`. Runs only on `Ok`; `Err`/`Defect` pass
1428
+ * through. A throw becomes a `Defect`. An async callback is rejected at
1429
+ * compile time ({@link NotThenable}).
1430
+ *
1431
+ * @typeParam K - the key the value is stored under.
1432
+ * @typeParam U - the value type.
1433
+ * @param name - the scope key.
1434
+ * @param f - computes a value from the accumulated scope.
1435
+ */
1436
+ let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): Result<Bound<T, K, U>, E>;
1437
+ /**
1438
+ * Replace the success value with a constant `value`.
1439
+ *
1440
+ * Runs only on `Ok`; `Err` and `Defect` pass through.
1441
+ *
1442
+ * @typeParam U - the replacement value type.
1443
+ */
1444
+ as<U>(value: U): Result<U, E>;
1445
+ /**
1446
+ * Drop the success value, collapsing the success type to `void`.
1447
+ *
1448
+ * The named form of `map(() => undefined)`. Runs only on `Ok` (the value is
1449
+ * replaced with `undefined`); `Err` and `Defect` pass through. Unlike
1450
+ * `as(undefined)` — which produces `Result<undefined, E>` — the success type
1451
+ * is `void`: the value's story ends here.
1452
+ */
1453
+ discard(): Result<void, E>;
1454
+ /**
1455
+ * Validate the success value — keep the `Ok` when `predicate` holds,
1456
+ * otherwise fail into the **modeled** channel with `Err(onFail(value))`.
1457
+ *
1458
+ * @remarks
1459
+ * The named form of `flatMap((v) => (p(v) ? Ok(v) : Err(e)))`. With a
1460
+ * **type-guard** predicate (`(v): v is U`) the success type is **refined** to
1461
+ * `U` on the way through (this overload). Runs only on `Ok` — a passing value
1462
+ * flows through as the *same* `Ok`; `Err` and `Defect` pass through
1463
+ * untouched. A throw in `predicate` or `onFail` becomes a `Defect`.
1464
+ *
1465
+ * Both callbacks are synchronous: an async `onFail` is rejected at compile
1466
+ * time ({@link NotThenable}), and an async predicate does not type-check
1467
+ * either — its `Promise<boolean>` is not a `boolean` (and, being truthy,
1468
+ * would have silently always passed).
1469
+ *
1470
+ * @typeParam U - the refined success type (type-guard form).
1471
+ * @typeParam E2 - the error type `onFail` produces.
1472
+ * @param predicate - the check; a type guard refines `T` to `U`.
1473
+ * @param onFail - maps the failing value to the modeled error.
1474
+ *
1475
+ * @example
1476
+ * ```ts
1477
+ * // boolean form: gate a value
1478
+ * Ok(-1).ensure((n) => n > 0, (n) => `negative: ${n}`); // Err("negative: -1")
1479
+ *
1480
+ * // type-guard form: refine the success type
1481
+ * declare const r: Result<string | number, "e">;
1482
+ * const s = r.ensure(
1483
+ * (v): v is string => typeof v === "string",
1484
+ * () => "not_a_string" as const,
1485
+ * ); // Result<string, "e" | "not_a_string">
1486
+ * ```
1487
+ */
1488
+ ensure<U extends T, E2>(predicate: (value: T) => value is U, onFail: (value: T) => E2 & NotThenable<E2>): Result<U, E | E2>;
1489
+ /**
1490
+ * Boolean form of {@link ResultMethods.ensure | ensure} — validates without
1491
+ * refining, keeping the success type `T`.
1492
+ */
1493
+ ensure<E2>(predicate: (value: T) => boolean, onFail: (value: T) => E2 & NotThenable<E2>): Result<T, E | E2>;
1494
+ /**
1495
+ * Transform the modeled error by **matching it exhaustively**.
1496
+ *
1497
+ * @remarks
1498
+ * The callback receives `match(error)` (an {@link ErrMatcher}) and the
1499
+ * injected `defect` helper. Chain `.with(pattern, handler)` and **return the
1500
+ * un-terminated builder** — `mapErrCases` calls `.exhaustive()` itself, so a
1501
+ * missing case is a compile error at the call site (there is no `.exhaustive()`
1502
+ * to forget, and no way to slip in `.otherwise()`). The outgoing error type is
1503
+ * the union of the branch returns with the `Defect` arm subtracted
1504
+ * (`Exclude<O, Defect>`) — a branch returning `defect(cause)` converts that case
1505
+ * to a `Defect` and drops it from `E`. Runs only on `Err`; `Ok` and `Defect`
1506
+ * pass through. A branch that throws also becomes a `Defect`. Branches are
1507
+ * **synchronous**: an `async` branch is a compile error (its `Promise` would
1508
+ * land in `E` un-triaged), and a thenable slipped past the types becomes a
1509
+ * `Defect`.
1510
+ *
1511
+ * **Name every case.** Match on anything the matcher supports — `_tag`,
1512
+ * `code`, structural shape, guards — and group the cases that share a handler
1513
+ * with `.with(a, b, handler)`. `.with(P._, …)` is the wildcard **escape
1514
+ * hatch**, not the default: it makes any match exhaustive, so it also absorbs
1515
+ * every case `E` grows later. Two uses are sanctioned — a helper generic in
1516
+ * `E`, where no arm list can prove exhaustiveness against an unresolved type
1517
+ * parameter, and an `E` that is a single type rather than a union of cases
1518
+ * (see {@link P} for both). `@unthrown/oxlint`'s `no-catch-all-pattern` (in
1519
+ * its `recommended` preset) flags the rest.
1520
+ *
1521
+ * @typeParam M - the exhaustive builder the callback returns.
1522
+ * @param f - builds the match over the error (returns the un-terminated builder).
1523
+ * @param _asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases - compile-time
1524
+ * only; never pass it. Empty for synchronous branches; an **async** branch
1525
+ * demands this impossible argument, so the call fails to compile (its name is
1526
+ * the fix).
1527
+ */
1528
+ mapErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M, ..._asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases: SyncBranches<MatchOut<M>, E>): Result<T, MatchErrOut<M>>;
1529
+ /**
1530
+ * Sequence from an `Err` by producing another `Result` — the error-channel
1531
+ * mirror of {@link ResultMethods.flatMap | flatMap}, **matching the error
1532
+ * exhaustively** ({@link ErrMatcher}; the combinator calls `.exhaustive()`).
1533
+ *
1534
+ * Each branch returns a `Result`; the outgoing channels are the unions of the
1535
+ * branch-returned `Result`s' channels. A branch may return `defect(cause)`.
1536
+ * Runs only on `Err`; `Ok` and `Defect` pass through.
1537
+ *
1538
+ * @typeParam M - the exhaustive builder the callback returns.
1539
+ * @param f - builds the match; each branch produces a fallback `Result`.
1540
+ */
1541
+ flatMapErrCases<M extends ExhaustiveMatch<Result<unknown, unknown> | Defect>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): Result<T | OkOf<MatchOut<M>>, ErrOf<MatchOut<M>>>;
1542
+ /**
1543
+ * Recover from an `Err` by producing a success value, emptying the error
1544
+ * channel — **matching the error exhaustively** ({@link ErrMatcher}). Pairs
1545
+ * with {@link ResultMethods.recoverDefect | recoverDefect}.
1546
+ *
1547
+ * @remarks
1548
+ * The result type is `Result<T | U, never>`, but `never` describes only the
1549
+ * **error** channel — a `Defect` can still be present at runtime. A branch may
1550
+ * return `defect(cause)` (which stays a `Defect`, not a recovery). Runs only on
1551
+ * `Err`; `Ok` and `Defect` pass through. Branches are **synchronous**: an
1552
+ * `async` branch is a compile error, and a thenable slipped past the types
1553
+ * becomes a `Defect`.
1554
+ *
1555
+ * @typeParam M - the exhaustive builder the callback returns.
1556
+ * @param f - builds the match; each branch produces a success value.
1557
+ * @param _asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases - compile-time
1558
+ * only; never pass it. Empty for synchronous branches; an **async** branch
1559
+ * demands this impossible argument, so the call fails to compile (its name is
1560
+ * the fix).
1561
+ */
1562
+ recoverErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M, ..._asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases: SyncBranches<MatchOut<M>, T | E>): Result<T | MatchErrOut<M>, never>;
1563
+ /**
1564
+ * Run a side effect on the error — **matched exhaustively** ({@link ErrMatcher})
1565
+ * — and pass the `Result` through unchanged.
1566
+ *
1567
+ * @remarks
1568
+ * The callback builds a match whose branches run side effects; their return
1569
+ * values are ignored and the original `Err` flows through. Exhaustive like the
1570
+ * transformers, and like them it wants every case named — `.with(P._, …)`
1571
+ * remains the wildcard escape hatch. If a branch throws, the
1572
+ * result is a `Defect` whose cause is an `AggregateError` of `[thrown, original
1573
+ * failure]` — observing a failure never destroys it. An **async branch is
1574
+ * rejected at compile time** ({@link NotThenable} on the builder output):
1575
+ * because the branch results are discarded, a returned `Promise` would float
1576
+ * unobserved and its rejection would vanish. The one branch return that is
1577
+ * **not** discarded is the injected `defect(cause)` marker: it is the
1578
+ * lint-clean, expression-position form of a `throw`, so it follows the throw
1579
+ * rule above (an `AggregateError` of `[the branch's cause, original
1580
+ * failure]`), never a silent no-op. A failable
1581
+ * `Result`-returning effect belongs in
1582
+ * {@link ResultMethods.flatTapErrCases | flatTapErrCases}.
1583
+ *
1584
+ * @param f - builds the match; branch returns are ignored, bar `defect(cause)`.
1585
+ */
1586
+ tapErrCases<R>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<R & NotThenable<R>>): Result<T, E>;
1587
+ /**
1588
+ * Run a **failable** side effect on the error, keeping the original error but
1589
+ * threading the effect's own error — **matched exhaustively**
1590
+ * ({@link ErrMatcher}).
1591
+ *
1592
+ * @remarks
1593
+ * The error-channel mirror of {@link ResultMethods.flatTap | flatTap}: each
1594
+ * branch returns a `Result` whose **success value is discarded** — on the
1595
+ * effect's `Ok` the original `Err` flows through, while an `Err`/`Defect` from a
1596
+ * branch short-circuits and threads its error. Note the asymmetry with a
1597
+ * *throw*: a branch that **returns** a Defect-state `Result` **replaces** the
1598
+ * original `Err` (Defect-dominance, the short-circuit rule — it is not
1599
+ * aggregated), whereas a branch that **throws** produces a `Defect`
1600
+ * aggregating `[thrown, original failure]` (observing a failure by throwing
1601
+ * never destroys it). A branch returning the injected `defect(cause)` marker —
1602
+ * reachable under a `returnType` pin — follows the *throw* rule, since it is
1603
+ * the lint-clean, expression-position form of one.
1604
+ *
1605
+ * @typeParam M - the exhaustive builder the callback returns.
1606
+ * @param f - builds the match; each branch is a failable effect (its `Ok` is ignored).
1607
+ */
1608
+ flatTapErrCases<E2>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<Result<unknown, E2>>): Result<T, E | E2>;
1609
+ /**
1610
+ * Recover from a `Defect` — the **only** combinator that can touch one.
1611
+ *
1612
+ * @remarks
1613
+ * Runs `f` only when a `Defect` is present, re-entering the modeled world by
1614
+ * returning a `Result` (an `Ok` or a fresh `Err`). `Ok` and `Err` pass
1615
+ * through. Recovering a Defect should be rare: usually you let it bubble to
1616
+ * the edge. If `f` throws, the throw becomes a new `Defect`.
1617
+ *
1618
+ * @typeParam U - a success type the recovery may produce.
1619
+ * @typeParam E2 - an error type the recovery may produce.
1620
+ * @param f - maps the Defect's unknown cause to a recovering `Result`.
1621
+ */
1622
+ recoverDefect<U, E2>(f: (cause: unknown) => Result<U, E2>): Result<T | U, E | E2>;
1623
+ /**
1624
+ * Run a side effect on a present `Defect`'s cause (e.g. logging) and pass the
1625
+ * `Defect` through unchanged. If `f` throws, the result is a `Defect` whose
1626
+ * cause is an `AggregateError` of `[thrown, original failure]` — observing a
1627
+ * failure never destroys it. An async callback is rejected at compile time
1628
+ * ({@link NotThenable}).
1629
+ *
1630
+ * @param f - the side effect over the unknown cause.
1631
+ */
1632
+ tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): Result<T, E>;
1633
+ /**
1634
+ * Run a side effect on **any failure** — `Err` or `Defect` — and pass the
1635
+ * `Result` through unchanged. The one cross-channel observer, for the shared
1636
+ * "it went KO" concern (logging, metrics, rollback) that would otherwise be
1637
+ * duplicated across {@link ResultMethods.tapErrCases | tapErrCases} and
1638
+ * {@link ResultMethods.tapDefect | tapDefect}.
1639
+ *
1640
+ * @remarks
1641
+ * `f` receives the narrowed **failure variant** ({@link FailureView}), not a
1642
+ * payload — the payload union `E | unknown` would collapse to `unknown` and
1643
+ * lose `E`'s typing. Branch on `failure.tag` to reach the typed payload
1644
+ * (`"Err"` → `failure.error: E`, `"Defect"` → `failure.cause: unknown`), or
1645
+ * treat it opaquely for a shared logger. Runs on `Err` and `Defect`; `Ok`
1646
+ * passes through. It **observes without consuming**: the failure flows on
1647
+ * unchanged — to also recover, use
1648
+ * {@link ResultMethods.recoverErrCases | recoverErrCases} /
1649
+ * {@link ResultMethods.recoverDefect | recoverDefect} (deliberately separate
1650
+ * acts) or {@link ResultMethods.match | match} at the edge. If `f` throws, the
1651
+ * result is a `Defect` whose cause is an `AggregateError` of `[thrown,
1652
+ * original failure]` — observing a failure never destroys it. An async
1653
+ * callback is rejected at compile time ({@link NotThenable}).
1654
+ *
1655
+ * @param f - the side effect over the failure variant (its return value is ignored).
1656
+ */
1657
+ tapFailure<R>(f: (failure: FailureView<E, T>) => R & NotThenable<R>): Result<T, E>;
1658
+ /**
1659
+ * Exhaustively fold all three runtime states into a single value.
1660
+ *
1661
+ * @remarks
1662
+ * Exactly one handler runs. Together with the throw-to-Defect guarantee, this
1663
+ * is typically the single place a pipeline is handled at the edge — mapping
1664
+ * `Ok`/`Err`/`Defect` to (for example) 2xx / 4xx / 5xx with no `try`/`catch`.
1665
+ *
1666
+ * The `errCases` handler does not take a single blanket callback: it receives
1667
+ * `match(error)` (an {@link ErrMatcher}) and **matches the error exhaustively**,
1668
+ * exactly like the error combinators — which is why the key carries the same
1669
+ * `…Cases` suffix. Chain `.with(pattern, handler)` and **return the
1670
+ * un-terminated builder** — `match` calls `.exhaustive()` itself, so a missing
1671
+ * case is a compile error at the call site (no `.exhaustive()` to forget).
1672
+ * Folding at the edge names every case too — `.with(P._, …)` is the wildcard
1673
+ * escape hatch, not the default. Unlike the combinators the branches
1674
+ * receive **no `defect` helper** — `match` is total elimination to a value,
1675
+ * with no `Defect` output channel; the `defect` case handles a `Result` that
1676
+ * already carries one. (A `Result` is also a discriminated union — for richer
1677
+ * whole-`Result` matching, `match(result).with(…)`.)
1678
+ *
1679
+ * @typeParam ROk - the `ok` handler return type.
1680
+ * @typeParam RDefect - the `defect` handler return type.
1681
+ * @typeParam M - the exhaustive builder the `errCases` handler returns.
1682
+ * @param cases - the `ok`/`defect` handlers plus the `errCases` matcher builder.
1683
+ */
1684
+ match<ROk, RDefect, M extends ExhaustiveMatch<unknown>>(cases: {
1685
+ ok: (value: T) => ROk;
1686
+ errCases: (matcher: ErrMatcher<E>) => M;
1687
+ defect: (cause: unknown) => RDefect;
1688
+ }): ROk | RDefect | MatchOut<M>;
1689
+ /**
1690
+ * Extract the success value.
1691
+ *
1692
+ * @remarks
1693
+ * Compiles only when the error channel is empty (`E = never`) — eliminate
1694
+ * modeled errors first (`match` / `recoverErrCases` / `flatMapErrCases`), or reach for the
1695
+ * `getOr` / `getOrElse` / `getOrNull` / `getOrUndefined` family (which
1696
+ * recover an `Err`). The gate is a `this` type that becomes an explanatory
1697
+ * string when `E` is not `never`, so the compile error names the fix.
1698
+ *
1699
+ * `E = never` empties only the **modeled** error channel — a `Defect` can
1700
+ * still be present, and `get()` **rethrows its original cause** (it
1701
+ * _panics_); `Result<T, never>` does not mean `get()` cannot throw.
1702
+ *
1703
+ * @returns the `Ok` value.
1704
+ */
1705
+ get(this: [E] extends [never] ? Result<T, never> : "unthrown: get() needs an empty error channel (E = never) — handle the Err first with recoverErrCases / match / flatMapErrCases, or use getOr / getOrElse / getOrNull / getOrUndefined"): T;
1423
1706
  /**
1424
- * The offending value: the `Err` error for `get()`, or the `Ok` value for
1425
- * `getErr()`.
1707
+ * Extract the modeled error.
1708
+ *
1709
+ * @remarks
1710
+ * Compiles only when the success channel is empty (`T = never`) — eliminate
1711
+ * the success case first. `T = never` is rarely the case in practice (a
1712
+ * `Result` you hold usually still has a success type), so to inspect an
1713
+ * error prefer an `isErr()` guard or, in tests, `@unthrown/vitest`'s
1714
+ * `toBeErrWith`. A `Defect` still **rethrows its original cause** (a defect is
1715
+ * a bug, not an absent value), so this does not mean `getErr()` can't throw.
1716
+ *
1717
+ * @returns the `Err` value.
1426
1718
  */
1427
- readonly error: E;
1428
- constructor(error: E);
1429
- }
1430
- /**
1431
- * Type guard: is `x` a {@link Result} (any of `Ok` / `Err` / `Defect`)?
1432
- *
1433
- * @remarks
1434
- * Unlike {@link isOk} / {@link isErr} / {@link isDefect}, which narrow a value
1435
- * already known to be a `Result`, this narrows from `unknown` — useful at an
1436
- * untyped boundary. It checks the value carries the `Result` prototype
1437
- * (`instanceof` first, falling back to the `Symbol.for("unthrown.Result")`
1438
- * brand the prototype carries — so a `Result` built by **another copy** of
1439
- * unthrown, e.g. the CJS and ESM builds loaded side by side, is still
1440
- * recognised). A look-alike plain object (`{ tag: "Ok" }`) carries neither and
1441
- * is **not** matched. An `AsyncResult` is not a `Result` and returns `false`.
1442
- *
1443
- * @returns `true` when `x` is a `Result` produced by this library.
1444
- *
1445
- * @example
1446
- * ```ts
1447
- * import { isResult, Ok, P } from "unthrown";
1448
- *
1449
- * isResult(Ok(1)); // => true
1450
- * isResult({ tag: "Ok" }); // => false (look-alike, wrong prototype)
1451
- * isResult(Ok(1).toAsync()); // => false (an AsyncResult is not a Result)
1452
- *
1453
- * const x: unknown = Ok(1);
1454
- * if (isResult(x))
1455
- * // `E` is `unknown` here — an untyped boundary has no cases to enumerate,
1456
- * // so the `P._` escape hatch is the only arm that can terminate the match:
1457
- * // oxlint-disable-next-line unthrown/no-catch-all-pattern -- untyped boundary: `E` is `unknown`
1458
- * x.match({
1459
- * ok: () => 1,
1460
- * errCases: (m) => m.with(P._, () => 0),
1461
- * defect: () => -1,
1462
- * });
1463
- * ```
1464
- *
1465
- * @category Guards
1466
- */
1467
- declare function isResult(x: unknown): x is Result$1<unknown, unknown>;
1468
- //#endregion
1469
- //#region src/do.d.ts
1719
+ getErr(this: [T] extends [never] ? Result<never, E> : "unthrown: getErr() needs an empty success channel (T = never) — narrow with isErr() first, or fold with match"): E;
1720
+ /**
1721
+ * The success value, or `fallback` on `Err`.
1722
+ *
1723
+ * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
1724
+ * @param fallback - returned when the result is an `Err` (may be a different type; the return widens to `T | U`).
1725
+ * @throws Re-throws on a `Defect` — a Defect is a bug, not an absent value, so
1726
+ * it is never silently replaced.
1727
+ */
1728
+ getOr<U>(fallback: U): T | U;
1729
+ /**
1730
+ * The success value, or `f(error)` on `Err`.
1731
+ *
1732
+ * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
1733
+ * @param f - lazily computes the fallback from the error (may return a different type; the return widens to `T | U`).
1734
+ * @throws Re-throws on a `Defect`.
1735
+ */
1736
+ getOrElse<U>(f: (error: E) => U): T | U;
1737
+ /**
1738
+ * The success value, or `null` on `Err`.
1739
+ *
1740
+ * @throws Re-throws on a `Defect`.
1741
+ */
1742
+ getOrNull(): T | null;
1743
+ /**
1744
+ * The success value, or `undefined` on `Err`.
1745
+ *
1746
+ * @throws Re-throws on a `Defect`.
1747
+ */
1748
+ getOrUndefined(): T | undefined;
1749
+ /**
1750
+ * The success value, or **throw** the modeled error on `Err`.
1751
+ *
1752
+ * @remarks
1753
+ * A deliberate escape hatch off the errors-as-values model — it **throws the
1754
+ * `Err` value as-is** at the call site, so a caller of the enclosing function
1755
+ * sees a throw rather than a channel. Its home is **tests and scripts**,
1756
+ * where "this `Result` had better be `Ok`" is the assertion and a throw is
1757
+ * the correct failure mode.
1758
+ *
1759
+ * In production code, fold the error channel instead:
1760
+ * {@link ResultMethods.recoverErrCases | recoverErrCases} empties `E`, so
1761
+ * {@link ResultMethods.get | get} compiles and a case routed to the injected
1762
+ * `defect(...)` panics with its original cause — with every case still named.
1763
+ * {@link ResultMethods.match | match} and
1764
+ * {@link ResultMethods.flatMapErrCases | flatMapErrCases} are the other two
1765
+ * ways to keep the error a value. `@unthrown/oxlint`'s opt-in
1766
+ * `no-get-or-throw` rule enforces this, exempting test files through an
1767
+ * oxlint `overrides` entry.
1768
+ *
1769
+ * Type-gated as the **complement** of {@link ResultMethods.get | get}: it
1770
+ * compiles only when the error channel is **non-empty** (`E` is not `never`) —
1771
+ * there must be a modeled error for it to throw. On a `Result<T, never>` there
1772
+ * is nothing to throw, so `getOrThrow` does not compile; use `get()` (which
1773
+ * gates the other way). Together they partition extraction by the error
1774
+ * channel's state, with no overlap.
1775
+ *
1776
+ * @returns the `Ok` value.
1777
+ * @throws the modeled `error` on `Err`; re-throws the original `cause` on a
1778
+ * `Defect` (a panic, like the rest of the `getOr…` family).
1779
+ */
1780
+ getOrThrow(this: [E] extends [never] ? "unthrown: getOrThrow is unnecessary here — the Err channel is empty (E = never), so there is nothing to throw. Use get() instead." : Result<T, E>): T;
1781
+ /** Whether this result is `Ok` — narrows `this` to its {@link OkView} on `true`. */
1782
+ isOk(): this is OkView<T, E>;
1783
+ /** Whether this result is `Err` — narrows `this` to its {@link ErrView} on `true`. */
1784
+ isErr(): this is ErrView<E, T>;
1785
+ /** Whether this result is a `Defect` — narrows `this` to its {@link DefectView} on `true`. */
1786
+ isDefect(): this is DefectView<T, E>;
1787
+ /** Lift this synchronous `Result` into an {@link AsyncResult}. */
1788
+ toAsync(): AsyncResult<T, E>;
1789
+ };
1470
1790
  /**
1471
- * Start a do-notation chain with an empty object scope, grown step by step with
1472
- * `bind` (for `Result`-returning steps) and `let` (for pure values).
1473
- *
1474
- * @remarks
1475
- * Capitalised because `do` is a reserved word. Each step receives the scope
1476
- * accumulated so far; the error types union across `bind`s, and a throw in any
1477
- * step becomes a `Defect`. To go asynchronous, lift the chain with `toAsync()`
1478
- * (then a `bind` may return an `AsyncResult`).
1479
- *
1480
- * @example
1481
- * ```ts
1482
- * import { Do, Ok } from "unthrown";
1483
- *
1484
- * const result = Do()
1485
- * .bind("user", () => findUser(id)) // Result<User, NotFound>
1486
- * .bind("org", ({ user }) => findOrg(user.orgId)) // Result<Org, NotFound>
1487
- * .let("label", ({ user, org }) => `${user.name} @ ${org.name}`)
1488
- * .map(({ user, org, label }) => render(user, org, label));
1489
- * // Result<View, NotFound>
1490
- * ```
1791
+ * The `Ok` variant of a {@link Result}: a success carrying a `value`. This is
1792
+ * what a successful `isOk` guard narrows to, making `.value` reachable. It also
1793
+ * carries the shared fluent surface ({@link ResultMethods}).
1491
1794
  *
1492
1795
  * @example
1493
1796
  * ```ts
1494
- * import { Do, Ok, Err } from "unthrown";
1495
- *
1496
- * // Ok path — the scope accumulates:
1497
- * Do()
1498
- * .bind("a", () => Ok(2))
1499
- * .let("b", ({ a }) => a * 10)
1500
- * .map(({ a, b }) => a + b); // => Ok(22)
1501
- *
1502
- * // Err path — the first Err short-circuits the rest:
1503
- * Do()
1504
- * .bind("a", () => Err("boom"))
1505
- * .let("b", ({ a }) => a); // => Err("boom")
1797
+ * if (r.isOk()) r.value; // r: OkView<T, E> here — .value is a T
1506
1798
  * ```
1507
1799
  *
1508
- * @category Do-notation
1800
+ * @category Types
1509
1801
  */
1510
- declare function Do(): Result$1<{}, never>;
1802
+ interface OkView<out T, out E = never> extends ResultMethods<T, E> {
1803
+ readonly tag: "Ok";
1804
+ readonly value: T;
1805
+ }
1511
1806
  /**
1512
- * Start an **asynchronous** do-notation chain with an empty object scope — the
1513
- * pre-lifted form of {@link Do}, sparing you `Do().toAsync()`.
1807
+ * The `Err` variant of a {@link Result}: a modeled failure carrying an `error`.
1808
+ * This is what a successful `isErr` guard narrows to, exposing `.error`. It also
1809
+ * carries the shared fluent surface ({@link ResultMethods}).
1514
1810
  *
1515
1811
  * @remarks
1516
- * From here a `bind` may return a `Result` **or** an `AsyncResult`; the scope
1517
- * accumulates exactly as in a sync {@link Do} chain, and a throw in any step
1518
- * becomes a `Defect`. Named with the `Async` suffix the async free functions
1519
- * carry (`OkAsync`, `allAsync`); the {@link AsyncResult} companion aliases it as
1520
- * `AsyncResult.Do` (the namespace already says "async", so the suffix drops).
1812
+ * **Note the parameter order: `ErrView<E, T>` puts the error type _first_** — the
1813
+ * reverse of the `<T, E>` order used by {@link OkView}, {@link DefectView}, and
1814
+ * {@link Result} — because `Result<T, E>` narrows to `ErrView<E, T>` (the error is
1815
+ * the payload the guard makes reachable). You rarely write it by hand (a failed
1816
+ * `isErr()` narrows to it for you); if you do, mind the flip — `ErrView<MyError,
1817
+ * MyValue>`, not `ErrView<MyValue, MyError>`.
1521
1818
  *
1522
1819
  * @example
1523
1820
  * ```ts
1524
- * import { DoAsync, Ok } from "unthrown";
1525
- *
1526
- * const result = await DoAsync()
1527
- * .bind("user", () => findUser(id)) // AsyncResult<User, NotFound>
1528
- * .bind("plan", ({ user }) => Ok(user.plan)) // a sync Result is accepted too
1529
- * .let("label", ({ user, plan }) => `${user.name} on ${plan}`);
1530
- * // Result<{ user: User; plan: Plan; label: string }, NotFound>
1821
+ * if (r.isErr()) r.error; // r: ErrView<E, T> here — .error is an E
1531
1822
  * ```
1532
1823
  *
1533
- * @category Do-notation
1534
- */
1535
- declare function DoAsync(): AsyncResult$1<{}, never>;
1536
- //#endregion
1537
- //#region src/interop.d.ts
1538
- /**
1539
- * Bridge a nullable value into a {@link Result}: absence becomes a **modeled**
1540
- * `Err`. The sanctioned alternative to an `Option` type.
1541
- *
1542
- * @remarks
1543
- * `null` and `undefined` map to `Err(onAbsent())`; any other value (including
1544
- * falsy ones like `0`, `""`, `false`) maps to `Ok`.
1545
- *
1546
- * @typeParam T - the (nullable) value type.
1547
- * @typeParam E - the error produced when the value is absent.
1548
- * @param value - the possibly-absent value.
1549
- * @param onAbsent - lazily produces the error for the absent case.
1550
- *
1551
- * @category Interop
1552
- *
1553
- * @example
1554
- * ```ts
1555
- * import { fromNullable } from "unthrown";
1556
- *
1557
- * const map = new Map([["a", 1]]);
1558
- * fromNullable(map.get("a"), () => "absent").getOr(0); // => 1
1559
- * fromNullable(map.get("z"), () => "absent"); // => Err("absent")
1560
- * fromNullable(0, () => "absent").getOr(-1); // => 0 (falsy but present)
1561
- * ```
1824
+ * @category Types
1562
1825
  */
1563
- declare function fromNullable<T, E>(value: T | null | undefined, onAbsent: () => E): Result$1<NonNullable<T>, E>;
1564
- /**
1565
- * Wrap a throwing synchronous function so it returns a {@link Result} instead of
1566
- * throwing.
1567
- *
1568
- * @remarks
1569
- * `qualify` **must** triage every thrown cause into a modeled error `E` or a
1570
- * `Defect` (via the injected `defect` helper, its second argument) — there is no
1571
- * path that leaves `unknown` in `E`. A throw inside `qualify` itself is treated
1572
- * as a `Defect`. `qualify` is **synchronous**: an `async` qualify is rejected at
1573
- * compile time ({@link NotThenable}) — its `Promise` would land in `E` un-triaged
1574
- * — and a thenable slipped past the types at runtime becomes a `Defect` (never
1575
- * an `Err(Promise)`), its orphaned rejection silenced.
1576
- *
1577
- * `fn` is **synchronous** too. An `async` `fn` rejects *after* this boundary has
1578
- * already returned, so its rejection could never reach `qualify`: it becomes a
1579
- * `Defect` (never `Ok(<Promise>)`) and the orphaned rejection is silenced rather
1580
- * than left to float. Reach for {@link fromPromise} to wrap async work.
1581
- *
1582
- * The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
1583
- * `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
1584
- * `qualify` that returns *only* `defect(cause)` yields `E = never` (a Defect is
1585
- * out-of-band and must not pollute the error channel); reach for
1586
- * {@link fromSafeThrowable} when every throw is a Defect.
1587
- *
1588
- * @typeParam A - the wrapped function's argument tuple.
1589
- * @typeParam T - the wrapped function's return type.
1590
- * @typeParam R - `qualify`'s return type; the modeled error `E` is
1591
- * `Exclude<R, Defect>` (its `Defect` arm, if any, is subtracted).
1592
- * @param fn - the throwing function to wrap.
1593
- * @param qualify - triages a thrown `cause` into a modeled `E`, or marks it
1594
- * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
1595
- * @returns a function with the same arguments returning `Result<T, E>`.
1596
- *
1597
- * @category Interop
1826
+ interface ErrView<out E, out T = never> extends ResultMethods<T, E> {
1827
+ readonly tag: "Err";
1828
+ readonly error: E;
1829
+ }
1830
+ /**
1831
+ * The `Defect` variant of a {@link Result}: an unmodeled failure carrying a
1832
+ * `cause`. This is what a successful `isDefect` guard narrows to, exposing
1833
+ * `.cause`. It also carries the shared fluent surface ({@link ResultMethods}).
1598
1834
  *
1599
1835
  * @example
1600
1836
  * ```ts
1601
- * import { fromThrowable } from "unthrown";
1602
- *
1603
- * // Model the parse failure as an `Err`, everything unexpected as a `Defect`.
1604
- * const parse = fromThrowable(
1605
- * (text: string) => JSON.parse(text) as unknown,
1606
- * (cause, defect) =>
1607
- * cause instanceof SyntaxError ? ("invalid_json" as const) : defect(cause),
1608
- * );
1609
- *
1610
- * parse('{"ok":true}').getOr(null); // => { ok: true }
1611
- * parse("nope"); // => Err("invalid_json")
1837
+ * if (r.isDefect()) r.cause; // r: DefectView<T, E> here — .cause is `unknown`
1612
1838
  * ```
1839
+ *
1840
+ * @category Types
1613
1841
  */
1614
- declare function fromThrowable<A extends unknown[], T, R>(fn: (...args: A) => T, qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R & NotThenable<R>): (...args: A) => Result$1<T, Exclude<R, Defect>>;
1842
+ interface DefectView<out T = never, out E = never> extends ResultMethods<T, E> {
1843
+ readonly tag: "Defect";
1844
+ readonly cause: unknown;
1845
+ }
1615
1846
  /**
1616
- * Wrap a throwing synchronous function asserted **not** to fail in any modeled
1617
- * way: any throw becomes a `Defect`.
1847
+ * A failure variant of a {@link Result}: an {@link ErrView} **or** a
1848
+ * {@link DefectView}. This is what a `tapFailure` callback receives — the
1849
+ * discriminated variant rather than a payload, because the payload union
1850
+ * `E | unknown` would collapse to `unknown` and lose `E`'s typing. Branch on
1851
+ * `tag` to narrow (`"Err"` → `.error: E`, `"Defect"` → `.cause: unknown`).
1618
1852
  *
1619
1853
  * @remarks
1620
- * The synchronous counterpart of {@link fromSafePromise}. Use it only when a
1621
- * throw genuinely indicates a bug rather than an anticipated outcome — the
1622
- * error channel is `never`, so there is nothing to triage; there is no
1623
- * `qualify`. When some throws *are* anticipated, reach for
1624
- * {@link fromThrowable} and triage them.
1625
- *
1626
- * `fn` is **synchronous**: an `async` `fn` becomes a `Defect` (never
1627
- * `Ok(<Promise>)`), with its orphaned rejection silenced rather than left to
1628
- * float. Reach for {@link fromSafePromise} to wrap async work.
1629
- *
1630
- * @typeParam A - the wrapped function's argument tuple.
1631
- * @typeParam T - the wrapped function's return type.
1632
- * @param fn - the throwing function to wrap.
1633
- * @returns a function with the same arguments returning `Result<T, never>`.
1634
- *
1635
- * @category Interop
1854
+ * Like {@link ErrView}, the error type comes **first** (`FailureView<E, T>`) —
1855
+ * the error is the payload you are usually here for, and a shared observer can
1856
+ * spell just `FailureView<MyError>`.
1636
1857
  *
1637
1858
  * @example
1638
1859
  * ```ts
1639
- * import { fromSafeThrowable } from "unthrown";
1640
- *
1641
- * // A decode failure here is a bug (the row came from our own schema), so
1642
- * // every throw is a defect — no throwaway `(cause, defect) => defect(cause)`.
1643
- * const decode = fromSafeThrowable((row: Row) => userSchema.parse(row));
1644
- *
1645
- * decode(row); // => Result<User, never> — a throw becomes a Defect
1860
+ * const logKo = (f: FailureView<ApiError>) =>
1861
+ * f.tag === "Err" ? logger.warn(f.error) : logger.error(f.cause);
1862
+ * result.tapFailure(logKo);
1646
1863
  * ```
1864
+ *
1865
+ * @typeParam E - the modeled error type.
1866
+ * @typeParam T - the success value type (phantom here; a failure carries none).
1867
+ * @category Types
1647
1868
  */
1648
- declare function fromSafeThrowable<A extends unknown[], T>(fn: (...args: A) => T): (...args: A) => Result$1<T, never>;
1869
+ type FailureView<E, T = never> = ErrView<E, T> | DefectView<T, E>;
1649
1870
  /**
1650
- * Wrap a `Promise` (or a thunk producing one) as an {@link AsyncResult}, forcing
1651
- * every rejection to be triaged.
1871
+ * A success-only thenable: awaitable, but deliberately **not** a full
1872
+ * `PromiseLike`.
1652
1873
  *
1653
1874
  * @remarks
1654
- * `qualify` **must** map each rejection cause into a modeled error `E` or a
1655
- * `Defect` (via the injected `defect` helper, its second argument). The returned
1656
- * `AsyncResult`'s internal promise never rejects; `await`-ing it always yields a
1657
- * `Result`. A throw inside `qualify` is itself a `Defect`. `qualify` is
1658
- * **synchronous**: an `async` qualify is rejected at compile time
1659
- * ({@link NotThenable}), and a thenable slipped past the types at runtime
1660
- * becomes a `Defect` (never an `Err(Promise)`), its orphaned rejection silenced.
1661
- *
1662
- * The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
1663
- * `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
1664
- * `qualify` that returns *only* `defect(cause)` yields `E = never`; when every
1665
- * rejection is a Defect, prefer {@link fromSafePromise}.
1666
- *
1667
- * @typeParam T - the resolved value type.
1668
- * @typeParam R - `qualify`'s return type; the modeled error `E` is
1669
- * `Exclude<R, Defect>` (its `Defect` arm, if any, is subtracted).
1670
- * @param promise - the promise, or a thunk returning one.
1671
- * @param qualify - triages a rejection `cause` into a modeled `E`, or marks it
1672
- * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
1673
- * @param _guard - compile-time only; never pass it. The phantom rest-tuple that
1674
- * enforces "qualify is synchronous": an `async` qualify makes this demand an
1675
- * impossible extra argument (whose type spells out the error), while a
1676
- * synchronous one leaves it empty. Encoded here — not on `qualify`'s return
1677
- * type — so `T`'s inference from `promise` is undisturbed.
1875
+ * An {@link AsyncResult}'s internal promise never rejects, so `await`-ing one
1876
+ * always yields a {@link Result} and never throws — there is no rejection
1877
+ * channel to model, and none is advertised. At runtime it is still a thenable
1878
+ * (the only way `await` can collapse it), and `Promise.all` / `Promise.resolve`
1879
+ * will still adopt it — harmlessly, since it settles to a `Result` and never
1880
+ * rejects. What the narrowing prevents is treating it as a full promise:
1881
+ * `.catch()` / `.finally()` do not type-check, because there is no rejection to
1882
+ * handle.
1678
1883
  *
1679
- * @category Interop
1884
+ * @typeParam T - the value `await` resolves to.
1680
1885
  *
1681
- * @example
1682
- * ```ts
1683
- * import { fromPromise } from "unthrown";
1886
+ * @category Types
1887
+ */
1888
+ type Awaitable<out T> = {
1889
+ then<R = T>(onfulfilled?: ((value: T) => R | PromiseLike<R>) | null): PromiseLike<R>;
1890
+ };
1891
+ /**
1892
+ * The async method surface every {@link AsyncResult} carries — the combinators
1893
+ * (`map`, `flatMap`, `mapErrCases`, `match`, `get`, …) with their asynchronous
1894
+ * signatures, documented one per entry below. The async mirror of
1895
+ * {@link ResultMethods}: each entry links its synchronous counterpart and states
1896
+ * only the async delta.
1684
1897
  *
1685
- * // A rejection with a NotFoundError becomes a modeled `Err`; anything else a Defect.
1686
- * const user = await fromPromise(fetchUser(id), (cause, defect) =>
1687
- * cause instanceof NotFoundError ? ("not_found" as const) : defect(cause),
1688
- * );
1898
+ * @remarks
1899
+ * Like {@link ResultMethods}, this type exists to **document** the surface — not
1900
+ * to be authored against; you obtain it by holding an `AsyncResult`. Its
1901
+ * combinator callbacks are **synchronous** (a raw `Promise` may never enter — see
1902
+ * the {@link AsyncResult} remarks); async work re-enters via {@link fromPromise}
1903
+ * and composes with `flatMap`. Systematic differences from the sync surface: the
1904
+ * binds return an `AsyncResult` (and additionally accept one), and the
1905
+ * eliminators return a `Promise`.
1689
1906
  *
1690
- * if (user.isOk()) user.value; // => the fetched user
1691
- * // when fetchUser rejects with NotFoundError: user is Err("not_found")
1692
- * ```
1907
+ * @typeParam T - the success value type.
1908
+ * @typeParam E - the modeled error type.
1909
+ * @category Methods
1693
1910
  */
1694
- declare function fromPromise<T, R>(promise: Promise<T> | (() => Promise<T>), qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R, ..._guard: [Extract<R, PromiseLike<unknown>>] extends [never] ? [] : ["unthrown: qualify must be synchronous — its Promise would land in E un-triaged"]): AsyncResult$1<T, Exclude<R, Defect>>;
1911
+ type AsyncResultMethods<out T, out E> = {
1912
+ /**
1913
+ * Asynchronous {@link ResultMethods.map | map}: transforms the success value
1914
+ * with `f`. `f` is synchronous; a throw becomes a `Defect`. An async callback
1915
+ * is rejected at compile time ({@link NotThenable}).
1916
+ */
1917
+ map<U>(f: (value: T) => U & NotThenable<U>): AsyncResult<U, E>;
1918
+ /**
1919
+ * Asynchronous {@link ResultMethods.flatMap | flatMap}. Unlike the sync form,
1920
+ * `f` may return a `Result` **or** an `AsyncResult` (never a raw `Promise`); a
1921
+ * throw becomes a `Defect`.
1922
+ *
1923
+ * @remarks
1924
+ * The async branch of `f`'s return type is spelled `Awaitable<Result<U, E2>> &
1925
+ * { flatMap: unknown }` rather than `AsyncResult<U, E2>`: this is what you get
1926
+ * by returning an `AsyncResult` (it satisfies both), but inference runs through
1927
+ * the `Awaitable` then-channel so `U`/`E2` stay precise instead of collapsing
1928
+ * to `unknown`, while the `{ flatMap: unknown }` marker still rejects a bare
1929
+ * `Promise` (it has no `flatMap`). Just return a `Result` or an `AsyncResult`.
1930
+ */
1931
+ flatMap<U, E2>(f: (value: T) => Result<U, E2> | (Awaitable<Result<U, E2>> & ReturnAnAsyncResultNotAPromise)): AsyncResult<U, E | E2>;
1932
+ /**
1933
+ * Asynchronous {@link ResultMethods.tap | tap}. `f` is synchronous; a throw
1934
+ * becomes a `Defect`. An async callback is rejected at compile time
1935
+ * ({@link NotThenable}) — and so is a returned `AsyncResult` (it is
1936
+ * awaitable). Beware the near-miss: _calling_ an `AsyncResult`-returning
1937
+ * effect inside the callback without returning it compiles and leaves the
1938
+ * effect floating — fire-and-forget, never awaited, its `Err`/`Defect`
1939
+ * unobserved. If the effect returns a `Result`/`AsyncResult`, use
1940
+ * {@link AsyncResultMethods.flatTap | flatTap}.
1941
+ */
1942
+ tap<R>(f: (value: T) => R & NotThenable<R>): AsyncResult<T, E>;
1943
+ /**
1944
+ * Asynchronous {@link ResultMethods.flatTap | flatTap} — a failable tap that
1945
+ * keeps the original value. `f` may return a `Result` **or** an `AsyncResult`;
1946
+ * its `Ok` value is discarded, an `Err`/`Defect` short-circuits, and a throw
1947
+ * becomes a `Defect`.
1948
+ */
1949
+ flatTap<E2>(f: (value: T) => Result<unknown, E2> | (Awaitable<Result<unknown, E2>> & ReturnAnAsyncResultNotAPromise)): AsyncResult<T, E | E2>;
1950
+ /**
1951
+ * Asynchronous {@link ResultMethods.bind | bind} (do-notation). `f` may return
1952
+ * a `Result` **or** an `AsyncResult`; its value is bound under `name` in the
1953
+ * accumulating scope.
1954
+ */
1955
+ bind<K extends string, U, E2>(name: K, f: (scope: T) => Result<U, E2> | (Awaitable<Result<U, E2>> & ReturnAnAsyncResultNotAPromise)): AsyncResult<Bound<T, K, U>, E | E2>;
1956
+ /**
1957
+ * Asynchronous {@link ResultMethods.let | let} (do-notation). `f` returns a
1958
+ * plain value, bound under `name`. An async callback is rejected at compile
1959
+ * time ({@link NotThenable}).
1960
+ */
1961
+ let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): AsyncResult<Bound<T, K, U>, E>;
1962
+ /** Asynchronous {@link ResultMethods.as | as}: replaces the value with `value`. */
1963
+ as<U>(value: U): AsyncResult<U, E>;
1964
+ /** Asynchronous {@link ResultMethods.discard | discard}: drops the value, collapsing the success type to `void`. */
1965
+ discard(): AsyncResult<void, E>;
1966
+ /**
1967
+ * Asynchronous {@link ResultMethods.ensure | ensure}: validate the success
1968
+ * value — and, with a type-guard predicate (this overload), **refine** it —
1969
+ * failing into the modeled channel with `Err(onFail(value))`. Both callbacks
1970
+ * are synchronous (an async `onFail` is rejected at compile time,
1971
+ * {@link NotThenable}); a throw in either becomes a `Defect`.
1972
+ */
1973
+ ensure<U extends T, E2>(predicate: (value: T) => value is U, onFail: (value: T) => E2 & NotThenable<E2>): AsyncResult<U, E | E2>;
1974
+ /** Boolean form of the asynchronous {@link ResultMethods.ensure | ensure} — validates without refining, keeping `T`. */
1975
+ ensure<E2>(predicate: (value: T) => boolean, onFail: (value: T) => E2 & NotThenable<E2>): AsyncResult<T, E | E2>;
1976
+ /**
1977
+ * Asynchronous {@link ResultMethods.mapErrCases | mapErrCases} — the same exhaustive
1978
+ * {@link ErrMatcher} form; the combinator calls `.exhaustive()`. Branches are
1979
+ * synchronous — an `async` branch is a compile error, as on the sync surface.
1980
+ */
1981
+ mapErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M, ..._asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases: SyncBranches<MatchOut<M>, E>): AsyncResult<T, MatchErrOut<M>>;
1982
+ /**
1983
+ * Asynchronous {@link ResultMethods.flatMapErrCases | flatMapErrCases} — the same
1984
+ * exhaustive {@link ErrMatcher} form. Unlike the sync form, a branch may
1985
+ * return a `Result` **or** an `AsyncResult`.
1986
+ */
1987
+ flatMapErrCases<M extends ExhaustiveMatch<Result<unknown, unknown> | (Awaitable<Result<unknown, unknown>> & ReturnAnAsyncResultNotAPromise) | Defect>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): AsyncResult<T | OkOf<MatchOut<M>> | AsyncOkOf<MatchOut<M>>, ErrOf<MatchOut<M>> | AsyncErrOf<MatchOut<M>>>;
1988
+ /**
1989
+ * Asynchronous {@link ResultMethods.recoverErrCases | recoverErrCases} — the same
1990
+ * exhaustive {@link ErrMatcher} form. Branches are synchronous; a throw
1991
+ * becomes a `Defect`.
1992
+ */
1993
+ recoverErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M, ..._asyncBranchBanned_liftWithFromPromiseThenFlatMapErrCases: SyncBranches<MatchOut<M>, T | E>): AsyncResult<T | MatchErrOut<M>, never>;
1994
+ /**
1995
+ * Asynchronous {@link ResultMethods.tapErrCases | tapErrCases}. `f` is synchronous; if it
1996
+ * throws — or a branch returns the injected `defect(cause)` marker, the
1997
+ * expression-position form of a throw — the result is a `Defect` whose cause
1998
+ * is an `AggregateError` of `[thrown, original failure]` — observing a failure
1999
+ * never destroys it. An
2000
+ * async branch is rejected at compile time ({@link NotThenable} on the
2001
+ * builder output) — other branch results are discarded, so a rejected
2002
+ * `Promise` would float unobserved. The
2003
+ * {@link AsyncResultMethods.tap | tap} fire-and-forget caveat applies here
2004
+ * too — a failable effect belongs in
2005
+ * {@link AsyncResultMethods.flatTapErrCases | flatTapErrCases}.
2006
+ */
2007
+ tapErrCases<R>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<R & NotThenable<R>>): AsyncResult<T, E>;
2008
+ /**
2009
+ * Asynchronous {@link ResultMethods.flatTapErrCases | flatTapErrCases} — the
2010
+ * error-channel mirror of `flatTap`. `f` may return a `Result` **or** an
2011
+ * `AsyncResult`; its `Ok` value is discarded, an `Err`/`Defect` from `f`
2012
+ * threads through, and if `f` throws — or a branch returns the injected
2013
+ * `defect(cause)` marker, the expression-position form of a throw — the result
2014
+ * is a `Defect` whose cause is an `AggregateError` of `[thrown, original
2015
+ * failure]` — observing a failure never destroys it.
2016
+ */
2017
+ flatTapErrCases<E2>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<Result<unknown, E2> | (Awaitable<Result<unknown, E2>> & ReturnAnAsyncResultNotAPromise)>): AsyncResult<T, E | E2>;
2018
+ /**
2019
+ * Asynchronous {@link ResultMethods.recoverDefect | recoverDefect}. `f` may
2020
+ * return a `Result` or an `AsyncResult`.
2021
+ */
2022
+ recoverDefect<U, E2>(f: (cause: unknown) => Result<U, E2> | AsyncResult<U, E2>): AsyncResult<T | U, E | E2>;
2023
+ /**
2024
+ * Asynchronous {@link ResultMethods.tapDefect | tapDefect}. If `f` throws, the
2025
+ * result is a `Defect` whose cause is an `AggregateError` of `[thrown,
2026
+ * original failure]` — observing a failure never destroys it. An async
2027
+ * callback is rejected at compile time ({@link NotThenable}).
2028
+ */
2029
+ tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): AsyncResult<T, E>;
2030
+ /**
2031
+ * Asynchronous {@link ResultMethods.tapFailure | tapFailure} — the
2032
+ * cross-channel observer. `f` receives the narrowed failure variant
2033
+ * ({@link FailureView}); if it throws, the result is a `Defect` whose cause
2034
+ * is an `AggregateError` of `[thrown, original failure]` — observing a
2035
+ * failure never destroys it. An async callback is rejected at compile time
2036
+ * ({@link NotThenable}).
2037
+ */
2038
+ tapFailure<R>(f: (failure: FailureView<E, T>) => R & NotThenable<R>): AsyncResult<T, E>;
2039
+ /**
2040
+ * Asynchronous {@link ResultMethods.match | match}. Handlers are synchronous
2041
+ * (the `errCases` handler returns an exhaustive {@link ErrMatcher} builder, no
2042
+ * `defect` helper); resolves to a `Promise` of the folded value.
2043
+ */
2044
+ match<ROk, RDefect, M extends ExhaustiveMatch<unknown>>(cases: {
2045
+ ok: (value: T) => ROk;
2046
+ errCases: (matcher: ErrMatcher<E>) => M;
2047
+ defect: (cause: unknown) => RDefect;
2048
+ }): Promise<ROk | RDefect | MatchOut<M>>;
2049
+ /**
2050
+ * Asynchronous {@link ResultMethods.get | get}. Compiles only when the
2051
+ * error channel is empty (`this: AsyncResult<T, never>`); the returned promise
2052
+ * rejects on a `Defect` (rethrowing its cause).
2053
+ */
2054
+ get(this: [E] extends [never] ? AsyncResult<T, never> : "unthrown: get() needs an empty error channel (E = never) — handle the Err first with recoverErrCases / match / flatMapErrCases, or use getOr / getOrElse / getOrNull / getOrUndefined"): Promise<T>;
2055
+ /**
2056
+ * Asynchronous {@link ResultMethods.getErr | getErr}. Compiles only when
2057
+ * the success channel is empty (`this: AsyncResult<never, E>`); the returned
2058
+ * promise rejects on a `Defect` (rethrowing its cause).
2059
+ */
2060
+ getErr(this: [T] extends [never] ? AsyncResult<never, E> : "unthrown: getErr() needs an empty success channel (T = never) — narrow with isErr() first, or fold with match"): Promise<E>;
2061
+ /** Asynchronous {@link ResultMethods.getOr | getOr}. */
2062
+ getOr<U>(fallback: U): Promise<T | U>;
2063
+ /** Asynchronous {@link ResultMethods.getOrElse | getOrElse}. */
2064
+ getOrElse<U>(f: (error: E) => U): Promise<T | U>;
2065
+ /** Asynchronous {@link ResultMethods.getOrNull | getOrNull}. */
2066
+ getOrNull(): Promise<T | null>;
2067
+ /** Asynchronous {@link ResultMethods.getOrUndefined | getOrUndefined}. */
2068
+ getOrUndefined(): Promise<T | undefined>;
2069
+ /**
2070
+ * Asynchronous {@link ResultMethods.getOrThrow | getOrThrow} — the returned
2071
+ * promise **rejects** with the modeled error on `Err` (or the original cause
2072
+ * on a `Defect`), rather than throwing synchronously. Gated the same way: it
2073
+ * compiles only when the error channel is non-empty (`E` is not `never`).
2074
+ */
2075
+ getOrThrow(this: [E] extends [never] ? "unthrown: getOrThrow is unnecessary here — the Err channel is empty (E = never), so there is nothing to throw. Use get() instead." : AsyncResult<T, E>): Promise<T>;
2076
+ };
1695
2077
  /**
1696
- * Wrap a `Promise` asserted **not** to fail in any modeled way: any rejection
1697
- * becomes a `Defect`.
1698
- *
1699
- * @remarks
1700
- * Use this only when a rejection genuinely indicates a bug rather than an
1701
- * anticipated outcome — the error channel is `never`, so there is nothing to
1702
- * triage. (`await`-ing still yields a `Result`; it never throws.) The
1703
- * synchronous counterpart is {@link fromSafeThrowable}.
1704
- *
1705
- * @typeParam T - the resolved value type.
1706
- * @param promise - the promise, or a thunk returning one.
2078
+ * Extract the success type `T` from a `Result` type — derive one type from
2079
+ * another instead of restating it (e.g. the payload a function returns).
1707
2080
  *
1708
- * @category Interop
2081
+ * @typeParam R - the `Result` type to inspect.
1709
2082
  *
1710
2083
  * @example
1711
2084
  * ```ts
1712
- * import { fromSafePromise } from "unthrown";
1713
- *
1714
- * (await fromSafePromise(Promise.resolve(3))).get(); // => 3
1715
- * // a rejection becomes a Defect (never a modeled Err):
1716
- * await fromSafePromise(Promise.reject(new Error("boom"))); // => Defect(Error("boom"))
2085
+ * type R = Result<User, NotFound>;
2086
+ * type U = OkOf<R>; // User
2087
+ * type E = ErrOf<R>; // NotFound
1717
2088
  * ```
1718
- */
1719
- declare function fromSafePromise<T>(promise: Promise<T> | (() => Promise<T>)): AsyncResult$1<T, never>;
1720
- /**
1721
- * The settler a {@link fromExecutor} executor receives. Settles the pending
1722
- * `AsyncResult` **once** — later calls are no-ops, exactly as `resolve` is on a
1723
- * `Promise`.
1724
- *
1725
- * @typeParam T - the success type.
1726
- * @typeParam E - the modeled error type.
1727
2089
  *
1728
2090
  * @category Types
1729
2091
  */
1730
- type Settle<T, E> = (result: Result$1<T, E> | Defect) => void;
2092
+ type OkOf<R> = R extends {
2093
+ readonly tag: "Ok";
2094
+ readonly value: infer T;
2095
+ } ? T : never;
1731
2096
  /**
1732
- * Build an {@link AsyncResult} from a callback-style API — this library's
1733
- * answer to `new Promise((resolve, reject) => …)`.
1734
- *
1735
- * @remarks
1736
- * The settler takes a **`Result`**, not a value-or-reason pair: the caller names
1737
- * the variant, so no `unknown` can enter `E` and there is no `qualify` to pass.
1738
- * For a failure that is *not* modeled, settle the injected `defect` helper's
1739
- * marker — the same injection `qualify` receives, and the only way to reach the
1740
- * defect channel from inside an asynchronous callback (a `throw` there runs in
1741
- * its own turn, long after the executor body returned).
1742
- *
1743
- * `T` and `E` cannot be inferred from the body, since `settle` is a parameter.
1744
- * Supply them explicitly, or let them flow from an annotated target. Absent
1745
- * either, both default to `never` (Thesis #3: no path may produce `unknown` in
1746
- * `E`) — so an unannotated call is a compile error at the `settle(...)` call
1747
- * site, not a silently-`unknown` channel.
1748
- *
1749
- * An executor that never settles yields an `AsyncResult` that never resolves —
1750
- * the one hazard {@link fromPromise} does not have, and identical to
1751
- * `new Promise`.
1752
- *
1753
- * @typeParam T - the success type.
1754
- * @typeParam E - the modeled error type.
1755
- * @param executor - runs immediately; receives the settler and the `defect` helper.
2097
+ * Extract the error type `E` from a `Result` type — the counterpart of
2098
+ * {@link OkOf}.
1756
2099
  *
1757
- * @category Interop
2100
+ * @typeParam R - the `Result` type to inspect.
1758
2101
  *
1759
2102
  * @example
1760
2103
  * ```ts
1761
- * import { fromExecutor, Err, Ok } from "unthrown";
1762
- *
1763
- * const listen = (port: number) =>
1764
- * fromExecutor<Server, PortInUse>((settle, defect) => {
1765
- * server.once("error", (cause) =>
1766
- * isAddrInUse(cause) ? settle(Err(new PortInUse(port))) : settle(defect(cause)),
1767
- * );
1768
- * server.listen(port, () => settle(Ok(server)));
1769
- * });
2104
+ * type E = ErrOf<Result<User, NotFound>>; // NotFound
1770
2105
  * ```
1771
- */
1772
- declare function fromExecutor<T = never, E = never>(executor: (settle: Settle<T, E>, defect: (cause: unknown) => Defect) => void): AsyncResult$1<T, E>;
1773
- /**
1774
- * The success channel of {@link all} / {@link allAsync}: a **positional tuple**
1775
- * for a fixed-length input (including the empty tuple), or a homogeneous
1776
- * **array** for a dynamic one.
1777
- *
1778
- * @remarks
1779
- * The split keys off the input's `length`: a fixed tuple has a literal length
1780
- * (`number extends Rs["length"]` is false → keep the positional `Ts`), while a
1781
- * general array has `length: number` (→ collapse to `Ts[number][]`). Checking
1782
- * length rather than `Rs extends [unknown, ...unknown[]]` keeps `all([])` typed
1783
- * as `Result<[], …>` instead of `Result<never[], …>`.
1784
- *
1785
- * @typeParam Rs - the tuple/array of input `Result` types.
1786
- * @typeParam Ts - per-element extracted success types (`OkOf` for `all`,
1787
- * `AsyncOkOf` for `allAsync`).
1788
- * @internal
1789
- */
1790
- type AllOk<Rs extends readonly unknown[], Ts extends readonly unknown[]> = number extends Rs["length"] ? Ts[number][] : Ts;
1791
- /**
1792
- * A `[key, error]` pair from a record aggregate, correlated per key.
1793
- *
1794
- * @remarks
1795
- * `-?` because the mapped type is homomorphic: an optional input key would
1796
- * otherwise carry its optionality through and put `undefined` in the entry
1797
- * union, breaking the documented `([key, error]) => …` destructure. An entry
1798
- * whose error channel is `never` (an infallible input — every `@unthrown/drizzle`
1799
- * read) is dropped rather than emitted as an uninhabited `[K, never]` arm, which
1800
- * a `switch` over the key would still have to write a dead case for.
1801
2106
  *
1802
- * @internal
2107
+ * @category Types
1803
2108
  */
1804
- type DictErrEntry<R> = { [K in keyof R]-?: [ErrOf<R[K]>] extends [never] ? never : readonly [K, ErrOf<R[K]>]; }[keyof R];
1805
- /** The {@link AsyncResult} counterpart of {@link DictErrEntry}. @internal */
1806
- type AsyncDictErrEntry<R> = { [K in keyof R]-?: [AsyncErrOf<R[K]>] extends [never] ? never : readonly [K, AsyncErrOf<R[K]>]; }[keyof R];
1807
- /** A non-empty readonly list — `merge` runs only when an `Err` was collected. @internal */
1808
- type NonEmpty<T> = readonly [T, ...T[]];
1809
- /** A record of `Result`s — the input to {@link allFromDict}. */
1810
- type ResultRecord = Record<string, Result$1<unknown, unknown>>;
1811
- /** A record of `AsyncResult`s — the input to {@link allFromDictAsync}. */
1812
- type AsyncResultRecord = Record<string, AsyncResult$1<unknown, unknown>>;
2109
+ type ErrOf<R> = R extends {
2110
+ readonly tag: "Err";
2111
+ readonly error: infer E;
2112
+ } ? E : never;
1813
2113
  /**
1814
- * Collect a tuple/array of {@link Result}s into a single `Result` of all their
1815
- * success values.
1816
- *
1817
- * @remarks
1818
- * Short-circuits on the **first** `Err` (later entries are not inspected for
1819
- * their error); any `Defect` present **dominates**, winning even over an earlier
1820
- * `Err`. A **fixed tuple** keeps its positional types — `all([Ok(1), Ok("a")])`
1821
- * is `Result<[number, string], …>` — while a **dynamic array** `Result<T, E>[]`
1822
- * collapses to `Result<T[], E>` with no cast. For a **record** keyed by name,
1823
- * use {@link allFromDict}. To report **every** `Err` instead of only the first,
1824
- * use {@link validateAll}.
2114
+ * Extract the success type `T` from an {@link AsyncResult} type — the async
2115
+ * counterpart of {@link OkOf}.
1825
2116
  *
1826
- * @category Aggregate
2117
+ * @typeParam R - the `AsyncResult` type to inspect.
1827
2118
  *
1828
2119
  * @example
1829
2120
  * ```ts
1830
- * import { all, Ok, Err } from "unthrown";
1831
- *
1832
- * all([Ok(1), Ok("a"), Ok(true)]).get(); // => [1, "a", true] (typed [number, string, boolean])
1833
- * all([Ok(1), Err("e"), Ok(3)]); // => Err("e") (short-circuits on the first Err)
2121
+ * type T = AsyncOkOf<AsyncResult<User, NotFound>>; // User
1834
2122
  * ```
1835
- */
1836
- declare function all<Rs extends readonly Result$1<unknown, unknown>[]>(results: readonly [...Rs]): Result$1<AllOk<Rs, { [K in keyof Rs]: OkOf<Rs[K]>; }>, ErrOf<Rs[number]>>;
1837
- /**
1838
- * Collect a **record** of {@link Result}s into a single `Result` of a record of
1839
- * their success values — `allFromDict({ a: Result<A, E>, b: Result<B, E> })` is
1840
- * `Result<{ a: A; b: B }, E>`. The named counterpart of {@link all}, for
1841
- * parallel work you'd rather not tuple.
1842
2123
  *
1843
- * @remarks
1844
- * Same folding rules as {@link all}: first `Err` short-circuits, any `Defect`
1845
- * dominates. This is **not** error accumulation — for that, reach for
1846
- * {@link validateAllFromDict}, which accumulates every `Err` and folds them into
1847
- * one modeled error.
1848
- *
1849
- * @category Aggregate
1850
- *
1851
- * @example
1852
- * ```ts
1853
- * import { allFromDict, Ok, Err } from "unthrown";
1854
- *
1855
- * allFromDict({ id: Ok(1), name: Ok("ada") }).get(); // => { id: 1, name: "ada" }
1856
- * allFromDict({ id: Ok(1), name: Err("missing") }); // => Err("missing")
1857
- * ```
2124
+ * @category Types
1858
2125
  */
1859
- declare function allFromDict<R extends ResultRecord>(results: R): Result$1<{ [K in keyof R]: OkOf<R[K]>; }, ErrOf<R[keyof R]>>;
2126
+ type AsyncOkOf<R> = R extends Awaitable<infer Res> ? OkOf<Res> : never;
1860
2127
  /**
1861
- * The asynchronous counterpart of {@link all}: combine a tuple/array of
1862
- * {@link AsyncResult}s into one `AsyncResult` of all their success values.
1863
- *
1864
- * @remarks
1865
- * The inputs are resolved **concurrently** (order preserved); the resolved
1866
- * `Result`s are then folded with the same rules as {@link all} — first `Err`
1867
- * short-circuits, any `Defect` dominates. As ever, the returned `AsyncResult`'s
1868
- * internal promise never rejects. For a **record**, use {@link allFromDictAsync};
1869
- * to report **every** `Err`, use {@link validateAllAsync}.
2128
+ * Extract the error type `E` from an {@link AsyncResult} type — the async
2129
+ * counterpart of {@link ErrOf}.
1870
2130
  *
1871
- * @category Aggregate
2131
+ * @typeParam R - the `AsyncResult` type to inspect.
1872
2132
  *
1873
2133
  * @example
1874
2134
  * ```ts
1875
- * import { allAsync, fromSafePromise } from "unthrown";
1876
- *
1877
- * const both = allAsync([
1878
- * fromSafePromise(Promise.resolve(1)),
1879
- * fromSafePromise(Promise.resolve(2)),
1880
- * ]);
1881
- * (await both).get(); // => [1, 2]
2135
+ * type E = AsyncErrOf<AsyncResult<User, NotFound>>; // NotFound
1882
2136
  * ```
1883
- */
1884
- declare function allAsync<Rs extends readonly AsyncResult$1<unknown, unknown>[]>(results: readonly [...Rs]): AsyncResult$1<AllOk<Rs, { [K in keyof Rs]: AsyncOkOf<Rs[K]>; }>, AsyncErrOf<Rs[number]>>;
1885
- /**
1886
- * The asynchronous counterpart of {@link allFromDict}: combine a record of
1887
- * {@link AsyncResult}s into one `AsyncResult` of a record of their values.
1888
- *
1889
- * @remarks
1890
- * Resolved concurrently (order preserved), folded with the {@link all} rules,
1891
- * and the internal promise never rejects. To report **every** `Err`, use
1892
- * {@link validateAllFromDictAsync}.
1893
- *
1894
- * @category Aggregate
1895
- *
1896
- * @example
1897
- * ```ts
1898
- * import { allFromDictAsync, fromSafePromise } from "unthrown";
1899
2137
  *
1900
- * const both = allFromDictAsync({
1901
- * a: fromSafePromise(Promise.resolve(1)),
1902
- * b: fromSafePromise(Promise.resolve("x")),
1903
- * });
1904
- * (await both).get(); // => { a: 1, b: "x" }
1905
- * ```
2138
+ * @category Types
1906
2139
  */
1907
- declare function allFromDictAsync<R extends AsyncResultRecord>(results: R): AsyncResult$1<{ [K in keyof R]: AsyncOkOf<R[K]>; }, AsyncErrOf<R[keyof R]>>;
2140
+ type AsyncErrOf<R> = R extends Awaitable<infer Res> ? ErrOf<Res> : never;
2141
+ //#endregion
2142
+ //#region src/constructors.d.ts
1908
2143
  /**
1909
- * Collect a tuple/array of {@link Result}s, **accumulating every** `Err` and
1910
- * merging them into a single modeled error — the accumulating counterpart of
1911
- * {@link all}.
1912
- *
1913
- * @remarks
1914
- * Same success channel as {@link all}: a **fixed tuple** keeps its positional
1915
- * types, a **dynamic array** collapses to `Result<T[], E2>`. The difference is
1916
- * the error channel — instead of the first `Err` winning, every `Err` is
1917
- * collected in input order and handed to `merge`, whose return becomes the
1918
- * modeled error.
1919
- *
1920
- * `merge` receives a **non-empty** list, so it is total: it is called only when
1921
- * at least one `Err` was collected. It is **not** called when every element is
1922
- * `Ok`, nor when a `Defect` is present.
1923
- *
1924
- * Any `Defect` still **dominates** — it wins over the accumulated errors, which
1925
- * are discarded and never reach `merge`. A defect means something in this batch
1926
- * failed in a way nobody modeled, so the violations computed alongside it are
1927
- * not trustworthy. An out-of-contract non-`Result` element becomes a
1928
- * `TypeError`-caused `Defect` the same way, and a throw inside `merge` becomes
1929
- * a `Defect` too.
1930
- *
1931
- * `merge` must be **synchronous** — an `async` one is a compile error
1932
- * ({@link NotThenable}), since a `Promise` in `E` is an unqualified rejection.
2144
+ * Construct a successful `void` {@link Result} — `Result<void, never>` —
2145
+ * sparing you `Ok(undefined)` and typing the success channel `void`, not
2146
+ * `undefined`.
1933
2147
  *
1934
- * For **schema-shaped** input (a request body, a form), reach for
1935
- * `@unthrown/standard-schema`'s `fromSchema` instead — a validator already
1936
- * hands you every issue as the modeled error. `validateAll` is for independent
1937
- * checks you wrote yourself. For a **record** keyed by name, use
1938
- * {@link validateAllFromDict}.
2148
+ * @example
2149
+ * ```ts
2150
+ * import { Ok } from "unthrown";
1939
2151
  *
1940
- * @typeParam Rs - the tuple/array of input `Result` types.
1941
- * @typeParam E2 - the merged error type.
1942
- * @param results - the results to collect.
1943
- * @param merge - folds the collected errors into one modeled error.
2152
+ * Ok(); // => a void success: Result<void, never>
2153
+ * ```
1944
2154
  *
1945
- * @category Aggregate
2155
+ * @category Constructors
2156
+ */
2157
+ export declare function Ok(): Result<void, never>;
2158
+ /**
2159
+ * Construct a successful {@link Result}.
2160
+ *
2161
+ * @typeParam T - the success value type.
2162
+ * @param value - the success value to wrap.
1946
2163
  *
1947
2164
  * @example
1948
2165
  * ```ts
1949
- * import { validateAll, Ok, Err } from "unthrown";
1950
- *
1951
- * // every Err is collected, not just the first
1952
- * validateAll([Ok(1), Err("stock"), Err("credit")], (errors) => errors.join(" and "));
1953
- * // => Err("stock and credit")
2166
+ * import { Ok } from "unthrown";
1954
2167
  *
1955
- * // all-Ok keeps the positional tuple; `merge` never runs
1956
- * validateAll([Ok(1), Ok("a")], (errors) => errors.join());
1957
- * // => Ok([1, "a"]) typed Result<[number, string], string>
2168
+ * Ok(2).map((n) => n + 1); // => Ok(3)
2169
+ * Ok(42).get(); // => 42
1958
2170
  * ```
2171
+ *
2172
+ * @category Constructors
1959
2173
  */
1960
- declare function validateAll<Rs extends readonly Result$1<unknown, unknown>[], E2>(results: readonly [...Rs], merge: (errors: NonEmpty<ErrOf<Rs[number]>>) => E2 & NotThenable<E2>): Result$1<AllOk<Rs, { [K in keyof Rs]: OkOf<Rs[K]>; }>, E2>;
2174
+ export declare function Ok<T>(value: T): Result<T, never>;
1961
2175
  /**
1962
- * Collect a **record** of {@link Result}s, accumulating every `Err` — the
1963
- * accumulating counterpart of {@link allFromDict}, and the named counterpart of
1964
- * {@link validateAll}.
2176
+ * Construct a failed {@link Result} carrying a **modeled** error.
1965
2177
  *
1966
- * @remarks
1967
- * `merge` receives a non-empty list of **`[key, error]` entries**, correlated
1968
- * per key: `{ a: Result<A, E1>; b: Result<B, E2> }` yields
1969
- * `["a", E1] | ["b", E2]`, so a `switch` on the key narrows the error and an
1970
- * impossible pairing does not typecheck. That is what keeps two checks sharing
1971
- * one error type distinguishable. Entries come in `Object.keys` order.
2178
+ * @typeParam E - the modeled error type.
2179
+ * @param error - the domain error to wrap.
1972
2180
  *
1973
- * Every other rule matches {@link validateAll}: any `Defect` dominates and
1974
- * discards the accumulated errors, a throw in `merge` becomes a `Defect`, and
1975
- * `merge` must be synchronous.
2181
+ * @example
2182
+ * ```ts
2183
+ * import { Err } from "unthrown";
1976
2184
  *
1977
- * @typeParam R - the record of input `Result` types.
1978
- * @typeParam E2 - the merged error type.
1979
- * @param results - the results to collect, keyed by name.
1980
- * @param merge - folds the collected `[key, error]` entries into one error.
2185
+ * Err("not_found").map((n) => n + 1); // => Err("not_found") (map skipped)
2186
+ * Err("not_found").getErr(); // => "not_found"
2187
+ * ```
1981
2188
  *
1982
- * @category Aggregate
2189
+ * @category Constructors
2190
+ */
2191
+ export declare function Err<E>(error: E): Result<never, E>;
2192
+ /**
2193
+ * Construct a successful `void` {@link AsyncResult} — `AsyncResult<void, never>`
2194
+ * — the pre-lifted form of the no-arg {@link Ok}, sparing you
2195
+ * `Ok(undefined).toAsync()`.
1983
2196
  *
1984
2197
  * @example
1985
2198
  * ```ts
1986
- * import { validateAllFromDict, Ok, Err } from "unthrown";
2199
+ * import { OkAsync } from "unthrown";
1987
2200
  *
1988
- * validateAllFromDict(
1989
- * { vatRate: Err("out of range"), currency: Ok("EUR"), dueDate: Err("past") },
1990
- * (entries) => entries.map(([key, error]) => `${key}: ${error}`).join("; "),
1991
- * );
1992
- * // => Err("vatRate: out of range; dueDate: past")
2201
+ * OkAsync(); // => a void success: AsyncResult<void, never>
1993
2202
  * ```
2203
+ *
2204
+ * @category Constructors
1994
2205
  */
1995
- declare function validateAllFromDict<R extends ResultRecord, E2>(results: R, merge: (entries: NonEmpty<DictErrEntry<R>>) => E2 & NotThenable<E2>): Result$1<{ [K in keyof R]: OkOf<R[K]>; }, E2>;
2206
+ export declare function OkAsync(): AsyncResult<void, never>;
1996
2207
  /**
1997
- * The asynchronous counterpart of {@link validateAll}: collect a tuple/array of
1998
- * {@link AsyncResult}s, accumulating every `Err` into one merged error.
2208
+ * Construct a successful {@link AsyncResult} from a pure value — the pre-lifted
2209
+ * form of {@link Ok}, sparing you `Ok(value).toAsync()`.
1999
2210
  *
2000
2211
  * @remarks
2001
- * Every {@link validateAll} rule holds, with the inputs resolved
2002
- * **concurrently** (order preserved) — as with {@link allAsync}, no work is
2003
- * short-circuited either way; the fail-fast/accumulating split is purely which
2004
- * errors get reported. The internal promise never rejects: an out-of-contract
2005
- * rejecting thenable becomes a dominating `Defect`. `merge` stays synchronous
2006
- * here too — this is exactly where its rejection would land unqualified in `E`.
2007
- * For a **record**, use {@link validateAllFromDictAsync}.
2008
- *
2009
- * @typeParam Rs - the tuple/array of input `AsyncResult` types.
2010
- * @typeParam E2 - the merged error type.
2011
- * @param results - the async results to collect.
2012
- * @param merge - folds the collected errors into one modeled error.
2212
+ * Reach for this on the synchronous/early branch of an `AsyncResult`-returning
2213
+ * function, so both branches share one return type without a trailing
2214
+ * `.toAsync()`. Named with the `Async` suffix the async free functions carry
2215
+ * (`allAsync`, `allFromDictAsync`); the {@link AsyncResult} companion aliases it
2216
+ * as `AsyncResult.Ok` (the namespace already says "async", so the suffix drops).
2013
2217
  *
2014
- * @category Aggregate
2218
+ * @typeParam T - the success value type.
2219
+ * @param value - the success value to wrap.
2015
2220
  *
2016
2221
  * @example
2017
2222
  * ```ts
2018
- * import { validateAllAsync, OkAsync, ErrAsync } from "unthrown";
2223
+ * import { OkAsync, type AsyncResult } from "unthrown";
2019
2224
  *
2020
- * const checked = validateAllAsync(
2021
- * [OkAsync(1), ErrAsync("stock"), ErrAsync("credit")],
2022
- * (errors) => errors.join(" and "),
2023
- * );
2024
- * // (await checked) => Err("stock and credit")
2225
+ * function loadItems(ids: string[]): AsyncResult<Item[], never> {
2226
+ * if (ids.length === 0) return OkAsync([]); // no more Ok([]).toAsync()
2227
+ * return itemRepository.load(ids);
2228
+ * }
2025
2229
  * ```
2230
+ *
2231
+ * @category Constructors
2026
2232
  */
2027
- declare function validateAllAsync<Rs extends readonly AsyncResult$1<unknown, unknown>[], E2>(results: readonly [...Rs], merge: (errors: NonEmpty<AsyncErrOf<Rs[number]>>) => E2 & NotThenable<E2>): AsyncResult$1<AllOk<Rs, { [K in keyof Rs]: AsyncOkOf<Rs[K]>; }>, E2>;
2233
+ export declare function OkAsync<T>(value: T): AsyncResult<T, never>;
2028
2234
  /**
2029
- * The asynchronous counterpart of {@link validateAllFromDict}: collect a record
2030
- * of {@link AsyncResult}s, accumulating every `Err` into one merged error.
2235
+ * Construct a failed {@link AsyncResult} carrying a **modeled** error — the
2236
+ * pre-lifted form of {@link Err}, sparing you `Err(error).toAsync()`.
2031
2237
  *
2032
2238
  * @remarks
2033
- * The {@link validateAllFromDict} rules, over inputs resolved concurrently as
2034
- * in {@link validateAllAsync}.
2035
- *
2036
- * @typeParam R - the record of input `AsyncResult` types.
2037
- * @typeParam E2 - the merged error type.
2038
- * @param results - the async results to collect, keyed by name.
2039
- * @param merge - folds the collected `[key, error]` entries into one error.
2239
+ * The error-channel mirror of {@link OkAsync}; see it for the naming and the
2240
+ * `AsyncResult.Err` companion alias.
2040
2241
  *
2041
- * @category Aggregate
2242
+ * @typeParam E - the modeled error type.
2243
+ * @param error - the domain error to wrap.
2042
2244
  *
2043
2245
  * @example
2044
2246
  * ```ts
2045
- * import { validateAllFromDictAsync, OkAsync, ErrAsync } from "unthrown";
2247
+ * import { ErrAsync } from "unthrown";
2046
2248
  *
2047
- * const checked = validateAllFromDictAsync(
2048
- * { stock: ErrAsync("none left"), credit: OkAsync(500) },
2049
- * (entries) => entries.map(([key, error]) => `${key}: ${error}`).join("; "),
2050
- * );
2051
- * // (await checked) => Err("stock: none left")
2249
+ * ErrAsync("not_found"); // AsyncResult<never, string>
2052
2250
  * ```
2251
+ *
2252
+ * @category Constructors
2053
2253
  */
2054
- declare function validateAllFromDictAsync<R extends AsyncResultRecord, E2>(results: R, merge: (entries: NonEmpty<AsyncDictErrEntry<R>>) => E2 & NotThenable<E2>): AsyncResult$1<{ [K in keyof R]: AsyncOkOf<R[K]>; }, E2>;
2055
- //#endregion
2056
- //#region src/facade.d.ts
2254
+ export declare function ErrAsync<E>(error: E): AsyncResult<never, E>;
2057
2255
  /**
2058
- * Companion object grouping the **`Result`-producing** entry points under a
2059
- * single, discoverable namespace: {@link Result.Ok}, {@link Result.Err},
2060
- * {@link Result.Do}, {@link Result.fromNullable}, {@link Result.fromThrowable},
2061
- * {@link Result.fromSafeThrowable}, {@link Result.all},
2062
- * {@link Result.allFromDict}, {@link Result.validateAll},
2063
- * {@link Result.validateAllFromDict}, {@link Result.isOk}, {@link Result.isErr},
2064
- * {@link Result.isDefect}, {@link Result.isResult}.
2065
- *
2066
- * @remarks
2067
- * Purely additive sugar — each member **is** the corresponding free function.
2068
- * The free functions remain the primary, tree-shakeable API; importing only
2069
- * `{ Ok }` never pulls this object in. The value `Result` and the type
2070
- * {@link Result} share one name (the companion-object pattern).
2071
- *
2072
- * The **async** entry points live on the sibling {@link AsyncResult} companion
2073
- * (`AsyncResult.fromPromise`, `AsyncResult.all`, …), grouped by what they
2074
- * return — a static lives in exactly one namespace.
2256
+ * Type guard: narrow a {@link Result} to its `Ok` variant, exposing `.value`.
2075
2257
  *
2076
- * @category Facade
2258
+ * @returns `true` when `r` is `Ok`.
2077
2259
  *
2078
2260
  * @example
2079
2261
  * ```ts
2080
- * import { Result } from "unthrown";
2081
- * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).get(); // => 2
2262
+ * import { isOk, Ok, Err, type Result } from "unthrown";
2263
+ *
2264
+ * isOk(Ok(1)); // => true
2265
+ * isOk(Err("boom")); // => false
2266
+ *
2267
+ * declare const r: Result<number, string>;
2268
+ * if (isOk(r)) r.value; // number, narrowed
2082
2269
  * ```
2270
+ *
2271
+ * @category Guards
2083
2272
  */
2084
- declare const Result: {
2085
- readonly Ok: typeof Ok;
2086
- readonly Err: typeof Err;
2087
- readonly Do: typeof Do;
2088
- readonly fromNullable: typeof fromNullable;
2089
- readonly fromThrowable: typeof fromThrowable;
2090
- readonly fromSafeThrowable: typeof fromSafeThrowable;
2091
- readonly all: typeof all;
2092
- readonly allFromDict: typeof allFromDict;
2093
- readonly validateAll: typeof validateAll;
2094
- readonly validateAllFromDict: typeof validateAllFromDict;
2095
- readonly isOk: typeof isOk;
2096
- readonly isErr: typeof isErr;
2097
- readonly isDefect: typeof isDefect;
2098
- readonly isResult: typeof isResult;
2099
- };
2273
+ export declare function isOk<T, E>(r: Result<T, E>): r is OkView<T, E>;
2100
2274
  /**
2101
- * `Result<T, E>` — the core discriminated union. Shares its name with the
2102
- * {@link Result | companion object} above (the value and type are one name); this
2103
- * is the type half.
2275
+ * Type guard: narrow a {@link Result} to its `Err` variant, exposing `.error`.
2104
2276
  *
2105
- * @remarks
2106
- * A `Result` is a discriminated union, so TypeDoc can't list its methods on this
2107
- * alias. Its fluent combinators (`map`, `flatMap`, `match`, `get`, …) are
2108
- * documented one per entry on {@link ResultMethods} — the shared method surface
2109
- * every variant carries. For "which one do I reach for?", see the
2110
- * [Choosing a combinator](/reference/combinators) guide.
2277
+ * @returns `true` when `r` is `Err`.
2111
2278
  *
2112
- * @category Facade
2279
+ * @example
2280
+ * ```ts
2281
+ * import { isErr, Ok, Err, type Result } from "unthrown";
2282
+ *
2283
+ * isErr(Err("boom")); // => true
2284
+ * isErr(Ok(1)); // => false
2285
+ *
2286
+ * declare const r: Result<number, string>;
2287
+ * if (isErr(r)) r.error; // string, narrowed
2288
+ * ```
2289
+ *
2290
+ * @category Guards
2113
2291
  */
2114
- type Result<T, E> = Result$1<T, E>;
2292
+ export declare function isErr<T, E>(r: Result<T, E>): r is ErrView<E, T>;
2115
2293
  /**
2116
- * Companion object grouping the **`AsyncResult`-producing** entry points under
2117
- * the matching namespace: {@link AsyncResult.Ok}, {@link AsyncResult.Err},
2118
- * {@link AsyncResult.Do}, {@link AsyncResult.fromExecutor},
2119
- * {@link AsyncResult.fromPromise}, {@link AsyncResult.fromSafePromise},
2120
- * {@link AsyncResult.all}, {@link AsyncResult.allFromDict},
2121
- * {@link AsyncResult.validateAll}, {@link AsyncResult.validateAllFromDict}.
2294
+ * Type guard: narrow a {@link Result} to its `Defect` variant, exposing `.cause`.
2122
2295
  *
2123
2296
  * @remarks
2124
- * The async sibling of {@link Result}. Statics are grouped by what they
2125
- * **return**, so the pre-lifted constructors, `fromExecutor`,
2126
- * `fromPromise`/`fromSafePromise`, and the async aggregates sit here rather
2127
- * than on {@link Result}; the namespace
2128
- * already conveys "async", so the members drop the `Async` suffix their free
2129
- * functions carry (`AsyncResult.Ok` is `OkAsync`; `AsyncResult.Err` is
2130
- * `ErrAsync`; `AsyncResult.Do` is `DoAsync`; `AsyncResult.all` is `allAsync`;
2131
- * `AsyncResult.allFromDict` is
2132
- * `allFromDictAsync`; `AsyncResult.validateAll` is `validateAllAsync`). Like
2133
- * {@link Result}, the free functions remain the
2134
- * primary, tree-shakeable API; the value `AsyncResult` and the type
2135
- * {@link AsyncResult} share one name.
2297
+ * A `Defect` has no public constructor — it only arises at a boundary (e.g. a
2298
+ * callback throwing inside a combinator). This guard is how you detect one.
2136
2299
  *
2137
- * @category Facade
2300
+ * @returns `true` when `r` is a `Defect`.
2138
2301
  *
2139
2302
  * @example
2140
2303
  * ```ts
2141
- * import { AsyncResult } from "unthrown";
2142
- * const user = await AsyncResult.fromPromise(
2143
- * fetchUser(id),
2144
- * (c, defect) => defect(c),
2145
- * );
2146
- * user.get(); // => the fetched user (on success)
2147
- * ```
2148
- */
2149
- declare const AsyncResult: {
2150
- readonly Ok: typeof OkAsync;
2151
- readonly Err: typeof ErrAsync;
2152
- readonly Do: typeof DoAsync;
2153
- readonly fromExecutor: typeof fromExecutor;
2154
- readonly fromPromise: typeof fromPromise;
2155
- readonly fromSafePromise: typeof fromSafePromise;
2156
- readonly all: typeof allAsync;
2157
- readonly allFromDict: typeof allFromDictAsync;
2158
- readonly validateAll: typeof validateAllAsync;
2159
- readonly validateAllFromDict: typeof validateAllFromDictAsync;
2160
- };
2161
- /**
2162
- * `AsyncResult<T, E>` — the async counterpart of {@link Result}. Shares its name
2163
- * with the {@link AsyncResult | companion object} above (value and type are one
2164
- * name); this is the type half.
2304
+ * import { isDefect, Ok } from "unthrown";
2165
2305
  *
2166
- * @remarks
2167
- * `AsyncResult` carries the async fluent surface; its combinators (`map`,
2168
- * `flatMap`, `match`, `get`, …) are documented one per entry — with their
2169
- * async signatures — on {@link AsyncResultMethods}. For "which one do I reach
2170
- * for?", see the [Choosing a combinator](/reference/combinators) guide.
2306
+ * // A throw inside a combinator is captured as a Defect:
2307
+ * const r = Ok(1).map(() => {
2308
+ * throw new Error("boom");
2309
+ * });
2310
+ * isDefect(r); // => true
2311
+ * isDefect(Ok(1)); // => false
2171
2312
  *
2172
- * @category Facade
2313
+ * if (isDefect(r)) r.cause; // unknown, narrowed
2314
+ * ```
2315
+ *
2316
+ * @category Guards
2173
2317
  */
2174
- type AsyncResult<T, E> = AsyncResult$1<T, E>;
2318
+ export declare function isDefect<T, E>(r: Result<T, E>): r is DefectView<T, E>;
2175
2319
  //#endregion
2176
2320
  //#region src/tagged.d.ts
2177
2321
  type Props = Record<string, unknown>;
@@ -2277,8 +2421,8 @@ type TaggedErrorConstructor<Tag extends string> = {
2277
2421
  * new HttpError({ status: 500 }).status; // => 500
2278
2422
  * ```
2279
2423
  */
2280
- declare function TaggedError<Tag extends string>(tag: Tag, options?: {
2424
+ export declare function TaggedError<Tag extends string>(tag: Tag, options?: {
2281
2425
  readonly name?: string;
2282
2426
  }): TaggedErrorConstructor<Tag>;
2283
2427
  //#endregion
2284
- export { type AsyncErrOf, type AsyncOkOf, AsyncResult, type AsyncResultMethods, type Awaitable, type DefectView, Do, DoAsync, Err, ErrAsync, type ErrMatcher, type ErrOf, type ErrView, type FailureView, GetError, type Matcher, NonExhaustiveError, type NotThenable, Ok, OkAsync, type OkOf, type OkView, P, type PatternMatcher, Result, type ResultMethods, type Settle, TaggedError, type TaggedErrorConstructor, type TaggedErrorInstance, type UniversalPattern, all, allAsync, allFromDict, allFromDictAsync, fromExecutor, fromNullable, fromPromise, fromSafePromise, fromSafeThrowable, fromThrowable, isDefect, isErr, isOk, isResult, match, validateAll, validateAllAsync, validateAllFromDict, validateAllFromDictAsync };
2428
+ export type { AsyncErrOf, AsyncOkOf, AsyncResultMethods, Awaitable, DefectView, ErrMatcher, ErrOf, ErrView, FailureView, Matcher, NotThenable, OkOf, OkView, PatternMatcher, ResultMethods, Settle, TaggedErrorConstructor, TaggedErrorInstance, UniversalPattern };