@onrails/result 0.1.3 → 0.2.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/DESIGN.md +2 -2
- package/README.md +0 -3
- package/RECIPES.md +16 -15
- package/dist/$.cjs +40 -0
- package/dist/$.cjs.map +1 -0
- package/dist/$.js +36 -0
- package/dist/$.js.map +1 -0
- package/dist/_.d.cts +2 -0
- package/dist/_.d.ts +2 -0
- package/dist/async-1fIhfzFu.d.ts +290 -0
- package/dist/async-u8U5P2EH.d.cts +290 -0
- package/dist/compat/neverthrow.cjs +234 -50
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +2 -2
- package/dist/compat/neverthrow.d.ts +2 -2
- package/dist/compat/neverthrow.js +231 -47
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +1 -24
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +3 -9
- package/dist/extra.d.ts +3 -9
- package/dist/extra.js +2 -24
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs +0 -2
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +2 -6
- package/dist/fluent.d.ts +2 -6
- package/dist/fluent.js +0 -2
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +226 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -51
- package/dist/index.d.ts +174 -51
- package/dist/index.js +227 -44
- package/dist/index.js.map +1 -1
- package/dist/interop.cjs +209 -23
- package/dist/interop.cjs.map +1 -1
- package/dist/interop.d.cts +60 -10
- package/dist/interop.d.ts +60 -10
- package/dist/interop.js +209 -23
- package/dist/interop.js.map +1 -1
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.cts +7 -5
- package/dist/pipe.d.ts +7 -5
- package/dist/pipe.js.map +1 -1
- package/dist/railway.cjs +205 -24
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +20 -3
- package/dist/railway.d.ts +20 -3
- package/dist/railway.js +205 -25
- package/dist/railway.js.map +1 -1
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.d.cts +29 -3
- package/dist/try-gen.d.ts +29 -3
- package/dist/try-gen.js.map +1 -1
- package/dist/types-BQ9vv0nD.d.cts +55 -0
- package/dist/types-BQ9vv0nD.d.ts +55 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +10 -3
- package/skills/railway-do-notation/SKILL.md +436 -0
- package/skills/result-composition/SKILL.md +325 -0
- package/dist/async-CH8Cs8KE.d.cts +0 -161
- package/dist/async-DKISqIBc.d.ts +0 -161
- package/dist/types-C2Dp1d5J.d.cts +0 -21
- package/dist/types-C2Dp1d5J.d.ts +0 -21
package/DESIGN.md
CHANGED
|
@@ -21,7 +21,7 @@ type Result<T, E> =
|
|
|
21
21
|
|
|
22
22
|
- `flatMap` is the canonical bind and widens error types (`E | F`).
|
|
23
23
|
- `match` is the canonical terminal collapse — positional, dual-form.
|
|
24
|
-
-
|
|
24
|
+
- For files that also import `match` from `ts-pattern`, dissolve the collision with a namespace import (`import * as R from "@onrails/result"` → `R.match`).
|
|
25
25
|
- `tap` / `tapErr` observe a track without changing the carried value.
|
|
26
26
|
- `recover` binds the error track and may return a failed workflow back to success.
|
|
27
27
|
- `pipe(value, ...fns)` is the variadic value-first pipe (up to 9 steps); `flow(...fns)` is the variadic point-free composition in `@onrails/result/pipe`.
|
|
@@ -69,7 +69,7 @@ Use `Railway` when named context removes nesting or positional tuple plumbing. P
|
|
|
69
69
|
## Combining results
|
|
70
70
|
|
|
71
71
|
- `combine` / `combineTuple` — sync, first-Err wins.
|
|
72
|
-
- `
|
|
72
|
+
- `ResultAsync.combineTuple` — async, sequential, first-Err in input order.
|
|
73
73
|
- `parallelTupleAsync` — async, concurrent (branches overlap), first-Err in input order.
|
|
74
74
|
- `ResultAsync.combine` — homogeneous async collection.
|
|
75
75
|
|
package/README.md
CHANGED
|
@@ -141,9 +141,6 @@ import { match } from "ts-pattern";
|
|
|
141
141
|
R.match(result, onOk, onErr);
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
> [!NOTE]
|
|
145
|
-
> `matchResult` is deprecated and will be removed in the next major version.
|
|
146
|
-
|
|
147
144
|
`unwrapOk` and `unwrapErr` are test/assertion helpers. Prefer `match`, `isOk`, or `isErr` in production control flow.
|
|
148
145
|
|
|
149
146
|
```ts
|
package/RECIPES.md
CHANGED
|
@@ -118,18 +118,17 @@ const saveOrder = (rawJson: string) =>
|
|
|
118
118
|
flatMap((u) => (u.active ? ok(u) : err({ kind: "inactive" as const }))),
|
|
119
119
|
mapErr((e) => ({ kind: "input" as const, cause: e })),
|
|
120
120
|
tap((u) => log.info({ msg: "validated", userId: u.id })),
|
|
121
|
-
(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
(e): DbError => ({ kind: "db", cause: e }),
|
|
126
|
-
),
|
|
121
|
+
asyncAfter((u) =>
|
|
122
|
+
tryAsync(
|
|
123
|
+
db.orders.insert({ userId: u.id, items: u.cart }),
|
|
124
|
+
(e): DbError => ({ kind: "db", cause: e }),
|
|
127
125
|
),
|
|
126
|
+
),
|
|
128
127
|
);
|
|
129
128
|
// ResultAsync<OrderRow, { kind: "input"; cause: ... } | DbError>
|
|
130
129
|
```
|
|
131
130
|
|
|
132
|
-
|
|
131
|
+
`asyncAfter` is dual-form like the rest of the core API: the data-last call `asyncAfter((u) => ...)` is the sync→async bridge that drops straight into `pipe`. The final step stays point-free — `pipe` feeds the upstream `Result` into the curried bridge automatically.
|
|
133
132
|
|
|
134
133
|
---
|
|
135
134
|
|
|
@@ -306,12 +305,10 @@ const fetchWithBackoff = (url: string) =>
|
|
|
306
305
|
pipe(
|
|
307
306
|
url,
|
|
308
307
|
fetchSync, // Result<Body, NetworkError | RateLimit | Fatal>
|
|
309
|
-
recover((e: NetworkError | RateLimit | Fatal) =>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return err(e);
|
|
314
|
-
}),
|
|
308
|
+
recover((e: NetworkError | RateLimit | Fatal) =>
|
|
309
|
+
// only retryable network errors do work; everything else bubbles up
|
|
310
|
+
e.kind === "network" && e.retryable ? fetchSync(url) : err(e),
|
|
311
|
+
),
|
|
315
312
|
);
|
|
316
313
|
// Result<Body, NetworkError | RateLimit | Fatal>
|
|
317
314
|
```
|
|
@@ -434,6 +431,8 @@ const loadSummary = flow(
|
|
|
434
431
|
// (id: string) => ResultAsync<Summary, Error>
|
|
435
432
|
```
|
|
436
433
|
|
|
434
|
+
The middle step can't go point-free: `profile` is reused across both the `flatMap` and the inner `map`. When a value is referenced by two or more async steps, this nested-closure shape is the cost of staying in `flow`. If it gets deeper, drop to the [`tryGen` escape hatch](#7-trygen-escape-hatch-for-branchy-logic) (Tier 3) and rejoin the pipe.
|
|
435
|
+
|
|
437
436
|
---
|
|
438
437
|
|
|
439
438
|
## 13. Functional Railway pipelines (`railway` + named steps)
|
|
@@ -441,7 +440,7 @@ const loadSummary = flow(
|
|
|
441
440
|
Use `railway(input, ...steps)` from `@onrails/result/railway` to build a multi-step async pipeline using point-free reusable step wrappers. If any step is async, the entire pipeline resolves to a `ResultAsync`.
|
|
442
441
|
|
|
443
442
|
```ts
|
|
444
|
-
import { railway,
|
|
443
|
+
import { railway, parseNamed, fromPromiseNamed, deriveNamed, select } from "@onrails/result/railway";
|
|
445
444
|
import { type ResultAsync } from "@onrails/result";
|
|
446
445
|
|
|
447
446
|
type Dashboard = { title: string };
|
|
@@ -457,7 +456,7 @@ declare const toError: (e: unknown) => Error;
|
|
|
457
456
|
const loadDashboard = (rawId: unknown): ResultAsync<Dashboard, Error> =>
|
|
458
457
|
railway(
|
|
459
458
|
rawId,
|
|
460
|
-
|
|
459
|
+
parseNamed("id", IdSchema, toError),
|
|
461
460
|
fromPromiseNamed(
|
|
462
461
|
"profile",
|
|
463
462
|
({ id }: IdContext) => fetchProfile(id),
|
|
@@ -470,6 +469,8 @@ const loadDashboard = (rawId: unknown): ResultAsync<Dashboard, Error> =>
|
|
|
470
469
|
);
|
|
471
470
|
```
|
|
472
471
|
|
|
472
|
+
Every step now reads name-first (`parseNamed("id", …)`, `fromPromiseNamed("profile", …)`, `deriveNamed("title", …)`) so the field names line up at the left edge. `parseWith(schema).as("id")` remains for the fluent trailing-name style.
|
|
473
|
+
|
|
473
474
|
---
|
|
474
475
|
|
|
475
476
|
## When NOT to go point-free
|
package/dist/$.cjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/result.ts
|
|
4
|
+
var err = (error) => ({
|
|
5
|
+
_tag: "Err",
|
|
6
|
+
error
|
|
7
|
+
});
|
|
8
|
+
var isErr = (result) => result._tag === "Err";
|
|
9
|
+
|
|
10
|
+
// src/try-gen.ts
|
|
11
|
+
var ErrSignal = class {
|
|
12
|
+
constructor(error) {
|
|
13
|
+
this.error = error;
|
|
14
|
+
}
|
|
15
|
+
error;
|
|
16
|
+
_tag = "ErrSignal";
|
|
17
|
+
};
|
|
18
|
+
var yieldResult = (result) => {
|
|
19
|
+
if (isErr(result)) {
|
|
20
|
+
throw new ErrSignal(result.error);
|
|
21
|
+
}
|
|
22
|
+
return result.value;
|
|
23
|
+
};
|
|
24
|
+
var $ = yieldResult;
|
|
25
|
+
var tryGen = (fn) => {
|
|
26
|
+
try {
|
|
27
|
+
return fn();
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof ErrSignal) {
|
|
30
|
+
return err(error.error);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.$ = $;
|
|
37
|
+
exports.tryGen = tryGen;
|
|
38
|
+
exports.yieldResult = yieldResult;
|
|
39
|
+
//# sourceMappingURL=$.cjs.map
|
|
40
|
+
//# sourceMappingURL=$.cjs.map
|
package/dist/$.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/result.ts","../src/try-gen.ts"],"names":[],"mappings":";;;AA2BO,IAAM,GAAA,GAAM,CAAyB,KAAA,MAA4B;AAAA,EACtE,IAAA,EAAM,KAAA;AAAA,EACN;AACF,CAAA,CAAA;AAwBO,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;;;AClD1F,IAAM,YAAN,MAAmB;AAAA,EAEjB,YAAqB,KAAA,EAAU;AAAV,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAAW;AAAA,EAAX,KAAA;AAAA,EADZ,IAAA,GAAO,WAAA;AAElB,CAAA;AAmBO,IAAM,WAAA,GAAc,CAAO,MAAA,KAA4B;AAC5D,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,IAAI,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;AAeO,IAAM,CAAA,GAAI;AAcV,IAAM,MAAA,GAAS,CAAO,EAAA,KAAyC;AACpE,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,SAAA,EAAW;AAC9B,MAAA,OAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"$.cjs","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error);\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, use a namespace\n * import (`import * as R from \"@onrails/result\"` → `R.match`) to dissolve\n * the collision.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — returns the `Ok` value, or **throws the original `Err`\n * value** when called on an `Err`. This is the assertion tier (RFC 0001 §4):\n * intended for `*.spec.ts` / `*.test.ts`, where throwing fails the test loudly.\n * In business logic prefer {@link match} or {@link unwrapOr}; the lint plugins\n * flag `unwrapOk` outside test files.\n *\n * @returns the unwrapped `Ok` value\n * @throws the carried `Err` value when the result is `Err`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapOk(ok(5))).toBe(5);\n * expect(() => unwrapOk(err(error))).toThrow(error);\n * ```\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — returns the `Err` value, or **throws a `TypeError`**\n * when called on an `Ok`. Mirror of {@link unwrapOk} on the error track and\n * part of the same assertion tier (RFC 0001 §4): intended for `*.spec.ts` /\n * `*.test.ts`. Prefer {@link match} / {@link unwrapOr} in business logic; the\n * lint plugins flag `unwrapErr` outside test files.\n *\n * @returns the unwrapped `Err` value\n * @throws `TypeError` when the result is `Ok`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapErr(err(\"x\"))).toBe(\"x\");\n * expect(() => unwrapErr(ok(5))).toThrow(TypeError);\n * ```\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/**\n * Wraps a throwing sync function, returning a function that produces a\n * {@link Result} instead of throwing. Thrown errors pass through `onThrow` to\n * become a typed `Err`; a normal return becomes `Ok`. The neverthrow analogue\n * is `Result.fromThrowable`.\n *\n * @param fn - the throwing function to wrap\n * @param onThrow - maps a thrown value to the `Err` channel\n * @returns a function with `fn`'s parameters that returns `Result<ReturnType, E>`\n *\n * @example\n * ```ts\n * type ParseError = { kind: \"parse\"; message: string };\n * const parse = trySync(\n * JSON.parse,\n * (e): ParseError => ({ kind: \"parse\", message: String(e) }),\n * );\n * parse(\"{}\"); // Ok({})\n * parse(\"nope\"); // Err({ kind: \"parse\", … })\n * ```\n */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns,\n * left-to-right. Use {@link pipe} when you already have a starting value;\n * use {@link flow} to define a reusable composed function with no value yet.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => (name ? ok(name) : err({ kind: \"empty\" as const }))),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import { err, isErr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Internal control-flow signal — not part of public error types */\nclass ErrSignal<E> {\n readonly _tag = \"ErrSignal\" as const;\n constructor(readonly error: E) {}\n}\n\n/**\n * Unwraps a {@link Result} inside a {@link tryGen} block — the sync analogue of\n * Rust's `?` operator. On `Ok` it returns the value; on `Err` it short-circuits\n * the enclosing `tryGen`, which returns that `Err`. Only valid inside `tryGen`;\n * prefer {@link flatMap} / `pipe` for long pipelines.\n *\n * @returns the unwrapped `Ok` value\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = yieldResult(parseA()); // unwrap or short-circuit\n * const b = yieldResult(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const yieldResult = <T, E>(result: Result<T, E>): T => {\n if (isErr(result)) {\n throw new ErrSignal(result.error);\n }\n return result.value;\n};\n\n/**\n * Terse alias of {@link yieldResult} (mirrors Rust's `?`), for compact\n * {@link tryGen} blocks.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const $ = yieldResult;\n\n/**\n * Run a block that uses {@link yieldResult}; returns the final `Result` or the first Err.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const tryGen = <T, E>(fn: () => Result<T, E>): Result<T, E> => {\n try {\n return fn();\n } catch (error) {\n if (error instanceof ErrSignal) {\n return err(error.error);\n }\n throw error;\n }\n};\n"]}
|
package/dist/$.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/result.ts
|
|
2
|
+
var err = (error) => ({
|
|
3
|
+
_tag: "Err",
|
|
4
|
+
error
|
|
5
|
+
});
|
|
6
|
+
var isErr = (result) => result._tag === "Err";
|
|
7
|
+
|
|
8
|
+
// src/try-gen.ts
|
|
9
|
+
var ErrSignal = class {
|
|
10
|
+
constructor(error) {
|
|
11
|
+
this.error = error;
|
|
12
|
+
}
|
|
13
|
+
error;
|
|
14
|
+
_tag = "ErrSignal";
|
|
15
|
+
};
|
|
16
|
+
var yieldResult = (result) => {
|
|
17
|
+
if (isErr(result)) {
|
|
18
|
+
throw new ErrSignal(result.error);
|
|
19
|
+
}
|
|
20
|
+
return result.value;
|
|
21
|
+
};
|
|
22
|
+
var $ = yieldResult;
|
|
23
|
+
var tryGen = (fn) => {
|
|
24
|
+
try {
|
|
25
|
+
return fn();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error instanceof ErrSignal) {
|
|
28
|
+
return err(error.error);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { $, tryGen, yieldResult };
|
|
35
|
+
//# sourceMappingURL=$.js.map
|
|
36
|
+
//# sourceMappingURL=$.js.map
|
package/dist/$.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/result.ts","../src/try-gen.ts"],"names":[],"mappings":";AA2BO,IAAM,GAAA,GAAM,CAAyB,KAAA,MAA4B;AAAA,EACtE,IAAA,EAAM,KAAA;AAAA,EACN;AACF,CAAA,CAAA;AAwBO,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;;;AClD1F,IAAM,YAAN,MAAmB;AAAA,EAEjB,YAAqB,KAAA,EAAU;AAAV,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAAW;AAAA,EAAX,KAAA;AAAA,EADZ,IAAA,GAAO,WAAA;AAElB,CAAA;AAmBO,IAAM,WAAA,GAAc,CAAO,MAAA,KAA4B;AAC5D,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,IAAI,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;AAeO,IAAM,CAAA,GAAI;AAcV,IAAM,MAAA,GAAS,CAAO,EAAA,KAAyC;AACpE,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,SAAA,EAAW;AAC9B,MAAA,OAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"$.js","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error);\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, use a namespace\n * import (`import * as R from \"@onrails/result\"` → `R.match`) to dissolve\n * the collision.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — returns the `Ok` value, or **throws the original `Err`\n * value** when called on an `Err`. This is the assertion tier (RFC 0001 §4):\n * intended for `*.spec.ts` / `*.test.ts`, where throwing fails the test loudly.\n * In business logic prefer {@link match} or {@link unwrapOr}; the lint plugins\n * flag `unwrapOk` outside test files.\n *\n * @returns the unwrapped `Ok` value\n * @throws the carried `Err` value when the result is `Err`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapOk(ok(5))).toBe(5);\n * expect(() => unwrapOk(err(error))).toThrow(error);\n * ```\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — returns the `Err` value, or **throws a `TypeError`**\n * when called on an `Ok`. Mirror of {@link unwrapOk} on the error track and\n * part of the same assertion tier (RFC 0001 §4): intended for `*.spec.ts` /\n * `*.test.ts`. Prefer {@link match} / {@link unwrapOr} in business logic; the\n * lint plugins flag `unwrapErr` outside test files.\n *\n * @returns the unwrapped `Err` value\n * @throws `TypeError` when the result is `Ok`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapErr(err(\"x\"))).toBe(\"x\");\n * expect(() => unwrapErr(ok(5))).toThrow(TypeError);\n * ```\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/**\n * Wraps a throwing sync function, returning a function that produces a\n * {@link Result} instead of throwing. Thrown errors pass through `onThrow` to\n * become a typed `Err`; a normal return becomes `Ok`. The neverthrow analogue\n * is `Result.fromThrowable`.\n *\n * @param fn - the throwing function to wrap\n * @param onThrow - maps a thrown value to the `Err` channel\n * @returns a function with `fn`'s parameters that returns `Result<ReturnType, E>`\n *\n * @example\n * ```ts\n * type ParseError = { kind: \"parse\"; message: string };\n * const parse = trySync(\n * JSON.parse,\n * (e): ParseError => ({ kind: \"parse\", message: String(e) }),\n * );\n * parse(\"{}\"); // Ok({})\n * parse(\"nope\"); // Err({ kind: \"parse\", … })\n * ```\n */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns,\n * left-to-right. Use {@link pipe} when you already have a starting value;\n * use {@link flow} to define a reusable composed function with no value yet.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => (name ? ok(name) : err({ kind: \"empty\" as const }))),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import { err, isErr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Internal control-flow signal — not part of public error types */\nclass ErrSignal<E> {\n readonly _tag = \"ErrSignal\" as const;\n constructor(readonly error: E) {}\n}\n\n/**\n * Unwraps a {@link Result} inside a {@link tryGen} block — the sync analogue of\n * Rust's `?` operator. On `Ok` it returns the value; on `Err` it short-circuits\n * the enclosing `tryGen`, which returns that `Err`. Only valid inside `tryGen`;\n * prefer {@link flatMap} / `pipe` for long pipelines.\n *\n * @returns the unwrapped `Ok` value\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = yieldResult(parseA()); // unwrap or short-circuit\n * const b = yieldResult(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const yieldResult = <T, E>(result: Result<T, E>): T => {\n if (isErr(result)) {\n throw new ErrSignal(result.error);\n }\n return result.value;\n};\n\n/**\n * Terse alias of {@link yieldResult} (mirrors Rust's `?`), for compact\n * {@link tryGen} blocks.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const $ = yieldResult;\n\n/**\n * Run a block that uses {@link yieldResult}; returns the final `Result` or the first Err.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const tryGen = <T, E>(fn: () => Result<T, E>): Result<T, E> => {\n try {\n return fn();\n } catch (error) {\n if (error instanceof ErrSignal) {\n return err(error.error);\n }\n throw error;\n }\n};\n"]}
|
package/dist/_.d.cts
ADDED
package/dist/_.d.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { R as Result, U as UnexpectedError } from './types-BQ9vv0nD.js';
|
|
2
|
+
|
|
3
|
+
type PromiseFactory<T, E> = () => Promise<Result<T, E>>;
|
|
4
|
+
type AsyncOk<R> = R extends ResultAsync<infer T, unknown> ? T : never;
|
|
5
|
+
type AsyncErr<R> = R extends ResultAsync<unknown, infer E> ? E : never;
|
|
6
|
+
type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = ResultAsync<{
|
|
7
|
+
[K in keyof R]: AsyncOk<R[K]>;
|
|
8
|
+
}, {
|
|
9
|
+
[K in keyof R]: AsyncErr<R[K]>;
|
|
10
|
+
}[number]>;
|
|
11
|
+
/**
|
|
12
|
+
* Async railway carrier. Wraps a deferred `Promise<Result<T, E>>` and exposes
|
|
13
|
+
* the same dual-track transforms as the sync {@link Result} — `map`, `flatMap`,
|
|
14
|
+
* `recover`, `tap`, `match`. The public API never surfaces `Promise<Result<…>>`
|
|
15
|
+
* directly: `await` the instance (it is thenable) or call {@link resolve} /
|
|
16
|
+
* {@link match} to settle it.
|
|
17
|
+
*
|
|
18
|
+
* Construct via the static factories ({@link ok}, {@link err}, {@link fromPromise},
|
|
19
|
+
* {@link defer}, …) rather than `new` — the constructor is `protected`.
|
|
20
|
+
*
|
|
21
|
+
* @typeParam T - the `Ok` value type
|
|
22
|
+
* @typeParam E - the `Err` error type
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const user = ResultAsync.fromPromise(api.getUser(id), toAppError)
|
|
27
|
+
* .map((u) => u.profile)
|
|
28
|
+
* .recover(() => ResultAsync.ok(guestProfile));
|
|
29
|
+
*
|
|
30
|
+
* const r = await user; // Result<Profile, AppError>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare class ResultAsync<T, E> {
|
|
34
|
+
protected readonly run: PromiseFactory<T, E>;
|
|
35
|
+
private promise;
|
|
36
|
+
protected constructor(run: PromiseFactory<T, E>);
|
|
37
|
+
/**
|
|
38
|
+
* Lifts an already-settled sync {@link Result} into a {@link ResultAsync}.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const ra = ResultAsync.fromResult(ok(42)); // ResultAsync<number, never>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
static fromResult<T, E>(result: Result<T, E>): ResultAsync<T, E>;
|
|
46
|
+
/**
|
|
47
|
+
* Wraps a `PromiseLike<T>` that may reject. Rejections pass through `onReject`
|
|
48
|
+
* to become a typed `Err`; resolution becomes `Ok<T>`.
|
|
49
|
+
*
|
|
50
|
+
* @param promise - the promise to wrap
|
|
51
|
+
* @param onReject - maps a rejection reason to the `Err` channel
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const body = ResultAsync.fromPromise(
|
|
56
|
+
* fetch(url).then((r) => r.text()),
|
|
57
|
+
* (e): NetError => ({ kind: "net", cause: String(e) }),
|
|
58
|
+
* );
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
static fromPromise<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
62
|
+
/**
|
|
63
|
+
* Wraps a `PromiseLike<T>` that is **guaranteed not to reject**, skipping the
|
|
64
|
+
* `onReject` mapper. Use only when rejection is provably impossible.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const now = ResultAsync.fromSafePromise(Promise.resolve(Date.now()));
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
static fromSafePromise<T, E = never>(promise: PromiseLike<T>): ResultAsync<T, E>;
|
|
72
|
+
/**
|
|
73
|
+
* Defers work until {@link resolve}. Unlike {@link fromPromise}, the factory
|
|
74
|
+
* does not run until the `ResultAsync` is resolved (e.g. by `combineTuple` /
|
|
75
|
+
* `combineTupleParallel`). The factory runs at most once — `resolve` memoizes.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const insert = ResultAsync.defer(() => db.orders.insert(row));
|
|
80
|
+
* // nothing has run yet
|
|
81
|
+
* const r = await insert; // factory runs exactly once here
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
static defer<T, E>(fn: () => Promise<Result<T, E>>): ResultAsync<T, E>;
|
|
85
|
+
/**
|
|
86
|
+
* Lifts a value into an `Ok` async result.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const r = ResultAsync.ok(42); // ResultAsync<number, never>
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
static ok<T>(value: T): ResultAsync<T, never>;
|
|
94
|
+
static ok<T, E>(value: T): ResultAsync<T, E>;
|
|
95
|
+
/**
|
|
96
|
+
* Lifts an error into an `Err` async result.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const r = ResultAsync.err({ kind: "not_found" as const });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
static err<T = never, E = unknown>(error: E): ResultAsync<T, E>;
|
|
104
|
+
/**
|
|
105
|
+
* Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
|
|
106
|
+
* {@link ResultAsync}, auto-flattening one level of double-wrapping. A thrown
|
|
107
|
+
* defect is routed through `onDefect`, defaulting to {@link UnexpectedError}.
|
|
108
|
+
*
|
|
109
|
+
* @param promise - a promise that already yields a `Result`
|
|
110
|
+
* @param onDefect - maps an unexpected throw to the `Err` channel
|
|
111
|
+
* @see {@link fromAsync} from `@onrails/result/interop`
|
|
112
|
+
*/
|
|
113
|
+
static fromResultPromise<T, E>(promise: Promise<Result<T, E>>, onDefect?: (error: unknown) => E | UnexpectedError): ResultAsync<T, E | UnexpectedError>;
|
|
114
|
+
/**
|
|
115
|
+
* Combines a homogeneous array of async results into one. Resolves
|
|
116
|
+
* **sequentially** in input order, short-circuiting on the first `Err`.
|
|
117
|
+
* For heterogeneous tuples that preserve per-index types, use
|
|
118
|
+
* {@link combineTuple}; for wall-clock overlap, {@link combineTupleParallel}.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const all = ResultAsync.combine([loadA(), loadB(), loadC()]);
|
|
123
|
+
* // ResultAsync<Item[], LoadError>
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
static combine<T, E>(results: readonly ResultAsync<T, E>[]): ResultAsync<T[], E>;
|
|
127
|
+
/**
|
|
128
|
+
* Heterogeneous async tuple combine — resolves branches **sequentially**
|
|
129
|
+
* (left-to-right), returning the first `Err` in input order. Preserves each
|
|
130
|
+
* branch's `Ok` type by position, so the result destructures type-safely.
|
|
131
|
+
* This is the canonical sequential async combine (replaces the former
|
|
132
|
+
* `sequenceTupleAsync`).
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()] as const);
|
|
137
|
+
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
138
|
+
* const r = await combined;
|
|
139
|
+
* if (isOk(r)) {
|
|
140
|
+
* const [cfg, catalog] = r.value; // typed per position
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
static combineTuple<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
145
|
+
/**
|
|
146
|
+
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
147
|
+
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* // independent IO — overlap them
|
|
152
|
+
* const combined = ResultAsync.combineTupleParallel([
|
|
153
|
+
* loadProfile(id),
|
|
154
|
+
* loadMetrics(id),
|
|
155
|
+
* ] as const);
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
static combineTupleParallel<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
159
|
+
/**
|
|
160
|
+
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* ResultAsync.ok(2).map((n) => n * 3); // ResultAsync<number> → Ok 6
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
map<U>(fn: (value: T) => U): ResultAsync<U, E>;
|
|
168
|
+
/**
|
|
169
|
+
* Transforms the `Err` value, passing `Ok` through unchanged — useful for
|
|
170
|
+
* unifying heterogeneous failures into one app-level union.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* load(id).mapErr((e): AppError => ({ kind: "load", cause: e }));
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
mapErr<F>(fn: (error: E) => F): ResultAsync<T, F>;
|
|
178
|
+
/**
|
|
179
|
+
* Canonical bind — chains a step that itself returns a `ResultAsync` or sync
|
|
180
|
+
* `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* authenticate(req)
|
|
185
|
+
* .flatMap((user) => loadProfile(user.id)) // ResultAsync
|
|
186
|
+
* .flatMap((p) => validate(p)); // sync Result also accepted
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
flatMap<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
190
|
+
inner: Result<U, F>;
|
|
191
|
+
}): ResultAsync<U, E | F>;
|
|
192
|
+
/**
|
|
193
|
+
* neverthrow-compat alias of {@link flatMap}. Kept as the documented compat
|
|
194
|
+
* tier; prefer `flatMap` in new code.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* authenticate(req).andThen((user) => loadProfile(user.id));
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
andThen<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
202
|
+
inner: Result<U, F>;
|
|
203
|
+
}): ResultAsync<U, E | F>;
|
|
204
|
+
/**
|
|
205
|
+
* Error-channel bind — runs `fn` only on `Err`, swapping in a recovery
|
|
206
|
+
* `ResultAsync` or sync `Result`. `Ok` passes through. The mirror of
|
|
207
|
+
* {@link flatMap} on the error track.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* loadFromCache(id).recover(() => loadFromOrigin(id));
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
recover<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
215
|
+
/**
|
|
216
|
+
* neverthrow-compat alias of {@link recover}. Kept as the documented compat
|
|
217
|
+
* tier; prefer `recover` in new code.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* loadFromCache(id).orElse(() => loadFromOrigin(id));
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
orElse<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
225
|
+
/**
|
|
226
|
+
* Runs a side effect on the `Ok` value and passes the result through
|
|
227
|
+
* unchanged; a no-op on `Err`. Mirrors {@link tapErr}.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* saveUser(u).tap((saved) => analytics.track("user_saved", saved.id));
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
tap(fn: (value: T) => void): ResultAsync<T, E>;
|
|
235
|
+
/**
|
|
236
|
+
* Runs a side effect on the `Err` value and passes the result through
|
|
237
|
+
* unchanged; a no-op on `Ok`. The error-track mirror of {@link tap}.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* saveUser(u).tapErr((e) => logger.warn("save failed", e));
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
tapErr(fn: (error: E) => void): ResultAsync<T, E>;
|
|
245
|
+
/**
|
|
246
|
+
* Settles to the `Ok` value, or `defaultValue` if `Err`. Terminal — returns a
|
|
247
|
+
* plain `Promise`, not a `ResultAsync`.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const profile = await loadProfile(id).unwrapOr(guestProfile);
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
unwrapOr<U>(defaultValue: U): Promise<T | U>;
|
|
255
|
+
/**
|
|
256
|
+
* Terminal collapse — folds both tracks into a single awaited value. Returns
|
|
257
|
+
* a plain `Promise`, settling the carrier exactly once.
|
|
258
|
+
*
|
|
259
|
+
* @param onOk - handles the `Ok` value
|
|
260
|
+
* @param onErr - handles the `Err` value
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* const status = await save(row).match(
|
|
265
|
+
* () => 200,
|
|
266
|
+
* (e) => (e.kind === "conflict" ? 409 : 500),
|
|
267
|
+
* );
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
match<U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
271
|
+
/**
|
|
272
|
+
* Settles the carrier to a bare sync {@link Result}, memoizing so the
|
|
273
|
+
* underlying factory runs at most once. Prefer `await ra` (the thenable) or
|
|
274
|
+
* {@link match} in app code; use `resolve` when you need the tagged union back.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const r = await load(id).resolve(); // Result<Data, LoadError>
|
|
279
|
+
* if (isOk(r)) use(r.value);
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
resolve(): Promise<Result<T, E>>;
|
|
283
|
+
/**
|
|
284
|
+
* Thenable shim — `await ra` resolves to a bare tagged-union `Result<T, E>`.
|
|
285
|
+
* Narrow with `isOk(r)` / `isErr(r)` to read `.value` / `.error`.
|
|
286
|
+
*/
|
|
287
|
+
then<R1 = Result<T, E>, R2 = never>(onfulfilled?: ((value: Result<T, E>) => R1 | PromiseLike<R1>) | undefined | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | undefined | null): Promise<R1 | R2>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export { ResultAsync as R };
|