@trigger.dev/sdk 0.0.0-prerelease-20251205122901 → 0.0.0-prerelease-20251209103333

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.
@@ -237,63 +237,14 @@ async function batchTriggerAndWait(id, items, options, requestOptions) {
237
237
  async function batchTrigger(id, items, options, requestOptions) {
238
238
  return await batchTrigger_internal("tasks.batchTrigger()", id, items, options, undefined, requestOptions);
239
239
  }
240
- /**
241
- * Triggers multiple runs of different tasks with specified payloads and options.
242
- *
243
- * @template TTask - The type of task(s) to be triggered, extends AnyTask
244
- *
245
- * @param {Array<BatchByIdItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
246
- * @param {BatchTriggerOptions} [options] - Optional batch-level trigger options
247
- * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
248
- *
249
- * @returns {Promise<BatchRunHandleFromTypes<InferRunTypes<TTask>>>} A promise that resolves with the batch run handle
250
- * containing batch ID, cached status, idempotency info, runs, and public access token
251
- *
252
- * @example
253
- * ```ts
254
- * import { batch } from "@trigger.dev/sdk/v3";
255
- * import type { myTask1, myTask2 } from "~/trigger/myTasks";
256
- *
257
- * // Trigger multiple tasks with different payloads
258
- * const result = await batch.trigger<typeof myTask1 | typeof myTask2>([
259
- * {
260
- * id: "my-task-1",
261
- * payload: { some: "data" },
262
- * options: {
263
- * queue: "default",
264
- * concurrencyKey: "key",
265
- * idempotencyKey: "unique-key",
266
- * delay: "5m",
267
- * tags: ["tag1", "tag2"]
268
- * }
269
- * },
270
- * {
271
- * id: "my-task-2",
272
- * payload: { other: "data" }
273
- * }
274
- * ]);
275
- * ```
276
- *
277
- * @description
278
- * Each task item in the array can include:
279
- * - `id`: The unique identifier of the task
280
- * - `payload`: The data to pass to the task
281
- * - `options`: Optional task-specific settings including:
282
- * - `queue`: Specify a queue for the task
283
- * - `concurrencyKey`: Control concurrent execution
284
- * - `idempotencyKey`: Prevent duplicate runs
285
- * - `idempotencyKeyTTL`: Time-to-live for idempotency key
286
- * - `delay`: Delay before task execution
287
- * - `ttl`: Time-to-live for the task
288
- * - `tags`: Array of tags for the task
289
- * - `maxAttempts`: Maximum retry attempts
290
- * - `metadata`: Additional metadata
291
- * - `maxDuration`: Maximum execution duration
292
- */
293
- async function batchTriggerById(items, options, requestOptions) {
240
+ // Implementation
241
+ async function batchTriggerById(...args) {
242
+ const [items, options, requestOptions] = args;
294
243
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
295
- const response = await apiClient.batchTriggerV3({
296
- items: await Promise.all(items.map(async (item, index) => {
244
+ // Check if items is an array or a stream
245
+ if (Array.isArray(items)) {
246
+ // Array path: existing logic
247
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
297
248
  const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
298
249
  const parsedPayload = taskMetadata?.fns.parsePayload
299
250
  ? await taskMetadata?.fns.parsePayload(item.payload)
@@ -301,6 +252,7 @@ async function batchTriggerById(items, options, requestOptions) {
301
252
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
302
253
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
303
254
  return {
255
+ index,
304
256
  task: item.id,
305
257
  payload: payloadPacket.data,
306
258
  options: {
@@ -322,255 +274,255 @@ async function batchTriggerById(items, options, requestOptions) {
322
274
  lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
323
275
  },
324
276
  };
325
- })),
326
- parentRunId: v3_1.taskContext.ctx?.run.id,
327
- }, {
328
- spanParentAsLink: true,
329
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
330
- }, {
331
- name: "batch.trigger()",
332
- tracer: tracer_js_1.tracer,
333
- icon: "trigger",
334
- onResponseBody(body, span) {
335
- if (body && typeof body === "object" && !Array.isArray(body)) {
336
- if ("id" in body && typeof body.id === "string") {
337
- span.setAttribute("batchId", body.id);
338
- }
339
- if ("runCount" in body && typeof body.runCount === "number") {
340
- span.setAttribute("runCount", body.runCount);
341
- }
342
- }
343
- },
344
- ...requestOptions,
345
- });
346
- const handle = {
347
- batchId: response.id,
348
- runCount: response.runCount,
349
- publicAccessToken: response.publicAccessToken,
350
- };
351
- return handle;
277
+ }));
278
+ // Execute 2-phase batch
279
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.trigger()", async (span) => {
280
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
281
+ parentRunId: v3_1.taskContext.ctx?.run.id,
282
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
283
+ }, requestOptions);
284
+ span.setAttribute("batchId", result.id);
285
+ span.setAttribute("runCount", result.runCount);
286
+ return result;
287
+ }, {
288
+ kind: api_1.SpanKind.PRODUCER,
289
+ attributes: {
290
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
291
+ },
292
+ });
293
+ const handle = {
294
+ batchId: response.id,
295
+ runCount: response.runCount,
296
+ publicAccessToken: response.publicAccessToken,
297
+ };
298
+ return handle;
299
+ }
300
+ else {
301
+ // Stream path: convert to AsyncIterable and transform
302
+ const asyncItems = normalizeToAsyncIterable(items);
303
+ const transformedItems = transformBatchItemsStream(asyncItems, options);
304
+ // Execute streaming 2-phase batch
305
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.trigger()", async (span) => {
306
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
307
+ parentRunId: v3_1.taskContext.ctx?.run.id,
308
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
309
+ }, requestOptions);
310
+ span.setAttribute("batchId", result.id);
311
+ span.setAttribute("runCount", result.runCount);
312
+ return result;
313
+ }, {
314
+ kind: api_1.SpanKind.PRODUCER,
315
+ attributes: {
316
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
317
+ },
318
+ });
319
+ const handle = {
320
+ batchId: response.id,
321
+ runCount: response.runCount,
322
+ publicAccessToken: response.publicAccessToken,
323
+ };
324
+ return handle;
325
+ }
352
326
  }
