@jagreehal/workflow 1.8.0 → 1.10.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.
@@ -0,0 +1,197 @@
1
+ import { AsyncResult } from './core.cjs';
2
+
3
+ /**
4
+ * @jagreehal/workflow/batch
5
+ *
6
+ * Batch processing utilities with progress tracking and checkpoints.
7
+ * Useful for I/O-heavy operations like generating embeddings, API calls, or database writes.
8
+ *
9
+ * Tree-shakable - only import if needed.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { processInBatches, ok, err, type AsyncResult } from '@jagreehal/workflow';
14
+ *
15
+ * // Generate embeddings in batches with progress tracking
16
+ * const embedText = async (text: string): AsyncResult<number[], 'EMBED_ERROR'> => {
17
+ * const response = await fetch('/api/embed', { body: text });
18
+ * return response.ok ? ok(await response.json()) : err('EMBED_ERROR');
19
+ * };
20
+ *
21
+ * const checkpoint = async (): AsyncResult<void, 'DB_ERROR'> => {
22
+ * await db.checkpoint();
23
+ * return ok(undefined);
24
+ * };
25
+ *
26
+ * const result = await processInBatches(
27
+ * texts,
28
+ * embedText,
29
+ * { batchSize: 20, concurrency: 3, batchDelayMs: 50 },
30
+ * {
31
+ * afterBatch: checkpoint,
32
+ * onProgress: (p) => console.log(`${p.percent}% complete`),
33
+ * }
34
+ * );
35
+ * ```
36
+ */
37
+
38
+ /**
39
+ * Configuration for batch processing.
40
+ */
41
+ interface BatchConfig {
42
+ /**
43
+ * Number of items to process per batch.
44
+ * Lower values = more frequent checkpoints, less memory pressure.
45
+ * Higher values = fewer checkpoints, more throughput.
46
+ * @default 20
47
+ */
48
+ batchSize: number;
49
+ /**
50
+ * Maximum concurrent operations within a batch.
51
+ * Controls parallelism for I/O-bound operations.
52
+ * @default 5
53
+ */
54
+ concurrency: number;
55
+ /**
56
+ * Delay in milliseconds between batches.
57
+ * Provides backpressure to allow event loop breathing and GC.
58
+ * Set to 0 for no delay.
59
+ * @default 0
60
+ */
61
+ batchDelayMs?: number;
62
+ }
63
+ /**
64
+ * Progress information for batch processing.
65
+ */
66
+ interface BatchProgress {
67
+ /** Current batch number (1-indexed) */
68
+ batch: number;
69
+ /** Total number of batches */
70
+ totalBatches: number;
71
+ /** Items processed so far */
72
+ processed: number;
73
+ /** Total items to process */
74
+ total: number;
75
+ /** Percentage complete (0-100) */
76
+ percent: number;
77
+ }
78
+ /**
79
+ * Options for batch processing.
80
+ */
81
+ interface BatchOptions<E> {
82
+ /**
83
+ * Hook called after each batch completes successfully.
84
+ * Useful for checkpointing (e.g., flushing database WAL).
85
+ * If this returns an error, processing stops.
86
+ */
87
+ afterBatch?: () => AsyncResult<void, E>;
88
+ /**
89
+ * Callback for progress reporting.
90
+ * Called after each batch completes.
91
+ */
92
+ onProgress?: (progress: BatchProgress) => void;
93
+ }
94
+ /**
95
+ * Error that occurred during batch processing.
96
+ */
97
+ interface BatchProcessingError<E> {
98
+ type: "BATCH_PROCESSING_ERROR";
99
+ /** The underlying error from the process function or afterBatch hook */
100
+ error: E;
101
+ /** Index of the item that failed (if process function failed) */
102
+ itemIndex?: number;
103
+ /** Batch number where the error occurred (1-indexed) */
104
+ batchNumber: number;
105
+ }
106
+ /**
107
+ * Error for invalid batch configuration.
108
+ */
109
+ interface InvalidBatchConfigError {
110
+ type: "INVALID_BATCH_CONFIG";
111
+ /** Description of what's wrong with the config */
112
+ reason: string;
113
+ /** The invalid field name */
114
+ field: "batchSize" | "concurrency";
115
+ /** The invalid value */
116
+ value: number;
117
+ }
118
+ /**
119
+ * Type guard for BatchProcessingError.
120
+ */
121
+ declare function isBatchProcessingError<E>(error: unknown): error is BatchProcessingError<E>;
122
+ /**
123
+ * Type guard for InvalidBatchConfigError.
124
+ */
125
+ declare function isInvalidBatchConfigError(error: unknown): error is InvalidBatchConfigError;
126
+ /**
127
+ * Process items in batches with bounded concurrency, progress tracking, and checkpoints.
128
+ *
129
+ * This function is designed for I/O-heavy operations where you need to:
130
+ * - Control memory pressure by processing in smaller batches
131
+ * - Checkpoint progress (e.g., flush database WAL) between batches
132
+ * - Add backpressure delays to allow GC and event loop breathing
133
+ * - Report progress to the user
134
+ *
135
+ * Processing stops on the first error encountered.
136
+ *
137
+ * @param items - Array of items to process
138
+ * @param process - Function to process each item, returns AsyncResult
139
+ * @param config - Batch configuration (size, concurrency, delay)
140
+ * @param options - Optional hooks for afterBatch and progress reporting
141
+ * @returns AsyncResult containing all processed results, or an error
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * // Basic usage
146
+ * const results = await processInBatches(
147
+ * items,
148
+ * async (item) => ok(await transform(item)),
149
+ * { batchSize: 10, concurrency: 3 }
150
+ * );
151
+ *
152
+ * // With checkpoints and progress
153
+ * const results = await processInBatches(
154
+ * items,
155
+ * processItem,
156
+ * { batchSize: 20, concurrency: 5, batchDelayMs: 50 },
157
+ * {
158
+ * afterBatch: () => db.checkpoint(),
159
+ * onProgress: (p) => updateProgressBar(p.percent),
160
+ * }
161
+ * );
162
+ * ```
163
+ */
164
+ declare function processInBatches<T, R, E>(items: readonly T[], process: (item: T, index: number) => AsyncResult<R, E>, config: BatchConfig, options?: BatchOptions<E>): AsyncResult<R[], E | BatchProcessingError<E> | InvalidBatchConfigError>;
165
+ /**
166
+ * Preset configurations for common batch processing scenarios.
167
+ */
168
+ declare const batchPresets: {
169
+ /**
170
+ * Conservative settings for memory-constrained environments.
171
+ * Good for WASM, serverless, or when processing large payloads.
172
+ */
173
+ readonly conservative: {
174
+ batchSize: number;
175
+ concurrency: number;
176
+ batchDelayMs: number;
177
+ };
178
+ /**
179
+ * Balanced settings for typical workloads.
180
+ */
181
+ readonly balanced: {
182
+ batchSize: number;
183
+ concurrency: number;
184
+ batchDelayMs: number;
185
+ };
186
+ /**
187
+ * Aggressive settings for maximum throughput.
188
+ * Use when memory is not a concern.
189
+ */
190
+ readonly aggressive: {
191
+ batchSize: number;
192
+ concurrency: number;
193
+ batchDelayMs: number;
194
+ };
195
+ };
196
+
197
+ export { type BatchConfig, type BatchOptions, type BatchProcessingError, type BatchProgress, type InvalidBatchConfigError, batchPresets, isBatchProcessingError, isInvalidBatchConfigError, processInBatches };
@@ -0,0 +1,197 @@
1
+ import { AsyncResult } from './core.js';
2
+
3
+ /**
4
+ * @jagreehal/workflow/batch
5
+ *
6
+ * Batch processing utilities with progress tracking and checkpoints.
7
+ * Useful for I/O-heavy operations like generating embeddings, API calls, or database writes.
8
+ *
9
+ * Tree-shakable - only import if needed.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { processInBatches, ok, err, type AsyncResult } from '@jagreehal/workflow';
14
+ *
15
+ * // Generate embeddings in batches with progress tracking
16
+ * const embedText = async (text: string): AsyncResult<number[], 'EMBED_ERROR'> => {
17
+ * const response = await fetch('/api/embed', { body: text });
18
+ * return response.ok ? ok(await response.json()) : err('EMBED_ERROR');
19
+ * };
20
+ *
21
+ * const checkpoint = async (): AsyncResult<void, 'DB_ERROR'> => {
22
+ * await db.checkpoint();
23
+ * return ok(undefined);
24
+ * };
25
+ *
26
+ * const result = await processInBatches(
27
+ * texts,
28
+ * embedText,
29
+ * { batchSize: 20, concurrency: 3, batchDelayMs: 50 },
30
+ * {
31
+ * afterBatch: checkpoint,
32
+ * onProgress: (p) => console.log(`${p.percent}% complete`),
33
+ * }
34
+ * );
35
+ * ```
36
+ */
37
+
38
+ /**
39
+ * Configuration for batch processing.
40
+ */
41
+ interface BatchConfig {
42
+ /**
43
+ * Number of items to process per batch.
44
+ * Lower values = more frequent checkpoints, less memory pressure.
45
+ * Higher values = fewer checkpoints, more throughput.
46
+ * @default 20
47
+ */
48
+ batchSize: number;
49
+ /**
50
+ * Maximum concurrent operations within a batch.
51
+ * Controls parallelism for I/O-bound operations.
52
+ * @default 5
53
+ */
54
+ concurrency: number;
55
+ /**
56
+ * Delay in milliseconds between batches.
57
+ * Provides backpressure to allow event loop breathing and GC.
58
+ * Set to 0 for no delay.
59
+ * @default 0
60
+ */
61
+ batchDelayMs?: number;
62
+ }
63
+ /**
64
+ * Progress information for batch processing.
65
+ */
66
+ interface BatchProgress {
67
+ /** Current batch number (1-indexed) */
68
+ batch: number;
69
+ /** Total number of batches */
70
+ totalBatches: number;
71
+ /** Items processed so far */
72
+ processed: number;
73
+ /** Total items to process */
74
+ total: number;
75
+ /** Percentage complete (0-100) */
76
+ percent: number;
77
+ }
78
+ /**
79
+ * Options for batch processing.
80
+ */
81
+ interface BatchOptions<E> {
82
+ /**
83
+ * Hook called after each batch completes successfully.
84
+ * Useful for checkpointing (e.g., flushing database WAL).
85
+ * If this returns an error, processing stops.
86
+ */
87
+ afterBatch?: () => AsyncResult<void, E>;
88
+ /**
89
+ * Callback for progress reporting.
90
+ * Called after each batch completes.
91
+ */
92
+ onProgress?: (progress: BatchProgress) => void;
93
+ }
94
+ /**
95
+ * Error that occurred during batch processing.
96
+ */
97
+ interface BatchProcessingError<E> {
98
+ type: "BATCH_PROCESSING_ERROR";
99
+ /** The underlying error from the process function or afterBatch hook */
100
+ error: E;
101
+ /** Index of the item that failed (if process function failed) */
102
+ itemIndex?: number;
103
+ /** Batch number where the error occurred (1-indexed) */
104
+ batchNumber: number;
105
+ }
106
+ /**
107
+ * Error for invalid batch configuration.
108
+ */
109
+ interface InvalidBatchConfigError {
110
+ type: "INVALID_BATCH_CONFIG";
111
+ /** Description of what's wrong with the config */
112
+ reason: string;
113
+ /** The invalid field name */
114
+ field: "batchSize" | "concurrency";
115
+ /** The invalid value */
116
+ value: number;
117
+ }
118
+ /**
119
+ * Type guard for BatchProcessingError.
120
+ */
121
+ declare function isBatchProcessingError<E>(error: unknown): error is BatchProcessingError<E>;
122
+ /**
123
+ * Type guard for InvalidBatchConfigError.
124
+ */
125
+ declare function isInvalidBatchConfigError(error: unknown): error is InvalidBatchConfigError;
126
+ /**
127
+ * Process items in batches with bounded concurrency, progress tracking, and checkpoints.
128
+ *
129
+ * This function is designed for I/O-heavy operations where you need to:
130
+ * - Control memory pressure by processing in smaller batches
131
+ * - Checkpoint progress (e.g., flush database WAL) between batches
132
+ * - Add backpressure delays to allow GC and event loop breathing
133
+ * - Report progress to the user
134
+ *
135
+ * Processing stops on the first error encountered.
136
+ *
137
+ * @param items - Array of items to process
138
+ * @param process - Function to process each item, returns AsyncResult
139
+ * @param config - Batch configuration (size, concurrency, delay)
140
+ * @param options - Optional hooks for afterBatch and progress reporting
141
+ * @returns AsyncResult containing all processed results, or an error
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * // Basic usage
146
+ * const results = await processInBatches(
147
+ * items,
148
+ * async (item) => ok(await transform(item)),
149
+ * { batchSize: 10, concurrency: 3 }
150
+ * );
151
+ *
152
+ * // With checkpoints and progress
153
+ * const results = await processInBatches(
154
+ * items,
155
+ * processItem,
156
+ * { batchSize: 20, concurrency: 5, batchDelayMs: 50 },
157
+ * {
158
+ * afterBatch: () => db.checkpoint(),
159
+ * onProgress: (p) => updateProgressBar(p.percent),
160
+ * }
161
+ * );
162
+ * ```
163
+ */
164
+ declare function processInBatches<T, R, E>(items: readonly T[], process: (item: T, index: number) => AsyncResult<R, E>, config: BatchConfig, options?: BatchOptions<E>): AsyncResult<R[], E | BatchProcessingError<E> | InvalidBatchConfigError>;
165
+ /**
166
+ * Preset configurations for common batch processing scenarios.
167
+ */
168
+ declare const batchPresets: {
169
+ /**
170
+ * Conservative settings for memory-constrained environments.
171
+ * Good for WASM, serverless, or when processing large payloads.
172
+ */
173
+ readonly conservative: {
174
+ batchSize: number;
175
+ concurrency: number;
176
+ batchDelayMs: number;
177
+ };
178
+ /**
179
+ * Balanced settings for typical workloads.
180
+ */
181
+ readonly balanced: {
182
+ batchSize: number;
183
+ concurrency: number;
184
+ batchDelayMs: number;
185
+ };
186
+ /**
187
+ * Aggressive settings for maximum throughput.
188
+ * Use when memory is not a concern.
189
+ */
190
+ readonly aggressive: {
191
+ batchSize: number;
192
+ concurrency: number;
193
+ batchDelayMs: number;
194
+ };
195
+ };
196
+
197
+ export { type BatchConfig, type BatchOptions, type BatchProcessingError, type BatchProgress, type InvalidBatchConfigError, batchPresets, isBatchProcessingError, isInvalidBatchConfigError, processInBatches };
package/dist/batch.js ADDED
@@ -0,0 +1,2 @@
1
+ var V=e=>({ok:!0,value:e}),b=(e,y)=>({ok:!1,error:e,...y?.cause!==void 0?{cause:y.cause}:{}});var ae=e=>typeof e=="object"&&e!==null&&e.type==="UNEXPECTED_ERROR",z=Symbol.for("step_timeout_marker");function ee(e){return typeof e!="object"||e===null?!1:e.type==="STEP_TIMEOUT"?!0:z in e}function ie(e){if(!(typeof e!="object"||e===null)){if(e.type==="STEP_TIMEOUT"){let y=e;return{timeoutMs:y.timeoutMs,stepName:y.stepName,stepKey:y.stepKey,attempt:y.attempt}}if(z in e)return e[z]}}var re=Symbol("early-exit");function ce(e,y){return{[re]:!0,error:e,meta:y}}function le(e){return typeof e=="object"&&e!==null&&e[re]===!0}var oe=Symbol("mapper-exception");function pe(e){return{[oe]:!0,thrown:e}}function Ee(e){return typeof e=="object"&&e!==null&&e[oe]===!0}function ye(e){return typeof e=="string"?{name:e}:e??{}}function Y(e,y){let{backoff:f,initialDelay:g,maxDelay:R,jitter:x}=y,c;switch(f){case"fixed":c=g;break;case"linear":c=g*e;break;case"exponential":c=g*Math.pow(2,e-1);break}if(c=Math.min(c,R),x){let t=c*.25*Math.random();c=c+t}return Math.floor(c)}function J(e){return new Promise(y=>setTimeout(y,e))}var te=Symbol("timeout");async function me(e,y,f){let g=new AbortController,R=y.error??{type:"STEP_TIMEOUT",stepName:f.name,stepKey:f.key,timeoutMs:y.ms,attempt:f.attempt},x,c=new Promise((v,u)=>{x=setTimeout(()=>{g.abort(),u({[te]:!0,error:R})},y.ms)}),t;y.signal?t=Promise.resolve(e(g.signal)):t=Promise.resolve(e());try{return await Promise.race([t,c])}catch(v){if(typeof v=="object"&&v!==null&&v[te]===!0){let u=v.error;if(typeof u=="object"&&u!==null&&u.type!=="STEP_TIMEOUT"){let h={timeoutMs:y.ms,stepName:f.name,stepKey:f.key,attempt:f.attempt};z in u?u[z]=h:Object.defineProperty(u,z,{value:h,enumerable:!1,writable:!0,configurable:!1})}throw u}throw v}finally{clearTimeout(x)}}var B={backoff:"exponential",initialDelay:100,maxDelay:3e4,jitter:!0,retryOn:()=>!0,onRetry:()=>{}};async function ne(e,y){let{onError:f,onEvent:g,catchUnexpected:R,workflowId:x,context:c}=y&&typeof y=="object"?y:{},t=x??crypto.randomUUID(),v=!f&&!R,u=[],h=0,U=n=>n??`step_${++h}`,o=n=>{let S=n.context!==void 0||c===void 0?n:{...n,context:c};if(S.type==="step_success"){let P=S.stepId;for(let K=u.length-1;K>=0;K--){let q=u[K];if(q.type==="race"&&!q.winnerId){q.winnerId=P;break}}}g?.(S,c)},A=ce,F=n=>le(n),N=(n,S)=>v?S?.origin==="result"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:n,...S.resultCause!==void 0?{cause:S.resultCause}:{}}}:S?.origin==="throw"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"throw",error:n,thrown:S.thrown}}:{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:n}}:n,X=n=>({type:"UNEXPECTED_ERROR",cause:n.meta.origin==="result"?{type:"STEP_FAILURE",origin:"result",error:n.error,...n.meta.resultCause!==void 0?{cause:n.meta.resultCause}:{}}:{type:"STEP_FAILURE",origin:"throw",error:n.error,thrown:n.meta.thrown}});try{let S=function(m,i){let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1;u.push({scopeId:a,type:"parallel"});let k=()=>{if(s)return;s=!0;let p=u.findIndex(l=>l.scopeId===a);p!==-1&&u.splice(p,1),o({type:"scope_end",workflowId:t,scopeId:a,ts:Date.now(),durationMs:performance.now()-r})};o({type:"scope_start",workflowId:t,scopeId:a,scopeType:"parallel",name:m,ts:Date.now()});try{let p=await i();if(k(),!p.ok)throw f?.(p.error,m,c),A(p.error,{origin:"result",resultCause:p.cause});return p.value}catch(p){throw k(),p}})()},P=function(m,i){let a=Object.keys(m),r=i.name??`Parallel(${a.join(", ")})`,s=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let k=performance.now(),p=!1;u.push({scopeId:s,type:"parallel"});let l=()=>{if(p)return;p=!0;let w=u.findIndex(d=>d.scopeId===s);w!==-1&&u.splice(w,1),o({type:"scope_end",workflowId:t,scopeId:s,ts:Date.now(),durationMs:performance.now()-k})};o({type:"scope_start",workflowId:t,scopeId:s,scopeType:"parallel",name:r,ts:Date.now()});try{let w=await new Promise(I=>{if(a.length===0){I([]);return}let M=!1,C=a.length,Q=new Array(a.length);for(let O=0;O<a.length;O++){let j=a[O],G=O;Promise.resolve(m[j]()).catch(T=>b({type:"PROMISE_REJECTED",cause:T},{cause:{type:"PROMISE_REJECTION",reason:T}})).then(T=>{if(!M){if(!T.ok){M=!0,I([{key:j,result:T}]);return}Q[G]={key:j,result:T},C--,C===0&&I(Q)}})}});l();let d={};for(let{key:I,result:M}of w){if(!M.ok)throw f?.(M.error,I,c),A(M.error,{origin:"result",resultCause:M.cause});d[I]=M.value}return d}catch(w){throw l(),w}})()};var Z=S,D=P;let n=(m,i)=>(async()=>{let a=ye(i),{name:r,key:s,retry:k,timeout:p}=a,l=U(s),w=g,d=w?performance.now():0;if(!(typeof m=="function")){if(k&&k.attempts>1)throw new Error("step: retry options require a function operation. Direct Promise/Result values cannot be re-executed on retry. Wrap your operation in a function: step(() => yourOperation, { retry: {...} })");if(p)throw new Error("step: timeout options require a function operation. Direct Promise/Result values cannot be wrapped with timeout after they've started. Wrap your operation in a function: step(() => yourOperation, { timeout: {...} })")}let C={attempts:Math.max(1,k?.attempts??1),backoff:k?.backoff??B.backoff,initialDelay:k?.initialDelay??B.initialDelay,maxDelay:k?.maxDelay??B.maxDelay,jitter:k?.jitter??B.jitter,retryOn:k?.retryOn??B.retryOn,onRetry:k?.onRetry??B.onRetry};g&&o({type:"step_start",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now()});let Q;for(let T=1;T<=C.attempts;T++){let ue=w?performance.now():0;try{let E;if(typeof m=="function"?p?E=await me(m,p,{name:r,key:s,attempt:T}):E=await m():E=await m,E.ok){let L=performance.now()-d;return o({type:"step_success",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:L}),s&&o({type:"step_complete",workflowId:t,stepKey:s,name:r,ts:Date.now(),durationMs:L,result:E}),E.value}if(Q=E,T<C.attempts&&C.retryOn(E.error,T)){let L=Y(T,C);o({type:"step_retry",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),attempt:T+1,maxAttempts:C.attempts,delayMs:L,error:E.error}),C.onRetry(E.error,T,L),await J(L);continue}C.attempts>1&&o({type:"step_retries_exhausted",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-d,attempts:T,lastError:E.error});break}catch(E){let L=performance.now()-ue;if(F(E))throw o({type:"step_aborted",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:L}),E;if(ee(E)){let _=ie(E),$=p?.ms??_?.timeoutMs??0;if(o({type:"step_timeout",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),timeoutMs:$,attempt:T}),T<C.attempts&&C.retryOn(E,T)){let H=Y(T,C);o({type:"step_retry",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),attempt:T+1,maxAttempts:C.attempts,delayMs:H,error:E}),C.onRetry(E,T,H),await J(H);continue}C.attempts>1&&o({type:"step_retries_exhausted",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-d,attempts:T,lastError:E})}if(T<C.attempts&&C.retryOn(E,T)){let _=Y(T,C);o({type:"step_retry",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),attempt:T+1,maxAttempts:C.attempts,delayMs:_,error:E}),C.onRetry(E,T,_),await J(_);continue}C.attempts>1&&!ee(E)&&o({type:"step_retries_exhausted",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-d,attempts:T,lastError:E});let W=performance.now()-d;if(R){let _;try{_=R(E)}catch($){throw pe($)}throw o({type:"step_error",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:W,error:_}),s&&o({type:"step_complete",workflowId:t,stepKey:s,name:r,ts:Date.now(),durationMs:W,result:b(_,{cause:E}),meta:{origin:"throw",thrown:E}}),f?.(_,r,c),A(_,{origin:"throw",thrown:E})}else{let _={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:E}};throw o({type:"step_error",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:W,error:_}),s&&o({type:"step_complete",workflowId:t,stepKey:s,name:r,ts:Date.now(),durationMs:W,result:b(_,{cause:E}),meta:{origin:"throw",thrown:E}}),E}}}let O=Q,j=performance.now()-d,G=N(O.error,{origin:"result",resultCause:O.cause});throw o({type:"step_error",workflowId:t,stepId:l,stepKey:s,name:r,ts:Date.now(),durationMs:j,error:G}),s&&o({type:"step_complete",workflowId:t,stepKey:s,name:r,ts:Date.now(),durationMs:j,result:O,meta:{origin:"result",resultCause:O.cause}}),f?.(O.error,r,c),A(O.error,{origin:"result",resultCause:O.cause})})();n.try=(m,i)=>{let a=i.name,r=i.key,s=U(r),k="error"in i?()=>i.error:i.onError,p=g;return(async()=>{let l=p?performance.now():0;g&&o({type:"step_start",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now()});try{let w=await m(),d=performance.now()-l;return o({type:"step_success",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:d}),r&&o({type:"step_complete",workflowId:t,stepKey:r,name:a,ts:Date.now(),durationMs:d,result:V(w)}),w}catch(w){let d=k(w),I=performance.now()-l,M=N(d,{origin:"throw",thrown:w});throw o({type:"step_error",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:I,error:M}),r&&o({type:"step_complete",workflowId:t,stepKey:r,name:a,ts:Date.now(),durationMs:I,result:b(d,{cause:w}),meta:{origin:"throw",thrown:w}}),f?.(d,a,c),A(d,{origin:"throw",thrown:w})}})()},n.fromResult=(m,i)=>{let a=i.name,r=i.key,s=U(r),k="error"in i?()=>i.error:i.onError,p=g;return(async()=>{let l=p?performance.now():0;g&&o({type:"step_start",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now()});let w=await m();if(w.ok){let d=performance.now()-l;return o({type:"step_success",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:d}),r&&o({type:"step_complete",workflowId:t,stepKey:r,name:a,ts:Date.now(),durationMs:d,result:V(w.value)}),w.value}else{let d=k(w.error),I=performance.now()-l,M=N(d,{origin:"result",resultCause:w.error});throw o({type:"step_error",workflowId:t,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:I,error:M}),r&&o({type:"step_complete",workflowId:t,stepKey:r,name:a,ts:Date.now(),durationMs:I,result:b(d,{cause:w.error}),meta:{origin:"result",resultCause:w.error}}),f?.(d,a,c),A(d,{origin:"result",resultCause:w.error})}})()},n.retry=(m,i)=>n(m,{name:i.name,key:i.key,retry:{attempts:i.attempts,backoff:i.backoff,initialDelay:i.initialDelay,maxDelay:i.maxDelay,jitter:i.jitter,retryOn:i.retryOn,onRetry:i.onRetry},timeout:i.timeout}),n.withTimeout=(m,i)=>n(m,{name:i.name,key:i.key,timeout:i}),n.parallel=((...m)=>{if(typeof m[0]=="string"){let i=m[0],a=m[1];return S(i,a)}else{let i=m[0],a=m[1]??{};return P(i,a)}}),n.race=(m,i)=>{let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1,k={scopeId:a,type:"race",winnerId:void 0};u.push(k);let p=()=>{if(s)return;s=!0;let l=u.findIndex(w=>w.scopeId===a);l!==-1&&u.splice(l,1),o({type:"scope_end",workflowId:t,scopeId:a,ts:Date.now(),durationMs:performance.now()-r,winnerId:k.winnerId})};o({type:"scope_start",workflowId:t,scopeId:a,scopeType:"race",name:m,ts:Date.now()});try{let l=await i();if(p(),!l.ok)throw f?.(l.error,m,c),A(l.error,{origin:"result",resultCause:l.cause});return l.value}catch(l){throw p(),l}})()},n.allSettled=(m,i)=>{let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1;u.push({scopeId:a,type:"allSettled"});let k=()=>{if(s)return;s=!0;let p=u.findIndex(l=>l.scopeId===a);p!==-1&&u.splice(p,1),o({type:"scope_end",workflowId:t,scopeId:a,ts:Date.now(),durationMs:performance.now()-r})};o({type:"scope_start",workflowId:t,scopeId:a,scopeType:"allSettled",name:m,ts:Date.now()});try{let p=await i();if(k(),!p.ok)throw f?.(p.error,m,c),A(p.error,{origin:"result",resultCause:p.cause});return p.value}catch(p){throw k(),p}})()};let q=await e(n);return V(q)}catch(n){if(Ee(n))throw n.thrown;if(F(n)){let P=n.meta.origin==="throw"?n.meta.thrown:n.meta.resultCause;if(R||f)return b(n.error,{cause:P});if(ae(n.error))return b(n.error,{cause:P});let K=X(n);return b(K,{cause:P})}if(R){let P=R(n);return f?.(P,"unexpected",c),b(P,{cause:n})}let S={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:n}};return f?.(S,"unexpected",c),b(S,{cause:n})}}ne.strict=(e,y)=>ne(e,y);function we(e){return typeof e=="object"&&e!==null&&e.type==="QUEUE_FULL"}function se(e,y){let{maxConcurrent:f,strategy:g="queue",maxQueueSize:R=1/0}=y,x=0,c=[];async function t(){if(x<f){x++;return}if(g==="reject")throw{type:"QUEUE_FULL",limiterName:e,queueSize:c.length,maxQueueSize:R};if(c.length>=R)throw{type:"QUEUE_FULL",limiterName:e,queueSize:c.length,maxQueueSize:R};return new Promise((u,h)=>{c.push({resolve:u,reject:h})})}function v(){x--,c.length>0&&x<f&&(x++,c.shift()?.resolve())}return{async execute(u){await t();try{return await u()}finally{v()}},async executeAll(u){let h=new Array(u.length),U=[];for(let o=0;o<u.length;o++){let A=o,F=this.execute(u[A]).then(N=>{h[A]=N});U.push(F)}return await Promise.all(U),h},async executeResult(u){try{await t()}catch(h){if(we(h))return b(h);throw h}try{return await u()}finally{v()}},getStats(){return{activeCount:x,maxConcurrent:f,queueSize:c.length,maxQueueSize:R}},reset(){for(x=0;c.length>0;)c.shift()?.reject(new Error("Limiter reset"))}}}function Te(e){return typeof e=="object"&&e!==null&&e.type==="BATCH_PROCESSING_ERROR"}function ge(e){return typeof e=="object"&&e!==null&&e.type==="INVALID_BATCH_CONFIG"}var fe=e=>new Promise(y=>setTimeout(y,e));async function Se(e,y,f,g){let{batchSize:R,concurrency:x,batchDelayMs:c=0}=f,{afterBatch:t,onProgress:v}=g??{};if(!Number.isInteger(R)||R<1)return b({type:"INVALID_BATCH_CONFIG",reason:"batchSize must be a positive integer",field:"batchSize",value:R});if(!Number.isInteger(x)||x<1)return b({type:"INVALID_BATCH_CONFIG",reason:"concurrency must be a positive integer",field:"concurrency",value:x});if(e.length===0)return V([]);let u=[],h=Math.ceil(e.length/R),U=se("batch-processor",{maxConcurrent:x});for(let o=0;o<h;o++){let A=o*R,F=Math.min(A+R,e.length),N=e.slice(A,F),X=o+1,Z=N.map((D,n)=>async()=>{let S=A+n,P=await y(D,S);if(!P.ok)throw{type:"BATCH_PROCESSING_ERROR",error:P.error,itemIndex:S,batchNumber:X};return P.value});try{let D=await U.executeAll(Z);u.push(...D)}catch(D){if(Te(D))return b(D);throw D}if(v?.({batch:X,totalBatches:h,processed:u.length,total:e.length,percent:Math.round(u.length/e.length*100)}),t){let D=await t();if(!D.ok)return b({type:"BATCH_PROCESSING_ERROR",error:D.error,batchNumber:X})}c>0&&o<h-1&&await fe(c)}return V(u)}var he={conservative:{batchSize:20,concurrency:3,batchDelayMs:50},balanced:{batchSize:50,concurrency:5,batchDelayMs:10},aggressive:{batchSize:100,concurrency:10,batchDelayMs:0}};export{he as batchPresets,Te as isBatchProcessingError,ge as isInvalidBatchConfigError,Se as processInBatches};
2
+ //# sourceMappingURL=batch.js.map