yield-result 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yield-result contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to nobility conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # yield-result
2
+
3
+ > Rust-style `?` operator for TypeScript using generators (`yield*`). Zero dependencies, lightweight, type-safe error handling without runtime exceptions.
4
+
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.5+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Vitest](https://img.shields.io/badge/tested%20with-vitest-blueviolet.svg)](https://vitest.dev/)
7
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
+
9
+ ---
10
+
11
+ ## ⚡ Why `yield-result`?
12
+
13
+ In languages like Rust, the `?` operator unbinds a `Result` value with automatic early-return short-circuiting on errors—avoiding deep callback nesting (`.then()`, `.flatMap()`) and `try/catch` exception overhead.
14
+
15
+ JavaScript/TypeScript does not support custom operator overloading, but ECMAScript **`yield*`** (generator delegation) provides the **exact same semantics natively**!
16
+
17
+ ### Syntax Comparison
18
+
19
+ ```typescript
20
+ // ❌ BEFORE: Try/catch nesting or monadic chain hell
21
+ async function processOrder(id: string) {
22
+ try {
23
+ const order = await fetchOrder(id);
24
+ if (!order) return { error: "Order not found" };
25
+
26
+ const stock = await reserveStock(order);
27
+ if (!stock.ok) return { error: stock.error };
28
+
29
+ return { ok: true, data: stock.value };
30
+ } catch (e) {
31
+ return { error: String(e) };
32
+ }
33
+ }
34
+
35
+ // ✅ WITH YIELD-RESULT: Linear, type-safe, synchronous or async, zero try/catch
36
+ import { safe, Result } from "yield-result";
37
+
38
+ const processOrder = (id: string) => safe.async(async function* () {
39
+ const order = yield* await fetchOrder(id); // If Err, short-circuits and early-returns Err!
40
+ const stock = yield* await reserveStock(order);
41
+
42
+ return { status: "Success", stock };
43
+ });
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 🚀 Installation
49
+
50
+ ```bash
51
+ npm install yield-result
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 🏎️ Performance Benchmarks
57
+
58
+ Benchmarked using `vitest bench` (powered by `tinybench`) on Node.js:
59
+
60
+ ### 🔴 Unhappy Path (Error Triggered)
61
+
62
+ When errors actually occur during domain execution:
63
+
64
+ | Implementation | Operations / sec | Benchmark Summary |
65
+ | :--- | :---: | :--- |
66
+ | **`yield-result` (`safe.sync`)** | **`207,485 ops/sec`** | ⚡ **3.55x FASTER than native try/catch** |
67
+ | **Native `try/catch` with `throw`** | **`58,515 ops/sec`** | 🐢 3.55x slower |
68
+
69
+ > 🏆 **Why is `yield-result` 3.55x faster on errors?**
70
+ > Native `throw new Error()` forces JavaScript V8 engines to pause execution, capture call stack frames, allocate Error objects, and deoptimize JIT loops. In `yield-result`, returning an `Err` is just returning a plain object without unwinding stack frames.
71
+
72
+ ### 🟢 Happy Path (Success Flow)
73
+
74
+ | Implementation | Operations / sec | Average Latency |
75
+ | :--- | :---: | :---: |
76
+ | **Native `try/catch` (no throw)** | `11,372,962 ops/sec` | `0.0001 ms` |
77
+ | **`yield-result` (`safe.sync`)** | `231,068 ops/sec` | `0.0043 ms` |
78
+
79
+ *Over 230,000 complete domain pipelines evaluated per second on a single thread—zero impact on network or database latencies.*
80
+
81
+ ---
82
+
83
+ ## 📖 Usage Guide
84
+
85
+ ### 1. Creating Results
86
+
87
+ ```typescript
88
+ import { ok, err, Result } from "yield-result";
89
+
90
+ function divide(a: number, b: number): Result<number, string> {
91
+ if (b === 0) return err("Division by zero");
92
+ return ok(a / b);
93
+ }
94
+ ```
95
+
96
+ ### 2. Composing Pipelines (`safe.sync` and `safe.async`)
97
+
98
+ Use `safe.sync` for synchronous workflows and `safe.async` for asynchronous workflows:
99
+
100
+ ```typescript
101
+ import { safe, ok, err } from "yield-result";
102
+
103
+ const result = safe.sync(function* () {
104
+ const a = yield* divide(100, 2); // a = 50
105
+ const b = yield* divide(a, 5); // b = 10
106
+ return a + b; // Returns ok(60)
107
+ });
108
+
109
+ console.log(result); // { ok: true, value: 60 }
110
+ ```
111
+
112
+ ### 3. Integrating with Throwable & Async Code
113
+
114
+ Convert legacy functions that throw exceptions or return Promises:
115
+
116
+ ```typescript
117
+ import { fromThrowable, fromPromise, fromAsyncFn } from "yield-result";
118
+
119
+ // Sync throwable functions (e.g. JSON.parse)
120
+ const jsonRes = fromThrowable(() => JSON.parse('{"status": "ok"}'));
121
+
122
+ // Promises
123
+ const fetchRes = await fromPromise(
124
+ fetch("https://api.example.com/data").then(r => r.json()),
125
+ (err) => `API Error: ${err}`
126
+ );
127
+
128
+ // Async Functions (handles both sync throws and promise rejections)
129
+ const asyncRes = await fromAsyncFn(() => loadData());
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 🛡️ Built-in Safety Features
135
+
136
+ ### Runtime Protection against Missing Asterisk (`*`)
137
+
138
+ Forgetting the asterisk (`yield` instead of `yield*`) is automatically intercepted at runtime and in TypeScript:
139
+
140
+ ```typescript
141
+ safe.sync(function* () {
142
+ // ❌ Dev forgets the asterisk:
143
+ const data = yield divide(10, 2);
144
+ });
145
+
146
+ // 💥 Throws an immediate explanatory Error:
147
+ // Error: Incorrect 'yield' usage: missing asterisk '*'. Use 'yield* result' instead of 'yield result'.
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 🛠️ Complete API Reference
153
+
154
+ ### Constructors & Guards
155
+ * **`ok(value)`** / **`Result.ok(value)`** — Creates an `Ok<T>` result.
156
+ * **`err(error)`** / **`Result.err(error)`** — Creates an `Err<E>` result.
157
+ * **`isOk(result)`** / **`isErr(result)`** — TypeScript type guards.
158
+
159
+ ### Flow Runners (`safe`)
160
+ * **`safe.sync(function* () { ... })`** — Executes a synchronous generator function with early-return short-circuiting on `Err`.
161
+ * **`safe.async(async function* () { ... })`** — Executes an async generator function with early-return short-circuiting on `Err`.
162
+
163
+ ### Exception Wrappers
164
+ * **`fromThrowable(fn, errorMapper?)`** — Wraps a throwing function into a `Result<T, E>`.
165
+ * **`fromPromise(promise, errorMapper?)`** — Wraps a Promise into `Promise<Result<T, E>>`.
166
+ * **`fromAsyncFn(fn, errorMapper?)`** — Wraps an async function into `Promise<Result<T, E>>`.
167
+
168
+ ### Combinators & Utilities
169
+ * **`map(result, fn)`** — Transforms the `Ok` value.
170
+ * **`mapErr(result, fn)`** — Transforms the `Err` value.
171
+ * **`andThen(result, fn)`** — Chains a new Result-returning function if `Ok`.
172
+ * **`unwrapOr(result, fallback)`** — Returns contained `Ok` value or fallback.
173
+ * **`unwrap(result)`** — Extracts `Ok` value or throws `Err`.
174
+ * **`expectResult(result, msg)`** — Extracts `Ok` value or throws custom error message.
175
+ * **`tap(result, fn)`** — Runs side-effect function if `Ok`.
176
+ * **`tapErr(result, fn)`** — Runs side-effect function if `Err`.
177
+ * **`match(result, { ok, err })`** — Pattern matches on Ok or Err.
178
+ * **`all([r1, r2, r3])`** — Combines `Result<T, E>[]` into `Result<T[], E>`.
179
+ * **`partition(results)`** — Partitions array of Results into `{ values, errors }`.
180
+
181
+ ---
182
+
183
+ ## 🧪 Testing & Benchmarks
184
+
185
+ ```bash
186
+ npm run test # Runs all Vitest unit tests (39 tests)
187
+ npm run test:types # Runs strict TypeScript type-checking (tsc --noEmit)
188
+ npm run bench # Runs performance benchmarks (vitest bench)
189
+ npm run build # Compiles output to dist/
190
+ ```
191
+
192
+ ---
193
+
194
+ ## ⚖️ License
195
+
196
+ MIT © yield-result contributors
197
+
198
+
package/dist/flow.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { Result, Err } from "./types.js";
2
+ /**
3
+ * Control flow runners for evaluating generator functions with short-circuiting on Err.
4
+ */
5
+ export declare const safe: {
6
+ /**
7
+ * Evaluates a synchronous generator function, returning Ok(value) on completion,
8
+ * or short-circuiting on the first yielded Err(error).
9
+ */
10
+ sync: <T, E = unknown>(fn: () => Generator<Err<E>, T, unknown>) => Result<T, E>;
11
+ /**
12
+ * Evaluates an async generator function, returning Ok(value) on completion,
13
+ * or short-circuiting on the first yielded Err(error).
14
+ */
15
+ async: <T, E = unknown>(fn: () => AsyncGenerator<Err<E>, T, unknown>) => Promise<Result<T, E>>;
16
+ };
package/dist/flow.js ADDED
@@ -0,0 +1,104 @@
1
+ import { ok, err, isOk, isErr } from "./types.js";
2
+ /**
3
+ * Control flow runners for evaluating generator functions with short-circuiting on Err.
4
+ */
5
+ export const safe = {
6
+ /**
7
+ * Evaluates a synchronous generator function, returning Ok(value) on completion,
8
+ * or short-circuiting on the first yielded Err(error).
9
+ */
10
+ sync: (fn) => {
11
+ let gen;
12
+ try {
13
+ gen = fn();
14
+ }
15
+ catch (e) {
16
+ return err(e);
17
+ }
18
+ try {
19
+ const next = gen.next();
20
+ if (!next.done) {
21
+ // Safety guard: detects if developer forgot the '*' when yielding an Ok or non-Err value
22
+ if (isOk(next.value)) {
23
+ try {
24
+ gen.return(undefined);
25
+ }
26
+ catch (_) { }
27
+ throw new Error("Incorrect 'yield' usage: missing asterisk '*'. Use 'yield* result' instead of 'yield result'.");
28
+ }
29
+ if (!isErr(next.value)) {
30
+ try {
31
+ gen.return(undefined);
32
+ }
33
+ catch (_) { }
34
+ throw new Error("Incorrect 'yield' usage: generator yielded a value that is not a Result Err. Make sure to use 'yield* result'.");
35
+ }
36
+ // Trigger resource cleanup for try...finally blocks in suspended generator
37
+ try {
38
+ gen.return(undefined);
39
+ }
40
+ catch (_) { }
41
+ return next.value;
42
+ }
43
+ return ok(next.value);
44
+ }
45
+ catch (e) {
46
+ if (e instanceof Error && e.message.includes("Incorrect 'yield' usage")) {
47
+ throw e;
48
+ }
49
+ try {
50
+ gen.return(undefined);
51
+ }
52
+ catch (_) { }
53
+ return err(e);
54
+ }
55
+ },
56
+ /**
57
+ * Evaluates an async generator function, returning Ok(value) on completion,
58
+ * or short-circuiting on the first yielded Err(error).
59
+ */
60
+ async: async (fn) => {
61
+ let gen;
62
+ try {
63
+ gen = fn();
64
+ }
65
+ catch (e) {
66
+ return err(e);
67
+ }
68
+ try {
69
+ const next = await gen.next();
70
+ if (!next.done) {
71
+ if (isOk(next.value)) {
72
+ try {
73
+ await gen.return(undefined);
74
+ }
75
+ catch (_) { }
76
+ throw new Error("Incorrect 'yield' usage: missing asterisk '*'. Use 'yield* result' instead of 'yield result'.");
77
+ }
78
+ if (!isErr(next.value)) {
79
+ try {
80
+ await gen.return(undefined);
81
+ }
82
+ catch (_) { }
83
+ throw new Error("Incorrect 'yield' usage: generator yielded a value that is not a Result Err. Make sure to use 'yield* result'.");
84
+ }
85
+ try {
86
+ await gen.return(undefined);
87
+ }
88
+ catch (_) { }
89
+ return next.value;
90
+ }
91
+ return ok(next.value);
92
+ }
93
+ catch (e) {
94
+ if (e instanceof Error && e.message.includes("Incorrect 'yield' usage")) {
95
+ throw e;
96
+ }
97
+ try {
98
+ await gen.return(undefined);
99
+ }
100
+ catch (_) { }
101
+ return err(e);
102
+ }
103
+ },
104
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./wrappers.js";
3
+ export * from "./flow.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./wrappers.js";
3
+ export * from "./flow.js";
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Core Result type: simple discriminated unions without classes.
3
+ *
4
+ * For `yield* result` to work inside a generator function, `result`
5
+ * must implement the Iterable protocol (`Symbol.iterator`).
6
+ *
7
+ * - Ok: the iterator returns `value` immediately without yielding.
8
+ * This allows `yield* ok(x)` to evaluate synchronously to `x`.
9
+ * - Err: the iterator yields `this` (the Err object) once.
10
+ * This propagates the Err object to the runner in `flow.ts` to abort execution.
11
+ */
12
+ export type Ok<T> = {
13
+ readonly ok: true;
14
+ readonly value: T;
15
+ readonly [Symbol.iterator]: () => Generator<never, T, unknown>;
16
+ };
17
+ export type Err<E> = {
18
+ readonly ok: false;
19
+ readonly error: E;
20
+ readonly [Symbol.iterator]: () => Generator<Err<E>, never, unknown>;
21
+ };
22
+ export type Result<T, E = string> = Ok<T> | Err<E>;
23
+ /**
24
+ * Creates a successful Result containing a value.
25
+ */
26
+ export declare const ok: <T>(value: T) => Ok<T>;
27
+ /**
28
+ * Creates an error Result containing an error value.
29
+ */
30
+ export declare const err: <E>(error: E) => Err<E>;
31
+ /**
32
+ * Type guard for Ok results.
33
+ */
34
+ export declare const isOk: <T, E>(result: Result<T, E>) => result is Ok<T>;
35
+ /**
36
+ * Type guard for Err results.
37
+ */
38
+ export declare const isErr: <T, E>(result: Result<T, E>) => result is Err<E>;
39
+ /**
40
+ * Maps a Result<T, E> to Result<U, E> by applying a function to a successful value.
41
+ */
42
+ export declare const map: <T, U, E>(result: Result<T, E>, fn: (value: T) => U) => Result<U, E>;
43
+ /**
44
+ * Maps a Result<T, E> to Result<T, F> by applying a function to an error value.
45
+ */
46
+ export declare const mapErr: <T, E, F>(result: Result<T, E>, fn: (error: E) => F) => Result<T, F>;
47
+ /**
48
+ * Chains another Result-returning function if the result is Ok.
49
+ */
50
+ export declare const andThen: <T, U, E>(result: Result<T, E>, fn: (value: T) => Result<U, E>) => Result<U, E>;
51
+ /**
52
+ * Returns the contained Ok value or a fallback value if Err.
53
+ */
54
+ export declare const unwrapOr: <T, E>(result: Result<T, E>, fallback: T) => T;
55
+ /**
56
+ * Extracts the contained Ok value, or throws the contained Error.
57
+ */
58
+ export declare const unwrap: <T, E>(result: Result<T, E>) => T;
59
+ /**
60
+ * Extracts the contained Ok value, or throws a custom error message.
61
+ */
62
+ export declare const expectResult: <T, E>(result: Result<T, E>, msg: string) => T;
63
+ /**
64
+ * Executes a side-effect function if the result is Ok.
65
+ */
66
+ export declare const tap: <T, E>(result: Result<T, E>, fn: (value: T) => void) => Result<T, E>;
67
+ /**
68
+ * Executes a side-effect function if the result is Err.
69
+ */
70
+ export declare const tapErr: <T, E>(result: Result<T, E>, fn: (error: E) => void) => Result<T, E>;
71
+ /**
72
+ * Pattern matches on a Result, executing the ok or err handler.
73
+ */
74
+ export declare const match: <T, E, R>(result: Result<T, E>, handlers: {
75
+ ok: (value: T) => R;
76
+ err: (error: E) => R;
77
+ }) => R;
78
+ /**
79
+ * Combines an array of Results into a single Result containing an array of values.
80
+ * Short-circuits on the first Err.
81
+ */
82
+ export declare const all: <T, E>(results: Result<T, E>[]) => Result<T[], E>;
83
+ /**
84
+ * Partitions an array of Results into separate values and errors arrays.
85
+ */
86
+ export declare const partition: <T, E>(results: Result<T, E>[]) => {
87
+ values: T[];
88
+ errors: E[];
89
+ };
90
+ /**
91
+ * Namespace object grouping all Result constructors, guards, and combinators.
92
+ */
93
+ export declare const Result: {
94
+ readonly ok: <T>(value: T) => Ok<T>;
95
+ readonly err: <E>(error: E) => Err<E>;
96
+ readonly isOk: <T, E>(result: Result<T, E>) => result is Ok<T>;
97
+ readonly isErr: <T, E>(result: Result<T, E>) => result is Err<E>;
98
+ readonly map: <T, U, E>(result: Result<T, E>, fn: (value: T) => U) => Result<U, E>;
99
+ readonly mapErr: <T, E, F>(result: Result<T, E>, fn: (error: E) => F) => Result<T, F>;
100
+ readonly andThen: <T, U, E>(result: Result<T, E>, fn: (value: T) => Result<U, E>) => Result<U, E>;
101
+ readonly unwrapOr: <T, E>(result: Result<T, E>, fallback: T) => T;
102
+ readonly unwrap: <T, E>(result: Result<T, E>) => T;
103
+ readonly expect: <T, E>(result: Result<T, E>, msg: string) => T;
104
+ readonly tap: <T, E>(result: Result<T, E>, fn: (value: T) => void) => Result<T, E>;
105
+ readonly tapErr: <T, E>(result: Result<T, E>, fn: (error: E) => void) => Result<T, E>;
106
+ readonly match: <T, E, R>(result: Result<T, E>, handlers: {
107
+ ok: (value: T) => R;
108
+ err: (error: E) => R;
109
+ }) => R;
110
+ readonly all: <T, E>(results: Result<T, E>[]) => Result<T[], E>;
111
+ readonly partition: <T, E>(results: Result<T, E>[]) => {
112
+ values: T[];
113
+ errors: E[];
114
+ };
115
+ };
package/dist/types.js ADDED
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Core Result type: simple discriminated unions without classes.
3
+ *
4
+ * For `yield* result` to work inside a generator function, `result`
5
+ * must implement the Iterable protocol (`Symbol.iterator`).
6
+ *
7
+ * - Ok: the iterator returns `value` immediately without yielding.
8
+ * This allows `yield* ok(x)` to evaluate synchronously to `x`.
9
+ * - Err: the iterator yields `this` (the Err object) once.
10
+ * This propagates the Err object to the runner in `flow.ts` to abort execution.
11
+ */
12
+ // Shared prototypes for memory optimization and structuredClone support
13
+ const OkPrototype = {
14
+ ok: true,
15
+ *[Symbol.iterator]() {
16
+ return this.value;
17
+ },
18
+ };
19
+ const ErrPrototype = {
20
+ ok: false,
21
+ *[Symbol.iterator]() {
22
+ yield this;
23
+ throw new Error("Err iterator resumed after yield — indicates an invalid generator runner execution.");
24
+ },
25
+ };
26
+ /**
27
+ * Creates a successful Result containing a value.
28
+ */
29
+ export const ok = (value) => {
30
+ const instance = Object.create(OkPrototype);
31
+ instance.ok = true;
32
+ instance.value = value;
33
+ return Object.freeze(instance);
34
+ };
35
+ /**
36
+ * Creates an error Result containing an error value.
37
+ */
38
+ export const err = (error) => {
39
+ const instance = Object.create(ErrPrototype);
40
+ instance.ok = false;
41
+ instance.error = error;
42
+ return Object.freeze(instance);
43
+ };
44
+ /**
45
+ * Type guard for Ok results.
46
+ */
47
+ export const isOk = (result) => result.ok;
48
+ /**
49
+ * Type guard for Err results.
50
+ */
51
+ export const isErr = (result) => !result.ok;
52
+ // --- Combinators ---
53
+ /**
54
+ * Maps a Result<T, E> to Result<U, E> by applying a function to a successful value.
55
+ */
56
+ export const map = (result, fn) => result.ok ? ok(fn(result.value)) : result;
57
+ /**
58
+ * Maps a Result<T, E> to Result<T, F> by applying a function to an error value.
59
+ */
60
+ export const mapErr = (result, fn) => result.ok ? result : err(fn(result.error));
61
+ /**
62
+ * Chains another Result-returning function if the result is Ok.
63
+ */
64
+ export const andThen = (result, fn) => (result.ok ? fn(result.value) : result);
65
+ /**
66
+ * Returns the contained Ok value or a fallback value if Err.
67
+ */
68
+ export const unwrapOr = (result, fallback) => result.ok ? result.value : fallback;
69
+ /**
70
+ * Extracts the contained Ok value, or throws the contained Error.
71
+ */
72
+ export const unwrap = (result) => {
73
+ if (result.ok)
74
+ return result.value;
75
+ if (result.error instanceof Error)
76
+ throw result.error;
77
+ throw new Error(String(result.error));
78
+ };
79
+ /**
80
+ * Extracts the contained Ok value, or throws a custom error message.
81
+ */
82
+ export const expectResult = (result, msg) => {
83
+ if (result.ok)
84
+ return result.value;
85
+ throw new Error(`${msg}: ${String(result.error)}`);
86
+ };
87
+ /**
88
+ * Executes a side-effect function if the result is Ok.
89
+ */
90
+ export const tap = (result, fn) => {
91
+ if (result.ok)
92
+ fn(result.value);
93
+ return result;
94
+ };
95
+ /**
96
+ * Executes a side-effect function if the result is Err.
97
+ */
98
+ export const tapErr = (result, fn) => {
99
+ if (!result.ok)
100
+ fn(result.error);
101
+ return result;
102
+ };
103
+ /**
104
+ * Pattern matches on a Result, executing the ok or err handler.
105
+ */
106
+ export const match = (result, handlers) => (result.ok ? handlers.ok(result.value) : handlers.err(result.error));
107
+ /**
108
+ * Combines an array of Results into a single Result containing an array of values.
109
+ * Short-circuits on the first Err.
110
+ */
111
+ export const all = (results) => {
112
+ const values = [];
113
+ for (const r of results) {
114
+ if (!r.ok)
115
+ return err(r.error);
116
+ values.push(r.value);
117
+ }
118
+ return ok(values);
119
+ };
120
+ /**
121
+ * Partitions an array of Results into separate values and errors arrays.
122
+ */
123
+ export const partition = (results) => {
124
+ const values = [];
125
+ const errors = [];
126
+ for (const r of results) {
127
+ if (r.ok) {
128
+ values.push(r.value);
129
+ }
130
+ else {
131
+ errors.push(r.error);
132
+ }
133
+ }
134
+ return { values, errors };
135
+ };
136
+ /**
137
+ * Namespace object grouping all Result constructors, guards, and combinators.
138
+ */
139
+ export const Result = {
140
+ ok,
141
+ err,
142
+ isOk,
143
+ isErr,
144
+ map,
145
+ mapErr,
146
+ andThen,
147
+ unwrapOr,
148
+ unwrap,
149
+ expect: expectResult,
150
+ tap,
151
+ tapErr,
152
+ match,
153
+ all,
154
+ partition,
155
+ };
@@ -0,0 +1,16 @@
1
+ import { Result } from "./types.js";
2
+ /**
3
+ * Wraps a synchronous function that may throw an exception into a Result<T, E>.
4
+ * Catches any thrown value and maps it using errorMapper if provided.
5
+ */
6
+ export declare const fromThrowable: <T, E = unknown>(fn: () => T, errorMapper?: (e: unknown) => E) => Result<T, E>;
7
+ /**
8
+ * Wraps a Promise into a Promise<Result<T, E>>.
9
+ * Catches any rejection and maps it using errorMapper if provided.
10
+ */
11
+ export declare const fromPromise: <T, E = unknown>(promise: Promise<T>, errorMapper?: (e: unknown) => E) => Promise<Result<T, E>>;
12
+ /**
13
+ * Wraps an async function that returns a Promise into a Promise<Result<T, E>>.
14
+ * Catches both synchronous exceptions thrown during execution and async rejections.
15
+ */
16
+ export declare const fromAsyncFn: <T, E = unknown>(fn: () => Promise<T>, errorMapper?: (e: unknown) => E) => Promise<Result<T, E>>;
@@ -0,0 +1,39 @@
1
+ import { ok, err } from "./types.js";
2
+ /**
3
+ * Wraps a synchronous function that may throw an exception into a Result<T, E>.
4
+ * Catches any thrown value and maps it using errorMapper if provided.
5
+ */
6
+ export const fromThrowable = (fn, errorMapper) => {
7
+ try {
8
+ return ok(fn());
9
+ }
10
+ catch (e) {
11
+ return err(errorMapper ? errorMapper(e) : e);
12
+ }
13
+ };
14
+ /**
15
+ * Wraps a Promise into a Promise<Result<T, E>>.
16
+ * Catches any rejection and maps it using errorMapper if provided.
17
+ */
18
+ export const fromPromise = async (promise, errorMapper) => {
19
+ try {
20
+ const data = await promise;
21
+ return ok(data);
22
+ }
23
+ catch (e) {
24
+ return err(errorMapper ? errorMapper(e) : e);
25
+ }
26
+ };
27
+ /**
28
+ * Wraps an async function that returns a Promise into a Promise<Result<T, E>>.
29
+ * Catches both synchronous exceptions thrown during execution and async rejections.
30
+ */
31
+ export const fromAsyncFn = async (fn, errorMapper) => {
32
+ try {
33
+ const promise = fn();
34
+ return await fromPromise(promise, errorMapper);
35
+ }
36
+ catch (e) {
37
+ return err(errorMapper ? errorMapper(e) : e);
38
+ }
39
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "yield-result",
3
+ "version": "0.1.0",
4
+ "description": "Rust-style ? operator for TypeScript using generators (yield*). Zero dependencies, type-safe error handling.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "test": "npx vitest run",
24
+ "test:types": "tsc -p tsconfig.json --noEmit",
25
+ "bench": "npx vitest bench",
26
+ "prepublishOnly": "npm run test && npm run test:types && npm run build"
27
+ },
28
+ "keywords": [
29
+ "result",
30
+ "error-handling",
31
+ "rust",
32
+ "generator",
33
+ "yield",
34
+ "monad",
35
+ "typescript",
36
+ "either",
37
+ "safe"
38
+ ],
39
+ "license": "MIT",
40
+ "devDependencies": {
41
+ "typescript": "^5.5.4",
42
+ "vitest": "^2.0.5"
43
+ }
44
+ }
45
+