353
- /**
354
- * Triggers multiple tasks and waits for all of them to complete before returning their results.
355
- * This function must be called from within a task.run() context.
356
- *
357
- * @template TTask - Union type of tasks to be triggered, extends AnyTask
358
- *
359
- * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
360
- * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
361
- *
362
- * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
363
- * success/failure status and strongly-typed outputs for each task
364
- *
365
- * @throws {Error} If called outside of a task.run() context
366
- * @throws {Error} If no API client is configured
367
- *
368
- * @example
369
- * ```ts
370
- * import { batch, task } from "@trigger.dev/sdk/v3";
371
- *
372
- * export const parentTask = task({
373
- * id: "parent-task",
374
- * run: async (payload: string) => {
375
- * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
376
- * {
377
- * id: "child-task-1",
378
- * payload: { foo: "World" },
379
- * options: {
380
- * queue: "default",
381
- * delay: "5m",
382
- * tags: ["batch", "child1"]
383
- * }
384
- * },
385
- * {
386
- * id: "child-task-2",
387
- * payload: { bar: 42 }
388
- * }
389
- * ]);
390
- *
391
- * // Type-safe result handling
392
- * for (const result of results) {
393
- * if (result.ok) {
394
- * switch (result.taskIdentifier) {
395
- * case "child-task-1":
396
- * console.log("Child task 1 output:", result.output); // string type
397
- * break;
398
- * case "child-task-2":
399
- * console.log("Child task 2 output:", result.output); // number type
400
- * break;
401
- * }
402
- * } else {
403
- * console.error("Task failed:", result.error);
404
- * }
405
- * }
406
- * }
407
- * });
408
- * ```
409
- *
410
- * @description
411
- * Each task item in the array can include:
412
- * - `id`: The task identifier (must match one of the tasks in the union type)
413
- * - `payload`: Strongly-typed payload matching the task's input type
414
- * - `options`: Optional task-specific settings including:
415
- * - `queue`: Specify a queue for the task
416
- * - `concurrencyKey`: Control concurrent execution
417
- * - `delay`: Delay before task execution
418
- * - `ttl`: Time-to-live for the task
419
- * - `tags`: Array of tags for the task
420
- * - `maxAttempts`: Maximum retry attempts
421
- * - `metadata`: Additional metadata
422
- * - `maxDuration`: Maximum execution duration
423
- *
424
- * The function provides full type safety for:
425
- * - Task IDs
426
- * - Payload types
427
- * - Return value types
428
- * - Error handling
429
- */
430
- async function batchTriggerByIdAndWait(items, options, requestOptions) {
327
+ // Implementation
328
+ async function batchTriggerByIdAndWait(...args) {
329
+ const [items, options, requestOptions] = args;
431
330
  const ctx = v3_1.taskContext.ctx;
432
331
  if (!ctx) {
433
332
  throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
434
333
  }
435
334
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
436
- return await tracer_js_1.tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
437
- const response = await apiClient.batchTriggerV3({
438
- items: await Promise.all(items.map(async (item, index) => {
439
- const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
440
- const parsedPayload = taskMetadata?.fns.parsePayload
441
- ? await taskMetadata?.fns.parsePayload(item.payload)
442
- : item.payload;
443
- const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
444
- const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
445
- return {
446
- task: item.id,
447
- payload: payloadPacket.data,
448
- options: {
449
- lockToVersion: v3_1.taskContext.worker?.version,
450
- queue: item.options?.queue ? { name: item.options.queue } : undefined,
451
- concurrencyKey: item.options?.concurrencyKey,
452
- test: v3_1.taskContext.ctx?.run.isTest,
453
- payloadType: payloadPacket.dataType,
454
- delay: item.options?.delay,
455
- ttl: item.options?.ttl,
456
- tags: item.options?.tags,
457
- maxAttempts: item.options?.maxAttempts,
458
- metadata: item.options?.metadata,
459
- maxDuration: item.options?.maxDuration,
460
- idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ??
461
- batchItemIdempotencyKey,
462
- idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
463
- machine: item.options?.machine,
464
- priority: item.options?.priority,
465
- region: item.options?.region,
466
- },
467
- };
468
- })),
469
- parentRunId: ctx.run.id,
470
- resumeParentOnCompletion: true,
335
+ // Check if items is an array or a stream
336
+ if (Array.isArray(items)) {
337
+ // Array path: existing logic
338
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
339
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
340
+ const parsedPayload = taskMetadata?.fns.parsePayload
341
+ ? await taskMetadata?.fns.parsePayload(item.payload)
342
+ : item.payload;
343
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
344
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
345
+ return {
346
+ index,
347
+ task: item.id,
348
+ payload: payloadPacket.data,
349
+ options: {
350
+ lockToVersion: v3_1.taskContext.worker?.version,
351
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
352
+ concurrencyKey: item.options?.concurrencyKey,
353
+ test: v3_1.taskContext.ctx?.run.isTest,
354
+ payloadType: payloadPacket.dataType,
355
+ delay: item.options?.delay,
356
+ ttl: item.options?.ttl,
357
+ tags: item.options?.tags,
358
+ maxAttempts: item.options?.maxAttempts,
359
+ metadata: item.options?.metadata,
360
+ maxDuration: item.options?.maxDuration,
361
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
362
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
363
+ machine: item.options?.machine,
364
+ priority: item.options?.priority,
365
+ region: item.options?.region,
366
+ },
367
+ };
368
+ }));
369
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
370
+ // Execute 2-phase batch
371
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
372
+ parentRunId: ctx.run.id,
373
+ resumeParentOnCompletion: true,
374
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
375
+ }, requestOptions);
376
+ span.setAttribute("batchId", response.id);
377
+ span.setAttribute("runCount", response.runCount);
378
+ const result = await v3_1.runtime.waitForBatch({
379
+ id: response.id,
380
+ runCount: response.runCount,
381
+ ctx,
382
+ });
383
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
384
+ return {
385
+ id: result.id,
386
+ runs,
387
+ };
471
388
  }, {
472
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
473
- }, requestOptions);
474
- span.setAttribute("batchId", response.id);
475
- span.setAttribute("runCount", response.runCount);
476
- const result = await v3_1.runtime.waitForBatch({
477
- id: response.id,
389
+ kind: api_1.SpanKind.PRODUCER,
390
+ attributes: {
391
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
392
+ },
393
+ });
394
+ }
395
+ else {
396
+ // Stream path: convert to AsyncIterable and transform
397
+ const asyncItems = normalizeToAsyncIterable(items);
398
+ const transformedItems = transformBatchItemsStreamForWait(asyncItems, options);
399
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
400
+ // Execute streaming 2-phase batch
401
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
402
+ parentRunId: ctx.run.id,
403
+ resumeParentOnCompletion: true,
404
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
405
+ }, requestOptions);
406
+ span.setAttribute("batchId", response.id);
407
+ span.setAttribute("runCount", response.runCount);
408
+ const result = await v3_1.runtime.waitForBatch({
409
+ id: response.id,
410
+ runCount: response.runCount,
411
+ ctx,
412
+ });
413
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
414
+ return {
415
+ id: result.id,
416
+ runs,
417
+ };
418
+ }, {
419
+ kind: api_1.SpanKind.PRODUCER,
420
+ attributes: {
421
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
422
+ },
423
+ });
424
+ }
425
+ }
426
+ // Implementation
427
+ async function batchTriggerTasks(...args) {
428
+ const [items, options, requestOptions] = args;
429
+ const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
430
+ // Check if items is an array or a stream
431
+ if (Array.isArray(items)) {
432
+ // Array path: existing logic
433
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
434
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
435
+ const parsedPayload = taskMetadata?.fns.parsePayload
436
+ ? await taskMetadata?.fns.parsePayload(item.payload)
437
+ : item.payload;
438
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
439
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
440
+ return {
441
+ index,
442
+ task: item.task.id,
443
+ payload: payloadPacket.data,
444
+ options: {
445
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
446
+ concurrencyKey: item.options?.concurrencyKey,
447
+ test: v3_1.taskContext.ctx?.run.isTest,
448
+ payloadType: payloadPacket.dataType,
449
+ delay: item.options?.delay,
450
+ ttl: item.options?.ttl,
451
+ tags: item.options?.tags,
452
+ maxAttempts: item.options?.maxAttempts,
453
+ metadata: item.options?.metadata,
454
+ maxDuration: item.options?.maxDuration,
455
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
456
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
457
+ machine: item.options?.machine,
458
+ priority: item.options?.priority,
459
+ region: item.options?.region,
460
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
461
+ },
462
+ };
463
+ }));
464
+ // Execute 2-phase batch
465
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.triggerByTask()", async (span) => {
466
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
467
+ parentRunId: v3_1.taskContext.ctx?.run.id,
468
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
469
+ }, requestOptions);
470
+ span.setAttribute("batchId", result.id);
471
+ span.setAttribute("runCount", result.runCount);
472
+ return result;
473
+ }, {
474
+ kind: api_1.SpanKind.PRODUCER,
475
+ attributes: {
476
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
477
+ },
478
+ });
479
+ const handle = {
480
+ batchId: response.id,
478
481
  runCount: response.runCount,
479
- ctx,
482
+ publicAccessToken: response.publicAccessToken,
483
+ };
484
+ return handle;
485
+ }
486
+ else {
487
+ // Stream path: convert to AsyncIterable and transform
488
+ const streamItems = items;
489
+ const asyncItems = normalizeToAsyncIterable(streamItems);
490
+ const transformedItems = transformBatchByTaskItemsStream(asyncItems, options);
491
+ // Execute streaming 2-phase batch
492
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.triggerByTask()", async (span) => {
493
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
494
+ parentRunId: v3_1.taskContext.ctx?.run.id,
495
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
496
+ }, requestOptions);
497
+ span.setAttribute("batchId", result.id);
498
+ span.setAttribute("runCount", result.runCount);
499
+ return result;
500
+ }, {
501
+ kind: api_1.SpanKind.PRODUCER,
502
+ attributes: {
503
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
504
+ },
480
505
  });
