@output.ai/core 0.5.1 → 0.5.2

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.
@@ -1,79 +1,495 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { z } from 'zod';
3
3
 
4
+ const inWorkflowContextMock = vi.hoisted( () => vi.fn( () => true ) );
4
5
  const traceDestinationsStepMock = vi.fn().mockResolvedValue( { local: '/tmp/trace' } );
5
- const proxyActivitiesMock = vi.fn( () => new Proxy( {}, {
6
- get: ( _, prop ) => {
7
- if ( prop === '__internal#getTraceDestinations' ) {
8
- return traceDestinationsStepMock;
6
+ const executeChildMock = vi.fn().mockResolvedValue( undefined );
7
+ const continueAsNewMock = vi.fn().mockResolvedValue( undefined );
8
+
9
+ const createStepsProxy = ( stepSpy = vi.fn() ) =>
10
+ new Proxy( {}, {
11
+ get: ( _, prop ) => {
12
+ if ( prop === '__internal#getTraceDestinations' ) {
13
+ return traceDestinationsStepMock;
14
+ }
15
+ if ( typeof prop === 'string' && ( prop.includes( '#' ) ) ) {
16
+ return stepSpy;
17
+ }
18
+ return vi.fn();
9
19
  }
10
- return vi.fn();
11
- }
12
- } ) );
13
- const workflowInfoMock = vi.fn( () => ( {
20
+ } );
21
+
22
+ const stepSpyRef = { current: vi.fn().mockResolvedValue( {} ) };
23
+ const proxyActivitiesMock = vi.fn( () => {
24
+ stepSpyRef.current = vi.fn().mockResolvedValue( {} );
25
+ return createStepsProxy( stepSpyRef.current );
26
+ } );
27
+
28
+ const workflowInfoReturn = {
14
29
  workflowId: 'wf-test-123',
15
30
  workflowType: 'test_wf',
16
31
  memo: {},
17
32
  startTime: new Date( '2025-01-01T00:00:00Z' ),
18
33
  continueAsNewSuggested: false
19
- } ) );
34
+ };
35
+ const workflowInfoMock = vi.fn( () => ( { ...workflowInfoReturn } ) );
20
36
 
21
37
  vi.mock( '@temporalio/workflow', () => ( {
22
- proxyActivities: proxyActivitiesMock,
23
- inWorkflowContext: () => true,
24
- executeChild: vi.fn(),
38
+ proxyActivities: ( ...args ) => proxyActivitiesMock( ...args ),
39
+ inWorkflowContext: inWorkflowContextMock,
40
+ executeChild: ( ...args ) => executeChildMock( ...args ),
25
41
  workflowInfo: workflowInfoMock,
26
- uuid4: () => 'uuid-mock',
42
+ uuid4: () => '550e8400e29b41d4a716446655440000',
27
43
  ParentClosePolicy: { TERMINATE: 'TERMINATE', ABANDON: 'ABANDON' },
28
- continueAsNew: vi.fn()
44
+ continueAsNew: continueAsNewMock
29
45
  } ) );
30
46
 
31
- vi.mock( '#consts', () => ( {
32
- SHARED_STEP_PREFIX: '__shared',
33
- ACTIVITY_GET_TRACE_DESTINATIONS: '__internal#getTraceDestinations',
34
- METADATA_ACCESS_SYMBOL: Symbol( 'metadata' )
35
- } ) );
47
+ vi.mock( '#consts', async importOriginal => {
48
+ const actual = await importOriginal();
49
+ return {
50
+ ...actual,
51
+ SHARED_STEP_PREFIX: '__shared',
52
+ ACTIVITY_GET_TRACE_DESTINATIONS: '__internal#getTraceDestinations'
53
+ };
54
+ } );
36
55
 
37
56
  describe( 'workflow()', () => {
38
57
  beforeEach( () => {
39
58
  vi.clearAllMocks();
40
- workflowInfoMock.mockReturnValue( {
41
- workflowId: 'wf-test-123',
42
- workflowType: 'test_wf',
43
- memo: {},
44
- startTime: new Date( '2025-01-01T00:00:00Z' ),
45
- continueAsNewSuggested: false
59
+ inWorkflowContextMock.mockReturnValue( true );
60
+ workflowInfoMock.mockReturnValue( { ...workflowInfoReturn } );
61
+ workflowInfoReturn.memo = {};
62
+ proxyActivitiesMock.mockImplementation( () => {
63
+ stepSpyRef.current = vi.fn().mockResolvedValue( {} );
64
+ return createStepsProxy( stepSpyRef.current );
65
+ } );
66
+ } );
67
+
68
+ describe( 'options and defaults', () => {
69
+ it( 'does not throw when options is omitted (disableTrace defaults to false)', async () => {
70
+ const { workflow } = await import( './workflow.js' );
71
+
72
+ const wf = workflow( {
73
+ name: 'no_options_wf',
74
+ description: 'Workflow without options',
75
+ inputSchema: z.object( { value: z.string() } ),
76
+ outputSchema: z.object( { value: z.string() } ),
77
+ fn: async ( { value } ) => ( { value } )
78
+ } );
79
+
80
+ const result = await wf( { value: 'hello' } );
81
+ expect( result.output ).toEqual( { value: 'hello' } );
82
+ } );
83
+
84
+ it( 'respects disableTrace: true when options is provided', async () => {
85
+ const { workflow } = await import( './workflow.js' );
86
+
87
+ const wf = workflow( {
88
+ name: 'trace_disabled_wf',
89
+ description: 'Workflow with tracing disabled',
90
+ inputSchema: z.object( { value: z.string() } ),
91
+ outputSchema: z.object( { value: z.string() } ),
92
+ options: { disableTrace: true },
93
+ fn: async ( { value } ) => ( { value } )
94
+ } );
95
+
96
+ const result = await wf( { value: 'hello' } );
97
+ expect( result.output ).toEqual( { value: 'hello' } );
98
+ } );
99
+
100
+ it( 'merges custom activityOptions with defaults via deepMerge', async () => {
101
+ const { workflow } = await import( './workflow.js' );
102
+
103
+ workflow( {
104
+ name: 'custom_activity_wf',
105
+ description: 'Workflow with custom activity options',
106
+ inputSchema: z.object( {} ),
107
+ outputSchema: z.object( {} ),
108
+ options: {
109
+ activityOptions: {
110
+ startToCloseTimeout: '5m',
111
+ retry: { maximumAttempts: 5 }
112
+ }
113
+ },
114
+ fn: async () => ( {} )
115
+ } );
116
+
117
+ expect( proxyActivitiesMock ).toHaveBeenCalledWith(
118
+ expect.objectContaining( {
119
+ startToCloseTimeout: '5m',
120
+ retry: expect.objectContaining( { maximumAttempts: 5 } )
121
+ } )
122
+ );
46
123
  } );
47
124
  } );
48
125
 
49
- it( 'does not throw when options is omitted (disableTrace defaults to false)', async () => {
50
- const { workflow } = await import( './workflow.js' );
126
+ describe( 'wrapper metadata', () => {
127
+ it( 'attaches name, description, inputSchema, outputSchema to wrapper via setMetadata', async () => {
128
+ const { workflow } = await import( './workflow.js' );
129
+ const inputSchema = z.object( { x: z.number() } );
130
+ const outputSchema = z.object( { y: z.number() } );
51
131
 
52
- const wf = workflow( {
53
- name: 'no_options_wf',
54
- description: 'Workflow without options',
55
- inputSchema: z.object( { value: z.string() } ),
56
- outputSchema: z.object( { value: z.string() } ),
57
- fn: async ( { value } ) => ( { value } )
132
+ const wf = workflow( {
133
+ name: 'meta_wf',
134
+ description: 'Meta workflow',
135
+ inputSchema,
136
+ outputSchema,
137
+ fn: async input => ( { y: input.x } )
138
+ } );
139
+
140
+ const symbols = Object.getOwnPropertySymbols( wf );
141
+ expect( symbols ).toHaveLength( 1 );
142
+ const meta = wf[symbols[0]];
143
+ expect( meta ).toEqual( { name: 'meta_wf', description: 'Meta workflow', inputSchema, outputSchema } );
144
+ } );
145
+ } );
146
+
147
+ describe( 'when not in workflow context (unit-test path)', () => {
148
+ it( 'validates input, runs fn with test context, validates output, returns plain output', async () => {
149
+ inWorkflowContextMock.mockReturnValue( false );
150
+ const { workflow } = await import( './workflow.js' );
151
+
152
+ const wf = workflow( {
153
+ name: 'unit_path_wf',
154
+ description: 'Unit path',
155
+ inputSchema: z.object( { a: z.string() } ),
156
+ outputSchema: z.object( { b: z.string() } ),
157
+ fn: async ( input, context ) => ( {
158
+ b: String( context.info.workflowId ) + input.a
159
+ } )
160
+ } );
161
+
162
+ const result = await wf( { a: '-ok' } );
163
+ expect( result ).toEqual( { b: 'test-workflow-ok' } );
164
+ expect( workflowInfoMock ).not.toHaveBeenCalled();
165
+ expect( traceDestinationsStepMock ).not.toHaveBeenCalled();
166
+ } );
167
+
168
+ it( 'merges extra.context into context when provided', async () => {
169
+ inWorkflowContextMock.mockReturnValue( false );
170
+ const { workflow } = await import( './workflow.js' );
171
+
172
+ const wf = workflow( {
173
+ name: 'extra_ctx_wf',
174
+ description: 'Extra context',
175
+ inputSchema: z.object( {} ),
176
+ outputSchema: z.object( { id: z.string() } ),
177
+ fn: async ( _, context ) => ( { id: context.extraId ?? 'default' } )
178
+ } );
179
+
180
+ const result = await wf( {}, { context: { extraId: 'injected' } } );
181
+ expect( result ).toEqual( { id: 'injected' } );
182
+ } );
183
+ } );
184
+
185
+ describe( 'input and output validation', () => {
186
+ it( 'throws ValidationError when input does not match inputSchema', async () => {
187
+ const { workflow } = await import( './workflow.js' );
188
+ const { ValidationError } = await import( '#errors' );
189
+
190
+ const wf = workflow( {
191
+ name: 'validate_in_wf',
192
+ description: 'Input validation',
193
+ inputSchema: z.object( { required: z.string() } ),
194
+ outputSchema: z.object( {} ),
195
+ fn: async () => ( {} )
196
+ } );
197
+
198
+ await expect( wf( { wrong: 1 } ) ).rejects.toThrow( ValidationError );
199
+ await expect( wf( { wrong: 1 } ) ).rejects.toThrow( /Workflow validate_in_wf input/ );
58
200
  } );
59
201
 
60
- const result = await wf( { value: 'hello' } );
61
- expect( result.output ).toEqual( { value: 'hello' } );
202
+ it( 'throws ValidationError when output does not match outputSchema', async () => {
203
+ const { workflow } = await import( './workflow.js' );
204
+ const { ValidationError } = await import( '#errors' );
205
+
206
+ const wf = workflow( {
207
+ name: 'validate_out_wf',
208
+ description: 'Output validation',
209
+ inputSchema: z.object( {} ),
210
+ outputSchema: z.object( { required: z.string() } ),
211
+ fn: async () => ( { other: 1 } )
212
+ } );
213
+
214
+ await expect( wf( {} ) ).rejects.toThrow( ValidationError );
215
+ await expect( wf( {} ) ).rejects.toThrow( /Workflow validate_out_wf output/ );
216
+ } );
62
217
  } );
63
218
 
64
- it( 'respects disableTrace: true when options is provided', async () => {
65
- const { workflow } = await import( './workflow.js' );
219
+ describe( 'root workflow (in workflow context)', () => {
220
+ it( 'calls getTraceDestinations, returns { output, trace } and assigns executionContext to memo', async () => {
221
+ const { workflow } = await import( './workflow.js' );
222
+
223
+ const wf = workflow( {
224
+ name: 'root_wf',
225
+ description: 'Root',
226
+ inputSchema: z.object( {} ),
227
+ outputSchema: z.object( { v: z.number() } ),
228
+ fn: async () => ( { v: 42 } )
229
+ } );
66
230
 
67
- const wf = workflow( {
68
- name: 'trace_disabled_wf',
69
- description: 'Workflow with tracing disabled',
70
- inputSchema: z.object( { value: z.string() } ),
71
- outputSchema: z.object( { value: z.string() } ),
72
- options: { disableTrace: true },
73
- fn: async ( { value } ) => ( { value } )
231
+ const result = await wf( {} );
232
+ expect( traceDestinationsStepMock ).toHaveBeenCalledTimes( 1 );
233
+ expect( result ).toEqual( {
234
+ output: { v: 42 },
235
+ trace: { destinations: { local: '/tmp/trace' } }
236
+ } );
237
+ const memo = workflowInfoMock().memo;
238
+ expect( memo.executionContext ).toEqual( {
239
+ workflowId: 'wf-test-123',
240
+ workflowName: 'root_wf',
241
+ disableTrace: false,
242
+ startTime: new Date( '2025-01-01T00:00:00Z' ).getTime()
243
+ } );
74
244
  } );
75
245
 
76
- const result = await wf( { value: 'hello' } );
77
- expect( result.output ).toEqual( { value: 'hello' } );
246
+ it( 'sets executionContext.disableTrace when options.disableTrace is true', async () => {
247
+ const { workflow } = await import( './workflow.js' );
248
+
249
+ const wf = workflow( {
250
+ name: 'root_no_trace_wf',
251
+ description: 'Root no trace',
252
+ inputSchema: z.object( {} ),
253
+ outputSchema: z.object( {} ),
254
+ options: { disableTrace: true },
255
+ fn: async () => ( {} )
256
+ } );
257
+
258
+ await wf( {} );
259
+ expect( workflowInfoMock().memo.executionContext.disableTrace ).toBe( true );
260
+ } );
261
+ } );
262
+
263
+ describe( 'child workflow (memo.executionContext already set)', () => {
264
+ it( 'does not call getTraceDestinations and returns plain output', async () => {
265
+ workflowInfoMock.mockReturnValue( {
266
+ ...workflowInfoReturn,
267
+ memo: { executionContext: { workflowId: 'parent-1', workflowName: 'parent_wf' } }
268
+ } );
269
+ const { workflow } = await import( './workflow.js' );
270
+
271
+ const wf = workflow( {
272
+ name: 'child_wf',
273
+ description: 'Child',
274
+ inputSchema: z.object( {} ),
275
+ outputSchema: z.object( { x: z.string() } ),
276
+ fn: async () => ( { x: 'child' } )
277
+ } );
278
+
279
+ const result = await wf( {} );
280
+ expect( traceDestinationsStepMock ).not.toHaveBeenCalled();
281
+ expect( result ).toEqual( { x: 'child' } );
282
+ } );
283
+ } );
284
+
285
+ describe( 'bound this: invokeStep, invokeSharedStep, invokeEvaluator', () => {
286
+ it( 'invokeStep calls steps with workflowName#stepName', async () => {
287
+ const getCalls = [];
288
+ proxyActivitiesMock.mockImplementation( () => new Proxy( {}, {
289
+ get: ( _, prop ) => {
290
+ if ( prop === '__internal#getTraceDestinations' ) {
291
+ return traceDestinationsStepMock;
292
+ }
293
+ if ( typeof prop === 'string' && prop.includes( '#' ) ) {
294
+ getCalls.push( prop );
295
+ return vi.fn().mockResolvedValue( {} );
296
+ }
297
+ return vi.fn();
298
+ }
299
+ } ) );
300
+
301
+ const { workflow } = await import( './workflow.js' );
302
+
303
+ const wf = workflow( {
304
+ name: 'invoke_wf',
305
+ description: 'Invoke',
306
+ inputSchema: z.object( {} ),
307
+ outputSchema: z.object( {} ),
308
+ async fn() {
309
+ await this.invokeStep( 'myStep', { foo: 1 } );
310
+ return {};
311
+ }
312
+ } );
313
+
314
+ await wf( {} );
315
+ expect( getCalls ).toContain( 'invoke_wf#myStep' );
316
+ } );
317
+
318
+ it( 'invokeSharedStep calls steps with SHARED_STEP_PREFIX#stepName', async () => {
319
+ const { workflow } = await import( './workflow.js' );
320
+ const sharedSpy = vi.fn().mockResolvedValue( {} );
321
+ proxyActivitiesMock.mockImplementation( () => new Proxy( {}, {
322
+ get: ( _, prop ) => {
323
+ if ( prop === '__internal#getTraceDestinations' ) {
324
+ return traceDestinationsStepMock;
325
+ }
326
+ if ( prop === '__shared#sharedStep' ) {
327
+ return sharedSpy;
328
+ }
329
+ return vi.fn();
330
+ }
331
+ } ) );
332
+
333
+ const wf = workflow( {
334
+ name: 'shared_wf',
335
+ description: 'Shared',
336
+ inputSchema: z.object( {} ),
337
+ outputSchema: z.object( {} ),
338
+ async fn() {
339
+ await this.invokeSharedStep( 'sharedStep', { data: 2 } );
340
+ return {};
341
+ }
342
+ } );
343
+
344
+ await wf( {} );
345
+ expect( sharedSpy ).toHaveBeenCalledWith( { data: 2 }, undefined );
346
+ } );
347
+
348
+ it( 'invokeEvaluator calls steps with workflowName#evaluatorName', async () => {
349
+ const evalSpy = vi.fn().mockResolvedValue( true );
350
+ proxyActivitiesMock.mockImplementation( () => new Proxy( {}, {
351
+ get: ( _, prop ) => {
352
+ if ( prop === '__internal#getTraceDestinations' ) {
353
+ return traceDestinationsStepMock;
354
+ }
355
+ if ( prop === 'eval_wf#myEvaluator' ) {
356
+ return evalSpy;
357
+ }
358
+ return vi.fn();
359
+ }
360
+ } ) );
361
+
362
+ const { workflow } = await import( './workflow.js' );
363
+
364
+ const wf = workflow( {
365
+ name: 'eval_wf',
366
+ description: 'Eval',
367
+ inputSchema: z.object( {} ),
368
+ outputSchema: z.object( {} ),
369
+ async fn() {
370
+ await this.invokeEvaluator( 'myEvaluator', { x: 3 } );
371
+ return {};
372
+ }
373
+ } );
374
+
375
+ await wf( {} );
376
+ expect( evalSpy ).toHaveBeenCalledWith( { x: 3 }, undefined );
377
+ } );
378
+ } );
379
+
380
+ describe( 'startWorkflow', () => {
381
+ it( 'calls executeChild with correct args and TERMINATE when not detached', async () => {
382
+ const { workflow } = await import( './workflow.js' );
383
+ const { ParentClosePolicy } = await import( '@temporalio/workflow' );
384
+
385
+ const wf = workflow( {
386
+ name: 'parent_wf',
387
+ description: 'Parent',
388
+ inputSchema: z.object( {} ),
389
+ outputSchema: z.object( {} ),
390
+ async fn() {
391
+ await this.startWorkflow( 'child_wf', { id: 1 } );
392
+ return {};
393
+ }
394
+ } );
395
+
396
+ await wf( {} );
397
+ expect( executeChildMock ).toHaveBeenCalledWith( 'child_wf', {
398
+ args: [ { id: 1 } ],
399
+ workflowId: expect.stringMatching( /^wf-test-123-/ ),
400
+ parentClosePolicy: ParentClosePolicy.TERMINATE,
401
+ memo: expect.objectContaining( {
402
+ executionContext: expect.any( Object ),
403
+ parentId: 'wf-test-123'
404
+ } )
405
+ } );
406
+ } );
407
+
408
+ it( 'uses ABANDON when extra.detached is true', async () => {
409
+ const { workflow } = await import( './workflow.js' );
410
+ const { ParentClosePolicy } = await import( '@temporalio/workflow' );
411
+
412
+ const wf = workflow( {
413
+ name: 'detach_wf',
414
+ description: 'Detach',
415
+ inputSchema: z.object( {} ),
416
+ outputSchema: z.object( {} ),
417
+ async fn() {
418
+ await this.startWorkflow( 'child_wf', null, { detached: true } );
419
+ return {};
420
+ }
421
+ } );
422
+
423
+ await wf( {} );
424
+ expect( executeChildMock ).toHaveBeenCalledWith( 'child_wf', expect.objectContaining( {
425
+ parentClosePolicy: ParentClosePolicy.ABANDON
426
+ } ) );
427
+ } );
428
+
429
+ it( 'passes empty args when input is null/omitted', async () => {
430
+ const { workflow } = await import( './workflow.js' );
431
+
432
+ const wf = workflow( {
433
+ name: 'no_input_wf',
434
+ description: 'No input',
435
+ inputSchema: z.object( {} ),
436
+ outputSchema: z.object( {} ),
437
+ async fn() {
438
+ await this.startWorkflow( 'child_wf' );
439
+ return {};
440
+ }
441
+ } );
442
+
443
+ await wf( {} );
444
+ expect( executeChildMock ).toHaveBeenCalledWith( 'child_wf', expect.objectContaining( {
445
+ args: []
446
+ } ) );
447
+ } );
448
+ } );
449
+
450
+ describe( 'context.control', () => {
451
+ it( 'exposes info.workflowId and control/info namespaces when not in workflow context', async () => {
452
+ inWorkflowContextMock.mockReturnValue( false );
453
+ const { workflow } = await import( './workflow.js' );
454
+
455
+ const wf = workflow( {
456
+ name: 'control_wf',
457
+ description: 'Control',
458
+ inputSchema: z.object( {} ),
459
+ outputSchema: z.object( {
460
+ workflowId: z.string(),
461
+ hasControl: z.boolean(),
462
+ hasInfo: z.boolean()
463
+ } ),
464
+ fn: async ( _, context ) => ( {
465
+ workflowId: context.info?.workflowId,
466
+ hasControl: 'control' in context,
467
+ hasInfo: 'info' in context
468
+ } )
469
+ } );
470
+
471
+ const result = await wf( {} );
472
+ expect( result.workflowId ).toBe( 'test-workflow' );
473
+ expect( result.hasControl ).toBe( true );
474
+ expect( result.hasInfo ).toBe( true );
475
+ } );
476
+ } );
477
+
478
+ describe( 'error handling (root workflow)', () => {
479
+ it( 'rethrows error from fn and rejects with same message', async () => {
480
+ const { workflow } = await import( './workflow.js' );
481
+
482
+ const wf = workflow( {
483
+ name: 'err_wf',
484
+ description: 'Error',
485
+ inputSchema: z.object( {} ),
486
+ outputSchema: z.object( {} ),
487
+ fn: async () => {
488
+ throw new Error( 'workflow failed' );
489
+ }
490
+ } );
491
+
492
+ await expect( wf( {} ) ).rejects.toThrow( 'workflow failed' );
493
+ } );
78
494
  } );
79
495
  } );
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Context instance builder
3
+ */
4
+ export class Context {
5
+
6
+ /**
7
+ * Builds a new context instance
8
+ * @param {object} options - Arguments to build a new context instance
9
+ * @param {string} workflowId
10
+ * @param {function} continueAsNew
11
+ * @param {function} isContinueAsNewSuggested
12
+ * @returns {object} context
13
+ */
14
+ static build( { workflowId, continueAsNew, isContinueAsNewSuggested } ) {
15
+ return {
16
+ /**
17
+ * Control namespace: This object adds functions to interact with Temporal flow mechanisms
18
+ */
19
+ control: {
20
+ continueAsNew,
21
+ isContinueAsNewSuggested
22
+ },
23
+ /**
24
+ * Info namespace: abstracts workflowInfo()
25
+ */
26
+ info: {
27
+ workflowId
28
+ }
29
+ };
30
+ }
31
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Result of a single job executed by `executeInParallel`.
3
+ *
4
+ * @typeParam T - The return type of the job function
5
+ */
6
+ export type ParallelJobResult<T> =
7
+ | { ok: true; result: T; index: number } |
8
+ { ok: false; error: unknown; index: number };
9
+
10
+ /**
11
+ * Execute jobs in parallel with optional concurrency limit.
12
+ *
13
+ * Returns all job results (successes and failures) sorted by original job index.
14
+ * Each result contains `ok` (boolean), `index` (original position), and either
15
+ * `result` (on success) or `error` (on failure).
16
+ *
17
+ * Jobs must be wrapped in arrow functions—do not pass promises directly.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const results = await executeInParallel( {
22
+ * jobs: [
23
+ * () => myStep( data1 ),
24
+ * () => myStep( data2 ),
25
+ * () => myStep( data3 )
26
+ * ],
27
+ * concurrency: 2
28
+ * } );
29
+ *
30
+ * // Handle the discriminated union (result only exists when ok is true)
31
+ * const successfulResults = results.filter( r => r.ok ).map( r => r.result );
32
+ *
33
+ * // Or handle each result individually
34
+ * for ( const r of results ) {
35
+ * if ( r.ok ) {
36
+ * console.log( `Job ${r.index} succeeded:`, r.result );
37
+ * } else {
38
+ * console.log( `Job ${r.index} failed:`, r.error );
39
+ * }
40
+ * }
41
+ * ```
42
+ *
43
+ * @param params - Parameters object
44
+ * @param params.jobs - Array of arrow functions returning step/activity calls (not promises directly)
45
+ * @param params.concurrency - Max concurrent jobs (default: Infinity)
46
+ * @param params.onJobCompleted - Optional callback invoked as each job completes (in completion order)
47
+ * @returns Array of results sorted by original job index
48
+ */
49
+ export declare function executeInParallel<T>( params: {
50
+ jobs: Array<() => Promise<T> | T>;
51
+ concurrency?: number;
52
+ onJobCompleted?: ( result: ParallelJobResult<T> ) => void;
53
+ } ): Promise<Array<ParallelJobResult<T>>>;
@@ -1,3 +1,4 @@
1
+ // THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
1
2
  import { validateExecuteInParallel } from './validations/static.js';
2
3
 
3
4
  /**
@@ -94,7 +94,7 @@ export function isPlainObject( object: unknown ): boolean;
94
94
  * @param arr - The array to shuffle
95
95
  * @returns A shuffled array copy
96
96
  */
97
- export function shuffleArray( arr: array ): array;
97
+ export function shuffleArray( arr: unknown[] ): unknown[];
98
98
 
99
99
  /**
100
100
  * Creates a new object by merging object `b` onto object `a`, biased toward `b`: