yield-result 0.1.4 β†’ 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,16 @@ const processOrder = (id: string) => safe.async(async function* () {
45
45
  });
46
46
  ```
47
47
 
48
+ ### πŸŽ“ How `yield*` Works Under the Hood (Didactic Guide)
49
+
50
+ Many JavaScript developers assume generators are complex, but in `yield-result`, generator delegation (**`yield*`**) is simply a **native protocol for unwrapping values**:
51
+
52
+ 1. **`Ok<T>` Protocol**: Implements `[Symbol.iterator]` to return `value` immediately without yielding anything. Therefore, `const x = yield* ok(10)` assigns `10` directly to `x` in 0 suspension steps.
53
+ 2. **`Err<E>` Protocol**: Implements `[Symbol.iterator]` to `yield` the `Err` instance once back to `safe.sync` / `safe.async`.
54
+ 3. **Short-Circuiting**: The runner (`safe.sync`) receives the `Err` object, immediately calls `gen.return()` (triggering any `try ... finally` cleanup in your function), and early-returns `err(E)`.
55
+
56
+ No exception throwing, no stack unwinding, 100% native ECMAScript iteration semantics.
57
+
48
58
  ---
49
59
 
50
60
  ## πŸš€ Installation
@@ -55,30 +65,24 @@ npm install yield-result
55
65
 
56
66
  ---
57
67
 
58
- ## 🏎️ Performance Benchmarks
59
-
60
- Benchmarked using `vitest bench` (powered by `tinybench`) on Node.js:
61
-
62
- ### πŸ”΄ Unhappy Path (Error Triggered)
68
+ ## πŸ”¬ Architectural Analysis & V8 Engine Optimization
63
69
 
64
- When errors actually occur during domain execution:
70
+ ### Why We Do Not Use `throw` Internally for Flow Control
65
71
 
66
- | Implementation | Operations / sec | Benchmark Summary |
67
- | :--- | :---: | :--- |
68
- | **`yield-result` (`safe.sync`)** | **`207,485 ops/sec`** | ⚑ **3.55x FASTER than native try/catch** |
69
- | **Native `try/catch` with `throw`** | **`58,515 ops/sec`** | 🐒 3.55x slower |
72
+ While libraries like `neverthrow` are fantastic, their generator-based flow control (`safeTry`) relies on calling `.safeUnwrap()`, which **throws native exceptions internally** to unwind the generator stack back to a `catch` block.
70
73
 
71
- > πŸ† **Why is `yield-result` 3.55x faster on errors?**
72
- > 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.
74
+ In JavaScript V8 engines (Node.js, Chrome), throwing exceptions forces the engine to:
75
+ 1. Pause the execution loop.
76
+ 2. Capture full call stack frames.
77
+ 3. Deoptimize JIT compiler loops.
73
78
 
74
- ### 🟒 Happy Path (Success Flow)
79
+ **`yield-result` takes a zero-exception approach**:
80
+ Our generator runners (`safe.sync` / `safe.async` / `Result.gen`) rely **exclusively on ECMAScript native iteration protocols (`Symbol.iterator`)**.
75
81
 
76
- | Implementation | Operations / sec | Average Latency |
77
- | :--- | :---: | :---: |
78
- | **Native `try/catch` (no throw)** | `11,372,962 ops/sec` | `0.0001 ms` |
79
- | **`yield-result` (`safe.sync`)** | `231,068 ops/sec` | `0.0043 ms` |
82
+ - **`Ok<T>`** returns its value directly in 0 suspension steps.
83
+ - **`Err<E>`** yields the `Err` object once to the runner, which immediately triggers `gen.return()` and early-returns the `Err` value.
80
84
 
81
- *Over 230,000 complete domain pipelines evaluated per second on a single threadβ€”zero impact on network or database latencies.*
85
+ By avoiding internal `throw` statements entirely, `yield-result` keeps your execution pipeline 100% JIT-optimizable by the V8 engine under both happy and unhappy path conditions.
82
86
 
83
87
  ---
84
88
 
@@ -281,10 +285,12 @@ safe.sync(function* () {
281
285
  * **`safe.sync(function* () { ... })`** / **`Result.gen(function* () { ... })`** β€” Executes a synchronous generator function with early-return short-circuiting on `Err`.
282
286
  * **`safe.async(async function* () { ... })`** / **`Result.gen.async(async function* () { ... })`** β€” Executes an async generator function with early-return short-circuiting on `Err`.
283
287
 
284
- ### Exception Wrappers
288
+ ### Exception Wrappers & Function Lifting
285
289
  * **`fromThrowable(fn, errorMapper?)`** β€” Wraps a throwing function into a `Result<T, E>`.
286
290
  * **`fromPromise(promise, errorMapper?)`** β€” Wraps a Promise into `Promise<Result<T, E>>`.
287
291
  * **`fromAsyncFn(fn, errorMapper?)`** β€” Wraps an async function into `Promise<Result<T, E>>`.
292
+ * **`lift(fn, errorMapper?)`** β€” Lifts a sync throwing function `(...args) => T` into a Result function `(...args) => Result<T, E>`.
293
+ * **`liftAsync(fn, errorMapper?)`** β€” Lifts an async function `(...args) => Promise<T>` into a Promise<Result> function `(...args) => Promise<Result<T, E>>`.
288
294
 
289
295
  ### Combinators & Utilities
290
296
  * **`map(result, fn)`** β€” Transforms the `Ok` value.
@@ -14,3 +14,11 @@ export declare const fromPromise: <T, E = unknown>(promise: Promise<T>, errorMap
14
14
  * Catches both synchronous exceptions thrown during execution and async rejections.
15
15
  */
16
16
  export declare const fromAsyncFn: <T, E = unknown>(fn: () => Promise<T>, errorMapper?: (e: unknown) => E) => Promise<Result<T, E>>;
17
+ /**
18
+ * Lifts a standard synchronous function that may throw into a Result-returning function.
19
+ */
20
+ export declare const lift: <A extends any[], T, E = unknown>(fn: (...args: A) => T, errorMapper?: (e: unknown) => E) => ((...args: A) => Result<T, E>);
21
+ /**
22
+ * Lifts an asynchronous function returning a Promise into a Promise<Result<T, E>> function.
23
+ */
24
+ export declare const liftAsync: <A extends any[], T, E = unknown>(fn: (...args: A) => Promise<T>, errorMapper?: (e: unknown) => E) => ((...args: A) => Promise<Result<T, E>>);
package/dist/wrappers.js CHANGED
@@ -37,3 +37,15 @@ export const fromAsyncFn = async (fn, errorMapper) => {
37
37
  return err(errorMapper ? errorMapper(e) : e);
38
38
  }
39
39
  };
40
+ /**
41
+ * Lifts a standard synchronous function that may throw into a Result-returning function.
42
+ */
43
+ export const lift = (fn, errorMapper) => {
44
+ return (...args) => fromThrowable(() => fn(...args), errorMapper);
45
+ };
46
+ /**
47
+ * Lifts an asynchronous function returning a Promise into a Promise<Result<T, E>> function.
48
+ */
49
+ export const liftAsync = (fn, errorMapper) => {
50
+ return (...args) => fromAsyncFn(() => fn(...args), errorMapper);
51
+ };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "yield-result",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Rust-style ? operator for TypeScript using generators (yield*). Zero dependencies, type-safe error handling.",
5
5
  "type": "module",
6
+ "sideEffects": false,
6
7
  "main": "./dist/index.js",
7
8
  "module": "./dist/index.js",
8
9
  "types": "./dist/index.d.ts",
@@ -46,9 +47,8 @@
46
47
  ],
47
48
  "license": "MIT",
48
49
  "devDependencies": {
50
+ "neverthrow": "^8.2.0",
49
51
  "typescript": "^5.5.4",
50
52
  "vitest": "^2.0.5"
51
53
  }
52
54
  }
53
-
54
-