@async-kit/flowx 0.1.25 → 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/CHANGELOG.md +12 -0
- package/README.md +18 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -114,6 +114,23 @@ const result = await pipeline<string>()
|
|
|
114
114
|
// → { raw: 'invalid json', error: SyntaxError }
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
If the **fallback itself throws**, the error is wrapped in `PipelineStepError` (same as a normal step error) so you always get a consistent error type:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
try {
|
|
121
|
+
await pipeline<string>()
|
|
122
|
+
.pipeWithFallback(
|
|
123
|
+
{ name: 'fetch', fn: s => primaryApi.get(s) },
|
|
124
|
+
(_err, id) => fallbackApi.get(id) // this might also fail
|
|
125
|
+
)
|
|
126
|
+
.run(userId);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (err instanceof PipelineStepError) {
|
|
129
|
+
console.log('step or fallback failed at step', err.stepIndex, err.cause);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
117
134
|
## `parallel(tasks, options?)`
|
|
118
135
|
|
|
119
136
|
Run tasks concurrently, resolve in declaration order. Rejects on first failure.
|
|
@@ -142,7 +159,7 @@ const total = await sequence(orders, 0, async (acc, order) => {
|
|
|
142
159
|
|
|
143
160
|
| Class | When |
|
|
144
161
|
|---|---|
|
|
145
|
-
| `PipelineStepError` | A step threw — has `.stepIndex`, `.stepName`, `.cause`, `.inputValue` |
|
|
162
|
+
| `PipelineStepError` | A step threw **or a fallback threw** — has `.stepIndex`, `.stepName`, `.cause`, `.inputValue` |
|
|
146
163
|
| `PipelineTimeoutError` | Per-step timeout exceeded — has `.stepIndex`, `.stepName`, `.timeoutMs` |
|
|
147
164
|
|
|
148
165
|
## Examples
|
package/dist/index.js
CHANGED
|
@@ -120,7 +120,11 @@ var Pipeline = class _Pipeline {
|
|
|
120
120
|
const nextRecord = this.records[i + 1];
|
|
121
121
|
if (nextRecord?.isFallback) {
|
|
122
122
|
const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);
|
|
123
|
-
|
|
123
|
+
try {
|
|
124
|
+
current = await nextRecord.fn(pipelineErr, inputValue);
|
|
125
|
+
} catch (fallbackErr) {
|
|
126
|
+
throw new PipelineStepError(stepIndex, record.name, fallbackErr, inputValue);
|
|
127
|
+
}
|
|
124
128
|
i++;
|
|
125
129
|
onStepComplete?.(stepIndex, record.name, current);
|
|
126
130
|
} else {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flowx.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\nimport type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\n\n// ─── Errors ──────────────────────────────────────────────────────────────────\n\nexport class PipelineStepError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public override readonly cause: unknown,\n public readonly inputValue: unknown\n ) {\n super(\n `Pipeline failed at step ${stepIndex}${stepName ? ` (${stepName})` : ''}: ${String(cause)}`\n );\n this.name = 'PipelineStepError';\n }\n}\n\nexport class PipelineTimeoutError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public readonly timeoutMs: number\n ) {\n super(`Step ${stepIndex}${stepName ? ` (${stepName})` : ''} timed out after ${timeoutMs}ms`);\n this.name = 'PipelineTimeoutError';\n }\n}\n\n// ─── Internal Step Record ────────────────────────────────────────────────────\n\ninterface StepRecord {\n fn: StepFn<unknown, unknown>;\n name: string | undefined;\n timeoutMs: number | undefined;\n isFallback: false;\n}\n\ninterface FallbackRecord {\n fn: (error: PipelineStepError, input: unknown) => unknown | Promise<unknown>;\n isFallback: true;\n}\n\ntype AnyRecord = StepRecord | FallbackRecord;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nconst neverAborted = new AbortController().signal;\n\nfunction raceStepTimeout<T>(\n promise: Promise<T>,\n ms: number,\n index: number,\n name: string | undefined\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new PipelineTimeoutError(index, name, ms)), ms);\n promise.then(\n (v) => { clearTimeout(timer); resolve(v); },\n (e) => { clearTimeout(timer); reject(e); }\n );\n });\n}\n\nfunction normalizeStep<In, Out>(\n step: StepFn<In, Out> | NamedStep<In, Out>\n): { fn: StepFn<In, Out>; name: string | undefined; timeoutMs: number | undefined } {\n if (typeof step === 'function') {\n return { fn: step, name: undefined, timeoutMs: undefined };\n }\n return { fn: step.fn, name: step.name, timeoutMs: step.timeoutMs };\n}\n\n// ─── Pipeline ────────────────────────────────────────────────────────────────\n\n/**\n * Flowx — composable, type-safe async pipeline builder.\n *\n * Each step receives the output of the previous step as its first argument,\n * plus a `StepContext` carrying the abort signal, step index, and step name.\n *\n * @example\n * const result = await pipeline<string>()\n * .pipe(s => s.trim())\n * .pipe({ name: 'uppercase', fn: s => s.toUpperCase() })\n * .tap(s => console.log('after uppercase:', s))\n * .run(' hello ');\n */\nexport class Pipeline<TIn, TOut> {\n private readonly records: AnyRecord[];\n\n constructor(records: AnyRecord[] = []) {\n this.records = records;\n }\n\n /**\n * Add a transform step. Accepts either a plain function or a `NamedStep`\n * object with an optional per-step `timeoutMs`.\n */\n pipe<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const record: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n return new Pipeline<TIn, TNext>([...this.records, record]);\n }\n\n /**\n * Add a step with an inline fallback. If the step throws, `fallback` is\n * called with the error and the original input to that step.\n */\n pipeWithFallback<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>,\n fallback: (error: PipelineStepError, input: TOut) => TNext | Promise<TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const stepRecord: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n const fallbackRecord: FallbackRecord = {\n fn: fallback as (error: PipelineStepError, input: unknown) => unknown,\n isFallback: true,\n };\n return new Pipeline<TIn, TNext>([...this.records, stepRecord, fallbackRecord]);\n }\n\n /**\n * Add a side-effect step. Runs `fn` for its effect only; passes the current\n * value through unchanged. Errors in `fn` propagate normally.\n */\n tap(fn: (value: TOut, context: StepContext) => void | Promise<void>): Pipeline<TIn, TOut> {\n const tapStep: StepFn<TOut, TOut> = async (value, ctx) => {\n await fn(value, ctx);\n return value;\n };\n return this.pipe(tapStep);\n }\n\n async run(input: TIn, options: FlowxOptions = {}): Promise<TOut> {\n const signal = options.signal ?? neverAborted;\n const { onStepComplete, stepTimeoutMs } = options;\n\n let current: unknown = input;\n let stepIndex = 0;\n\n for (let i = 0; i < this.records.length; i++) {\n const record = this.records[i];\n\n if (record.isFallback) {\n // Fallback records are consumed inline by the preceding step handler.\n continue;\n }\n\n if (signal.aborted) {\n throw new DOMException('Pipeline aborted', 'AbortError');\n }\n\n const inputValue = current;\n const ctx: StepContext = { signal, stepIndex, stepName: record.name };\n const effectiveTimeout = record.timeoutMs ?? stepTimeoutMs;\n\n try {\n const rawResult = record.fn(current, ctx);\n let stepPromise: Promise<unknown> = rawResult instanceof Promise ? rawResult : Promise.resolve(rawResult);\n if (effectiveTimeout != null) {\n stepPromise = raceStepTimeout(stepPromise, effectiveTimeout, stepIndex, record.name);\n }\n current = await stepPromise;\n onStepComplete?.(stepIndex, record.name, current);\n } catch (err) {\n // Check if the next record is a fallback for this step.\n const nextRecord = this.records[i + 1];\n if (nextRecord?.isFallback) {\n const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);\n current = await nextRecord.fn(pipelineErr, inputValue);\n i++; // skip the fallback record in the outer loop\n onStepComplete?.(stepIndex, record.name, current);\n } else {\n throw new PipelineStepError(stepIndex, record.name, err, inputValue);\n }\n }\n\n stepIndex++;\n }\n\n return current as TOut;\n }\n\n get stepCount(): number {\n return this.records.filter((r) => !r.isFallback).length;\n }\n\n /** Returns step metadata for debugging/tooling. */\n toArray(): Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> {\n let idx = 0;\n const result: Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> = [];\n for (const r of this.records) {\n if (!r.isFallback) {\n result.push({ index: idx++, name: r.name, timeoutMs: r.timeoutMs });\n }\n }\n return result;\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/** Create a new empty pipeline starting with type `T`. */\nexport function pipeline<T>(): Pipeline<T, T> {\n return new Pipeline<T, T>();\n}\n\n// ─── parallel ────────────────────────────────────────────────────────────────\n\n/**\n * Run tasks concurrently, return results in declaration order.\n * Rejects if any task throws (like `Promise.all`).\n */\nexport async function parallel<T>(\n tasks: Array<() => Promise<T>>,\n options: ParallelOptions = {}\n): Promise<T[]> {\n const { concurrency, signal } = options;\n\n if (concurrency == null || concurrency >= tasks.length) {\n // Unbounded — just Promise.all\n if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');\n return Promise.all(tasks.map((t) => t()));\n }\n\n // Bounded concurrency via semaphore\n const results: T[] = new Array(tasks.length);\n let nextIndex = 0;\n let hasError = false;\n let firstError: unknown;\n\n const run = async (): Promise<void> => {\n while (nextIndex < tasks.length) {\n if (signal?.aborted || hasError) break;\n const idx = nextIndex++;\n try {\n results[idx] = await tasks[idx]();\n } catch (err) {\n hasError = true;\n firstError = err;\n break;\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, run);\n await Promise.all(workers);\n\n if (hasError) throw firstError;\n return results;\n}\n\n/**\n * Run tasks concurrently and return all results regardless of success/failure.\n * Equivalent to `Promise.allSettled` but with optional concurrency control.\n */\nexport async function parallelSettled<T>(\n tasks: Array<() => Promise<T>>,\n options: Pick<ParallelOptions, 'concurrency' | 'signal'> = {}\n): Promise<PromiseSettledResult<T>[]> {\n const { concurrency, signal } = options;\n const wrapped = tasks.map((t) => (): Promise<PromiseSettledResult<T>> =>\n t().then(\n (value) => ({ status: 'fulfilled' as const, value }),\n (reason) => ({ status: 'rejected' as const, reason })\n )\n );\n\n return parallel(wrapped, { concurrency, signal }) as Promise<PromiseSettledResult<T>[]>;\n}\n\n// ─── sequence ────────────────────────────────────────────────────────────────\n\n/**\n * Async left-fold over an array. Like `Array.reduce` but supports async reducers\n * and can be cancelled via `AbortSignal`.\n */\nexport async function sequence<T, A>(\n items: T[],\n initial: A,\n fn: (acc: A, item: T, index: number) => Promise<A> | A,\n options: SequenceOptions = {}\n): Promise<A> {\n const { signal } = options;\n let acc = initial;\n for (let i = 0; i < items.length; i++) {\n if (signal?.aborted) throw new DOMException('Sequence aborted', 'AbortError');\n acc = await fn(acc, items[i], i);\n }\n return acc;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/flowx.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\nimport type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\n\n// ─── Errors ──────────────────────────────────────────────────────────────────\n\nexport class PipelineStepError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public override readonly cause: unknown,\n public readonly inputValue: unknown\n ) {\n super(\n `Pipeline failed at step ${stepIndex}${stepName ? ` (${stepName})` : ''}: ${String(cause)}`\n );\n this.name = 'PipelineStepError';\n }\n}\n\nexport class PipelineTimeoutError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public readonly timeoutMs: number\n ) {\n super(`Step ${stepIndex}${stepName ? ` (${stepName})` : ''} timed out after ${timeoutMs}ms`);\n this.name = 'PipelineTimeoutError';\n }\n}\n\n// ─── Internal Step Record ────────────────────────────────────────────────────\n\ninterface StepRecord {\n fn: StepFn<unknown, unknown>;\n name: string | undefined;\n timeoutMs: number | undefined;\n isFallback: false;\n}\n\ninterface FallbackRecord {\n fn: (error: PipelineStepError, input: unknown) => unknown | Promise<unknown>;\n isFallback: true;\n}\n\ntype AnyRecord = StepRecord | FallbackRecord;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nconst neverAborted = new AbortController().signal;\n\nfunction raceStepTimeout<T>(\n promise: Promise<T>,\n ms: number,\n index: number,\n name: string | undefined\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new PipelineTimeoutError(index, name, ms)), ms);\n promise.then(\n (v) => { clearTimeout(timer); resolve(v); },\n (e) => { clearTimeout(timer); reject(e); }\n );\n });\n}\n\nfunction normalizeStep<In, Out>(\n step: StepFn<In, Out> | NamedStep<In, Out>\n): { fn: StepFn<In, Out>; name: string | undefined; timeoutMs: number | undefined } {\n if (typeof step === 'function') {\n return { fn: step, name: undefined, timeoutMs: undefined };\n }\n return { fn: step.fn, name: step.name, timeoutMs: step.timeoutMs };\n}\n\n// ─── Pipeline ────────────────────────────────────────────────────────────────\n\n/**\n * Flowx — composable, type-safe async pipeline builder.\n *\n * Each step receives the output of the previous step as its first argument,\n * plus a `StepContext` carrying the abort signal, step index, and step name.\n *\n * @example\n * const result = await pipeline<string>()\n * .pipe(s => s.trim())\n * .pipe({ name: 'uppercase', fn: s => s.toUpperCase() })\n * .tap(s => console.log('after uppercase:', s))\n * .run(' hello ');\n */\nexport class Pipeline<TIn, TOut> {\n private readonly records: AnyRecord[];\n\n constructor(records: AnyRecord[] = []) {\n this.records = records;\n }\n\n /**\n * Add a transform step. Accepts either a plain function or a `NamedStep`\n * object with an optional per-step `timeoutMs`.\n */\n pipe<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const record: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n return new Pipeline<TIn, TNext>([...this.records, record]);\n }\n\n /**\n * Add a step with an inline fallback. If the step throws, `fallback` is\n * called with the error and the original input to that step.\n */\n pipeWithFallback<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>,\n fallback: (error: PipelineStepError, input: TOut) => TNext | Promise<TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const stepRecord: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n const fallbackRecord: FallbackRecord = {\n fn: fallback as (error: PipelineStepError, input: unknown) => unknown,\n isFallback: true,\n };\n return new Pipeline<TIn, TNext>([...this.records, stepRecord, fallbackRecord]);\n }\n\n /**\n * Add a side-effect step. Runs `fn` for its effect only; passes the current\n * value through unchanged. Errors in `fn` propagate normally.\n */\n tap(fn: (value: TOut, context: StepContext) => void | Promise<void>): Pipeline<TIn, TOut> {\n const tapStep: StepFn<TOut, TOut> = async (value, ctx) => {\n await fn(value, ctx);\n return value;\n };\n return this.pipe(tapStep);\n }\n\n async run(input: TIn, options: FlowxOptions = {}): Promise<TOut> {\n const signal = options.signal ?? neverAborted;\n const { onStepComplete, stepTimeoutMs } = options;\n\n let current: unknown = input;\n let stepIndex = 0;\n\n for (let i = 0; i < this.records.length; i++) {\n const record = this.records[i];\n\n if (record.isFallback) {\n // Fallback records are consumed inline by the preceding step handler.\n continue;\n }\n\n if (signal.aborted) {\n throw new DOMException('Pipeline aborted', 'AbortError');\n }\n\n const inputValue = current;\n const ctx: StepContext = { signal, stepIndex, stepName: record.name };\n const effectiveTimeout = record.timeoutMs ?? stepTimeoutMs;\n\n try {\n const rawResult = record.fn(current, ctx);\n let stepPromise: Promise<unknown> = rawResult instanceof Promise ? rawResult : Promise.resolve(rawResult);\n if (effectiveTimeout != null) {\n stepPromise = raceStepTimeout(stepPromise, effectiveTimeout, stepIndex, record.name);\n }\n current = await stepPromise;\n onStepComplete?.(stepIndex, record.name, current);\n } catch (err) {\n // Check if the next record is a fallback for this step.\n const nextRecord = this.records[i + 1];\n if (nextRecord?.isFallback) {\n const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);\n try {\n current = await nextRecord.fn(pipelineErr, inputValue);\n } catch (fallbackErr) {\n // Fallback itself threw — wrap so callers can distinguish it\n throw new PipelineStepError(stepIndex, record.name, fallbackErr, inputValue);\n }\n i++; // skip the fallback record in the outer loop\n onStepComplete?.(stepIndex, record.name, current);\n } else {\n throw new PipelineStepError(stepIndex, record.name, err, inputValue);\n }\n }\n\n stepIndex++;\n }\n\n return current as TOut;\n }\n\n get stepCount(): number {\n return this.records.filter((r) => !r.isFallback).length;\n }\n\n /** Returns step metadata for debugging/tooling. */\n toArray(): Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> {\n let idx = 0;\n const result: Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> = [];\n for (const r of this.records) {\n if (!r.isFallback) {\n result.push({ index: idx++, name: r.name, timeoutMs: r.timeoutMs });\n }\n }\n return result;\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/** Create a new empty pipeline starting with type `T`. */\nexport function pipeline<T>(): Pipeline<T, T> {\n return new Pipeline<T, T>();\n}\n\n// ─── parallel ────────────────────────────────────────────────────────────────\n\n/**\n * Run tasks concurrently, return results in declaration order.\n * Rejects if any task throws (like `Promise.all`).\n */\nexport async function parallel<T>(\n tasks: Array<() => Promise<T>>,\n options: ParallelOptions = {}\n): Promise<T[]> {\n const { concurrency, signal } = options;\n\n if (concurrency == null || concurrency >= tasks.length) {\n // Unbounded — just Promise.all\n if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');\n return Promise.all(tasks.map((t) => t()));\n }\n\n // Bounded concurrency via semaphore\n const results: T[] = new Array(tasks.length);\n let nextIndex = 0;\n let hasError = false;\n let firstError: unknown;\n\n const run = async (): Promise<void> => {\n while (nextIndex < tasks.length) {\n if (signal?.aborted || hasError) break;\n const idx = nextIndex++;\n try {\n results[idx] = await tasks[idx]();\n } catch (err) {\n hasError = true;\n firstError = err;\n break;\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, run);\n await Promise.all(workers);\n\n if (hasError) throw firstError;\n return results;\n}\n\n/**\n * Run tasks concurrently and return all results regardless of success/failure.\n * Equivalent to `Promise.allSettled` but with optional concurrency control.\n */\nexport async function parallelSettled<T>(\n tasks: Array<() => Promise<T>>,\n options: Pick<ParallelOptions, 'concurrency' | 'signal'> = {}\n): Promise<PromiseSettledResult<T>[]> {\n const { concurrency, signal } = options;\n const wrapped = tasks.map((t) => (): Promise<PromiseSettledResult<T>> =>\n t().then(\n (value) => ({ status: 'fulfilled' as const, value }),\n (reason) => ({ status: 'rejected' as const, reason })\n )\n );\n\n return parallel(wrapped, { concurrency, signal }) as Promise<PromiseSettledResult<T>[]>;\n}\n\n// ─── sequence ────────────────────────────────────────────────────────────────\n\n/**\n * Async left-fold over an array. Like `Array.reduce` but supports async reducers\n * and can be cancelled via `AbortSignal`.\n */\nexport async function sequence<T, A>(\n items: T[],\n initial: A,\n fn: (acc: A, item: T, index: number) => Promise<A> | A,\n options: SequenceOptions = {}\n): Promise<A> {\n const { signal } = options;\n let acc = initial;\n for (let i = 0; i < items.length; i++) {\n if (signal?.aborted) throw new DOMException('Sequence aborted', 'AbortError');\n acc = await fn(acc, items[i], i);\n }\n return acc;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -118,7 +118,11 @@ var Pipeline = class _Pipeline {
|
|
|
118
118
|
const nextRecord = this.records[i + 1];
|
|
119
119
|
if (nextRecord?.isFallback) {
|
|
120
120
|
const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);
|
|
121
|
-
|
|
121
|
+
try {
|
|
122
|
+
current = await nextRecord.fn(pipelineErr, inputValue);
|
|
123
|
+
} catch (fallbackErr) {
|
|
124
|
+
throw new PipelineStepError(stepIndex, record.name, fallbackErr, inputValue);
|
|
125
|
+
}
|
|
122
126
|
i++;
|
|
123
127
|
onStepComplete?.(stepIndex, record.name, current);
|
|
124
128
|
} else {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flowx.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\nimport type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\n\n// ─── Errors ──────────────────────────────────────────────────────────────────\n\nexport class PipelineStepError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public override readonly cause: unknown,\n public readonly inputValue: unknown\n ) {\n super(\n `Pipeline failed at step ${stepIndex}${stepName ? ` (${stepName})` : ''}: ${String(cause)}`\n );\n this.name = 'PipelineStepError';\n }\n}\n\nexport class PipelineTimeoutError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public readonly timeoutMs: number\n ) {\n super(`Step ${stepIndex}${stepName ? ` (${stepName})` : ''} timed out after ${timeoutMs}ms`);\n this.name = 'PipelineTimeoutError';\n }\n}\n\n// ─── Internal Step Record ────────────────────────────────────────────────────\n\ninterface StepRecord {\n fn: StepFn<unknown, unknown>;\n name: string | undefined;\n timeoutMs: number | undefined;\n isFallback: false;\n}\n\ninterface FallbackRecord {\n fn: (error: PipelineStepError, input: unknown) => unknown | Promise<unknown>;\n isFallback: true;\n}\n\ntype AnyRecord = StepRecord | FallbackRecord;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nconst neverAborted = new AbortController().signal;\n\nfunction raceStepTimeout<T>(\n promise: Promise<T>,\n ms: number,\n index: number,\n name: string | undefined\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new PipelineTimeoutError(index, name, ms)), ms);\n promise.then(\n (v) => { clearTimeout(timer); resolve(v); },\n (e) => { clearTimeout(timer); reject(e); }\n );\n });\n}\n\nfunction normalizeStep<In, Out>(\n step: StepFn<In, Out> | NamedStep<In, Out>\n): { fn: StepFn<In, Out>; name: string | undefined; timeoutMs: number | undefined } {\n if (typeof step === 'function') {\n return { fn: step, name: undefined, timeoutMs: undefined };\n }\n return { fn: step.fn, name: step.name, timeoutMs: step.timeoutMs };\n}\n\n// ─── Pipeline ────────────────────────────────────────────────────────────────\n\n/**\n * Flowx — composable, type-safe async pipeline builder.\n *\n * Each step receives the output of the previous step as its first argument,\n * plus a `StepContext` carrying the abort signal, step index, and step name.\n *\n * @example\n * const result = await pipeline<string>()\n * .pipe(s => s.trim())\n * .pipe({ name: 'uppercase', fn: s => s.toUpperCase() })\n * .tap(s => console.log('after uppercase:', s))\n * .run(' hello ');\n */\nexport class Pipeline<TIn, TOut> {\n private readonly records: AnyRecord[];\n\n constructor(records: AnyRecord[] = []) {\n this.records = records;\n }\n\n /**\n * Add a transform step. Accepts either a plain function or a `NamedStep`\n * object with an optional per-step `timeoutMs`.\n */\n pipe<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const record: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n return new Pipeline<TIn, TNext>([...this.records, record]);\n }\n\n /**\n * Add a step with an inline fallback. If the step throws, `fallback` is\n * called with the error and the original input to that step.\n */\n pipeWithFallback<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>,\n fallback: (error: PipelineStepError, input: TOut) => TNext | Promise<TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const stepRecord: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n const fallbackRecord: FallbackRecord = {\n fn: fallback as (error: PipelineStepError, input: unknown) => unknown,\n isFallback: true,\n };\n return new Pipeline<TIn, TNext>([...this.records, stepRecord, fallbackRecord]);\n }\n\n /**\n * Add a side-effect step. Runs `fn` for its effect only; passes the current\n * value through unchanged. Errors in `fn` propagate normally.\n */\n tap(fn: (value: TOut, context: StepContext) => void | Promise<void>): Pipeline<TIn, TOut> {\n const tapStep: StepFn<TOut, TOut> = async (value, ctx) => {\n await fn(value, ctx);\n return value;\n };\n return this.pipe(tapStep);\n }\n\n async run(input: TIn, options: FlowxOptions = {}): Promise<TOut> {\n const signal = options.signal ?? neverAborted;\n const { onStepComplete, stepTimeoutMs } = options;\n\n let current: unknown = input;\n let stepIndex = 0;\n\n for (let i = 0; i < this.records.length; i++) {\n const record = this.records[i];\n\n if (record.isFallback) {\n // Fallback records are consumed inline by the preceding step handler.\n continue;\n }\n\n if (signal.aborted) {\n throw new DOMException('Pipeline aborted', 'AbortError');\n }\n\n const inputValue = current;\n const ctx: StepContext = { signal, stepIndex, stepName: record.name };\n const effectiveTimeout = record.timeoutMs ?? stepTimeoutMs;\n\n try {\n const rawResult = record.fn(current, ctx);\n let stepPromise: Promise<unknown> = rawResult instanceof Promise ? rawResult : Promise.resolve(rawResult);\n if (effectiveTimeout != null) {\n stepPromise = raceStepTimeout(stepPromise, effectiveTimeout, stepIndex, record.name);\n }\n current = await stepPromise;\n onStepComplete?.(stepIndex, record.name, current);\n } catch (err) {\n // Check if the next record is a fallback for this step.\n const nextRecord = this.records[i + 1];\n if (nextRecord?.isFallback) {\n const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);\n current = await nextRecord.fn(pipelineErr, inputValue);\n i++; // skip the fallback record in the outer loop\n onStepComplete?.(stepIndex, record.name, current);\n } else {\n throw new PipelineStepError(stepIndex, record.name, err, inputValue);\n }\n }\n\n stepIndex++;\n }\n\n return current as TOut;\n }\n\n get stepCount(): number {\n return this.records.filter((r) => !r.isFallback).length;\n }\n\n /** Returns step metadata for debugging/tooling. */\n toArray(): Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> {\n let idx = 0;\n const result: Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> = [];\n for (const r of this.records) {\n if (!r.isFallback) {\n result.push({ index: idx++, name: r.name, timeoutMs: r.timeoutMs });\n }\n }\n return result;\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/** Create a new empty pipeline starting with type `T`. */\nexport function pipeline<T>(): Pipeline<T, T> {\n return new Pipeline<T, T>();\n}\n\n// ─── parallel ────────────────────────────────────────────────────────────────\n\n/**\n * Run tasks concurrently, return results in declaration order.\n * Rejects if any task throws (like `Promise.all`).\n */\nexport async function parallel<T>(\n tasks: Array<() => Promise<T>>,\n options: ParallelOptions = {}\n): Promise<T[]> {\n const { concurrency, signal } = options;\n\n if (concurrency == null || concurrency >= tasks.length) {\n // Unbounded — just Promise.all\n if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');\n return Promise.all(tasks.map((t) => t()));\n }\n\n // Bounded concurrency via semaphore\n const results: T[] = new Array(tasks.length);\n let nextIndex = 0;\n let hasError = false;\n let firstError: unknown;\n\n const run = async (): Promise<void> => {\n while (nextIndex < tasks.length) {\n if (signal?.aborted || hasError) break;\n const idx = nextIndex++;\n try {\n results[idx] = await tasks[idx]();\n } catch (err) {\n hasError = true;\n firstError = err;\n break;\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, run);\n await Promise.all(workers);\n\n if (hasError) throw firstError;\n return results;\n}\n\n/**\n * Run tasks concurrently and return all results regardless of success/failure.\n * Equivalent to `Promise.allSettled` but with optional concurrency control.\n */\nexport async function parallelSettled<T>(\n tasks: Array<() => Promise<T>>,\n options: Pick<ParallelOptions, 'concurrency' | 'signal'> = {}\n): Promise<PromiseSettledResult<T>[]> {\n const { concurrency, signal } = options;\n const wrapped = tasks.map((t) => (): Promise<PromiseSettledResult<T>> =>\n t().then(\n (value) => ({ status: 'fulfilled' as const, value }),\n (reason) => ({ status: 'rejected' as const, reason })\n )\n );\n\n return parallel(wrapped, { concurrency, signal }) as Promise<PromiseSettledResult<T>[]>;\n}\n\n// ─── sequence ────────────────────────────────────────────────────────────────\n\n/**\n * Async left-fold over an array. Like `Array.reduce` but supports async reducers\n * and can be cancelled via `AbortSignal`.\n */\nexport async function sequence<T, A>(\n items: T[],\n initial: A,\n fn: (acc: A, item: T, index: number) => Promise<A> | A,\n options: SequenceOptions = {}\n): Promise<A> {\n const { signal } = options;\n let acc = initial;\n for (let i = 0; i < items.length; i++) {\n if (signal?.aborted) throw new DOMException('Sequence aborted', 'AbortError');\n acc = await fn(acc, items[i], i);\n }\n return acc;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/flowx.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\nimport type { StepFn, StepContext, NamedStep, FlowxOptions, ParallelOptions, SequenceOptions } from './types.js';\n\n// ─── Errors ──────────────────────────────────────────────────────────────────\n\nexport class PipelineStepError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public override readonly cause: unknown,\n public readonly inputValue: unknown\n ) {\n super(\n `Pipeline failed at step ${stepIndex}${stepName ? ` (${stepName})` : ''}: ${String(cause)}`\n );\n this.name = 'PipelineStepError';\n }\n}\n\nexport class PipelineTimeoutError extends Error {\n constructor(\n public readonly stepIndex: number,\n public readonly stepName: string | undefined,\n public readonly timeoutMs: number\n ) {\n super(`Step ${stepIndex}${stepName ? ` (${stepName})` : ''} timed out after ${timeoutMs}ms`);\n this.name = 'PipelineTimeoutError';\n }\n}\n\n// ─── Internal Step Record ────────────────────────────────────────────────────\n\ninterface StepRecord {\n fn: StepFn<unknown, unknown>;\n name: string | undefined;\n timeoutMs: number | undefined;\n isFallback: false;\n}\n\ninterface FallbackRecord {\n fn: (error: PipelineStepError, input: unknown) => unknown | Promise<unknown>;\n isFallback: true;\n}\n\ntype AnyRecord = StepRecord | FallbackRecord;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nconst neverAborted = new AbortController().signal;\n\nfunction raceStepTimeout<T>(\n promise: Promise<T>,\n ms: number,\n index: number,\n name: string | undefined\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new PipelineTimeoutError(index, name, ms)), ms);\n promise.then(\n (v) => { clearTimeout(timer); resolve(v); },\n (e) => { clearTimeout(timer); reject(e); }\n );\n });\n}\n\nfunction normalizeStep<In, Out>(\n step: StepFn<In, Out> | NamedStep<In, Out>\n): { fn: StepFn<In, Out>; name: string | undefined; timeoutMs: number | undefined } {\n if (typeof step === 'function') {\n return { fn: step, name: undefined, timeoutMs: undefined };\n }\n return { fn: step.fn, name: step.name, timeoutMs: step.timeoutMs };\n}\n\n// ─── Pipeline ────────────────────────────────────────────────────────────────\n\n/**\n * Flowx — composable, type-safe async pipeline builder.\n *\n * Each step receives the output of the previous step as its first argument,\n * plus a `StepContext` carrying the abort signal, step index, and step name.\n *\n * @example\n * const result = await pipeline<string>()\n * .pipe(s => s.trim())\n * .pipe({ name: 'uppercase', fn: s => s.toUpperCase() })\n * .tap(s => console.log('after uppercase:', s))\n * .run(' hello ');\n */\nexport class Pipeline<TIn, TOut> {\n private readonly records: AnyRecord[];\n\n constructor(records: AnyRecord[] = []) {\n this.records = records;\n }\n\n /**\n * Add a transform step. Accepts either a plain function or a `NamedStep`\n * object with an optional per-step `timeoutMs`.\n */\n pipe<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const record: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n return new Pipeline<TIn, TNext>([...this.records, record]);\n }\n\n /**\n * Add a step with an inline fallback. If the step throws, `fallback` is\n * called with the error and the original input to that step.\n */\n pipeWithFallback<TNext>(\n step: StepFn<TOut, TNext> | NamedStep<TOut, TNext>,\n fallback: (error: PipelineStepError, input: TOut) => TNext | Promise<TNext>\n ): Pipeline<TIn, TNext> {\n const { fn, name, timeoutMs } = normalizeStep(step);\n const stepRecord: StepRecord = {\n fn: fn as StepFn<unknown, unknown>,\n name,\n timeoutMs,\n isFallback: false,\n };\n const fallbackRecord: FallbackRecord = {\n fn: fallback as (error: PipelineStepError, input: unknown) => unknown,\n isFallback: true,\n };\n return new Pipeline<TIn, TNext>([...this.records, stepRecord, fallbackRecord]);\n }\n\n /**\n * Add a side-effect step. Runs `fn` for its effect only; passes the current\n * value through unchanged. Errors in `fn` propagate normally.\n */\n tap(fn: (value: TOut, context: StepContext) => void | Promise<void>): Pipeline<TIn, TOut> {\n const tapStep: StepFn<TOut, TOut> = async (value, ctx) => {\n await fn(value, ctx);\n return value;\n };\n return this.pipe(tapStep);\n }\n\n async run(input: TIn, options: FlowxOptions = {}): Promise<TOut> {\n const signal = options.signal ?? neverAborted;\n const { onStepComplete, stepTimeoutMs } = options;\n\n let current: unknown = input;\n let stepIndex = 0;\n\n for (let i = 0; i < this.records.length; i++) {\n const record = this.records[i];\n\n if (record.isFallback) {\n // Fallback records are consumed inline by the preceding step handler.\n continue;\n }\n\n if (signal.aborted) {\n throw new DOMException('Pipeline aborted', 'AbortError');\n }\n\n const inputValue = current;\n const ctx: StepContext = { signal, stepIndex, stepName: record.name };\n const effectiveTimeout = record.timeoutMs ?? stepTimeoutMs;\n\n try {\n const rawResult = record.fn(current, ctx);\n let stepPromise: Promise<unknown> = rawResult instanceof Promise ? rawResult : Promise.resolve(rawResult);\n if (effectiveTimeout != null) {\n stepPromise = raceStepTimeout(stepPromise, effectiveTimeout, stepIndex, record.name);\n }\n current = await stepPromise;\n onStepComplete?.(stepIndex, record.name, current);\n } catch (err) {\n // Check if the next record is a fallback for this step.\n const nextRecord = this.records[i + 1];\n if (nextRecord?.isFallback) {\n const pipelineErr = new PipelineStepError(stepIndex, record.name, err, inputValue);\n try {\n current = await nextRecord.fn(pipelineErr, inputValue);\n } catch (fallbackErr) {\n // Fallback itself threw — wrap so callers can distinguish it\n throw new PipelineStepError(stepIndex, record.name, fallbackErr, inputValue);\n }\n i++; // skip the fallback record in the outer loop\n onStepComplete?.(stepIndex, record.name, current);\n } else {\n throw new PipelineStepError(stepIndex, record.name, err, inputValue);\n }\n }\n\n stepIndex++;\n }\n\n return current as TOut;\n }\n\n get stepCount(): number {\n return this.records.filter((r) => !r.isFallback).length;\n }\n\n /** Returns step metadata for debugging/tooling. */\n toArray(): Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> {\n let idx = 0;\n const result: Array<{ index: number; name: string | undefined; timeoutMs: number | undefined }> = [];\n for (const r of this.records) {\n if (!r.isFallback) {\n result.push({ index: idx++, name: r.name, timeoutMs: r.timeoutMs });\n }\n }\n return result;\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/** Create a new empty pipeline starting with type `T`. */\nexport function pipeline<T>(): Pipeline<T, T> {\n return new Pipeline<T, T>();\n}\n\n// ─── parallel ────────────────────────────────────────────────────────────────\n\n/**\n * Run tasks concurrently, return results in declaration order.\n * Rejects if any task throws (like `Promise.all`).\n */\nexport async function parallel<T>(\n tasks: Array<() => Promise<T>>,\n options: ParallelOptions = {}\n): Promise<T[]> {\n const { concurrency, signal } = options;\n\n if (concurrency == null || concurrency >= tasks.length) {\n // Unbounded — just Promise.all\n if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');\n return Promise.all(tasks.map((t) => t()));\n }\n\n // Bounded concurrency via semaphore\n const results: T[] = new Array(tasks.length);\n let nextIndex = 0;\n let hasError = false;\n let firstError: unknown;\n\n const run = async (): Promise<void> => {\n while (nextIndex < tasks.length) {\n if (signal?.aborted || hasError) break;\n const idx = nextIndex++;\n try {\n results[idx] = await tasks[idx]();\n } catch (err) {\n hasError = true;\n firstError = err;\n break;\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, run);\n await Promise.all(workers);\n\n if (hasError) throw firstError;\n return results;\n}\n\n/**\n * Run tasks concurrently and return all results regardless of success/failure.\n * Equivalent to `Promise.allSettled` but with optional concurrency control.\n */\nexport async function parallelSettled<T>(\n tasks: Array<() => Promise<T>>,\n options: Pick<ParallelOptions, 'concurrency' | 'signal'> = {}\n): Promise<PromiseSettledResult<T>[]> {\n const { concurrency, signal } = options;\n const wrapped = tasks.map((t) => (): Promise<PromiseSettledResult<T>> =>\n t().then(\n (value) => ({ status: 'fulfilled' as const, value }),\n (reason) => ({ status: 'rejected' as const, reason })\n )\n );\n\n return parallel(wrapped, { concurrency, signal }) as Promise<PromiseSettledResult<T>[]>;\n}\n\n// ─── sequence ────────────────────────────────────────────────────────────────\n\n/**\n * Async left-fold over an array. Like `Array.reduce` but supports async reducers\n * and can be cancelled via `AbortSignal`.\n */\nexport async function sequence<T, A>(\n items: T[],\n initial: A,\n fn: (acc: A, item: T, index: number) => Promise<A> | A,\n options: SequenceOptions = {}\n): Promise<A> {\n const { signal } = options;\n let acc = initial;\n for (let i = 0; i < items.length; i++) {\n if (signal?.aborted) throw new DOMException('Sequence aborted', 'AbortError');\n acc = await fn(acc, items[i], i);\n }\n return acc;\n}\n"]}
|