481
- const runs = await handleBatchTaskRunExecutionResultV2(result.items);
482
- return {
483
- id: result.id,
484
- runs,
506
+ const handle = {
507
+ batchId: response.id,
508
+ runCount: response.runCount,
509
+ publicAccessToken: response.publicAccessToken,
485
510
  };
486
- }, {
487
- kind: api_1.SpanKind.PRODUCER,
488
- attributes: {
489
- [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
490
- },
491
- });
511
+ return handle;
512
+ }
492
513
  }
493
- /**
494
- * Triggers multiple tasks and waits for all of them to complete before returning their results.
495
- * This function must be called from within a task.run() context.
496
- *
497
- * @template TTask - Union type of tasks to be triggered, extends AnyTask
498
- *
499
- * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
500
- * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
501
- *
502
- * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
503
- * success/failure status and strongly-typed outputs for each task
504
- *
505
- * @throws {Error} If called outside of a task.run() context
506
- * @throws {Error} If no API client is configured
507
- *
508
- * @example
509
- * ```ts
510
- * import { batch, task } from "@trigger.dev/sdk/v3";
511
- *
512
- * export const parentTask = task({
513
- * id: "parent-task",
514
- * run: async (payload: string) => {
515
- * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
516
- * {
517
- * id: "child-task-1",
518
- * payload: { foo: "World" },
519
- * options: {
520
- * queue: "default",
521
- * delay: "5m",
522
- * tags: ["batch", "child1"]
523
- * }
524
- * },
525
- * {
526
- * id: "child-task-2",
527
- * payload: { bar: 42 }
528
- * }
529
- * ]);
530
- *
531
- * // Type-safe result handling
532
- * for (const result of results) {
533
- * if (result.ok) {
534
- * switch (result.taskIdentifier) {
535
- * case "child-task-1":
536
- * console.log("Child task 1 output:", result.output); // string type
537
- * break;
538
- * case "child-task-2":
539
- * console.log("Child task 2 output:", result.output); // number type
540
- * break;
541
- * }
542
- * } else {
543
- * console.error("Task failed:", result.error);
544
- * }
545
- * }
546
- * }
547
- * });
548
- * ```
549
- *
550
- * @description
551
- * Each task item in the array can include:
552
- * - `id`: The task identifier (must match one of the tasks in the union type)
553
- * - `payload`: Strongly-typed payload matching the task's input type
554
- * - `options`: Optional task-specific settings including:
555
- * - `queue`: Specify a queue for the task
556
- * - `concurrencyKey`: Control concurrent execution
557
- * - `delay`: Delay before task execution
558
- * - `ttl`: Time-to-live for the task
559
- * - `tags`: Array of tags for the task
560
- * - `maxAttempts`: Maximum retry attempts
561
- * - `metadata`: Additional metadata
562
- * - `maxDuration`: Maximum execution duration
563
- *
564
- * The function provides full type safety for:
565
- * - Task IDs
566
- * - Payload types
567
- * - Return value types
568
- * - Error handling
569
- */
570
- async function batchTriggerTasks(items, options, requestOptions) {
514
+ // Implementation
515
+ async function batchTriggerAndWaitTasks(...args) {
516
+ const [items, options, requestOptions] = args;
517
+ const ctx = v3_1.taskContext.ctx;
518
+ if (!ctx) {
519
+ throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
520
+ }
571
521
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
572
- const response = await apiClient.batchTriggerV3({
573
- items: await Promise.all(items.map(async (item, index) => {
522
+ // Check if items is an array or a stream
523
+ if (Array.isArray(items)) {
524
+ // Array path: existing logic
525
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
574
526
  const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
575
527
  const parsedPayload = taskMetadata?.fns.parsePayload
576
528
  ? await taskMetadata?.fns.parsePayload(item.payload)
@@ -578,9 +530,11 @@ async function batchTriggerTasks(items, options, requestOptions) {
578
530
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
579
531
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
580
532
  return {
533
+ index,
581
534
  task: item.task.id,
582
535
  payload: payloadPacket.data,
583
536
  options: {
537
+ lockToVersion: v3_1.taskContext.worker?.version,
584
538
  queue: item.options?.queue ? { name: item.options.queue } : undefined,
585
539
  concurrencyKey: item.options?.concurrencyKey,
586
540
  test: v3_1.taskContext.ctx?.run.isTest,
@@ -596,176 +550,394 @@ async function batchTriggerTasks(items, options, requestOptions) {
596
550
  machine: item.options?.machine,
597
551
  priority: item.options?.priority,
598
552
  region: item.options?.region,
599
- lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
600
553
  },
601
554
  };
602
- })),
603
- parentRunId: v3_1.taskContext.ctx?.run.id,
604
- }, {
605
- spanParentAsLink: true,
606
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
607
- }, {
608
- name: "batch.triggerByTask()",
609
- tracer: tracer_js_1.tracer,
610
- icon: "trigger",
611
- onResponseBody(body, span) {
612
- if (body && typeof body === "object" && !Array.isArray(body)) {
613
- if ("id" in body && typeof body.id === "string") {
614
- span.setAttribute("batchId", body.id);
615
- }
616
- if ("runCount" in body && typeof body.runCount === "number") {
617
- span.setAttribute("runCount", body.runCount);
618
- }
619
- }
620
- },
621
- ...requestOptions,
622
- });
623
- const handle = {
624
- batchId: response.id,
625
- runCount: response.runCount,
626
- publicAccessToken: response.publicAccessToken,
627
- };
628
- return handle;
555
+ }));
556
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
557
+ // Execute 2-phase batch
558
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
559
+ parentRunId: ctx.run.id,
560
+ resumeParentOnCompletion: true,
561
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
562
+ }, requestOptions);
563
+ span.setAttribute("batchId", response.id);
564
+ span.setAttribute("runCount", response.runCount);
565
+ const result = await v3_1.runtime.waitForBatch({
566
+ id: response.id,
567
+ runCount: response.runCount,
568
+ ctx,
569
+ });
570
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
571
+ return {
572
+ id: result.id,
573
+ runs,
574
+ };
575
+ }, {
576
+ kind: api_1.SpanKind.PRODUCER,
577
+ attributes: {
578
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
579
+ },
580
+ });
581
+ }
582
+ else {
583
+ // Stream path: convert to AsyncIterable and transform
584
+ const streamItems = items;
585
+ const asyncItems = normalizeToAsyncIterable(streamItems);
586
+ const transformedItems = transformBatchByTaskItemsStreamForWait(asyncItems, options);
587
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
588
+ // Execute streaming 2-phase batch
589
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
590
+ parentRunId: ctx.run.id,
591
+ resumeParentOnCompletion: true,
592
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
593
+ }, requestOptions);
594
+ span.setAttribute("batchId", response.id);
595
+ span.setAttribute("runCount", response.runCount);
596
+ const result = await v3_1.runtime.waitForBatch({
597
+ id: response.id,
598
+ runCount: response.runCount,
599
+ ctx,
600
+ });
601
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
602
+ return {
603
+ id: result.id,
604
+ runs,
605
+ };
606
+ }, {
607
+ kind: api_1.SpanKind.PRODUCER,
608
+ attributes: {
609
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
610
+ },
611
+ });
612
+ }
629
613
  }
630
614
  /**
631
- * Triggers multiple tasks and waits for all of them to complete before returning their results.
632
- * This function must be called from within a task.run() context.
615
+ * Helper function that executes a 2-phase batch trigger:
616
+ * 1. Creates the batch record with expected run count
617
+ * 2. Streams items as NDJSON to the server
633
618
  *
634
- * @template TTask - Union type of tasks to be triggered, extends AnyTask
635
- *
636
- * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
637
- * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
638
- *
639
- * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
640
- * success/failure status and strongly-typed outputs for each task
619
+ * @internal
620
+ */
621
+ async function executeBatchTwoPhase(apiClient, items, options, requestOptions) {
622
+ // Phase 1: Create batch
623
+ const batch = await apiClient.createBatch({
624
+ runCount: items.length,
625
+ parentRunId: options.parentRunId,
626
+ resumeParentOnCompletion: options.resumeParentOnCompletion,
627
+ idempotencyKey: options.idempotencyKey,
628
+ }, requestOptions);
629
+ // If the batch was cached (idempotent replay), skip streaming items
630
+ if (!batch.isCached) {
631
+ // Phase 2: Stream items
632
+ await apiClient.streamBatchItems(batch.id, items, requestOptions);
633
+ }
634
+ return {
635
+ id: batch.id,
636
+ runCount: batch.runCount,
637
+ publicAccessToken: batch.publicAccessToken,
638
+ };
639
+ }
640
+ /**
641
+ * Execute a streaming 2-phase batch trigger where items are streamed from an AsyncIterable.
642
+ * Unlike executeBatchTwoPhase, this doesn't know the count upfront.
641
643
  *
642
- * @throws {Error} If called outside of a task.run() context
643
- * @throws {Error} If no API client is configured
644
+ * @internal
645
+ */
646
+ async function executeBatchTwoPhaseStreaming(apiClient, items, options, requestOptions) {
647
+ // For streaming, we need to buffer items to get the count first
648
+ // This is because createBatch requires runCount upfront
649
+ // In the future, we could add a streaming-first endpoint that doesn't require this
650
+ const itemsArray = [];
651
+ for await (const item of items) {
652
+ itemsArray.push(item);
653
+ }
654
+ // Now we can use the regular 2-phase approach
655
+ return executeBatchTwoPhase(apiClient, itemsArray, options, requestOptions);
656
+ }
657
+ // ============================================================================
658
+ // Streaming Helpers
659
+ // ============================================================================
660
+ /**
661
+ * Type guard to check if a value is an AsyncIterable
662
+ */
663
+ function isAsyncIterable(value) {
664
+ return (value != null &&
665
+ typeof value === "object" &&
666
+ Symbol.asyncIterator in value &&
667
+ typeof value[Symbol.asyncIterator] === "function");
668
+ }
669
+ /**
670
+ * Type guard to check if a value is a ReadableStream
671
+ */
672
+ function isReadableStream(value) {
673
+ return (value != null &&
674
+ typeof value === "object" &&
675
+ "getReader" in value &&
676
+ typeof value.getReader === "function");
677
+ }
678
+ /**
679
+ * Convert a ReadableStream to an AsyncIterable
680
+ */
681
+ async function* readableStreamToAsyncIterable(stream) {
682
+ const reader = stream.getReader();
683
+ try {
684
+ while (true) {
685
+ const { done, value } = await reader.read();
686
+ if (done)
687
+ break;
688
+ yield value;
689
+ }
690
+ }
691
+ finally {
692
+ reader.releaseLock();
693
+ }
694
+ }
695
+ /**
696
+ * Normalize stream input to AsyncIterable
697
+ */
698
+ function normalizeToAsyncIterable(input) {
699
+ if (isReadableStream(input)) {
700
+ return readableStreamToAsyncIterable(input);
701
+ }
702
+ return input;
703
+ }
704
+ /**
705
+ * Transform a stream of BatchByIdItem to BatchItemNDJSON format.
706
+ * Handles payload serialization and idempotency key generation.
644
707
  *
645
- * @example
646
- * ```ts
647
- * import { batch, task } from "@trigger.dev/sdk/v3";
708
+ * @internal
709
+ */
710
+ async function* transformBatchItemsStream(items, options) {
711
+ let index = 0;
712
+ for await (const item of items) {
713
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
714
+ const parsedPayload = taskMetadata?.fns.parsePayload
715
+ ? await taskMetadata?.fns.parsePayload(item.payload)
716
+ : item.payload;
717
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
718
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
719
+ yield {
720
+ index: index++,
721
+ task: item.id,
722
+ payload: payloadPacket.data,
723
+ options: {
724
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
725
+ concurrencyKey: item.options?.concurrencyKey,
726
+ test: v3_1.taskContext.ctx?.run.isTest,
727
+ payloadType: payloadPacket.dataType,
728
+ delay: item.options?.delay,
729
+ ttl: item.options?.ttl,
730
+ tags: item.options?.tags,
731
+ maxAttempts: item.options?.maxAttempts,
732
+ metadata: item.options?.metadata,
733
+ maxDuration: item.options?.maxDuration,
734
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
735
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
736
+ machine: item.options?.machine,
737
+ priority: item.options?.priority,
738
+ region: item.options?.region,
739
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
740
+ },
741
+ };
742
+ }
743
+ }
744
+ /**
745
+ * Transform a stream of BatchByIdAndWaitItem to BatchItemNDJSON format for triggerAndWait.
746
+ * Uses the current worker version for lockToVersion.
648
747
  *
649
- * export const parentTask = task({
650
- * id: "parent-task",
651
- * run: async (payload: string) => {
652
- * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
653
- * {
654
- * id: "child-task-1",
655
- * payload: { foo: "World" },
656
- * options: {
657
- * queue: "default",
658
- * delay: "5m",
659
- * tags: ["batch", "child1"]
660
- * }
661
- * },
662
- * {
663
- * id: "child-task-2",
664
- * payload: { bar: 42 }
665
- * }
666
- * ]);
748
+ * @internal
749
+ */
750
+ async function* transformBatchItemsStreamForWait(items, options) {
751
+ let index = 0;
752
+ for await (const item of items) {
753
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
754
+ const parsedPayload = taskMetadata?.fns.parsePayload
755
+ ? await taskMetadata?.fns.parsePayload(item.payload)
756
+ : item.payload;
757
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
758
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
759
+ yield {
760
+ index: index++,
761
+ task: item.id,
762
+ payload: payloadPacket.data,
763
+ options: {
764
+ lockToVersion: v3_1.taskContext.worker?.version,
765
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
766
+ concurrencyKey: item.options?.concurrencyKey,
767
+ test: v3_1.taskContext.ctx?.run.isTest,
768
+ payloadType: payloadPacket.dataType,
769
+ delay: item.options?.delay,
770
+ ttl: item.options?.ttl,
771
+ tags: item.options?.tags,
772
+ maxAttempts: item.options?.maxAttempts,
773
+ metadata: item.options?.metadata,
774
+ maxDuration: item.options?.maxDuration,
775
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
776
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
777
+ machine: item.options?.machine,
778
+ priority: item.options?.priority,
779
+ region: item.options?.region,
780
+ },
781
+ };
782
+ }
783
+ }
784
+ /**
785
+ * Transform a stream of BatchByTaskItem to BatchItemNDJSON format.
667
786
  *
668
- * // Type-safe result handling
669
- * for (const result of results) {
670
- * if (result.ok) {
671
- * switch (result.taskIdentifier) {
672
- * case "child-task-1":
673
- * console.log("Child task 1 output:", result.output); // string type
674
- * break;
675
- * case "child-task-2":
676
- * console.log("Child task 2 output:", result.output); // number type
677
- * break;
678
- * }
679
- * } else {
680
- * console.error("Task failed:", result.error);
681
- * }
682
- * }
683
- * }
684
- * });
685
- * ```
787
+ * @internal
788
+ */
789
+ async function* transformBatchByTaskItemsStream(items, options) {
790
+ let index = 0;
791
+ for await (const item of items) {
792
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
793
+ const parsedPayload = taskMetadata?.fns.parsePayload
794
+ ? await taskMetadata?.fns.parsePayload(item.payload)
795
+ : item.payload;
796
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
797
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
798
+ yield {
799
+ index: index++,
800
+ task: item.task.id,
801
+ payload: payloadPacket.data,
802
+ options: {
803
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
804
+ concurrencyKey: item.options?.concurrencyKey,
805
+ test: v3_1.taskContext.ctx?.run.isTest,
806
+ payloadType: payloadPacket.dataType,
807
+ delay: item.options?.delay,
808
+ ttl: item.options?.ttl,
809
+ tags: item.options?.tags,
810
+ maxAttempts: item.options?.maxAttempts,
811
+ metadata: item.options?.metadata,
812
+ maxDuration: item.options?.maxDuration,
813
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
814
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
815
+ machine: item.options?.machine,
816
+ priority: item.options?.priority,
817
+ region: item.options?.region,
818
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
819
+ },
820
+ };
821
+ }
822
+ }
823
+ /**
824
+ * Transform a stream of BatchByTaskAndWaitItem to BatchItemNDJSON format for triggerAndWait.
686
825
  *
687
- * @description
688
- * Each task item in the array can include:
689
- * - `id`: The task identifier (must match one of the tasks in the union type)
690
- * - `payload`: Strongly-typed payload matching the task's input type
691
- * - `options`: Optional task-specific settings including:
692
- * - `queue`: Specify a queue for the task
693
- * - `concurrencyKey`: Control concurrent execution
694
- * - `delay`: Delay before task execution
695
- * - `ttl`: Time-to-live for the task
696
- * - `tags`: Array of tags for the task
697
- * - `maxAttempts`: Maximum retry attempts
698
- * - `metadata`: Additional metadata
699
- * - `maxDuration`: Maximum execution duration
826
+ * @internal
827
+ */
828
+ async function* transformBatchByTaskItemsStreamForWait(items, options) {
829
+ let index = 0;
830
+ for await (const item of items) {
831
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
832
+ const parsedPayload = taskMetadata?.fns.parsePayload
833
+ ? await taskMetadata?.fns.parsePayload(item.payload)
834
+ : item.payload;
835
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
836
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
837
+ yield {
838
+ index: index++,
839
+ task: item.task.id,
840
+ payload: payloadPacket.data,
841
+ options: {
842
+ lockToVersion: v3_1.taskContext.worker?.version,
843
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
844
+ concurrencyKey: item.options?.concurrencyKey,
845
+ test: v3_1.taskContext.ctx?.run.isTest,
846
+ payloadType: payloadPacket.dataType,
847
+ delay: item.options?.delay,
848
+ ttl: item.options?.ttl,
849
+ tags: item.options?.tags,
850
+ maxAttempts: item.options?.maxAttempts,
851
+ metadata: item.options?.metadata,
852
+ maxDuration: item.options?.maxDuration,
853
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
854
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
855
+ machine: item.options?.machine,
856
+ priority: item.options?.priority,
857
+ region: item.options?.region,
858
+ },
859
+ };
860
+ }
861
+ }
862
+ /**
863
+ * Transform a stream of BatchItem (single task type) to BatchItemNDJSON format.
700
864
  *
701
- * The function provides full type safety for:
702
- * - Task IDs
703
- * - Payload types
704
- * - Return value types
705
- * - Error handling
865
+ * @internal
706
866
  */
