diff --git a/.changeset/max-compute-seconds.md b/.changeset/max-compute-seconds.md new file mode 100644 index 00000000000..56cac672521 --- /dev/null +++ b/.changeset/max-compute-seconds.md @@ -0,0 +1,7 @@ +--- +"@trigger.dev/core": patch +"@trigger.dev/sdk": patch +"trigger.dev": patch +--- + +Add `maxComputeSeconds` as the user-facing replacement for `maxDuration` on `defineConfig`, task definitions, and trigger options. The new name makes the unit (compute-time seconds) unambiguous at the call site. `maxDuration` is JSDoc-deprecated and still accepted; if both are set, `maxComputeSeconds` wins. The `init` templates have been updated to use the new name. diff --git a/packages/cli-v3/templates/examples/schedule.mjs.template b/packages/cli-v3/templates/examples/schedule.mjs.template index 5622b37a91e..56b7fafd74d 100644 --- a/packages/cli-v3/templates/examples/schedule.mjs.template +++ b/packages/cli-v3/templates/examples/schedule.mjs.template @@ -4,8 +4,8 @@ export const firstScheduledTask = schedules.task({ id: "first-scheduled-task", // Every hour cron: "0 * * * *", - // Set an optional maxDuration to prevent tasks from running indefinitely - maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute + // Set an optional maxComputeSeconds to prevent tasks from running indefinitely + maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute run: async (payload, { ctx }) => { // The payload contains the last run timestamp that you can use to check if this is the first run // And calculate the time since the last run diff --git a/packages/cli-v3/templates/examples/schedule.ts.template b/packages/cli-v3/templates/examples/schedule.ts.template index 5622b37a91e..56b7fafd74d 100644 --- a/packages/cli-v3/templates/examples/schedule.ts.template +++ b/packages/cli-v3/templates/examples/schedule.ts.template @@ -4,8 +4,8 @@ export const firstScheduledTask = schedules.task({ id: "first-scheduled-task", // Every hour cron: "0 * * * *", - // Set an optional maxDuration to prevent tasks from running indefinitely - maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute + // Set an optional maxComputeSeconds to prevent tasks from running indefinitely + maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute run: async (payload, { ctx }) => { // The payload contains the last run timestamp that you can use to check if this is the first run // And calculate the time since the last run diff --git a/packages/cli-v3/templates/examples/simple.mjs.template b/packages/cli-v3/templates/examples/simple.mjs.template index 8b1bf12d4f1..054bc2e7cd5 100644 --- a/packages/cli-v3/templates/examples/simple.mjs.template +++ b/packages/cli-v3/templates/examples/simple.mjs.template @@ -2,8 +2,8 @@ import { logger, task, wait } from "@trigger.dev/sdk/v3"; export const helloWorldTask = task({ id: "hello-world", - // Set an optional maxDuration to prevent tasks from running indefinitely - maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute + // Set an optional maxComputeSeconds to prevent tasks from running indefinitely + maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute run: async (payload, { ctx }) => { logger.log("Hello, world!", { payload, ctx }); diff --git a/packages/cli-v3/templates/examples/simple.ts.template b/packages/cli-v3/templates/examples/simple.ts.template index 9ef3d529f3a..83ae9280412 100644 --- a/packages/cli-v3/templates/examples/simple.ts.template +++ b/packages/cli-v3/templates/examples/simple.ts.template @@ -2,8 +2,8 @@ import { logger, task, wait } from "@trigger.dev/sdk/v3"; export const helloWorldTask = task({ id: "hello-world", - // Set an optional maxDuration to prevent tasks from running indefinitely - maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute + // Set an optional maxComputeSeconds to prevent tasks from running indefinitely + maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute run: async (payload: any, { ctx }) => { logger.log("Hello, world!", { payload, ctx }); diff --git a/packages/cli-v3/templates/trigger.config.mjs.template b/packages/cli-v3/templates/trigger.config.mjs.template index d50033647e2..5b1e857d341 100644 --- a/packages/cli-v3/templates/trigger.config.mjs.template +++ b/packages/cli-v3/templates/trigger.config.mjs.template @@ -4,10 +4,10 @@ export default defineConfig({ project: "${projectRef}", runtime: "${runtime}", logLevel: "log", - // The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped. + // The max compute seconds a task is allowed to run. If the run exceeds this, it will be stopped. // You can override this on an individual task. // See https://trigger.dev/docs/runs/max-duration - maxDuration: 3600, + maxComputeSeconds: 3600, retries: { enabledInDev: true, default: { diff --git a/packages/cli-v3/templates/trigger.config.ts.template b/packages/cli-v3/templates/trigger.config.ts.template index d50033647e2..5b1e857d341 100644 --- a/packages/cli-v3/templates/trigger.config.ts.template +++ b/packages/cli-v3/templates/trigger.config.ts.template @@ -4,10 +4,10 @@ export default defineConfig({ project: "${projectRef}", runtime: "${runtime}", logLevel: "log", - // The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped. + // The max compute seconds a task is allowed to run. If the run exceeds this, it will be stopped. // You can override this on an individual task. // See https://trigger.dev/docs/runs/max-duration - maxDuration: 3600, + maxComputeSeconds: 3600, retries: { enabledInDev: true, default: { diff --git a/packages/core/src/v3/config.ts b/packages/core/src/v3/config.ts index 40334f04280..efa9c841386 100644 --- a/packages/core/src/v3/config.ts +++ b/packages/core/src/v3/config.ts @@ -165,6 +165,17 @@ export type TriggerConfig = { */ logLevel?: LogLevel; + /** + * The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. + * + * Minimum value is 5 seconds. + * + * Setting this value will affect all tasks in the project. You can override it on a per-task basis. + * + * @see https://trigger.dev/docs/tasks/overview#maxcomputeseconds-option + */ + maxComputeSeconds?: number; + /** * The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. * @@ -172,9 +183,10 @@ export type TriggerConfig = { * * Setting this value will effect all tasks in the project. * + * @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins. * @see https://trigger.dev/docs/tasks/overview#maxduration-option */ - maxDuration: number; + maxDuration?: number; /** * Set a default time-to-live (TTL) for all task runs in the project. If a run is not executed within this time, it will be removed from the queue and never execute. diff --git a/packages/core/src/v3/types/tasks.ts b/packages/core/src/v3/types/tasks.ts index d04d088ef1a..8a29b8f5308 100644 --- a/packages/core/src/v3/types/tasks.ts +++ b/packages/core/src/v3/types/tasks.ts @@ -271,10 +271,19 @@ type CommonTaskOptions< } | MachinePresetName; + /** + * The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. + * + * Minimum value is 5 seconds. + */ + maxComputeSeconds?: number; + /** * The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. * * Minimum value is 5 seconds + * + * @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins. */ maxDuration?: number; @@ -876,12 +885,23 @@ export type TriggerOptions = { */ metadata?: Record; + /** + * The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. + * + * This will override the task's `maxComputeSeconds` (or the legacy `maxDuration`). + * + * Minimum value is 5 seconds. + */ + maxComputeSeconds?: number; + /** * The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped. * * This will override the task's maxDuration. * * Minimum value is 5 seconds + * + * @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins. */ maxDuration?: number; diff --git a/packages/trigger-sdk/src/v3/config.test.ts b/packages/trigger-sdk/src/v3/config.test.ts new file mode 100644 index 00000000000..fb2b4289d8c --- /dev/null +++ b/packages/trigger-sdk/src/v3/config.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from "vitest"; +import { defineConfig } from "./config.js"; + +describe("defineConfig - maxComputeSeconds", () => { + it("uses maxComputeSeconds when only maxComputeSeconds is set", () => { + const cfg = defineConfig({ project: "p", maxComputeSeconds: 600 }); + expect(cfg.maxDuration).toBe(600); + }); + + it("uses maxDuration when only maxDuration is set", () => { + const cfg = defineConfig({ project: "p", maxDuration: 600 }); + expect(cfg.maxDuration).toBe(600); + }); + + it("prefers maxComputeSeconds when both are set", () => { + const cfg = defineConfig({ project: "p", maxComputeSeconds: 600, maxDuration: 9999 }); + expect(cfg.maxDuration).toBe(600); + }); + + it("leaves maxDuration unset when neither is provided", () => { + const cfg = defineConfig({ project: "p" }); + expect(cfg.maxDuration).toBeUndefined(); + }); + + it("strips maxComputeSeconds from the returned config", () => { + const cfg = defineConfig({ project: "p", maxComputeSeconds: 600 }); + expect((cfg as { maxComputeSeconds?: number }).maxComputeSeconds).toBeUndefined(); + }); +}); diff --git a/packages/trigger-sdk/src/v3/config.ts b/packages/trigger-sdk/src/v3/config.ts index 3bf32ac6670..39d62985628 100644 --- a/packages/trigger-sdk/src/v3/config.ts +++ b/packages/trigger-sdk/src/v3/config.ts @@ -9,7 +9,15 @@ export type { } from "@trigger.dev/core/v3"; export function defineConfig(config: TriggerConfig): TriggerConfig { - return config; + // `maxComputeSeconds` is the new name for `maxDuration`. If both are set, the new + // name wins. Internally the SDK and platform still read `maxDuration`, so we + // collapse the two fields here at the user-facing boundary. + const { maxComputeSeconds, maxDuration, ...rest } = config; + const resolved = maxComputeSeconds ?? maxDuration; + return { + ...rest, + ...(resolved !== undefined ? { maxDuration: resolved } : {}), + }; } export type { TriggerConfig }; diff --git a/packages/trigger-sdk/src/v3/maxComputeSeconds.test.ts b/packages/trigger-sdk/src/v3/maxComputeSeconds.test.ts new file mode 100644 index 00000000000..6898a16610a --- /dev/null +++ b/packages/trigger-sdk/src/v3/maxComputeSeconds.test.ts @@ -0,0 +1,20 @@ +import { describe, it, expect } from "vitest"; +import { resolveMaxComputeSeconds } from "./maxComputeSeconds.js"; + +describe("resolveMaxComputeSeconds", () => { + it("returns maxComputeSeconds when only maxComputeSeconds is set", () => { + expect(resolveMaxComputeSeconds({ maxComputeSeconds: 300 })).toBe(300); + }); + + it("returns maxDuration when only maxDuration is set", () => { + expect(resolveMaxComputeSeconds({ maxDuration: 300 })).toBe(300); + }); + + it("prefers maxComputeSeconds when both are set", () => { + expect(resolveMaxComputeSeconds({ maxComputeSeconds: 300, maxDuration: 999 })).toBe(300); + }); + + it("returns undefined when neither is set", () => { + expect(resolveMaxComputeSeconds({})).toBeUndefined(); + }); +}); diff --git a/packages/trigger-sdk/src/v3/maxComputeSeconds.ts b/packages/trigger-sdk/src/v3/maxComputeSeconds.ts new file mode 100644 index 00000000000..d2045d4eeb3 --- /dev/null +++ b/packages/trigger-sdk/src/v3/maxComputeSeconds.ts @@ -0,0 +1,13 @@ +/** + * Collapse the user-facing `maxComputeSeconds` (new name) and `maxDuration` (deprecated) + * into a single value. If both are provided, `maxComputeSeconds` wins. + * + * Internal SDK/CLI/platform code only reads `maxDuration`, so all call sites that + * accept user input should funnel through this helper before forwarding the value. + */ +export function resolveMaxComputeSeconds(input: { + maxComputeSeconds?: number; + maxDuration?: number; +}): number | undefined { + return input.maxComputeSeconds ?? input.maxDuration; +} diff --git a/packages/trigger-sdk/src/v3/shared.ts b/packages/trigger-sdk/src/v3/shared.ts index c69bceeb535..cce0a034352 100644 --- a/packages/trigger-sdk/src/v3/shared.ts +++ b/packages/trigger-sdk/src/v3/shared.ts @@ -31,6 +31,7 @@ import { TaskRunExecutionResult, TaskRunPromise, } from "@trigger.dev/core/v3"; +import { resolveMaxComputeSeconds } from "./maxComputeSeconds.js"; import { tracer } from "./tracer.js"; import type { @@ -235,7 +236,7 @@ export function createTask< queue: params.queue, retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined, machine: typeof params.machine === "string" ? { preset: params.machine } : params.machine, - maxDuration: params.maxDuration, + maxDuration: resolveMaxComputeSeconds(params), ttl: params.ttl, payloadSchema: params.jsonSchema, fns: { @@ -367,7 +368,7 @@ export function createSchemaTask< queue: params.queue, retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined, machine: typeof params.machine === "string" ? { preset: params.machine } : params.machine, - maxDuration: params.maxDuration, + maxDuration: resolveMaxComputeSeconds(params), ttl: params.ttl, fns: { run: params.run, @@ -643,7 +644,7 @@ export async function batchTriggerById( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -900,7 +901,7 @@ export async function batchTriggerByIdAndWait( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -1159,7 +1160,7 @@ export async function batchTriggerTasks( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -1421,7 +1422,7 @@ export async function batchTriggerAndWaitTasks( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -1876,7 +1877,7 @@ async function* transformBatchItemsStreamForWait( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -1926,7 +1927,7 @@ async function* transformBatchByTaskItemsStream( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: (await makeIdempotencyKey(item.options?.idempotencyKey)) ?? batchItemIdempotencyKey, idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, @@ -2091,7 +2092,7 @@ async function* transformSingleTaskBatchItemsStreamForWait( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: finalIdempotencyKey?.toString(), idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, idempotencyKeyOptions, @@ -2141,7 +2142,7 @@ async function trigger_internal( tags: options?.tags, maxAttempts: options?.maxAttempts, metadata: options?.metadata, - maxDuration: options?.maxDuration, + maxDuration: options ? resolveMaxComputeSeconds(options) : undefined, parentRunId: taskContext.ctx?.run.id, machine: options?.machine, priority: options?.priority, @@ -2225,7 +2226,7 @@ async function batchTrigger_internal( tags: item.options?.tags, maxAttempts: item.options?.maxAttempts, metadata: item.options?.metadata, - maxDuration: item.options?.maxDuration, + maxDuration: item.options ? resolveMaxComputeSeconds(item.options) : undefined, idempotencyKey: finalIdempotencyKey?.toString(), idempotencyKeyTTL: item.options?.idempotencyKeyTTL ?? options?.idempotencyKeyTTL, idempotencyKeyOptions, @@ -2398,7 +2399,7 @@ async function triggerAndWait_internal { await setTimeout(payload.sleepFor * 1000, { signal }); }, @@ -150,7 +150,7 @@ export const maxDurationParentTask = task({ run: async (payload: { sleepFor?: number; maxDuration?: number }, { ctx, signal }) => { const result = await maxDurationTask.triggerAndWait( { sleepFor: payload.sleepFor ?? 10 }, - { maxDuration: timeout.None } + { maxComputeSeconds: timeout.None } ); return result; diff --git a/references/hello-world/trigger.config.ts b/references/hello-world/trigger.config.ts index e0b875cd6d1..86de9f25a05 100644 --- a/references/hello-world/trigger.config.ts +++ b/references/hello-world/trigger.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ maxExecutionsPerProcess: 20, }, logLevel: "debug", - maxDuration: 3600, + maxComputeSeconds: 3600, ttl: "1h", retries: { enabledInDev: true,