yield-result 0.1.5 β†’ 0.1.7

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.
Files changed (2) hide show
  1. package/README.md +52 -18
  2. package/package.json +2 -3
package/README.md CHANGED
@@ -65,30 +65,24 @@ npm install yield-result
65
65
 
66
66
  ---
67
67
 
68
- ## 🏎️ Performance Benchmarks
68
+ ## πŸ”¬ Architectural Analysis & V8 Engine Optimization
69
69
 
70
- Benchmarked using `vitest bench` (powered by `tinybench`) on Node.js:
70
+ ### Why We Do Not Use `throw` Internally for Flow Control
71
71
 
72
- ### πŸ”΄ Unhappy Path (Error Triggered)
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.
73
73
 
74
- When errors actually occur during domain execution:
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.
75
78
 
76
- | Implementation | Operations / sec | Benchmark Summary |
77
- | :--- | :---: | :--- |
78
- | **`yield-result` (`safe.sync`)** | **`207,485 ops/sec`** | ⚑ **3.55x FASTER than native try/catch** |
79
- | **Native `try/catch` with `throw`** | **`58,515 ops/sec`** | 🐒 3.55x slower |
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`)**.
80
81
 
81
- > πŸ† **Why is `yield-result` 3.55x faster on errors?**
82
- > 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.
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.
83
84
 
84
- ### 🟒 Happy Path (Success Flow)
85
-
86
- | Implementation | Operations / sec | Average Latency |
87
- | :--- | :---: | :---: |
88
- | **Native `try/catch` (no throw)** | `11,372,962 ops/sec` | `0.0001 ms` |
89
- | **`yield-result` (`safe.sync`)** | `231,068 ops/sec` | `0.0043 ms` |
90
-
91
- *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.
92
86
 
93
87
  ---
94
88
 
@@ -261,6 +255,46 @@ export async function handleOrderCheckout(orderId: string) {
261
255
 
262
256
  ---
263
257
 
258
+ ## 🚦 Guidelines & Anti-Patterns: Async/Await + Generator Boundaries
259
+
260
+ Combining `async/await` with generator delegation (`yield* await asyncFn()`) is a powerful pattern, but mixing two control-flow models requires adhering to key architectural guidelines to avoid performance pitfalls and concurrency bottlenecks.
261
+
262
+ ### πŸ”΄ Anti-Pattern 1: Sequential Request Waterfalls
263
+
264
+ Avoid unwrapping multiple independent async calls sequentially inside `safe.async`:
265
+
266
+ ```typescript
267
+ // ❌ ANTI-PATTERN: Creates a sequential waterfall latency bottleneck!
268
+ const user = yield* await fetchUser(id); // Waits 100ms
269
+ const config = yield* await fetchConfig(id); // Waits 100ms (Total: 200ms)
270
+ ```
271
+
272
+ **βœ… Recommended Pattern (Parallel Concurrency):**
273
+ Execute independent Promises concurrently with `Promise.all` before unwrapping:
274
+
275
+ ```typescript
276
+ // βœ… RECOMMENDED: Concurrently fetches in parallel (Total: 100ms)
277
+ const [userRes, configRes] = await Promise.all([fetchUser(id), fetchConfig(id)]);
278
+ const user = yield* userRes;
279
+ const config = yield* configRes;
280
+ ```
281
+
282
+ ---
283
+
284
+ ### πŸ”΄ Anti-Pattern 2: Missing `await` on Async Result Promises
285
+
286
+ Forgetting `await` before yielding an async function that returns `Promise<Result<T, E>>`:
287
+
288
+ ```typescript
289
+ // ❌ ANTI-PATTERN: Forgetting 'await' on Promise<Result>
290
+ const user = yield* fetchUserAsync(id); // Error: Trying to iterate over a Promise object!
291
+ ```
292
+
293
+ **βœ… Recommended Pattern:**
294
+ Always use `yield* await fetchUserAsync(id)` when calling functions that return a `Promise<Result<T, E>>`.
295
+
296
+ ---
297
+
264
298
  ## πŸ›‘οΈ Built-in Safety Features
265
299
 
266
300
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yield-result",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Rust-style ? operator for TypeScript using generators (yield*). Zero dependencies, type-safe error handling.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -47,9 +47,8 @@
47
47
  ],
48
48
  "license": "MIT",
49
49
  "devDependencies": {
50
+ "neverthrow": "^8.2.0",
50
51
  "typescript": "^5.5.4",
51
52
  "vitest": "^2.0.5"
52
53
  }
53
54
  }
54
-
55
-