707
- async function batchTriggerAndWaitTasks(items, options, requestOptions) {
708
- const ctx = v3_1.taskContext.ctx;
709
- if (!ctx) {
710
- throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
867
+ async function* transformSingleTaskBatchItemsStream(taskIdentifier, items, parsePayload, options, queue) {
868
+ let index = 0;
869
+ for await (const item of items) {
870
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
871
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
872
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
873
+ yield {
874
+ index: index++,
875
+ task: taskIdentifier,
876
+ payload: payloadPacket.data,
877
+ options: {
878
+ queue: item.options?.queue
879
+ ? { name: item.options.queue }
880
+ : queue
881
+ ? { name: queue }
882
+ : undefined,
883
+ concurrencyKey: item.options?.concurrencyKey,
884
+ test: v3_1.taskContext.ctx?.run.isTest,
885
+ payloadType: payloadPacket.dataType,
886
+ delay: item.options?.delay,
887
+ ttl: item.options?.ttl,
888
+ tags: item.options?.tags,
889
+ maxAttempts: item.options?.maxAttempts,
890
+ metadata: item.options?.metadata,
891
+ maxDuration: item.options?.maxDuration,
892
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
893
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
894
+ machine: item.options?.machine,
895
+ priority: item.options?.priority,
896
+ region: item.options?.region,
897
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
898
+ },
899
+ };
711
900
  }
712
- const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
713
- return await tracer_js_1.tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
714
- const response = await apiClient.batchTriggerV3({
715
- items: await Promise.all(items.map(async (item, index) => {
716
- const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
717
- const parsedPayload = taskMetadata?.fns.parsePayload
718
- ? await taskMetadata?.fns.parsePayload(item.payload)
719
- : item.payload;
720
- const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
721
- const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
722
- return {
723
- task: item.task.id,
724
- payload: payloadPacket.data,
725
- options: {
726
- lockToVersion: v3_1.taskContext.worker?.version,
727
- queue: item.options?.queue ? { name: item.options.queue } : undefined,
728
- concurrencyKey: item.options?.concurrencyKey,
729
- test: v3_1.taskContext.ctx?.run.isTest,
730
- payloadType: payloadPacket.dataType,
731
- delay: item.options?.delay,
732
- ttl: item.options?.ttl,
733
- tags: item.options?.tags,
734
- maxAttempts: item.options?.maxAttempts,
735
- metadata: item.options?.metadata,
736
- maxDuration: item.options?.maxDuration,
737
- idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ??
738
- batchItemIdempotencyKey,
739
- idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
740
- machine: item.options?.machine,
741
- priority: item.options?.priority,
742
- region: item.options?.region,
743
- },
744
- };
745
- })),
746
- parentRunId: ctx.run.id,
747
- resumeParentOnCompletion: true,
748
- }, {
749
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
750
- }, requestOptions);
751
- span.setAttribute("batchId", response.id);
752
- span.setAttribute("runCount", response.runCount);
753
- const result = await v3_1.runtime.waitForBatch({
754
- id: response.id,
755
- runCount: response.runCount,
756
- ctx,
757
- });
758
- const runs = await handleBatchTaskRunExecutionResultV2(result.items);
759
- return {
760
- id: result.id,
761
- runs,
901
+ }
902
+ /**
903
+ * Transform a stream of BatchTriggerAndWaitItem (single task type) to BatchItemNDJSON format.
904
+ *
905
+ * @internal
906
+ */
907
+ async function* transformSingleTaskBatchItemsStreamForWait(taskIdentifier, items, parsePayload, options, queue) {
908
+ let index = 0;
909
+ for await (const item of items) {
910
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
911
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
912
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
913
+ yield {
914
+ index: index++,
915
+ task: taskIdentifier,
916
+ payload: payloadPacket.data,
917
+ options: {
918
+ lockToVersion: v3_1.taskContext.worker?.version,
919
+ queue: item.options?.queue
920
+ ? { name: item.options.queue }
921
+ : queue
922
+ ? { name: queue }
923
+ : undefined,
924
+ concurrencyKey: item.options?.concurrencyKey,
925
+ test: v3_1.taskContext.ctx?.run.isTest,
926
+ payloadType: payloadPacket.dataType,
927
+ delay: item.options?.delay,
928
+ ttl: item.options?.ttl,
929
+ tags: item.options?.tags,
930
+ maxAttempts: item.options?.maxAttempts,
931
+ metadata: item.options?.metadata,
932
+ maxDuration: item.options?.maxDuration,
933
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
934
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
935
+ machine: item.options?.machine,
936
+ priority: item.options?.priority,
937
+ region: item.options?.region,
938
+ },
762
939
  };
763
- }, {
764
- kind: api_1.SpanKind.PRODUCER,
765
- attributes: {
766
- [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
767
- },
768
- });
940
+ }
769
941
  }
770
942
  async function trigger_internal(name, id, payload, parsePayload, options, requestOptions) {
771
943
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
@@ -812,12 +984,15 @@ async function trigger_internal(name, id, payload, parsePayload, options, reques
812
984
  async function batchTrigger_internal(name, taskIdentifier, items, options, parsePayload, requestOptions, queue) {
813
985
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
814
986
  const ctx = v3_1.taskContext.ctx;
815
- const response = await apiClient.batchTriggerV3({
816
- items: await Promise.all(items.map(async (item, index) => {
987
+ // Check if items is an array or a stream
988
+ if (Array.isArray(items)) {
989
+ // Prepare items as BatchItemNDJSON
990
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
817
991
  const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
818
992
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
819
993
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
820
994
  return {
995
+ index,
821
996
  task: taskIdentifier,
822
997
  payload: payloadPacket.data,
823
998
  options: {
@@ -843,33 +1018,73 @@ async function batchTrigger_internal(name, taskIdentifier, items, options, parse
843
1018
  lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
844
1019
  },
845
1020
  };
846
- })),
847
- parentRunId: ctx?.run.id,
848
- }, {
849
- spanParentAsLink: true,
850
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
851
- }, {
852
- name,
853
- tracer: tracer_js_1.tracer,
854
- icon: "trigger",
855
- onResponseBody(body, span) {
856
- if (body && typeof body === "object" && !Array.isArray(body)) {
857
- if ("id" in body && typeof body.id === "string") {
858
- span.setAttribute("batchId", body.id);
859
- }
860
- if ("runCount" in body && Array.isArray(body.runCount)) {
861
- span.setAttribute("runCount", body.runCount);
862
- }
863
- }
864
- },
865
- ...requestOptions,
866
- });
867
- const handle = {
868
- batchId: response.id,
869
- runCount: response.runCount,
870
- publicAccessToken: response.publicAccessToken,
871
- };
872
- return handle;
1021
+ }));
1022
+ // Execute 2-phase batch
1023
+ const response = await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1024
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
1025
+ parentRunId: ctx?.run.id,
1026
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1027
+ }, requestOptions);
1028
+ span.setAttribute("batchId", result.id);
1029
+ span.setAttribute("runCount", result.runCount);
1030
+ return result;
1031
+ }, {
1032
+ kind: api_1.SpanKind.PRODUCER,
1033
+ attributes: {
1034
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1035
+ ...(0, v3_1.accessoryAttributes)({
1036
+ items: [
1037
+ {
1038
+ text: taskIdentifier,
1039
+ variant: "normal",
1040
+ },
1041
+ ],
1042
+ style: "codepath",
1043
+ }),
1044
+ },
1045
+ });
1046
+ const handle = {
1047
+ batchId: response.id,
1048
+ runCount: response.runCount,
1049
+ publicAccessToken: response.publicAccessToken,
1050
+ };
1051
+ return handle;
1052
+ }
1053
+ else {
1054
+ // Stream path: convert to AsyncIterable and transform
1055
+ const asyncItems = normalizeToAsyncIterable(items);
1056
+ const transformedItems = transformSingleTaskBatchItemsStream(taskIdentifier, asyncItems, parsePayload, options, queue);
1057
+ // Execute streaming 2-phase batch
1058
+ const response = await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1059
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
1060
+ parentRunId: ctx?.run.id,
1061
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1062
+ }, requestOptions);
1063
+ span.setAttribute("batchId", result.id);
1064
+ span.setAttribute("runCount", result.runCount);
1065
+ return result;
1066
+ }, {
1067
+ kind: api_1.SpanKind.PRODUCER,
1068
+ attributes: {
1069
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1070
+ ...(0, v3_1.accessoryAttributes)({
1071
+ items: [
1072
+ {
1073
+ text: taskIdentifier,
1074
+ variant: "normal",
1075
+ },
1076
+ ],
1077
+ style: "codepath",
1078
+ }),
1079
+ },
1080
+ });
1081
+ const handle = {
1082
+ batchId: response.id,
1083
+ runCount: response.runCount,
1084
+ publicAccessToken: response.publicAccessToken,
1085
+ };
1086
+ return handle;
1087
+ }
873
1088
  }
