@trigger.dev/sdk 3.2.2 → 3.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,5 @@
1
1
  import { SpanKind } from "@opentelemetry/api";
2
- import { SEMATTRS_MESSAGING_DESTINATION, SEMATTRS_MESSAGING_OPERATION, SEMATTRS_MESSAGING_SYSTEM, } from "@opentelemetry/semantic-conventions";
3
- import { accessoryAttributes, apiClientManager, conditionallyImportPacket, convertToolParametersToSchema, createErrorTaskError, defaultRetryOptions, getSchemaParseFn, logger, makeIdempotencyKey, parsePacket, runtime, SemanticInternalAttributes, stringifyIO, SubtaskUnwrapError, taskCatalog, taskContext, TaskRunPromise, } from "@trigger.dev/core/v3";
2
+ import { accessoryAttributes, apiClientManager, conditionallyImportPacket, convertToolParametersToSchema, createErrorTaskError, defaultRetryOptions, getSchemaParseFn, makeIdempotencyKey, parsePacket, runtime, SemanticInternalAttributes, stringifyIO, SubtaskUnwrapError, taskCatalog, taskContext, TaskRunPromise, } from "@trigger.dev/core/v3";
4
3
  import { runs } from "./runs.js";
5
4
  import { tracer } from "./tracer.js";
6
5
  export { SubtaskUnwrapError, TaskRunPromise };
@@ -26,11 +25,11 @@ export function createTask(params) {
26
25
  ...options,
27
26
  });
28
27
  },
29
- batchTrigger: async (items) => {
28
+ batchTrigger: async (items, options) => {
30
29
  const taskMetadata = taskCatalog.getTaskManifest(params.id);
31
30
  return await batchTrigger_internal(taskMetadata && taskMetadata.exportName
32
31
  ? `${taskMetadata.exportName}.batchTrigger()`
33
- : `batchTrigger()`, params.id, items, undefined, undefined, customQueue);
32
+ : `batchTrigger()`, params.id, items, options, undefined, undefined, customQueue);
34
33
  },
35
34
  triggerAndWait: (payload, options) => {
36
35
  const taskMetadata = taskCatalog.getTaskManifest(params.id);
@@ -117,11 +116,11 @@ export function createSchemaTask(params) {
117
116
  ...options,
118
117
  }, requestOptions);
119
118
  },
120
- batchTrigger: async (items, requestOptions) => {
119
+ batchTrigger: async (items, options, requestOptions) => {
121
120
  const taskMetadata = taskCatalog.getTaskManifest(params.id);
122
121
  return await batchTrigger_internal(taskMetadata && taskMetadata.exportName
123
122
  ? `${taskMetadata.exportName}.batchTrigger()`
124
- : `batchTrigger()`, params.id, items, parsePayload, requestOptions, customQueue);
123
+ : `batchTrigger()`, params.id, items, options, parsePayload, requestOptions, customQueue);
125
124
  },
