@trigger.dev/sdk 4.3.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TaskRunPromise = exports.SubtaskUnwrapError = void 0;
3
+ exports.BatchTriggerError = exports.TaskRunPromise = exports.SubtaskUnwrapError = void 0;
4
4
  exports.queue = queue;
5
5
  exports.createTask = createTask;
6
6
  exports.createToolTask = createToolTask;
@@ -13,6 +13,7 @@ exports.batchTriggerById = batchTriggerById;
13
13
  exports.batchTriggerByIdAndWait = batchTriggerByIdAndWait;
14
14
  exports.batchTriggerTasks = batchTriggerTasks;
15
15
  exports.batchTriggerAndWaitTasks = batchTriggerAndWaitTasks;
16
+ exports.readableStreamToAsyncIterable = readableStreamToAsyncIterable;
16
17
  const api_1 = require("@opentelemetry/api");
17
18
  const v3_1 = require("@trigger.dev/core/v3");
18
19
  Object.defineProperty(exports, "SubtaskUnwrapError", { enumerable: true, get: function () { return v3_1.SubtaskUnwrapError; } });
@@ -237,63 +238,14 @@ async function batchTriggerAndWait(id, items, options, requestOptions) {
237
238
  async function batchTrigger(id, items, options, requestOptions) {
238
239
  return await batchTrigger_internal("tasks.batchTrigger()", id, items, options, undefined, requestOptions);
239
240
  }
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) {
241
+ // Implementation
242
+ async function batchTriggerById(...args) {
243
+ const [items, options, requestOptions] = args;
294
244
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
295
- const response = await apiClient.batchTriggerV3({
296
- items: await Promise.all(items.map(async (item, index) => {
245
+ // Check if items is an array or a stream
246
+ if (Array.isArray(items)) {
247
+ // Array path: existing logic
248
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
297
249
  const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
298
250
  const parsedPayload = taskMetadata?.fns.parsePayload
299
251
  ? await taskMetadata?.fns.parsePayload(item.payload)
@@ -301,6 +253,7 @@ async function batchTriggerById(items, options, requestOptions) {
301
253
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
302
254
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
303
255
  return {
256
+ index,
304
257
  task: item.id,
305
258
  payload: payloadPacket.data,
306
259
  options: {
@@ -320,257 +273,266 @@ async function batchTriggerById(items, options, requestOptions) {
320
273
  priority: item.options?.priority,
321
274
  region: item.options?.region,
322
275
  lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
276
+ debounce: item.options?.debounce,
323
277
  },
324
278
  };
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;
279
+ }));
280
+ // Execute 2-phase batch
281
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.trigger()", async (span) => {
282
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
283
+ parentRunId: v3_1.taskContext.ctx?.run.id,
284
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
285
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
286
+ }, requestOptions);
287
+ span.setAttribute("batchId", result.id);
288
+ span.setAttribute("runCount", result.runCount);
289
+ return result;
290
+ }, {
291
+ kind: api_1.SpanKind.PRODUCER,
292
+ attributes: {
293
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
294
+ },
295
+ });
296
+ const handle = {
297
+ batchId: response.id,
298
+ runCount: response.runCount,
299
+ publicAccessToken: response.publicAccessToken,
300
+ };
301
+ return handle;
302
+ }
303
+ else {
304
+ // Stream path: convert to AsyncIterable and transform
305
+ const asyncItems = normalizeToAsyncIterable(items);
306
+ const transformedItems = transformBatchItemsStream(asyncItems, options);
307
+ // Execute streaming 2-phase batch
308
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.trigger()", async (span) => {
309
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
310
+ parentRunId: v3_1.taskContext.ctx?.run.id,
311
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
312
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
313
+ }, requestOptions);
314
+ span.setAttribute("batchId", result.id);
315
+ span.setAttribute("runCount", result.runCount);
316
+ return result;
317
+ }, {
318
+ kind: api_1.SpanKind.PRODUCER,
319
+ attributes: {
320
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
321
+ },
322
+ });
323
+ const handle = {
324
+ batchId: response.id,
325
+ runCount: response.runCount,
326
+ publicAccessToken: response.publicAccessToken,
327
+ };
328
+ return handle;
329
+ }
352
330
  }
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) {
331
+ // Implementation
332
+ async function batchTriggerByIdAndWait(...args) {
333
+ const [items, options, requestOptions] = args;
431
334
  const ctx = v3_1.taskContext.ctx;
432
335
  if (!ctx) {
433
336
  throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
434
337
  }
435
338
  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,
339
+ // Check if items is an array or a stream
340
+ if (Array.isArray(items)) {
341
+ // Array path: existing logic
342
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
343
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
344
+ const parsedPayload = taskMetadata?.fns.parsePayload
345
+ ? await taskMetadata?.fns.parsePayload(item.payload)
346
+ : item.payload;
347
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
348
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
349
+ return {
350
+ index,
351
+ task: item.id,
352
+ payload: payloadPacket.data,
353
+ options: {
354
+ lockToVersion: v3_1.taskContext.worker?.version,
355
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
356
+ concurrencyKey: item.options?.concurrencyKey,
357
+ test: v3_1.taskContext.ctx?.run.isTest,
358
+ payloadType: payloadPacket.dataType,
359
+ delay: item.options?.delay,
360
+ ttl: item.options?.ttl,
361
+ tags: item.options?.tags,
362
+ maxAttempts: item.options?.maxAttempts,
363
+ metadata: item.options?.metadata,
364
+ maxDuration: item.options?.maxDuration,
365
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
366
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
367
+ machine: item.options?.machine,
368
+ priority: item.options?.priority,
369
+ region: item.options?.region,
370
+ debounce: item.options?.debounce,
371
+ },
372
+ };
373
+ }));
374
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
375
+ // Execute 2-phase batch
376
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
377
+ parentRunId: ctx.run.id,
378
+ resumeParentOnCompletion: true,
379
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
380
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
381
+ }, requestOptions);
382
+ span.setAttribute("batchId", response.id);
383
+ span.setAttribute("runCount", response.runCount);
384
+ const result = await v3_1.runtime.waitForBatch({
385
+ id: response.id,
386
+ runCount: response.runCount,
387
+ ctx,
388
+ });
389
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
390
+ return {
391
+ id: result.id,
392
+ runs,
393
+ };
471
394
  }, {
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,
395
+ kind: api_1.SpanKind.PRODUCER,
396
+ attributes: {
397
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
398
+ },
399
+ });
400
+ }
401
+ else {
402
+ // Stream path: convert to AsyncIterable and transform
403
+ const asyncItems = normalizeToAsyncIterable(items);
404
+ const transformedItems = transformBatchItemsStreamForWait(asyncItems, options);
405
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
406
+ // Execute streaming 2-phase batch
407
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
408
+ parentRunId: ctx.run.id,
409
+ resumeParentOnCompletion: true,
410
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
411
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
412
+ }, requestOptions);
413
+ span.setAttribute("batchId", response.id);
414
+ span.setAttribute("runCount", response.runCount);
415
+ const result = await v3_1.runtime.waitForBatch({
416
+ id: response.id,
417
+ runCount: response.runCount,
418
+ ctx,
419
+ });
420
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
421
+ return {
422
+ id: result.id,
423
+ runs,
424
+ };
425
+ }, {
426
+ kind: api_1.SpanKind.PRODUCER,
427
+ attributes: {
428
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
429
+ },
430
+ });
431
+ }
432
+ }
433
+ // Implementation
434
+ async function batchTriggerTasks(...args) {
435
+ const [items, options, requestOptions] = args;
436
+ const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
437
+ // Check if items is an array or a stream
438
+ if (Array.isArray(items)) {
439
+ // Array path: existing logic
440
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
441
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
442
+ const parsedPayload = taskMetadata?.fns.parsePayload
443
+ ? await taskMetadata?.fns.parsePayload(item.payload)
444
+ : item.payload;
445
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
446
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
447
+ return {
448
+ index,
449
+ task: item.task.id,
450
+ payload: payloadPacket.data,
451
+ options: {
452
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
453
+ concurrencyKey: item.options?.concurrencyKey,
454
+ test: v3_1.taskContext.ctx?.run.isTest,
455
+ payloadType: payloadPacket.dataType,
456
+ delay: item.options?.delay,
457
+ ttl: item.options?.ttl,
458
+ tags: item.options?.tags,
459
+ maxAttempts: item.options?.maxAttempts,
460
+ metadata: item.options?.metadata,
461
+ maxDuration: item.options?.maxDuration,
462
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
463
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
464
+ machine: item.options?.machine,
465
+ priority: item.options?.priority,
466
+ region: item.options?.region,
467
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
468
+ debounce: item.options?.debounce,
469
+ },
470
+ };
471
+ }));
472
+ // Execute 2-phase batch
473
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.triggerByTask()", async (span) => {
474
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
475
+ parentRunId: v3_1.taskContext.ctx?.run.id,
476
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
477
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
478
+ }, requestOptions);
479
+ span.setAttribute("batchId", result.id);
480
+ span.setAttribute("runCount", result.runCount);
481
+ return result;
482
+ }, {
483
+ kind: api_1.SpanKind.PRODUCER,
484
+ attributes: {
485
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
486
+ },
487
+ });
488
+ const handle = {
489
+ batchId: response.id,
478
490
  runCount: response.runCount,
479
- ctx,
491
+ publicAccessToken: response.publicAccessToken,
492
+ };
493
+ return handle;
494
+ }
495
+ else {
496
+ // Stream path: convert to AsyncIterable and transform
497
+ const streamItems = items;
498
+ const asyncItems = normalizeToAsyncIterable(streamItems);
499
+ const transformedItems = transformBatchByTaskItemsStream(asyncItems, options);
500
+ // Execute streaming 2-phase batch
501
+ const response = await tracer_js_1.tracer.startActiveSpan("batch.triggerByTask()", async (span) => {
502
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
503
+ parentRunId: v3_1.taskContext.ctx?.run.id,
504
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
505
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
506
+ }, requestOptions);
507
+ span.setAttribute("batchId", result.id);
508
+ span.setAttribute("runCount", result.runCount);
509
+ return result;
510
+ }, {
511
+ kind: api_1.SpanKind.PRODUCER,
512
+ attributes: {
513
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
514
+ },
480
515
  });