874
1089
  async function triggerAndWait_internal(name, id, payload, parsePayload, options, requestOptions) {
875
1090
  const ctx = v3_1.taskContext.ctx;
@@ -931,72 +1146,115 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, optio
931
1146
  throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
932
1147
  }
933
1148
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
934
- return await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
935
- const response = await apiClient.batchTriggerV3({
936
- items: await Promise.all(items.map(async (item, index) => {
937
- const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
938
- const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
939
- const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
940
- return {
941
- task: id,
942
- payload: payloadPacket.data,
943
- options: {
944
- lockToVersion: v3_1.taskContext.worker?.version,
945
- queue: item.options?.queue
946
- ? { name: item.options.queue }
947
- : queue
948
- ? { name: queue }
949
- : undefined,
950
- concurrencyKey: item.options?.concurrencyKey,
951
- test: v3_1.taskContext.ctx?.run.isTest,
952
- payloadType: payloadPacket.dataType,
953
- delay: item.options?.delay,
954
- ttl: item.options?.ttl,
955
- tags: item.options?.tags,
956
- maxAttempts: item.options?.maxAttempts,
957
- metadata: item.options?.metadata,
958
- maxDuration: item.options?.maxDuration,
959
- idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ??
960
- batchItemIdempotencyKey,
961
- idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
962
- machine: item.options?.machine,
963
- priority: item.options?.priority,
964
- region: item.options?.region,
965
- },
966
- };
967
- })),
968
- resumeParentOnCompletion: true,
969
- parentRunId: ctx.run.id,
1149
+ // Check if items is an array or a stream
1150
+ if (Array.isArray(items)) {
1151
+ // Prepare items as BatchItemNDJSON
1152
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
1153
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
1154
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
1155
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
1156
+ return {
1157
+ index,
1158
+ task: id,
1159
+ payload: payloadPacket.data,
1160
+ options: {
1161
+ lockToVersion: v3_1.taskContext.worker?.version,
1162
+ queue: item.options?.queue
1163
+ ? { name: item.options.queue }
1164
+ : queue
1165
+ ? { name: queue }
1166
+ : undefined,
1167
+ concurrencyKey: item.options?.concurrencyKey,
1168
+ test: v3_1.taskContext.ctx?.run.isTest,
1169
+ payloadType: payloadPacket.dataType,
1170
+ delay: item.options?.delay,
1171
+ ttl: item.options?.ttl,
1172
+ tags: item.options?.tags,
1173
+ maxAttempts: item.options?.maxAttempts,
1174
+ metadata: item.options?.metadata,
1175
+ maxDuration: item.options?.maxDuration,
1176
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
1177
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
1178
+ machine: item.options?.machine,
1179
+ priority: item.options?.priority,
1180
+ region: item.options?.region,
1181
+ },
1182
+ };
1183
+ }));
1184
+ return await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1185
+ // Execute 2-phase batch
1186
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
1187
+ parentRunId: ctx.run.id,
1188
+ resumeParentOnCompletion: true,
1189
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1190
+ }, requestOptions);
1191
+ span.setAttribute("batchId", response.id);
1192
+ span.setAttribute("runCount", response.runCount);
1193
+ const result = await v3_1.runtime.waitForBatch({
1194
+ id: response.id,
1195
+ runCount: response.runCount,
1196
+ ctx,
1197
+ });
1198
+ const runs = await handleBatchTaskRunExecutionResult(result.items, id);
1199
+ return {
1200
+ id: result.id,
1201
+ runs,
1202
+ };
970
1203
  }, {
971
- processingStrategy: options?.triggerSequentially ? "sequential" : undefined,
972
- }, requestOptions);
973
- span.setAttribute("batchId", response.id);
974
- span.setAttribute("runCount", response.runCount);
975
- const result = await v3_1.runtime.waitForBatch({
976
- id: response.id,
977
- runCount: response.runCount,
978
- ctx,
1204
+ kind: api_1.SpanKind.PRODUCER,
1205
+ attributes: {
1206
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1207
+ ...(0, v3_1.accessoryAttributes)({
1208
+ items: [
1209
+ {
1210
+ text: id,
1211
+ variant: "normal",
1212
+ },
1213
+ ],
1214
+ style: "codepath",
1215
+ }),
1216
+ },
979
1217
  });