126
125
  triggerAndWait: (payload, options) => {
127
126
  const taskMetadata = taskCatalog.getTaskManifest(params.id);
@@ -264,8 +263,546 @@ export async function triggerAndPoll(id, payload, options, requestOptions) {
264
263
  const handle = await trigger(id, payload, options, requestOptions);
265
264
  return runs.poll(handle, options, requestOptions);
266
265
  }
267
- export async function batchTrigger(id, items, requestOptions) {
268
- return await batchTrigger_internal("tasks.batchTrigger()", id, items, undefined, requestOptions);
266
+ export async function batchTrigger(id, items, options, requestOptions) {
267
+ return await batchTrigger_internal("tasks.batchTrigger()", id, items, options, undefined, requestOptions);
268
+ }
269
+ /**
270
+ * Triggers multiple runs of different tasks with specified payloads and options.
271
+ *
272
+ * @template TTask - The type of task(s) to be triggered, extends AnyTask
273
+ *
274
+ * @param {Array<BatchByIdItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
275
+ * @param {BatchTriggerOptions} [options] - Optional batch-level trigger options
276
+ * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
277
+ *
278
+ * @returns {Promise<BatchRunHandleFromTypes<InferRunTypes<TTask>>>} A promise that resolves with the batch run handle
279
+ * containing batch ID, cached status, idempotency info, runs, and public access token
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * import { batch } from "@trigger.dev/sdk/v3";
284
+ * import type { myTask1, myTask2 } from "~/trigger/myTasks";
285
+ *
286
+ * // Trigger multiple tasks with different payloads
287
+ * const result = await batch.trigger<typeof myTask1 | typeof myTask2>([
288
+ * {
289
+ * id: "my-task-1",
290
+ * payload: { some: "data" },
291
+ * options: {
292
+ * queue: "default",
293
+ * concurrencyKey: "key",
294
+ * idempotencyKey: "unique-key",
295
+ * delay: "5m",
296
+ * tags: ["tag1", "tag2"]
297
+ * }
298
+ * },
299
+ * {
300
+ * id: "my-task-2",
301
+ * payload: { other: "data" }
302
+ * }
303
+ * ]);
304
+ * ```
305
+ *
306
+ * @description
307
+ * Each task item in the array can include:
308
+ * - `id`: The unique identifier of the task
309
+ * - `payload`: The data to pass to the task
310
+ * - `options`: Optional task-specific settings including:
311
+ * - `queue`: Specify a queue for the task
312
+ * - `concurrencyKey`: Control concurrent execution
313
+ * - `idempotencyKey`: Prevent duplicate runs
314
+ * - `idempotencyKeyTTL`: Time-to-live for idempotency key
315
+ * - `delay`: Delay before task execution
316
+ * - `ttl`: Time-to-live for the task
317
+ * - `tags`: Array of tags for the task
318
+ * - `maxAttempts`: Maximum retry attempts
319
+ * - `metadata`: Additional metadata
320
+ * - `maxDuration`: Maximum execution duration
321
+ */
322
+ export async function batchTriggerById(items, options, requestOptions) {
323
+ const apiClient = apiClientManager.clientOrThrow();
324
+ const response = await apiClient.batchTriggerV2({
325
+ items: await Promise.all(items.map(async (item) => {
326
+ const taskMetadata = taskCatalog.getTask(item.id);
327
+ const parsedPayload = taskMetadata?.fns.parsePayload
328
+ ? await taskMetadata?.fns.parsePayload(item.payload)
329
+ : item.payload;
330
+ const payloadPacket = await stringifyIO(parsedPayload);
331
+ return {
332
+ task: item.id,
333
+ payload: payloadPacket.data,
334
+ options: {
335
+ queue: item.options?.queue,
336
+ concurrencyKey: item.options?.concurrencyKey,
337
+ test: taskContext.ctx?.run.isTest,
338
+ payloadType: payloadPacket.dataType,
339
+ idempotencyKey: await makeIdempotencyKey(item.options?.idempotencyKey),
340
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL,
341
+ delay: item.options?.delay,
342
+ ttl: item.options?.ttl,
343
+ tags: item.options?.tags,
344
+ maxAttempts: item.options?.maxAttempts,
345
+ parentAttempt: taskContext.ctx?.attempt.id,
346
+ metadata: item.options?.metadata,
347
+ maxDuration: item.options?.maxDuration,
348
+ },
349
+ };
350
+ })),
351
+ }, {
352
+ spanParentAsLink: true,
353
+ idempotencyKey: await makeIdempotencyKey(options?.idempotencyKey),
354
+ idempotencyKeyTTL: options?.idempotencyKeyTTL,
355
+ }, {
356
+ name: "batch.trigger()",
357
+ tracer,
358
+ icon: "trigger",
359
+ onResponseBody(body, span) {
360
+ if (body && typeof body === "object" && !Array.isArray(body)) {
361
+ if ("id" in body && typeof body.id === "string") {
362
+ span.setAttribute("batchId", body.id);
363
+ }
364
+ if ("runs" in body && Array.isArray(body.runs)) {
365
+ span.setAttribute("runCount", body.runs.length);
366
+ }
367
+ if ("isCached" in body && typeof body.isCached === "boolean") {
368
+ if (body.isCached) {
369
+ console.warn(`Result is a cached response because the request was idempotent.`);
370
+ }
371
+ span.setAttribute("isCached", body.isCached);
372
+ }
373
+ if ("idempotencyKey" in body && typeof body.idempotencyKey === "string") {
374
+ span.setAttribute("idempotencyKey", body.idempotencyKey);
375
+ }
376
+ }
377
+ },
378
+ ...requestOptions,
379
+ });
380
+ const handle = {
381
+ batchId: response.id,
382
+ isCached: response.isCached,
383
+ idempotencyKey: response.idempotencyKey,
384
+ runs: response.runs,
385
+ publicAccessToken: response.publicAccessToken,
386
+ };
387
+ return handle;
388
+ }
389
+ /**
390
+ * Triggers multiple tasks and waits for all of them to complete before returning their results.
391
+ * This function must be called from within a task.run() context.
392
+ *
393
+ * @template TTask - Union type of tasks to be triggered, extends AnyTask
394
+ *
395
+ * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
396
+ * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
397
+ *
398
+ * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
399
+ * success/failure status and strongly-typed outputs for each task
400
+ *
401
+ * @throws {Error} If called outside of a task.run() context
402
+ * @throws {Error} If no API client is configured
403
+ *
404
+ * @example
405
+ * ```ts
406
+ * import { batch, task } from "@trigger.dev/sdk/v3";
407
+ *
408
+ * export const parentTask = task({
409
+ * id: "parent-task",
410
+ * run: async (payload: string) => {
411
+ * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
412
+ * {
413
+ * id: "child-task-1",
414
+ * payload: { foo: "World" },
415
+ * options: {
416
+ * queue: "default",
417
+ * delay: "5m",
418
+ * tags: ["batch", "child1"]
419
+ * }
420
+ * },
421
+ * {
422
+ * id: "child-task-2",
423
+ * payload: { bar: 42 }
424
+ * }
425
+ * ]);
426
+ *
427
+ * // Type-safe result handling
428
+ * for (const result of results) {
429
+ * if (result.ok) {
430
+ * switch (result.taskIdentifier) {
431
+ * case "child-task-1":
432
+ * console.log("Child task 1 output:", result.output); // string type
433
+ * break;
434
+ * case "child-task-2":
435
+ * console.log("Child task 2 output:", result.output); // number type
436
+ * break;
437
+ * }
438
+ * } else {
439
+ * console.error("Task failed:", result.error);
440
+ * }
441
+ * }
442
+ * }
443
+ * });
444
+ * ```
445
+ *
446
+ * @description
447
+ * Each task item in the array can include:
448
+ * - `id`: The task identifier (must match one of the tasks in the union type)
449
+ * - `payload`: Strongly-typed payload matching the task's input type
450
+ * - `options`: Optional task-specific settings including:
451
+ * - `queue`: Specify a queue for the task
452
+ * - `concurrencyKey`: Control concurrent execution
453
+ * - `delay`: Delay before task execution
454
+ * - `ttl`: Time-to-live for the task
455
+ * - `tags`: Array of tags for the task
456
+ * - `maxAttempts`: Maximum retry attempts
457
+ * - `metadata`: Additional metadata
458
+ * - `maxDuration`: Maximum execution duration
459
+ *
460
+ * The function provides full type safety for:
461
+ * - Task IDs
462
+ * - Payload types
463
+ * - Return value types
464
+ * - Error handling
465
+ */
466
+ export async function batchTriggerByIdAndWait(items, requestOptions) {
467
+ const ctx = taskContext.ctx;
468
+ if (!ctx) {
469
+ throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
470
+ }
471
+ const apiClient = apiClientManager.clientOrThrow();
472
+ return await tracer.startActiveSpan("batch.triggerAndWait()", async (span) => {
473
+ const response = await apiClient.batchTriggerV2({
474
+ items: await Promise.all(items.map(async (item) => {
475
+ const taskMetadata = taskCatalog.getTask(item.id);
476
+ const parsedPayload = taskMetadata?.fns.parsePayload
477
+ ? await taskMetadata?.fns.parsePayload(item.payload)
478
+ : item.payload;
479
+ const payloadPacket = await stringifyIO(parsedPayload);
480
+ return {
481
+ task: item.id,
482
+ payload: payloadPacket.data,
483
+ options: {
484
+ lockToVersion: taskContext.worker?.version,
485
+ queue: item.options?.queue,
486
+ concurrencyKey: item.options?.concurrencyKey,
487
+ test: taskContext.ctx?.run.isTest,
488
+ payloadType: payloadPacket.dataType,
489
+ delay: item.options?.delay,
490
+ ttl: item.options?.ttl,
491
+ tags: item.options?.tags,
492
+ maxAttempts: item.options?.maxAttempts,
493
+ metadata: item.options?.metadata,
494
+ maxDuration: item.options?.maxDuration,
495
+ },
496
+ };
497
+ })),
498
+ dependentAttempt: ctx.attempt.id,
499
+ }, {}, requestOptions);
500
+ span.setAttribute("batchId", response.id);
501
+ span.setAttribute("runCount", response.runs.length);
502
+ span.setAttribute("isCached", response.isCached);
503
+ if (response.isCached) {
504
+ console.warn(`Result is a cached response because the request was idempotent.`);
505
+ }
506
+ if (response.idempotencyKey) {
507
+ span.setAttribute("idempotencyKey", response.idempotencyKey);
508
+ }
509
+ const result = await runtime.waitForBatch({
510
+ id: response.id,
511
+ runs: response.runs.map((run) => run.id),
512
+ ctx,
513
+ });
514
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
515
+ return {
516
+ id: result.id,
517
+ runs,
518
+ };
519
+ }, {
520
+ kind: SpanKind.PRODUCER,
521
+ attributes: {
522
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
523
+ },
524
+ });
525
+ }
526
+ /**
527
+ * Triggers multiple tasks and waits for all of them to complete before returning their results.
528
+ * This function must be called from within a task.run() context.
529
+ *
530
+ * @template TTask - Union type of tasks to be triggered, extends AnyTask
531
+ *
532
+ * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
533
+ * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
534
+ *
535
+ * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
536
+ * success/failure status and strongly-typed outputs for each task
537
+ *
538
+ * @throws {Error} If called outside of a task.run() context
539
+ * @throws {Error} If no API client is configured
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * import { batch, task } from "@trigger.dev/sdk/v3";
544
+ *
545
+ * export const parentTask = task({
546
+ * id: "parent-task",
547
+ * run: async (payload: string) => {
548
+ * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
549
+ * {
550
+ * id: "child-task-1",
551
+ * payload: { foo: "World" },
552
+ * options: {
553
+ * queue: "default",
554
+ * delay: "5m",
555
+ * tags: ["batch", "child1"]
556
+ * }
557
+ * },
558
+ * {
559
+ * id: "child-task-2",
560
+ * payload: { bar: 42 }
561
+ * }
562
+ * ]);
563
+ *
564
+ * // Type-safe result handling
565
+ * for (const result of results) {
566
+ * if (result.ok) {
567
+ * switch (result.taskIdentifier) {
568
+ * case "child-task-1":
569
+ * console.log("Child task 1 output:", result.output); // string type
570
+ * break;
571
+ * case "child-task-2":
572
+ * console.log("Child task 2 output:", result.output); // number type
573
+ * break;
574
+ * }
575
+ * } else {
576
+ * console.error("Task failed:", result.error);
577
+ * }
578
+ * }
579
+ * }
580
+ * });
581
+ * ```
582
+ *
583
+ * @description
584
+ * Each task item in the array can include:
585
+ * - `id`: The task identifier (must match one of the tasks in the union type)
586
+ * - `payload`: Strongly-typed payload matching the task's input type
587
+ * - `options`: Optional task-specific settings including:
588
+ * - `queue`: Specify a queue for the task
589
+ * - `concurrencyKey`: Control concurrent execution
590
+ * - `delay`: Delay before task execution
591
+ * - `ttl`: Time-to-live for the task
592
+ * - `tags`: Array of tags for the task
593
+ * - `maxAttempts`: Maximum retry attempts
594
+ * - `metadata`: Additional metadata
595
+ * - `maxDuration`: Maximum execution duration
596
+ *
597
+ * The function provides full type safety for:
598
+ * - Task IDs
599
+ * - Payload types
600
+ * - Return value types
601
+ * - Error handling
602
+ */
603
+ export async function batchTriggerTasks(items, options, requestOptions) {
604
+ const apiClient = apiClientManager.clientOrThrow();
605
+ const response = await apiClient.batchTriggerV2({
606
+ items: await Promise.all(items.map(async (item) => {
607
+ const taskMetadata = taskCatalog.getTask(item.task.id);
608
+ const parsedPayload = taskMetadata?.fns.parsePayload
609
+ ? await taskMetadata?.fns.parsePayload(item.payload)
610
+ : item.payload;
611
+ const payloadPacket = await stringifyIO(parsedPayload);
612
+ return {
613
+ task: item.task.id,
614
+ payload: payloadPacket.data,
615
+ options: {
616
+ queue: item.options?.queue,
617
+ concurrencyKey: item.options?.concurrencyKey,
618
+ test: taskContext.ctx?.run.isTest,
619
+ payloadType: payloadPacket.dataType,
620
+ idempotencyKey: await makeIdempotencyKey(item.options?.idempotencyKey),
621
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL,
622
+ delay: item.options?.delay,
623
+ ttl: item.options?.ttl,
624
+ tags: item.options?.tags,
625
+ maxAttempts: item.options?.maxAttempts,
626
+ parentAttempt: taskContext.ctx?.attempt.id,
627
+ metadata: item.options?.metadata,
628
+ maxDuration: item.options?.maxDuration,
629
+ },
630
+ };
631
+ })),
632
+ }, {
633
+ spanParentAsLink: true,
634
+ idempotencyKey: await makeIdempotencyKey(options?.idempotencyKey),
635
+ idempotencyKeyTTL: options?.idempotencyKeyTTL,
636
+ }, {
637
+ name: "batch.triggerByTask()",
638
+ tracer,
639
+ icon: "trigger",
640
+ onResponseBody(body, span) {
641
+ if (body && typeof body === "object" && !Array.isArray(body)) {
642
+ if ("id" in body && typeof body.id === "string") {
643
+ span.setAttribute("batchId", body.id);
644
+ }
645
+ if ("runs" in body && Array.isArray(body.runs)) {
646
+ span.setAttribute("runCount", body.runs.length);
647
+ }
648
+ if ("isCached" in body && typeof body.isCached === "boolean") {
649
+ if (body.isCached) {
650
+ console.warn(`Result is a cached response because the request was idempotent.`);
651
+ }
652
+ span.setAttribute("isCached", body.isCached);
653
+ }
654
+ if ("idempotencyKey" in body && typeof body.idempotencyKey === "string") {
655
+ span.setAttribute("idempotencyKey", body.idempotencyKey);
656
+ }
657
+ }
658
+ },
659
+ ...requestOptions,
660
+ });
661
+ const handle = {
662
+ batchId: response.id,
663
+ isCached: response.isCached,
664
+ idempotencyKey: response.idempotencyKey,
665
+ runs: response.runs,
666
+ publicAccessToken: response.publicAccessToken,
667
+ };
668
+ return handle;
669
+ }
670
+ /**
671
+ * Triggers multiple tasks and waits for all of them to complete before returning their results.
672
+ * This function must be called from within a task.run() context.
673
+ *
674
+ * @template TTask - Union type of tasks to be triggered, extends AnyTask
675
+ *
676
+ * @param {Array<BatchByIdAndWaitItem<InferRunTypes<TTask>>>} items - Array of task items to trigger
677
+ * @param {TriggerApiRequestOptions} [requestOptions] - Optional API request configuration
678
+ *
679
+ * @returns {Promise<BatchByIdResult<TTask>>} A promise that resolves with the batch results, including
680
+ * success/failure status and strongly-typed outputs for each task
681
+ *
682
+ * @throws {Error} If called outside of a task.run() context
683
+ * @throws {Error} If no API client is configured
684
+ *
685
+ * @example
686
+ * ```ts
687
+ * import { batch, task } from "@trigger.dev/sdk/v3";
688
+ *
689
+ * export const parentTask = task({
690
+ * id: "parent-task",
691
+ * run: async (payload: string) => {
692
+ * const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
693
+ * {
694
+ * id: "child-task-1",
695
+ * payload: { foo: "World" },
696
+ * options: {
697
+ * queue: "default",
698
+ * delay: "5m",
699
+ * tags: ["batch", "child1"]
700
+ * }
701
+ * },
702
+ * {
703
+ * id: "child-task-2",
704
+ * payload: { bar: 42 }
705
+ * }
706
+ * ]);
707
+ *
708
+ * // Type-safe result handling
709
+ * for (const result of results) {
710
+ * if (result.ok) {
711
+ * switch (result.taskIdentifier) {
712
+ * case "child-task-1":
713
+ * console.log("Child task 1 output:", result.output); // string type
714
+ * break;
715
+ * case "child-task-2":
716
+ * console.log("Child task 2 output:", result.output); // number type
717
+ * break;
718
+ * }
719
+ * } else {
720
+ * console.error("Task failed:", result.error);
721
+ * }
722
+ * }
723
+ * }
724
+ * });
725
+ * ```
726
+ *
727
+ * @description
728
+ * Each task item in the array can include:
729
+ * - `id`: The task identifier (must match one of the tasks in the union type)
730
+ * - `payload`: Strongly-typed payload matching the task's input type
731
+ * - `options`: Optional task-specific settings including:
732
+ * - `queue`: Specify a queue for the task
733
+ * - `concurrencyKey`: Control concurrent execution
734
+ * - `delay`: Delay before task execution
735
+ * - `ttl`: Time-to-live for the task
736
+ * - `tags`: Array of tags for the task
737
+ * - `maxAttempts`: Maximum retry attempts
738
+ * - `metadata`: Additional metadata
739
+ * - `maxDuration`: Maximum execution duration
740
+ *
741
+ * The function provides full type safety for:
742
+ * - Task IDs
743
+ * - Payload types
744
+ * - Return value types
745
+ * - Error handling
746
+ */
747
+ export async function batchTriggerAndWaitTasks(items, requestOptions) {
748
+ const ctx = taskContext.ctx;
749
+ if (!ctx) {
750
+ throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
751
+ }
752
+ const apiClient = apiClientManager.clientOrThrow();
753
+ return await tracer.startActiveSpan("batch.triggerByTaskAndWait()", async (span) => {
754
+ const response = await apiClient.batchTriggerV2({
755
+ items: await Promise.all(items.map(async (item) => {
756
+ const taskMetadata = taskCatalog.getTask(item.task.id);
757
+ const parsedPayload = taskMetadata?.fns.parsePayload
758
+ ? await taskMetadata?.fns.parsePayload(item.payload)
759
+ : item.payload;
760
+ const payloadPacket = await stringifyIO(parsedPayload);
761
+ return {
762
+ task: item.task.id,
763
+ payload: payloadPacket.data,
764
+ options: {
765
+ lockToVersion: taskContext.worker?.version,
766
+ queue: item.options?.queue,
767
+ concurrencyKey: item.options?.concurrencyKey,
768
+ test: taskContext.ctx?.run.isTest,
769
+ payloadType: payloadPacket.dataType,
770
+ delay: item.options?.delay,
771
+ ttl: item.options?.ttl,
772
+ tags: item.options?.tags,
773
+ maxAttempts: item.options?.maxAttempts,
774
+ metadata: item.options?.metadata,
775
+ maxDuration: item.options?.maxDuration,
776
+ },
777
+ };
778
+ })),
779
+ dependentAttempt: ctx.attempt.id,
780
+ }, {}, requestOptions);
781
+ span.setAttribute("batchId", response.id);
782
+ span.setAttribute("runCount", response.runs.length);
783
+ span.setAttribute("isCached", response.isCached);
784
+ if (response.isCached) {
785
+ console.warn(`Result is a cached response because the request was idempotent.`);
786
+ }
787
+ if (response.idempotencyKey) {
788
+ span.setAttribute("idempotencyKey", response.idempotencyKey);
789
+ }
790
+ const result = await runtime.waitForBatch({
791
+ id: response.id,
792
+ runs: response.runs.map((run) => run.id),
793
+ ctx,
794
+ });
795
+ const runs = await handleBatchTaskRunExecutionResultV2(result.items);
796
+ return {
797
+ id: result.id,
798
+ runs,
799
+ };
800
+ }, {
801
+ kind: SpanKind.PRODUCER,
802
+ attributes: {
803
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
804
+ },
805
+ });
269
806
  }
270
807
  async function trigger_internal(name, id, payload, parsePayload, options, requestOptions) {
271
808
  const apiClient = apiClientManager.clientOrThrow();
@@ -279,6 +816,7 @@ async function trigger_internal(name, id, payload, parsePayload, options, reques
279
816
  test: taskContext.ctx?.run.isTest,
280
817
  payloadType: payloadPacket.dataType,
281
818
  idempotencyKey: await makeIdempotencyKey(options?.idempotencyKey),
819
+ idempotencyKeyTTL: options?.idempotencyKeyTTL,
282
820
  delay: options?.delay,
283
821
  ttl: options?.ttl,
284
822
  tags: options?.tags,
@@ -293,30 +831,25 @@ async function trigger_internal(name, id, payload, parsePayload, options, reques
293
831
  name,
294
832
  tracer,
295
833
  icon: "trigger",
296
- attributes: {
297
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
298
- ["messaging.client_id"]: taskContext.worker?.id,
299
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
300
- },
301
834
  onResponseBody: (body, span) => {
302
- body &&
303
- typeof body === "object" &&
304
- !Array.isArray(body) &&
305
- "id" in body &&
306
- typeof body.id === "string" &&
307
- span.setAttribute("messaging.message.id", body.id);
835
+ if (body && typeof body === "object" && !Array.isArray(body)) {
836
+ if ("id" in body && typeof body.id === "string") {
837
+ span.setAttribute("runId", body.id);
838
+ }
839
+ }
308
840
  },
309
841
  ...requestOptions,
310
842
  });
311
843
  return handle;
312
844
  }
313
- async function batchTrigger_internal(name, id, items, parsePayload, requestOptions, queue) {
845
+ async function batchTrigger_internal(name, taskIdentifier, items, options, parsePayload, requestOptions, queue) {
314
846
  const apiClient = apiClientManager.clientOrThrow();
315
- const response = await apiClient.batchTriggerTask(id, {
847
+ const response = await apiClient.batchTriggerV2({
316
848
  items: await Promise.all(items.map(async (item) => {
317
849
  const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
318
850
  const payloadPacket = await stringifyIO(parsedPayload);
319
851
  return {
852
+ task: taskIdentifier,
320
853
  payload: payloadPacket.data,
321
854
  options: {
322
855
  queue: item.options?.queue ?? queue,
@@ -324,6 +857,7 @@ async function batchTrigger_internal(name, id, items, parsePayload, requestOptio
324
857
  test: taskContext.ctx?.run.isTest,
325
858
  payloadType: payloadPacket.dataType,
326
859
  idempotencyKey: await makeIdempotencyKey(item.options?.idempotencyKey),
860
+ idempotencyKeyTTL: item.options?.idempotencyKeyTTL,
327
861
  delay: item.options?.delay,
328
862
  ttl: item.options?.ttl,
329
863
  tags: item.options?.tags,
@@ -334,20 +868,40 @@ async function batchTrigger_internal(name, id, items, parsePayload, requestOptio
334
868
  },
335
869
  };
336
870
  })),
337
- }, { spanParentAsLink: true }, {
871
+ }, {
872
+ spanParentAsLink: true,
873
+ idempotencyKey: await makeIdempotencyKey(options?.idempotencyKey),
874
+ idempotencyKeyTTL: options?.idempotencyKeyTTL,
875
+ }, {
338
876
  name,
339
877
  tracer,
340
878
  icon: "trigger",
341
- attributes: {
342
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
343
- ["messaging.client_id"]: taskContext.worker?.id,
344
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
879
+ onResponseBody(body, span) {
880
+ if (body && typeof body === "object" && !Array.isArray(body)) {
881
+ if ("id" in body && typeof body.id === "string") {
882
+ span.setAttribute("batchId", body.id);
883
+ }
884
+ if ("runs" in body && Array.isArray(body.runs)) {
885
+ span.setAttribute("runCount", body.runs.length);
886
+ }
887
+ if ("isCached" in body && typeof body.isCached === "boolean") {
888
+ if (body.isCached) {
889
+ console.warn(`Result is a cached response because the request was idempotent.`);
890
+ }
891
+ span.setAttribute("isCached", body.isCached);
892
+ }
893
+ if ("idempotencyKey" in body && typeof body.idempotencyKey === "string") {
894
+ span.setAttribute("idempotencyKey", body.idempotencyKey);
895
+ }
896
+ }
345
897
  },
346
898
  ...requestOptions,
347
899
  });
348
900
  const handle = {
349
- batchId: response.batchId,
350
- runs: response.runs.map((id) => ({ id })),
901
+ batchId: response.id,
902
+ isCached: response.isCached,
903
+ idempotencyKey: response.idempotencyKey,
904
+ runs: response.runs,
351
905
  publicAccessToken: response.publicAccessToken,
352
906
  };
353
907
  return handle;
@@ -370,7 +924,6 @@ async function triggerAndWait_internal(name, id, payload, parsePayload, options,
370
924
  concurrencyKey: options?.concurrencyKey,
371
925
  test: taskContext.ctx?.run.isTest,
372
926
  payloadType: payloadPacket.dataType,
373
- idempotencyKey: await makeIdempotencyKey(options?.idempotencyKey),
374
927
  delay: options?.delay,
375
928
  ttl: options?.ttl,
376
929
  tags: options?.tags,
@@ -379,31 +932,16 @@ async function triggerAndWait_internal(name, id, payload, parsePayload, options,
379
932
  maxDuration: options?.maxDuration,
380
933
  },
381
934
  }, {}, requestOptions);
382
- span.setAttribute("messaging.message.id", response.id);
383
- if (options?.idempotencyKey) {
384
- // If an idempotency key is provided, we can check if the result is already available
385
- const result = await apiClient.getRunResult(response.id);
386
- if (result) {
387
- logger.log(`Result reused from previous task run with idempotency key '${options.idempotencyKey}'.`, {
388
- runId: response.id,
389
- idempotencyKey: options.idempotencyKey,
390
- });
391
- return await handleTaskRunExecutionResult(result);
392
- }
393
- }
935
+ span.setAttribute("runId", response.id);
394
936
  const result = await runtime.waitForTask({
395
937
  id: response.id,
396
938
  ctx,
397
939
  });
398
- return await handleTaskRunExecutionResult(result);
940
+ return await handleTaskRunExecutionResult(result, id);
399
941
  }, {
400
942
  kind: SpanKind.PRODUCER,
401
943
  attributes: {
402
944
  [SemanticInternalAttributes.STYLE_ICON]: "trigger",
403
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
404
- ["messaging.client_id"]: taskContext.worker?.id,
405
- [SEMATTRS_MESSAGING_DESTINATION]: id,
406
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
407
945
  ...accessoryAttributes({
408
946
  items: [
409
947
  {
@@ -423,11 +961,12 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, reque
423
961
  }
424
962
  const apiClient = apiClientManager.clientOrThrow();
425
963
  return await tracer.startActiveSpan(name, async (span) => {
426
- const response = await apiClient.batchTriggerTask(id, {
964
+ const response = await apiClient.batchTriggerV2({
427
965
  items: await Promise.all(items.map(async (item) => {
428
966
  const parsedPayload = parsePayload ? await parsePayload(item.payload) : item.payload;
429
967
  const payloadPacket = await stringifyIO(parsedPayload);
430
968
  return {
969
+ task: id,
431
970
  payload: payloadPacket.data,
432
971
  options: {
433
972
  lockToVersion: taskContext.worker?.version,
@@ -435,7 +974,6 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, reque
435
974
  concurrencyKey: item.options?.concurrencyKey,
436
975
  test: taskContext.ctx?.run.isTest,
437
976
  payloadType: payloadPacket.dataType,
438
- idempotencyKey: await makeIdempotencyKey(item.options?.idempotencyKey),
439
977
  delay: item.options?.delay,
440
978
  ttl: item.options?.ttl,
441
979
  tags: item.options?.tags,
@@ -447,52 +985,21 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, reque
447
985
  })),
448
986
  dependentAttempt: ctx.attempt.id,
449
987
  }, {}, requestOptions);
450
- span.setAttribute("messaging.message.id", response.batchId);
451
- const getBatchResults = async () => {
452
- // We need to check if the results are already available, but only if any of the items options has an idempotency key
453
- const hasIdempotencyKey = items.some((item) => item.options?.idempotencyKey);
454
- if (hasIdempotencyKey) {
455
- const results = await apiClient.getBatchResults(response.batchId);
456
- if (results) {
457
- return results;
458
- }
459
- }
460
- return {
461
- id: response.batchId,
462
- items: [],
463
- };
464
- };
465
- const existingResults = await getBatchResults();
466
- const incompleteRuns = response.runs.filter((runId) => !existingResults.items.some((item) => item.id === runId));
467
- if (incompleteRuns.length === 0) {
468
- logger.log(`Results reused from previous task runs because of the provided idempotency keys.`);
469
- // All runs are already completed
470
- const runs = await handleBatchTaskRunExecutionResult(existingResults.items);
471
- return {
472
- id: existingResults.id,
473
- runs,
474
- };
988
+ span.setAttribute("batchId", response.id);
989
+ span.setAttribute("runCount", response.runs.length);
990
+ span.setAttribute("isCached", response.isCached);
991
+ if (response.isCached) {
992
+ console.warn(`Result is a cached response because the request was idempotent.`);
993
+ }
994
+ if (response.idempotencyKey) {
995
+ span.setAttribute("idempotencyKey", response.idempotencyKey);
475
996
  }
476
997
  const result = await runtime.waitForBatch({
477
- id: response.batchId,
478
- runs: incompleteRuns,
998
+ id: response.id,
999
+ runs: response.runs.map((run) => run.id),
479
1000
  ctx,
480
1001
  });
481
- // Combine the already completed runs with the newly completed runs, ordered by the original order
482
- const combinedItems = [];
483
- for (const runId of response.runs) {
484
- const existingItem = existingResults.items.find((item) => item.id === runId);
485
- if (existingItem) {
486
- combinedItems.push(existingItem);
487
- }
488
- else {
489
- const newItem = result.items.find((item) => item.id === runId);
490
- if (newItem) {
491
- combinedItems.push(newItem);
492
- }
493
- }
494
- }
495
- const runs = await handleBatchTaskRunExecutionResult(combinedItems);
1002
+ const runs = await handleBatchTaskRunExecutionResult(result.items, id);
496
1003
  return {
497
1004
  id: result.id,
498
1005
  runs,
@@ -501,11 +1008,6 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, reque
501
1008
  kind: SpanKind.PRODUCER,
502
1009
  attributes: {
503
1010
  [SemanticInternalAttributes.STYLE_ICON]: "trigger",
504
- ["messaging.batch.message_count"]: items.length,
505
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
506
- ["messaging.client_id"]: taskContext.worker?.id,
507
- [SEMATTRS_MESSAGING_DESTINATION]: id,
508
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
509
1011
  ...accessoryAttributes({
510
1012
  items: [
511
1013
  {
@@ -518,17 +1020,35 @@ async function batchTriggerAndWait_internal(name, id, items, parsePayload, reque
518
1020
  },
519
1021
  });
520
1022
  }
521
- async function handleBatchTaskRunExecutionResult(items) {
1023
+ async function handleBatchTaskRunExecutionResult(items, taskIdentifier) {
1024
+ const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");
1025
+ if (!someObjectStoreOutputs) {
1026
+ const results = await Promise.all(items.map(async (item) => {
1027
+ return await handleTaskRunExecutionResult(item, taskIdentifier);
1028
+ }));
1029
+ return results;
1030
+ }
1031
+ return await tracer.startActiveSpan("store.downloadPayloads", async (span) => {
1032
+ const results = await Promise.all(items.map(async (item) => {
1033
+ return await handleTaskRunExecutionResult(item, taskIdentifier);
1034
+ }));
1035
+ return results;
1036
+ }, {
1037
+ kind: SpanKind.INTERNAL,
1038
+ [SemanticInternalAttributes.STYLE_ICON]: "cloud-download",
1039
+ });
1040
+ }
1041
+ async function handleBatchTaskRunExecutionResultV2(items) {
522
1042
  const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");
523
1043
  if (!someObjectStoreOutputs) {
524
1044
  const results = await Promise.all(items.map(async (item) => {
525
- return await handleTaskRunExecutionResult(item);
1045
+ return await handleTaskRunExecutionResult(item, item.taskIdentifier ?? "unknown");
526
1046
  }));
527
1047
  return results;
528
1048
  }
529
1049
  return await tracer.startActiveSpan("store.downloadPayloads", async (span) => {
530
1050
  const results = await Promise.all(items.map(async (item) => {
531
- return await handleTaskRunExecutionResult(item);
1051
+ return await handleTaskRunExecutionResult(item, item.taskIdentifier ?? "unknown");
532
1052
  }));
533
1053
  return results;
534
1054
  }, {
@@ -536,13 +1056,14 @@ async function handleBatchTaskRunExecutionResult(items) {
536
1056
  [SemanticInternalAttributes.STYLE_ICON]: "cloud-download",
537
1057
  });
538
1058
  }
539
- async function handleTaskRunExecutionResult(execution) {
1059
+ async function handleTaskRunExecutionResult(execution, taskIdentifier) {
540
1060
  if (execution.ok) {
541
1061
  const outputPacket = { data: execution.output, dataType: execution.outputType };
542
1062
  const importedPacket = await conditionallyImportPacket(outputPacket, tracer);
543
1063
  return {
544
1064
  ok: true,
545
1065
  id: execution.id,
1066
+ taskIdentifier: (execution.taskIdentifier ?? taskIdentifier),
546
1067
  output: await parsePacket(importedPacket),
547
1068
  };
548
1069
  }
@@ -550,6 +1071,7 @@ async function handleTaskRunExecutionResult(execution) {
550
1071
  return {
551
1072
  ok: false,
552
1073
  id: execution.id,
1074
+ taskIdentifier: (execution.taskIdentifier ?? taskIdentifier),
553
1075
  error: createErrorTaskError(execution.error),
554
1076
  };
555
1077
  }