481
- const runs = await handleBatchTaskRunExecutionResultV2(result.items);
482
- return {
483
- id: result.id,
484
- runs,
516
+ const handle = {
517
+ batchId: response.id,
518
+ runCount: response.runCount,
519
+ publicAccessToken: response.publicAccessToken,
485
520
  };
486
- }, {
487
- kind: api_1.SpanKind.PRODUCER,
488
- attributes: {
489
- [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
490
- },
491
- });
521
+ return handle;
522
+ }
492
523
  }
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) {
524
+ // Implementation
525
+ async function batchTriggerAndWaitTasks(...args) {
526
+ const [items, options, requestOptions] = args;
527
+ const ctx = v3_1.taskContext.ctx;
528
+ if (!ctx) {
529
+ throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
530
+ }
571
531
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
572
- const response = await apiClient.batchTriggerV3({
573
- items: await Promise.all(items.map(async (item, index) => {
532
+ // Check if items is an array or a stream
533
+ if (Array.isArray(items)) {
534
+ // Array path: existing logic
535
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
574
536
  const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
575
537
  const parsedPayload = taskMetadata?.fns.parsePayload
576
538
  ? await taskMetadata?.fns.parsePayload(item.payload)
@@ -578,9 +540,11 @@ async function batchTriggerTasks(items, options, requestOptions) {
578
540
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
579
541
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
580
542
  return {
543
+ index,
581
544
  task: item.task.id,
582
545
  payload: payloadPacket.data,
583
546
  options: {
547
+ lockToVersion: v3_1.taskContext.worker?.version,
584
548
  queue: item.options?.queue ? { name: item.options.queue } : undefined,
585
549
  concurrencyKey: item.options?.concurrencyKey,
586
550
  test: v3_1.taskContext.ctx?.run.isTest,
@@ -596,176 +560,458 @@ async function batchTriggerTasks(items, options, requestOptions) {
596
560
  machine: item.options?.machine,
597
561
  priority: item.options?.priority,
598
562
  region: item.options?.region,
599
- lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
563
+ debounce: item.options?.debounce,
600
564
  },
601
565
  };
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;
566
+ }));
567
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
568
+ // Execute 2-phase batch
569
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
570
+ parentRunId: ctx.run.id,
571
+ resumeParentOnCompletion: true,
572
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
573
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
574
+ }, requestOptions);
575
+ span.setAttribute("batchId", response.id);
576
+ span.setAttribute("runCount", response.runCount);
577
+ const result = await v3_1.runtime.waitForBatch({
578
+ id: response.id,
579
+ runCount: response.runCount,
580
+ ctx,
581
+ });
582
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
583
+ return {
584
+ id: result.id,
585
+ runs,
586
+ };
587
+ }, {
588
+ kind: api_1.SpanKind.PRODUCER,
589
+ attributes: {
590
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
591
+ },
592
+ });
593
+ }
594
+ else {
595
+ // Stream path: convert to AsyncIterable and transform
596
+ const streamItems = items;
597
+ const asyncItems = normalizeToAsyncIterable(streamItems);
598
+ const transformedItems = transformBatchByTaskItemsStreamForWait(asyncItems, options);
599
+ return await tracer_js_1.tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
600
+ // Execute streaming 2-phase batch
601
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
602
+ parentRunId: ctx.run.id,
603
+ resumeParentOnCompletion: true,
604
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
605
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
606
+ }, requestOptions);
607
+ span.setAttribute("batchId", response.id);
608
+ span.setAttribute("runCount", response.runCount);
609
+ const result = await v3_1.runtime.waitForBatch({
610
+ id: response.id,
611
+ runCount: response.runCount,
612
+ ctx,
613
+ });
614
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
615
+ return {
616
+ id: result.id,
617
+ runs,
618
+ };
619
+ }, {
620
+ kind: api_1.SpanKind.PRODUCER,
621
+ attributes: {
622
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
623
+ },
624
+ });
625
+ }
629
626
  }
630
627
  /**
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.
628
+ * Helper function that executes a 2-phase batch trigger:
629
+ * 1. Creates the batch record with expected run count
630
+ * 2. Streams items as NDJSON to the server
633
631
  *
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
632
+ * @param apiClient - The API client instance
633
+ * @param items - Array of batch items
634
+ * @param options - Batch options including trace context settings
635
+ * @param options.spanParentAsLink - If true, child runs will have separate trace IDs with a link to parent.
636
+ * Use true for batchTrigger (fire-and-forget), false for batchTriggerAndWait.
637
+ * @param requestOptions - Optional request options
638
+ * @internal
639
+ */
640
+ async function executeBatchTwoPhase(apiClient, items, options, requestOptions) {
641
+ let batch;
642
+ try {
643
+ // Phase 1: Create batch
644
+ batch = await apiClient.createBatch({
645
+ runCount: items.length,
646
+ parentRunId: options.parentRunId,
647
+ resumeParentOnCompletion: options.resumeParentOnCompletion,
648
+ idempotencyKey: options.idempotencyKey,
649
+ }, { spanParentAsLink: options.spanParentAsLink }, requestOptions);
650
+ }
651
+ catch (error) {
652
+ // Wrap with context about which phase failed
653
+ throw new BatchTriggerError(`Failed to create batch with ${items.length} items`, {
654
+ cause: error,
655
+ phase: "create",
656
+ itemCount: items.length,
657
+ });
658
+ }
659
+ // If the batch was cached (idempotent replay), skip streaming items
660
+ if (!batch.isCached) {
661
+ try {
662
+ // Phase 2: Stream items
663
+ await apiClient.streamBatchItems(batch.id, items, requestOptions);
664
+ }
665
+ catch (error) {
666
+ // Wrap with context about which phase failed and include batch ID
667
+ throw new BatchTriggerError(`Failed to stream items for batch ${batch.id} (${items.length} items)`, { cause: error, phase: "stream", batchId: batch.id, itemCount: items.length });
668
+ }
669
+ }
670
+ return {
671
+ id: batch.id,
672
+ runCount: batch.runCount,
673
+ publicAccessToken: batch.publicAccessToken,
674
+ };
675
+ }
676
+ /**
677
+ * Error thrown when batch trigger operations fail.
678
+ * Includes context about which phase failed and the batch details.
679
+ */
680
+ class BatchTriggerError extends Error {
681
+ phase;
682
+ batchId;
683
+ itemCount;
684
+ constructor(message, options) {
685
+ super(message, { cause: options.cause });
686
+ this.name = "BatchTriggerError";
687
+ this.phase = options.phase;
688
+ this.batchId = options.batchId;
689
+ this.itemCount = options.itemCount;
690
+ }
691
+ }
692
+ exports.BatchTriggerError = BatchTriggerError;
693
+ /**
694
+ * Execute a streaming 2-phase batch trigger where items are streamed from an AsyncIterable.
695
+ * Unlike executeBatchTwoPhase, this doesn't know the count upfront.
638
696
  *
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
697
+ * @param apiClient - The API client instance
698
+ * @param items - AsyncIterable of batch items
699
+ * @param options - Batch options including trace context settings
700
+ * @param options.spanParentAsLink - If true, child runs will have separate trace IDs with a link to parent.
701
+ * Use true for batchTrigger (fire-and-forget), false for batchTriggerAndWait.
702
+ * @param requestOptions - Optional request options
703
+ * @internal
704
+ */
705
+ async function executeBatchTwoPhaseStreaming(apiClient, items, options, requestOptions) {
706
+ // For streaming, we need to buffer items to get the count first
707
+ // This is because createBatch requires runCount upfront
708
+ // In the future, we could add a streaming-first endpoint that doesn't require this
709
+ const itemsArray = [];
710
+ for await (const item of items) {
711
+ itemsArray.push(item);
712
+ }
713
+ // Now we can use the regular 2-phase approach
714
+ return executeBatchTwoPhase(apiClient, itemsArray, options, requestOptions);
715
+ }
716
+ // ============================================================================
717
+ // Streaming Helpers
718
+ // ============================================================================
719
+ /**
720
+ * Type guard to check if a value is an AsyncIterable
721
+ */
722
+ function isAsyncIterable(value) {
723
+ return (value != null &&
724
+ typeof value === "object" &&
725
+ Symbol.asyncIterator in value &&
726
+ typeof value[Symbol.asyncIterator] === "function");
727
+ }
728
+ /**
729
+ * Type guard to check if a value is a ReadableStream
730
+ */
731
+ function isReadableStream(value) {
732
+ return (value != null &&
733
+ typeof value === "object" &&
734
+ "getReader" in value &&
735
+ typeof value.getReader === "function");
736
+ }
737
+ /**
738
+ * Convert a ReadableStream to an AsyncIterable.
739
+ * Properly cancels the stream when the consumer terminates early.
641
740
  *
642
- * @throws {Error} If called outside of a task.run() context
643
- * @throws {Error} If no API client is configured
741
+ * @internal Exported for testing purposes
742
+ */
743
+ async function* readableStreamToAsyncIterable(stream) {
744
+ const reader = stream.getReader();
745
+ try {
746
+ while (true) {
747
+ const { done, value } = await reader.read();
748
+ if (done)
749
+ break;
750
+ yield value;
751
+ }
752
+ }
753
+ finally {
754
+ try {
755
+ await reader.cancel();
756
+ }
757
+ catch {
758
+ // Ignore errors - stream might already be errored or closed
759
+ }
760
+ reader.releaseLock();
761
+ }
762
+ }
763
+ /**
764
+ * Normalize stream input to AsyncIterable
765
+ */
766
+ function normalizeToAsyncIterable(input) {
767
+ if (isReadableStream(input)) {
768
+ return readableStreamToAsyncIterable(input);
769
+ }
770
+ return input;
771
+ }
772
+ /**
773
+ * Transform a stream of BatchByIdItem to BatchItemNDJSON format.
774
+ * Handles payload serialization and idempotency key generation.
644
775
  *
645
- * @example
646
- * ```ts
647
- * import { batch, task } from "@trigger.dev/sdk/v3";
776
+ * @internal
777
+ */
778
+ async function* transformBatchItemsStream(items, options) {
779
+ let index = 0;
780
+ for await (const item of items) {
781
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
782
+ const parsedPayload = taskMetadata?.fns.parsePayload
783
+ ? await taskMetadata?.fns.parsePayload(item.payload)
784
+ : item.payload;
785
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
786
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
787
+ yield {
788
+ index: index++,
789
+ task: item.id,
790
+ payload: payloadPacket.data,
791
+ options: {
792
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
793
+ concurrencyKey: item.options?.concurrencyKey,
794
+ test: v3_1.taskContext.ctx?.run.isTest,
795
+ payloadType: payloadPacket.dataType,
796
+ delay: item.options?.delay,
797
+ ttl: item.options?.ttl,
798
+ tags: item.options?.tags,
799
+ maxAttempts: item.options?.maxAttempts,
800
+ metadata: item.options?.metadata,
801
+ maxDuration: item.options?.maxDuration,
802
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
803
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
804
+ machine: item.options?.machine,
805
+ priority: item.options?.priority,
806
+ region: item.options?.region,
807
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
808
+ debounce: item.options?.debounce,
809
+ },
810
+ };
811
+ }
812
+ }
813
+ /**
814
+ * Transform a stream of BatchByIdAndWaitItem to BatchItemNDJSON format for triggerAndWait.
815
+ * Uses the current worker version for lockToVersion.
648
816
  *
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
- * ]);
817
+ * @internal
818
+ */
819
+ async function* transformBatchItemsStreamForWait(items, options) {
820
+ let index = 0;
821
+ for await (const item of items) {
822
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.id);
823
+ const parsedPayload = taskMetadata?.fns.parsePayload
824
+ ? await taskMetadata?.fns.parsePayload(item.payload)
825
+ : item.payload;
826
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
827
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
828
+ yield {
829
+ index: index++,
830
+ task: item.id,
831
+ payload: payloadPacket.data,
832
+ options: {
833
+ lockToVersion: v3_1.taskContext.worker?.version,
834
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
835
+ concurrencyKey: item.options?.concurrencyKey,
836
+ test: v3_1.taskContext.ctx?.run.isTest,
837
+ payloadType: payloadPacket.dataType,
838
+ delay: item.options?.delay,
839
+ ttl: item.options?.ttl,
840
+ tags: item.options?.tags,
841
+ maxAttempts: item.options?.maxAttempts,
842
+ metadata: item.options?.metadata,
843
+ maxDuration: item.options?.maxDuration,
844
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
845
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
846
+ machine: item.options?.machine,
847
+ priority: item.options?.priority,
848
+ region: item.options?.region,
849
+ debounce: item.options?.debounce,
850
+ },
851
+ };
852
+ }
853
+ }
854
+ /**
855
+ * Transform a stream of BatchByTaskItem to BatchItemNDJSON format.
667
856
  *
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
- * ```
857
+ * @internal
858
+ */
859
+ async function* transformBatchByTaskItemsStream(items, options) {
860
+ let index = 0;
861
+ for await (const item of items) {
862
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
863
+ const parsedPayload = taskMetadata?.fns.parsePayload
864
+ ? await taskMetadata?.fns.parsePayload(item.payload)
865
+ : item.payload;
866
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
867
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
868
+ yield {
869
+ index: index++,
870
+ task: item.task.id,
871
+ payload: payloadPacket.data,
872
+ options: {
873
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
874
+ concurrencyKey: item.options?.concurrencyKey,
875
+ test: v3_1.taskContext.ctx?.run.isTest,
876
+ payloadType: payloadPacket.dataType,
877
+ delay: item.options?.delay,
878
+ ttl: item.options?.ttl,
879
+ tags: item.options?.tags,
880
+ maxAttempts: item.options?.maxAttempts,
881
+ metadata: item.options?.metadata,
882
+ maxDuration: item.options?.maxDuration,
883
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
884
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
885
+ machine: item.options?.machine,
886
+ priority: item.options?.priority,
887
+ region: item.options?.region,
888
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
889
+ debounce: item.options?.debounce,
890
+ },
891
+ };
892
+ }
893
+ }
894
+ /**
895
+ * Transform a stream of BatchByTaskAndWaitItem to BatchItemNDJSON format for triggerAndWait.
686
896
  *
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
897
+ * @internal
898
+ */
899
+ async function* transformBatchByTaskItemsStreamForWait(items, options) {
900
+ let index = 0;
901
+ for await (const item of items) {
902
+ const taskMetadata = v3_1.resourceCatalog.getTask(item.task.id);
903
+ const parsedPayload = taskMetadata?.fns.parsePayload
904
+ ? await taskMetadata?.fns.parsePayload(item.payload)
905
+ : item.payload;
906
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
907
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
908
+ yield {
909
+ index: index++,
910
+ task: item.task.id,
911
+ payload: payloadPacket.data,
912
+ options: {
913
+ lockToVersion: v3_1.taskContext.worker?.version,
914
+ queue: item.options?.queue ? { name: item.options.queue } : undefined,
915
+ concurrencyKey: item.options?.concurrencyKey,
916
+ test: v3_1.taskContext.ctx?.run.isTest,
917
+ payloadType: payloadPacket.dataType,
918
+ delay: item.options?.delay,
919
+ ttl: item.options?.ttl,
920
+ tags: item.options?.tags,
921
+ maxAttempts: item.options?.maxAttempts,
922
+ metadata: item.options?.metadata,
923
+ maxDuration: item.options?.maxDuration,
924
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
925
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
926
+ machine: item.options?.machine,
927
+ priority: item.options?.priority,
928
+ region: item.options?.region,
929
+ debounce: item.options?.debounce,
930
+ },
931
+ };
932
+ }
933
+ }
934
+ /**
935
+ * Transform a stream of BatchItem (single task type) to BatchItemNDJSON format.
700
936
  *
701
- * The function provides full type safety for:
702
- * - Task IDs
703
- * - Payload types
704
- * - Return value types
705
- * - Error handling
937
+ * @internal
706
938
  */
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()");
939
+ async function* transformSingleTaskBatchItemsStream(taskIdentifier, items, parsePayload, options, queue) {
940
+ let index = 0;
941
+ for await (const item of items) {
942
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
943
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
944
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
945
+ yield {
946
+ index: index++,
947
+ task: taskIdentifier,
948
+ payload: payloadPacket.data,
949
+ options: {
950
+ queue: item.options?.queue
951
+ ? { name: item.options.queue }
952
+ : queue
953
+ ? { name: queue }
954
+ : undefined,
955
+ concurrencyKey: item.options?.concurrencyKey,
956
+ test: v3_1.taskContext.ctx?.run.isTest,
957
+ payloadType: payloadPacket.dataType,
958
+ delay: item.options?.delay,
959
+ ttl: item.options?.ttl,
960
+ tags: item.options?.tags,
961
+ maxAttempts: item.options?.maxAttempts,
962
+ metadata: item.options?.metadata,
963
+ maxDuration: item.options?.maxDuration,
964
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
965
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
966
+ machine: item.options?.machine,
967
+ priority: item.options?.priority,
968
+ region: item.options?.region,
969
+ lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
970
+ debounce: item.options?.debounce,
971
+ },
972
+ };
711
973
  }
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,
974
+ }
975
+ /**
976
+ * Transform a stream of BatchTriggerAndWaitItem (single task type) to BatchItemNDJSON format.
977
+ *
978
+ * @internal
979
+ */
980
+ async function* transformSingleTaskBatchItemsStreamForWait(taskIdentifier, items, parsePayload, options, queue) {
981
+ let index = 0;
982
+ for await (const item of items) {
983
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
984
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
985
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
986
+ yield {
987
+ index: index++,
988
+ task: taskIdentifier,
989
+ payload: payloadPacket.data,
990
+ options: {
991
+ lockToVersion: v3_1.taskContext.worker?.version,
992
+ queue: item.options?.queue
993
+ ? { name: item.options.queue }
994
+ : queue
995
+ ? { name: queue }
996
+ : undefined,
997
+ concurrencyKey: item.options?.concurrencyKey,
998
+ test: v3_1.taskContext.ctx?.run.isTest,
999
+ payloadType: payloadPacket.dataType,
1000
+ delay: item.options?.delay,
1001
+ ttl: item.options?.ttl,
1002
+ tags: item.options?.tags,
1003
+ maxAttempts: item.options?.maxAttempts,
1004
+ metadata: item.options?.metadata,
1005
+ maxDuration: item.options?.maxDuration,
1006
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
1007
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
1008
+ machine: item.options?.machine,
1009
+ priority: item.options?.priority,
1010
+ region: item.options?.region,
1011
+ debounce: item.options?.debounce,
1012
+ },
762
1013
  };
763
- }, {
764
- kind: api_1.SpanKind.PRODUCER,
765
- attributes: {
766
- [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
767
- },
768
- });
1014
+ }
769
1015
  }
770
1016
  async function trigger_internal(name, id, payload, parsePayload, options, requestOptions) {
771
1017
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
@@ -791,6 +1037,7 @@ async function trigger_internal(name, id, payload, parsePayload, options, reques
791
1037
  priority: options?.priority,
792
1038
  region: options?.region,
793
1039
  lockToVersion: options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
1040
+ debounce: options?.debounce,
794
1041
  },
795
1042
  }, {
796
1043
  spanParentAsLink: true,
@@ -812,12 +1059,15 @@ async function trigger_internal(name, id, payload, parsePayload, options, reques
812
1059
  async function batchTrigger_internal(name, taskIdentifier, items, options, parsePayload, requestOptions, queue) {
813
1060
  const apiClient = v3_1.apiClientManager.clientOrThrow(requestOptions?.clientConfig);
814
1061
  const ctx = v3_1.taskContext.ctx;
815
- const response = await apiClient.batchTriggerV3({
816
- items: await Promise.all(items.map(async (item, index) => {
1062
+ // Check if items is an array or a stream
1063
+ if (Array.isArray(items)) {
1064
+ // Prepare items as BatchItemNDJSON
1065
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
817
1066
  const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
818
1067
  const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
819
1068
  const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
820
1069
  return {
1070
+ index,
821
1071
  task: taskIdentifier,
822
1072
  payload: payloadPacket.data,
823
1073
  options: {
@@ -843,33 +1093,75 @@ async function batchTrigger_internal(name, taskIdentifier, items, options, parse
843
1093
  lockToVersion: item.options?.version ?? (0, v3_1.getEnvVar)("TRIGGER_VERSION"),
844
1094
  },
845
1095
  };
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;
1096
+ }));
1097
+ // Execute 2-phase batch
1098
+ const response = await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1099
+ const result = await executeBatchTwoPhase(apiClient, ndJsonItems, {
1100
+ parentRunId: ctx?.run.id,
1101
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1102
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
1103
+ }, requestOptions);
1104
+ span.setAttribute("batchId", result.id);
1105
+ span.setAttribute("runCount", result.runCount);
1106
+ return result;
1107
+ }, {
1108
+ kind: api_1.SpanKind.PRODUCER,
1109
+ attributes: {
1110
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1111
+ ...(0, v3_1.accessoryAttributes)({
1112
+ items: [
1113
+ {
1114
+ text: taskIdentifier,
1115
+ variant: "normal",
1116
+ },
1117
+ ],
1118
+ style: "codepath",
1119
+ }),
1120
+ },
1121
+ });
1122
+ const handle = {
1123
+ batchId: response.id,
1124
+ runCount: response.runCount,
1125
+ publicAccessToken: response.publicAccessToken,
1126
+ };
1127
+ return handle;
1128
+ }
1129
+ else {
1130
+ // Stream path: convert to AsyncIterable and transform
1131
+ const asyncItems = normalizeToAsyncIterable(items);
1132
+ const transformedItems = transformSingleTaskBatchItemsStream(taskIdentifier, asyncItems, parsePayload, options, queue);
1133
+ // Execute streaming 2-phase batch
1134
+ const response = await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1135
+ const result = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
1136
+ parentRunId: ctx?.run.id,
1137
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1138
+ spanParentAsLink: true, // Fire-and-forget: child runs get separate trace IDs
1139
+ }, requestOptions);
1140
+ span.setAttribute("batchId", result.id);
1141
+ span.setAttribute("runCount", result.runCount);
1142
+ return result;
1143
+ }, {
1144
+ kind: api_1.SpanKind.PRODUCER,
1145
+ attributes: {
1146
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1147
+ ...(0, v3_1.accessoryAttributes)({
1148
+ items: [
1149
+ {
1150
+ text: taskIdentifier,
1151
+ variant: "normal",
1152
+ },
1153
+ ],
1154
+ style: "codepath",
1155
+ }),
1156
+ },
1157
+ });
1158
+ const handle = {
1159
+ batchId: response.id,
1160
+ runCount: response.runCount,
1161
+ publicAccessToken: response.publicAccessToken,
1162
+ };
1163
+ return handle;
1164
+ }
873
1165
  }
874
1166
  async function triggerAndWait_internal(name, id, payload, parsePayload, options, requestOptions) {
875
1167
  const ctx = v3_1.taskContext.ctx;
@@ -901,6 +1193,7 @@ async function triggerAndWait_internal(name, id, payload, parsePayload, options,
901
1193
  machine: options?.machine,
902
1194
  priority: options?.priority,
903
1195
  region: options?.region,
1196
+ debounce: options?.debounce,
904
1197
  },
905
1198
  }, {}, requestOptions);
906
1199
  span.setAttribute("runId", response.id);
@@ -931,72 +1224,117 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, optio
931
1224
  throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
932
1225
  }
933
1226
  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,
1227
+ // Check if items is an array or a stream
1228
+ if (Array.isArray(items)) {
1229
+ // Prepare items as BatchItemNDJSON
1230
+ const ndJsonItems = await Promise.all(items.map(async (item, index) => {
1231
+ const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
1232
+ const payloadPacket = await (0, v3_1.stringifyIO)(parsedPayload);
1233
+ const batchItemIdempotencyKey = await (0, v3_1.makeIdempotencyKey)((0, v3_1.flattenIdempotencyKey)([options?.idempotencyKey, `${index}`]));
1234
+ return {
1235
+ index,
1236
+ task: id,
1237
+ payload: payloadPacket.data,
1238
+ options: {
1239
+ lockToVersion: v3_1.taskContext.worker?.version,
1240
+ queue: item.options?.queue
1241
+ ? { name: item.options.queue }
1242
+ : queue
1243
+ ? { name: queue }
1244
+ : undefined,
1245
+ concurrencyKey: item.options?.concurrencyKey,
1246
+ test: v3_1.taskContext.ctx?.run.isTest,
1247
+ payloadType: payloadPacket.dataType,
1248
+ delay: item.options?.delay,
1249
+ ttl: item.options?.ttl,
1250
+ tags: item.options?.tags,
1251
+ maxAttempts: item.options?.maxAttempts,
1252
+ metadata: item.options?.metadata,
1253
+ maxDuration: item.options?.maxDuration,
1254
+ idempotencyKey: (await (0, v3_1.makeIdempotencyKey)(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey,
1255
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL,
1256
+ machine: item.options?.machine,
1257
+ priority: item.options?.priority,
1258
+ region: item.options?.region,
1259
+ },
1260
+ };
1261
+ }));
1262
+ return await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1263
+ // Execute 2-phase batch
1264
+ const response = await executeBatchTwoPhase(apiClient, ndJsonItems, {
1265
+ parentRunId: ctx.run.id,
1266
+ resumeParentOnCompletion: true,
1267
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1268
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
1269
+ }, requestOptions);
1270
+ span.setAttribute("batchId", response.id);
1271
+ span.setAttribute("runCount", response.runCount);
1272
+ const result = await v3_1.runtime.waitForBatch({
1273
+ id: response.id,
1274
+ runCount: response.runCount,
1275
+ ctx,
1276
+ });
1277
+ const runs = await handleBatchTaskRunExecutionResult(result.items, id);
1278
+ return {
1279
+ id: result.id,
1280
+ runs,
1281
+ };
970
1282
  }, {
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,
1283
+ kind: api_1.SpanKind.PRODUCER,
1284
+ attributes: {
1285
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1286
+ ...(0, v3_1.accessoryAttributes)({
1287
+ items: [
1288
+ {
1289
+ text: id,
1290
+ variant: "normal",
1291
+ },
1292
+ ],
1293
+ style: "codepath",
1294
+ }),
1295
+ },
979
1296
  });
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
- });
1297
+ }
1298
+ else {
1299
+ // Stream path: convert to AsyncIterable and transform
1300
+ const asyncItems = normalizeToAsyncIterable(items);
1301
+ const transformedItems = transformSingleTaskBatchItemsStreamForWait(id, asyncItems, parsePayload, options, queue);
1302
+ return await tracer_js_1.tracer.startActiveSpan(name, async (span) => {
1303
+ // Execute streaming 2-phase batch
1304
+ const response = await executeBatchTwoPhaseStreaming(apiClient, transformedItems, {
1305
+ parentRunId: ctx.run.id,
1306
+ resumeParentOnCompletion: true,
1307
+ idempotencyKey: await (0, v3_1.makeIdempotencyKey)(options?.idempotencyKey),
1308
+ spanParentAsLink: false, // Waiting: child runs share parent's trace ID
1309
+ }, requestOptions);
1310
+ span.setAttribute("batchId", response.id);
1311
+ span.setAttribute("runCount", response.runCount);
1312
+ const result = await v3_1.runtime.waitForBatch({
1313
+ id: response.id,
1314
+ runCount: response.runCount,
1315
+ ctx,
1316
+ });
1317
+ const runs = await handleBatchTaskRunExecutionResult(result.items, id);
1318
+ return {
1319
+ id: result.id,
1320
+ runs,
1321
+ };
1322
+ }, {
1323
+ kind: api_1.SpanKind.PRODUCER,
1324
+ attributes: {
1325
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "trigger",
1326
+ ...(0, v3_1.accessoryAttributes)({
1327
+ items: [
1328
+ {
1329
+ text: id,
1330
+ variant: "normal",
1331
+ },
1332
+ ],
1333
+ style: "codepath",
1334
+ }),
1335
+ },
1336
+ });
1337
+ }
1000
1338
  }
1001
1339
  async function handleBatchTaskRunExecutionResult(items, taskIdentifier) {
1002
1340
  const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");