980
- const runs = await handleBatchTaskRunExecutionResult(result.items, id);
981
- return {
982
- id: result.id,
983
- runs,
984
- };
985
- }, {
986
- kind: api_1.SpanKind.PRODUCER,
987
- attributes: {
988
- [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
989
- ...(0, v3_1.accessoryAttributes)({
990
- items: [
991
- {
992
- text: id,
993
- variant: "normal",
994
- },
995
- ],
996
- style: "codepath",
997
- }),
998
- },
999
- });
1218
+ }
1219
+ else {
1220
+ // Stream path: convert to AsyncIterable and transform
1221
+ const asyncItems = normalizeToAsyncIterable(items);
1222
+ const transformedItems = transformSingleTaskBatchItemsStreamForWait(id, asyncItems, parsePayload, options, queue);
1223
+ return await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1224
+ // Execute streaming 2-phase batch
1225
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
1226
+ parentRunId: ctx.run.id,
1227
+ resumeParentOnCompletion: true,
1228
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1229
+ }, requestOptions);
1230
+ span.setAttribute("batchId", response.id);
1231
+ span.setAttribute("runCount", response.runCount);
1232
+ const result = await v3_1.runtime.waitForBatch({
1233
+ id: response.id,
1234
+ runCount: response.runCount,
1235
+ ctx,
1236
+ });
1237
+ const runs = await handleBatchTaskRunExecutionResult(result.items, id);
1238
+ return {
1239
+ id: result.id,
1240
+ runs,
1241
+ };
1242
+ }, {
1243
+ kind: api_1.SpanKind.PRODUCER,
1244
+ attributes: {
1245
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1246
+ ...(0, v3_1.accessoryAttributes)({
1247
+ items: [
1248
+ {
1249
+ text: id,
1250
+ variant: "normal",
1251
+ },
1252
+ ],
1253
+ style: "codepath",
1254
+ }),
1255
+ },
1256
+ });
1257
+ }
1000
1258
  }
1001
1259
  async function handleBatchTaskRunExecutionResult(items, taskIdentifier) {
1002
1260
  const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");