@jesscss/awaitable-pipe 2.0.0-alpha.1

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,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) Matthew Dean
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 the following 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.
22
+
23
+
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # awaitable-pipe
2
+
3
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
4
+
5
+ A tiny, zero-dependency pipe with friendly types that “just works”: it stays sync when everything is sync, and turns into a Promise only when something is async. No wrappers, no ceremony.
6
+
7
+ - **Stays sync when it can**: all-sync pipelines return a plain value
8
+ - **Goes async when it must**: any async input/step returns a Promise
9
+ - **One place for errors**: `safePipe` gives you a single `onError` + optional `fallback`
10
+ - **Steps-only API**: start with an initializer step (() => value | Promise), or omit it entirely
11
+ - **Typed nicely**: TypeScript keeps the sync/async shape without Result-like wrappers
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add @jesscss/awaitable-pipe
17
+ # or
18
+ npm i @jesscss/awaitable-pipe
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```ts
24
+ import { pipe, safePipe } from '@jesscss/awaitable-pipe';
25
+
26
+ // Sync stays sync
27
+ const upper = (s: string) => s.toUpperCase();
28
+ const exclaim = (s: string) => s + '!';
29
+ const out = pipe(() => 'ok', upper, exclaim); // 'OK!'
30
+
31
+ // Mixed becomes Promise
32
+ const load = async (s: string) => s + '!';
33
+ const outP = pipe(() => 'ok', upper, load); // Promise<string>
34
+ const result = await outP; // 'OK!'
35
+
36
+ // Start without an initial value
37
+ const s2 = pipe((x?: number) => (x ?? 2) * 3); // 6
38
+
39
+ // Single-point error handling (never throws)
40
+ const boom = () => { throw new Error('nope'); };
41
+ const safe = safePipe({ onError: console.error, fallback: 'X' }, () => 'ok', boom, upper);
42
+ // 'X'
43
+ ```
44
+
45
+ ## Why would you want this?
46
+ JavaScript Promises are great, but they aren’t free. Every async hop schedules work, allocates objects, and pushes errors across an async boundary. In hot paths, that overhead can add up.
47
+
48
+ That said, consider this a micro-optimization! This is really only a faster approach than forced async / await when a very small percentage of unknown function calls result in Promises (< 10% of steps returning Promises in performance testing). If a greater percentage of steps would normally return promises, then using an async / await pattern on all unknown results (regardless of whether or not those results are a Promise, which JavaScript is fine with) will generally be faster than using this library.
49
+
50
+ ### Features
51
+
52
+ - **Zero extra overhead for sync work**: when your steps are synchronous, you get plain values—no microtasks, no `await`, no extra Promise allocations.
53
+ - **Seamless async when you need it**: if any step is async, the pipeline naturally promotes to a Promise—no special handling required.
54
+ - **Cleaner stacks**: sync-only flows keep straightforward stack traces and easier debugging.
55
+ - **Simple error strategy**: prefer natural throw/reject with `pipe`, or centralize it once with `safePipe` without wrapping results.
56
+
57
+ ## API
58
+
59
+ ### pipe(...steps)
60
+ - **Return shape**: sync returns a value; any async → Promise
61
+ - **Errors**: sync errors throw; async errors reject
62
+ - **Inputs**: value, Promise, thunk (() => value|Promise), or omit (first step gets `undefined`)
63
+
64
+ ```ts
65
+ // compose sync functions → string
66
+ const a = pipe(() => 'hi', (s) => s.trim(), (s) => s.toUpperCase());
67
+
68
+ // mix in async → Promise<string>
69
+ const b = pipe(() => 'hi', async (s) => s + '!', (s) => s + '?');
70
+
71
+ // no initial value
72
+ const c = pipe((x?: number) => (x ?? 1) + 1, (n) => n * 10); // 20
73
+ ```
74
+
75
+ ### safePipe(options, ...steps)
76
+ If you prefer not to throw or reject, `safePipe` centralizes error handling. You get an optional `onError` callback and a `fallback` value (or thunk). On error, the pipeline returns the fallback (or `undefined` if you didn’t provide one).
77
+
78
+ - **Never throws**: errors are caught and routed to `onError`
79
+ - **Return shape preserved**: still sync-if-sync, async-if-async
80
+ - **Start forms**: options-first (no explicit initial value) or steps-only
81
+
82
+ ```ts
83
+ // Sync-only path
84
+ const r1 = safePipe({ onError: console.warn, fallback: 'X' }, () => 'ok', (s: string) => s.toUpperCase()); // 'OK'
85
+
86
+ // Sync error → fallback
87
+ const r2 = safePipe({ onError: console.warn, fallback: 'X' },
88
+ () => { throw new Error('boom'); },
89
+ (s: string) => s.toUpperCase()
90
+ ); // 'X'
91
+
92
+ // Async path → Promise<string>
93
+ const r3 = await safePipe({ onError: console.warn, fallback: 'X' },
94
+ () => 'ok',
95
+ async (s: string) => s + '!',
96
+ (s: string) => s + '?'
97
+ ); // 'ok!?'
98
+
99
+ // No fallback provided. On error, returns undefined (never throws).
100
+ const r5 = safePipe({ onError: console.warn },
101
+ () => { throw new Error('boom'); },
102
+ (s: string) => s.toUpperCase()
103
+ ); // undefined
104
+ ```
105
+
106
+ ## Composing pipes
107
+ You can feed the output of one pipe (value or Promise) into another. Types keep up with you.
108
+
109
+ ```ts
110
+ const p1 = pipe(() => 'hi', (s: string) => s.toUpperCase()); // string
111
+ const p2 = pipe(() => p1, (s) => s + '!'); // 'HI!'
112
+
113
+ const p3 = pipe(() => 'hi', async s => s + '!'); // Promise<string>
114
+ const p4 = pipe(() => p3, (s) => s + '?'); // Promise<string>
115
+ const fin = await p4; // 'hi!?'
116
+ ```
117
+
118
+ ## Per-step helpers
119
+ Sometimes you want to guard or handle errors at a specific step without switching the whole pipeline to safe mode. Use these helpers as steps inside `pipe` or `safePipe`:
120
+
121
+ ```ts
122
+ import { pipe, tryStep, guard, serialForEach, serialReduce } from '@jesscss/awaitable-pipe';
123
+
124
+ // tryStep: catch at this step only, with optional onError and fallback
125
+ const step = tryStep((n: number) => {
126
+ if (n < 0) throw new Error('no negatives');
127
+ return n * 2;
128
+ }, {
129
+ onError: (err, n) => console.warn('bad number:', n, err),
130
+ fallback: 0 // could also be (err, n) => 0
131
+ });
132
+
133
+ const out = pipe(() => 5, step); // 10
134
+ const out2 = pipe(() => -1, step); // 0
135
+
136
+ // onError can throw to rethrow the error (or a transformed error) for upstream handling
137
+ const stepWithRethrow = tryStep((input: string) => {
138
+ if (input === 'bad') throw new ReferenceError('not found');
139
+ return input.toUpperCase();
140
+ }, {
141
+ onError: (error, input) => {
142
+ // Conditionally rethrow based on error type
143
+ if (error instanceof ReferenceError) {
144
+ throw error; // Re-throw for upstream handling
145
+ }
146
+ // Otherwise, just log - fallback will be used
147
+ console.log('Handled error:', error);
148
+ },
149
+ fallback: 'default'
150
+ });
151
+
152
+ const result1 = pipe(() => 'good', stepWithRethrow); // 'GOOD'
153
+ const result2 = pipe(() => 'bad', stepWithRethrow); // Throws ReferenceError
154
+ const result3 = pipe(() => 'other', stepWithRethrow); // 'default' (if step throws non-ReferenceError)
155
+
156
+ // Or use rethrow: true to always rethrow after onError
157
+ const alwaysRethrow = tryStep((n: number) => {
158
+ if (n < 0) throw new Error('no negatives');
159
+ return n * 2;
160
+ }, {
161
+ onError: (err, n) => console.warn('Error:', err),
162
+ rethrow: true // Always rethrow the original error after onError
163
+ });
164
+
165
+ // guard: ensure a condition holds at this step (sync or async)
166
+ const positive = guard((n: number) => n > 0, (n) => new Error(`not positive: ${n}`));
167
+ const ok = pipe(() => 3, positive); // 3
168
+ // pipe(() => -2, positive) would throw: Error('not positive: -2')
169
+
170
+ // serialForEach: sync-first loop that promotes to async if a step returns a Promise
171
+ const items = [1, 2, 3];
172
+ await serialForEach(items, async (n, i) => {
173
+ if (i === 1) await new Promise(r => setTimeout(r, 10));
174
+ });
175
+
176
+ // serialReduce: sync-first reduce that promotes to async on demand
177
+ const sum = await serialReduce(items, 0, async (acc, n, i) => {
178
+ if (i === 2) await Promise.resolve();
179
+ return acc + n;
180
+ });
181
+ // sum === 6
182
+ ```
183
+
184
+ ## License
185
+ MIT
@@ -0,0 +1,16 @@
1
+ import type { MaybePromise } from './utils.js';
2
+ export { serialForEach, serialReduce } from './utils.js';
3
+ export type StepErrorOptions<TIn, R> = {
4
+ onError?: (error: unknown, input: TIn) => void;
5
+ fallback?: R | ((error: unknown, input: TIn) => R);
6
+ rethrow?: boolean;
7
+ };
8
+ export declare function tryStep<TIn, R>(fn: (input: TIn) => MaybePromise<R>, options: StepErrorOptions<TIn, R> & {
9
+ rethrow: true;
10
+ }): (input: TIn) => MaybePromise<R>;
11
+ export declare function tryStep<R>(fn: () => MaybePromise<R>, options: StepErrorOptions<undefined, R> & {
12
+ rethrow: true;
13
+ }): () => MaybePromise<R>;
14
+ export declare function tryStep<R>(fn: () => MaybePromise<R>, options?: StepErrorOptions<undefined, R | undefined>): () => MaybePromise<R | undefined>;
15
+ export declare function tryStep<TIn, R>(fn: (input: TIn) => MaybePromise<R>, options?: StepErrorOptions<TIn, R | undefined>): (input: TIn) => MaybePromise<R | undefined>;
16
+ export declare function guard<T>(predicate: (value: T) => MaybePromise<boolean>, errorFactory?: (value: T) => unknown): (value: T) => MaybePromise<T>;
package/lib/helpers.js ADDED
@@ -0,0 +1,111 @@
1
+ import { isThenable } from './utils.js';
2
+ export { serialForEach, serialReduce } from './utils.js';
3
+ // Type guard to help TypeScript narrow no-arg functions
4
+ function isNoArgFunction(fn) {
5
+ return fn.length === 0;
6
+ }
7
+ export function tryStep(fn, options = {}) {
8
+ // Check if fn takes no arguments (for first step)
9
+ if (isNoArgFunction(fn)) {
10
+ const noInputFn = fn; // Type guard narrows this to () => MaybePromise<R>
11
+ // Even though the function takes no args, the wrapper should accept input for pipe chaining
12
+ // and pass it to fallback/onError
13
+ const resultFn = (input) => {
14
+ try {
15
+ const out = noInputFn();
16
+ if (isThenable(out)) {
17
+ return out.catch((e) => {
18
+ try {
19
+ options.onError?.(e, input);
20
+ }
21
+ catch (onErrorThrown) {
22
+ // Swallow onError errors and continue to fallback
23
+ }
24
+ if (options.rethrow === true) {
25
+ return Promise.reject(e);
26
+ }
27
+ const fb = options.fallback;
28
+ return typeof fb === 'function' ? fb(e, input) : fb;
29
+ });
30
+ }
31
+ return out;
32
+ }
33
+ catch (e) {
34
+ try {
35
+ options.onError?.(e, input);
36
+ }
37
+ catch (onErrorThrown) {
38
+ // Swallow onError errors and continue to fallback
39
+ }
40
+ if (options.rethrow === true) {
41
+ throw e;
42
+ }
43
+ const fb = options.fallback;
44
+ return typeof fb === 'function' ? fb(e, input) : fb;
45
+ }
46
+ };
47
+ // Cast to match overload return types
48
+ // Overloads say () => MaybePromise<R>, but implementation accepts optional input for fallback/onError
49
+ // At runtime, JavaScript allows calling () => A with an argument, so this works
50
+ // TypeScript sees () => MaybePromise<R> to match pipe's first overload
51
+ if (options.rethrow === true) {
52
+ // Type assertion: TypeScript sees () => MaybePromise<R>, runtime accepts optional input
53
+ return resultFn;
54
+ }
55
+ return resultFn;
56
+ }
57
+ // Original implementation for functions that take input
58
+ const inputFn = fn;
59
+ return (input) => {
60
+ try {
61
+ const out = fn(input);
62
+ if (isThenable(out)) {
63
+ return out.catch((e) => {
64
+ try {
65
+ options.onError?.(e, input);
66
+ }
67
+ catch (onErrorThrown) {
68
+ // Swallow onError errors and continue to fallback
69
+ }
70
+ if (options.rethrow === true) {
71
+ return Promise.reject(e);
72
+ }
73
+ const fb = options.fallback;
74
+ return typeof fb === 'function' ? fb(e, input) : fb;
75
+ });
76
+ }
77
+ return out;
78
+ }
79
+ catch (e) {
80
+ try {
81
+ options.onError?.(e, input);
82
+ }
83
+ catch (onErrorThrown) {
84
+ // Swallow onError errors and continue to fallback
85
+ }
86
+ if (options.rethrow === true) {
87
+ throw e;
88
+ }
89
+ const fb = options.fallback;
90
+ return typeof fb === 'function' ? fb(e, input) : fb;
91
+ }
92
+ };
93
+ }
94
+ export function guard(predicate, errorFactory = (v) => new Error('ensure failed')) {
95
+ return (value) => {
96
+ const ok = predicate(value);
97
+ if (isThenable(ok)) {
98
+ return ok.then(passed => {
99
+ if (!passed) {
100
+ throw errorFactory(value);
101
+ }
102
+ return value;
103
+ });
104
+ }
105
+ if (!ok) {
106
+ throw errorFactory(value);
107
+ }
108
+ return value;
109
+ };
110
+ }
111
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type { SafePipeOptions } from './pipe.js';
2
+ export { pipe, safePipe } from './pipe.js';
3
+ export { tryStep, guard, serialForEach, serialReduce } from './helpers.js';
4
+ export type { MaybePromise } from './utils.js';
5
+ export { isThenable, isPromise } from './utils.js';
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { pipe, safePipe } from './pipe.js';
2
+ export { tryStep, guard, serialForEach, serialReduce } from './helpers.js';
3
+ export { isThenable, isPromise } from './utils.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
package/lib/pipe.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ type Unwrap<T> = T extends Promise<infer U> ? U : T;
2
+ type RetOf<F> = F extends (a: any) => infer R ? R : never;
3
+ type ParamOf<F> = F extends (...args: infer P) => any ? (P extends [infer A, ...any[]] ? A : never) : never;
4
+ type Apply<In, F> = [
5
+ ParamOf<F>
6
+ ] extends [never] ? RetOf<F> : In extends Promise<any> ? Promise<Awaited<RetOf<F>>> : RetOf<F>;
7
+ type PipeResult<In, Fns extends any[], Acc = In> = Fns extends [infer F, ...infer Rest] ? PipeResult<Apply<Acc, F>, Rest> : Acc;
8
+ export declare function pipe<A, R1>(fn1: () => A, fn2: (a: Unwrap<A>) => R1): PipeResult<undefined, [() => A, (a: Unwrap<A>) => R1]>;
9
+ export declare function pipe<A, R1>(fn1: (a?: A) => R1): PipeResult<undefined, [(a?: A) => R1]>;
10
+ export declare function pipe<A, R1, R2>(fn1: () => A, fn2: (a: Unwrap<A>) => R1, fn3: (b: Unwrap<R1>) => R2): PipeResult<undefined, [() => A, (a: Unwrap<A>) => R1, (b: Unwrap<R1>) => R2]>;
11
+ export declare function pipe<A, R1, R2, R3>(fn1: () => A, fn2: (a: Unwrap<A>) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3): PipeResult<undefined, [() => A, (a: Unwrap<A>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3]>;
12
+ export declare function pipe<A, R1, R2, R3, R4>(fn1: () => A, fn2: (a: Unwrap<A>) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3, fn5: (d: Unwrap<R3>) => R4): PipeResult<undefined, [() => A, (a: Unwrap<A>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4]>;
13
+ export declare function pipe<A, R1, R2, R3, R4, R5>(fn1: () => A, fn2: (a: Unwrap<A>) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3, fn5: (d: Unwrap<R3>) => R4, fn6: (e: Unwrap<R4>) => R5): PipeResult<undefined, [() => A, (a: Unwrap<A>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4, (e: Unwrap<R4>) => R5]>;
14
+ export declare function pipe<T, R1>(input: T | Promise<T> | (() => T) | (() => Promise<T>), fn1: (a: Unwrap<T>) => R1): PipeResult<T, [(a: Unwrap<T>) => R1]>;
15
+ export declare function pipe<T, R1, R2>(input: T | Promise<T> | (() => T) | (() => Promise<T>), fn1: (a: Unwrap<T>) => R1, fn2: (b: Unwrap<R1>) => R2): PipeResult<T, [(a: Unwrap<T>) => R1, (b: Unwrap<R1>) => R2]>;
16
+ export declare function pipe<T, R1, R2, R3>(input: T | Promise<T> | (() => T) | (() => Promise<T>), fn1: (a: Unwrap<T>) => R1, fn2: (b: Unwrap<R1>) => R2, fn3: (c: Unwrap<R2>) => R3): PipeResult<T, [(a: Unwrap<T>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3]>;
17
+ export declare function pipe<T, R1, R2, R3, R4>(input: T | Promise<T> | (() => T) | (() => Promise<T>), fn1: (a: Unwrap<T>) => R1, fn2: (b: Unwrap<R1>) => R2, fn3: (c: Unwrap<R2>) => R3, fn4: (d: Unwrap<R3>) => R4): PipeResult<T, [(a: Unwrap<T>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4]>;
18
+ export declare function pipe<T, R1, R2, R3, R4, R5>(input: T | Promise<T> | (() => T) | (() => Promise<T>), fn1: (a: Unwrap<T>) => R1, fn2: (b: Unwrap<R1>) => R2, fn3: (c: Unwrap<R2>) => R3, fn4: (d: Unwrap<R3>) => R4, fn5: (e: Unwrap<R4>) => R5): PipeResult<T, [(a: Unwrap<T>) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4, (e: Unwrap<R4>) => R5]>;
19
+ export declare function pipe<A>(input: A | Promise<A> | (() => A) | (() => Promise<A>), ...fns: Array<(a: A) => A | Promise<A>>): A | Promise<A>;
20
+ export type SafePipeOptions<R = unknown> = {
21
+ onError?: (error: unknown) => void;
22
+ fallback?: R | (() => R);
23
+ };
24
+ export declare function safePipe<A, R1>(options: SafePipeOptions<any>, fn1: (a?: A) => R1): PipeResult<undefined, [(a?: A) => R1]> extends Promise<any> ? Promise<R1 | undefined> : R1 | undefined;
25
+ export declare function safePipe<A, R1, R2>(options: SafePipeOptions<any>, fn1: (a?: A) => R1, fn2: (b: Unwrap<R1>) => R2): PipeResult<undefined, [(a?: A) => R1, (b: Unwrap<R1>) => R2]> extends Promise<any> ? Promise<R2 | undefined> : R2 | undefined;
26
+ export declare function safePipe<A, R1, R2, R3>(options: SafePipeOptions<any>, fn1: (a?: A) => R1, fn2: (b: Unwrap<R1>) => R2, fn3: (c: Unwrap<R2>) => R3): PipeResult<undefined, [(a?: A) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3]> extends Promise<any> ? Promise<R3 | undefined> : R3 | undefined;
27
+ export declare function safePipe<A, R1>(fn1: () => A, fn2: (a: A) => R1): PipeResult<undefined, [() => A, (a: A) => R1]> extends Promise<any> ? Promise<R1 | undefined> : R1 | undefined;
28
+ export declare function safePipe<A, R1, R2>(fn1: () => A, fn2: (a: A) => R1, fn3: (b: Unwrap<R1>) => R2): PipeResult<undefined, [() => A, (a: A) => R1, (b: Unwrap<R1>) => R2]> extends Promise<any> ? Promise<R2 | undefined> : R2 | undefined;
29
+ export declare function safePipe<A, R1, R2, R3>(fn1: () => A, fn2: (a: A) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3): PipeResult<undefined, [() => A, (a: A) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3]> extends Promise<any> ? Promise<R3 | undefined> : R3 | undefined;
30
+ export declare function safePipe<A, R1, R2, R3, R4>(fn1: () => A, fn2: (a: A) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3, fn5: (d: Unwrap<R3>) => R4): PipeResult<undefined, [() => A, (a: A) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4]> extends Promise<any> ? Promise<R4 | undefined> : R4 | undefined;
31
+ export declare function safePipe<A, R1, R2, R3, R4, R5>(fn1: () => A, fn2: (a: A) => R1, fn3: (b: Unwrap<R1>) => R2, fn4: (c: Unwrap<R2>) => R3, fn5: (d: Unwrap<R3>) => R4, fn6: (e: Unwrap<R4>) => R5): PipeResult<undefined, [() => A, (a: A) => R1, (b: Unwrap<R1>) => R2, (c: Unwrap<R2>) => R3, (d: Unwrap<R3>) => R4, (e: Unwrap<R4>) => R5]> extends Promise<any> ? Promise<R5 | undefined> : R5 | undefined;
32
+ export {};
package/lib/pipe.js ADDED
@@ -0,0 +1,126 @@
1
+ import { isThenable } from './utils.js';
2
+ function runAsync(v, fns, startIndex) {
3
+ let p = Promise.resolve(v);
4
+ for (let i = startIndex; i < fns.length; i++) {
5
+ const fn = fns[i];
6
+ p = p.then(val => fn(val));
7
+ }
8
+ return p;
9
+ }
10
+ // Note: Intentionally omitting generic varargs overloads to improve contextual typing
11
+ export function pipe(...args) {
12
+ let input;
13
+ let fns;
14
+ if (args.length === 0)
15
+ return undefined;
16
+ const first = args[0];
17
+ const second = args[1];
18
+ const rest = args.slice(1);
19
+ // Disambiguation:
20
+ // - If first and second are both functions → treat as steps-only (no explicit input)
21
+ // - Else if more than one arg → treat first as input (value | Promise | thunk)
22
+ // - Else single function → steps-only
23
+ const stepsOnly = typeof first === 'function' && typeof second === 'function';
24
+ if (stepsOnly) {
25
+ input = undefined;
26
+ fns = args;
27
+ }
28
+ else if (args.length > 1) {
29
+ input = typeof first === 'function' ? first() : first;
30
+ fns = rest;
31
+ }
32
+ else {
33
+ input = undefined;
34
+ fns = [first];
35
+ }
36
+ if (isThenable(input)) {
37
+ return runAsync(input, fns, 0);
38
+ }
39
+ for (let i = 0; i < fns.length; i++) {
40
+ const fn = fns[i];
41
+ const out = fn(input);
42
+ if (isThenable(out)) {
43
+ return runAsync(out, fns, i + 1);
44
+ }
45
+ input = out;
46
+ }
47
+ return input;
48
+ }
49
+ function resolveFallback(fb) {
50
+ return typeof fb === 'function' ? fb() : fb;
51
+ }
52
+ function runAsyncSafe(v, fns, startIndex, opts) {
53
+ const { onError, fallback } = opts;
54
+ const callOnError = (e) => {
55
+ try {
56
+ onError?.(e);
57
+ }
58
+ catch {
59
+ // Swallow errors from onError to keep guarantees: never throw
60
+ }
61
+ };
62
+ let p = Promise.resolve(v);
63
+ for (let i = startIndex; i < fns.length; i++) {
64
+ const fn = fns[i];
65
+ p = p.then(val => {
66
+ try {
67
+ return fn(val);
68
+ }
69
+ catch (e) {
70
+ callOnError(e);
71
+ return resolveFallback(fallback);
72
+ }
73
+ }, e => {
74
+ callOnError(e);
75
+ return resolveFallback(fallback);
76
+ });
77
+ }
78
+ return p.catch(e => {
79
+ callOnError(e);
80
+ return resolveFallback(fallback);
81
+ });
82
+ }
83
+ export function safePipe(...args) {
84
+ let input;
85
+ let options;
86
+ let fns;
87
+ if (args.length === 0)
88
+ return undefined;
89
+ const first = args[0];
90
+ const second = args[1];
91
+ const looksLikeOptions = (x) => !!x && typeof x === 'object' && !Array.isArray(x);
92
+ // Special-case: options-first with no steps should return undefined
93
+ if (args.length === 1 && !!first && typeof first === 'object') {
94
+ return undefined;
95
+ }
96
+ const bothFns = typeof first === 'function' && typeof second === 'function';
97
+ if (looksLikeOptions(first) || bothFns) {
98
+ // options-first or steps-only (no explicit input)
99
+ input = undefined;
100
+ options = looksLikeOptions(first) ? first : {};
101
+ fns = (looksLikeOptions(first) ? args.slice(1) : args);
102
+ }
103
+ else {
104
+ throw new TypeError('safePipe requires steps-only or options-first with steps');
105
+ }
106
+ input = undefined;
107
+ for (let i = 0; i < fns.length; i++) {
108
+ const fn = fns[i];
109
+ try {
110
+ const out = fn(input);
111
+ if (isThenable(out)) {
112
+ return runAsyncSafe(out, fns, i + 1, options);
113
+ }
114
+ input = out;
115
+ }
116
+ catch (e) {
117
+ try {
118
+ options?.onError?.(e);
119
+ }
120
+ catch { }
121
+ return resolveFallback(options?.fallback);
122
+ }
123
+ }
124
+ return input;
125
+ }
126
+ //# sourceMappingURL=pipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe.js","sourceRoot":"","sources":["../src/pipe.ts"],"names":[],"mappings":"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"}
package/lib/utils.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
2
+ export declare function isThenable(x: unknown): x is Promise<unknown>;
3
+ export declare function isPromise<T = unknown>(x: unknown): x is Promise<T>;
4
+ /**
5
+ * Serial forEach over an array where the step may return a Promise.
6
+ * Runs synchronously until an async step is encountered, then switches to async for the remainder.
7
+ */
8
+ export declare function serialForEach<T>(items: readonly T[], step: (item: T, index: number) => MaybePromise<void | undefined>): MaybePromise<void | undefined>;
9
+ /**
10
+ * Serial reduce over an array where the step may return a Promise.
11
+ * Runs synchronously until an async step is encountered, then switches to async for the remainder.
12
+ */
13
+ export declare function serialReduce<T, A>(items: readonly T[], seed: A, step: (acc: A, item: T, index: number) => MaybePromise<A>): MaybePromise<A>;
package/lib/utils.js ADDED
@@ -0,0 +1,47 @@
1
+ export function isThenable(x) {
2
+ return !!x && (typeof x === 'object' || typeof x === 'function') && typeof x.then === 'function';
3
+ }
4
+ export function isPromise(x) {
5
+ return !!x && typeof x === 'object' && typeof x.then === 'function' && typeof x.catch === 'function';
6
+ }
7
+ /**
8
+ * Serial forEach over an array where the step may return a Promise.
9
+ * Runs synchronously until an async step is encountered, then switches to async for the remainder.
10
+ */
11
+ export function serialForEach(items, step) {
12
+ for (let i = 0; i < items.length; i++) {
13
+ const out = step(items[i], i);
14
+ if (isThenable(out)) {
15
+ return (async () => {
16
+ await out;
17
+ for (let j = i + 1; j < items.length; j++) {
18
+ await step(items[j], j);
19
+ }
20
+ return undefined;
21
+ })();
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * Serial reduce over an array where the step may return a Promise.
28
+ * Runs synchronously until an async step is encountered, then switches to async for the remainder.
29
+ */
30
+ export function serialReduce(items, seed, step) {
31
+ let acc = seed;
32
+ for (let i = 0; i < items.length; i++) {
33
+ const out = step(acc, items[i], i);
34
+ if (isThenable(out)) {
35
+ return (async () => {
36
+ acc = await out;
37
+ for (let j = i + 1; j < items.length; j++) {
38
+ acc = await step(acc, items[j], j);
39
+ }
40
+ return acc;
41
+ })();
42
+ }
43
+ acc = out;
44
+ }
45
+ return acc;
46
+ }
47
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,CAAU;IACnC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,UAAU,CAAC,IAAI,OAAQ,CAAS,CAAC,IAAI,KAAK,UAAU,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,SAAS,CAAc,CAAU;IAC/C,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAAS,CAAC,IAAI,KAAK,UAAU,IAAI,OAAQ,CAAS,CAAC,KAAK,KAAK,UAAU,CAAC;AACzH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAI,KAAmB,EAAE,IAAgE;IACpH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;QAC/B,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,IAAI,EAAE;gBACjB,MAAM,GAAG,CAAC;gBACV,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC1C,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;gBAC3B,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAO,KAAmB,EAAE,IAAO,EAAE,IAAyD;IACxH,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;QACpC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,IAAI,EAAE;gBACjB,GAAG,GAAG,MAAM,GAAQ,CAAC;gBACrB,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC1C,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAM,CAAC;gBAC3C,CAAC;gBACD,OAAO,GAAG,CAAC;YACb,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;QACD,GAAG,GAAG,GAAQ,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@jesscss/awaitable-pipe",
3
+ "version": "2.0.0-alpha.1",
4
+ "description": "A tiny, strongly-typed pipe that stays sync when possible and becomes a Promise when needed. With an optional single-point error handler.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./lib/index.js",
11
+ "types": "./lib/index.d.ts",
12
+ "source": "./src/index.ts"
13
+ },
14
+ "./*": {
15
+ "types": "./lib/*.d.ts",
16
+ "import": "./lib/*.js",
17
+ "source": "./src/*.ts"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "lib",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/matthew-dean/jess.git"
29
+ },
30
+ "keywords": [
31
+ "pipe",
32
+ "functional",
33
+ "async",
34
+ "sync",
35
+ "typescript",
36
+ "error-handling"
37
+ ],
38
+ "author": "Matthew Dean <matthew-dean@users.noreply.github.com>",
39
+ "license": "MIT",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "sideEffects": false,
44
+ "devDependencies": {
45
+ "typescript": "~5.8.2"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc -p tsconfig.build.json",
49
+ "dev": "tsc -w -p tsconfig.build.json",
50
+ "test": "vitest run",
51
+ "test:types": "tsc -p tsconfig.json --noEmit",
52
+ "test:watch": "vitest"
53
+ }
54
+ }