feat(runtime): add experimental Node.js 24 and 26 task runtimes (#4085)
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
## Summary Adds experimental Node.js 24 and 26 task runtimes through the `experimental-node-24` and `experimental-node-26` config values. Existing runtime defaults and the `node`, `node-22`, and `bun` behavior remain unchanged. The unprefixed `node-24` and `node-26` config values remain unavailable until the runtimes are ready for general use. ## Design Experimental config values normalize to canonical runtime identifiers before build manifests are created, keeping deployment metadata and execution behavior consistent. Kubernetes task pods also use the runtime-default seccomp profile so modern Node.js versions fall back from io_uring to checkpoint-compatible system calls.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@trigger.dev/core": patch
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Add experimental Node.js 24 and 26 task runtimes. Set `runtime` to `experimental-node-24` or `experimental-node-26` in `trigger.config.ts`.
|
||||
@@ -120,6 +120,7 @@ class ManagedSupervisor {
|
||||
snapshotPollIntervalSeconds: env.RUNNER_SNAPSHOT_POLL_INTERVAL_SECONDS,
|
||||
additionalEnvVars: env.RUNNER_ADDITIONAL_ENV_VARS,
|
||||
dockerAutoremove: env.DOCKER_AUTOREMOVE_EXITED_CONTAINERS,
|
||||
checkpointsEnabled: !!env.TRIGGER_CHECKPOINT_URL,
|
||||
} satisfies WorkloadManagerOptions;
|
||||
|
||||
this.resourceMonitor = env.RESOURCE_MONITOR_ENABLED
|
||||
@@ -615,6 +616,7 @@ class ManagedSupervisor {
|
||||
projectId: message.project.id,
|
||||
deploymentFriendlyId: message.deployment.friendlyId,
|
||||
deploymentVersion: message.backgroundWorker.version,
|
||||
runtime: message.backgroundWorker.runtime,
|
||||
runId: message.run.id,
|
||||
runFriendlyId: message.run.friendlyId,
|
||||
version: message.version,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
BLOCK_IO_URING_SECCOMP_PROFILE,
|
||||
withBlockIoUringSeccompProfile,
|
||||
} from "./kubernetesPodSpec.js";
|
||||
|
||||
const basePodSpec = {
|
||||
restartPolicy: "Never" as const,
|
||||
automountServiceAccountToken: false,
|
||||
securityContext: {
|
||||
runAsNonRoot: true,
|
||||
runAsUser: 1000,
|
||||
fsGroup: 1000,
|
||||
},
|
||||
};
|
||||
|
||||
describe("withBlockIoUringSeccompProfile", () => {
|
||||
it("adds the Localhost io_uring profile for node-24 and above, preserving pod security defaults", () => {
|
||||
for (const runtime of ["node-24", "node-26", "node-30", "experimental-node-24"]) {
|
||||
const podSpec = withBlockIoUringSeccompProfile(basePodSpec, runtime);
|
||||
|
||||
expect(podSpec).toMatchObject({
|
||||
...basePodSpec,
|
||||
securityContext: {
|
||||
...basePodSpec.securityContext,
|
||||
seccompProfile: {
|
||||
type: "Localhost",
|
||||
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves the pod spec unchanged for runtimes that do not create io_uring fds", () => {
|
||||
for (const runtime of ["node", "node-22", "bun", undefined, null, ""]) {
|
||||
expect(withBlockIoUringSeccompProfile(basePodSpec, runtime)).toEqual(basePodSpec);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import { PlacementTagProcessor } from "@trigger.dev/core/v3/serverOnly";
|
||||
import { env } from "../env.js";
|
||||
import { type K8sApi, createK8sApi, type k8s } from "../clients/kubernetes.js";
|
||||
import { getRunnerId } from "../util.js";
|
||||
import { withBlockIoUringSeccompProfile } from "./kubernetesPodSpec.js";
|
||||
|
||||
type ResourceQuantities = {
|
||||
[K in "cpu" | "memory" | "ephemeral-storage"]?: string;
|
||||
@@ -105,6 +106,11 @@ export class KubernetesWorkloadManager implements WorkloadManager {
|
||||
const runnerId = getRunnerId(opts.runFriendlyId, opts.nextAttemptNumber);
|
||||
|
||||
try {
|
||||
const basePodSpec = this.addPlacementTags(this.#defaultPodSpec, opts.placementTags);
|
||||
const podSpec = this.opts.checkpointsEnabled
|
||||
? withBlockIoUringSeccompProfile(basePodSpec, opts.runtime)
|
||||
: basePodSpec;
|
||||
|
||||
await this.k8s.core.createNamespacedPod({
|
||||
namespace: this.namespace,
|
||||
body: {
|
||||
@@ -119,7 +125,7 @@ export class KubernetesWorkloadManager implements WorkloadManager {
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
...this.addPlacementTags(this.#defaultPodSpec, opts.placementTags),
|
||||
...podSpec,
|
||||
affinity: this.#getAffinity(opts),
|
||||
tolerations: this.#getScheduleTolerations(this.#isScheduledRun(opts)),
|
||||
terminationGracePeriodSeconds: 60 * 60,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { k8s } from "../clients/kubernetes.js";
|
||||
|
||||
/**
|
||||
* Relative path (kubelet seccomp root) of the profile blocking only io_uring
|
||||
* syscalls. Must match the profile deployed to worker nodes.
|
||||
*/
|
||||
export const BLOCK_IO_URING_SECCOMP_PROFILE = "profiles/block-io-uring.json";
|
||||
|
||||
/**
|
||||
* Node >= 24 always creates io_uring fds, which can't be checkpointed. Blocking
|
||||
* io_uring_setup makes libuv fall back to epoll. Other runtimes don't need this,
|
||||
* so the profile is only applied for node-24+. Tolerates an "experimental-" prefix.
|
||||
*/
|
||||
export function withBlockIoUringSeccompProfile(
|
||||
podSpec: Omit<k8s.V1PodSpec, "containers">,
|
||||
runtime: string | null | undefined
|
||||
): Omit<k8s.V1PodSpec, "containers"> {
|
||||
const match = runtime ? /^(?:experimental-)?node-(\d+)$/.exec(runtime) : null;
|
||||
if (!match || Number(match[1]) < 24) {
|
||||
return podSpec;
|
||||
}
|
||||
|
||||
return {
|
||||
...podSpec,
|
||||
securityContext: {
|
||||
...podSpec.securityContext,
|
||||
seccompProfile: {
|
||||
type: "Localhost",
|
||||
localhostProfile: BLOCK_IO_URING_SECCOMP_PROFILE,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,8 @@ export interface WorkloadManagerOptions {
|
||||
snapshotPollIntervalSeconds?: number;
|
||||
additionalEnvVars?: Record<string, string>;
|
||||
dockerAutoremove?: boolean;
|
||||
// Whether CRIU checkpoint/restore is enabled for this deployment
|
||||
checkpointsEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface WorkloadManager {
|
||||
@@ -40,6 +42,8 @@ export interface WorkloadManagerCreateOptions {
|
||||
projectId: string;
|
||||
deploymentFriendlyId: string;
|
||||
deploymentVersion: string;
|
||||
// Canonical runtime identifier (e.g. "node", "node-22", "node-24")
|
||||
runtime?: string;
|
||||
runId: string;
|
||||
runFriendlyId: string;
|
||||
snapshotId: string;
|
||||
|
||||
@@ -597,6 +597,7 @@ export class DequeueSystem {
|
||||
id: result.worker.id,
|
||||
friendlyId: result.worker.friendlyId,
|
||||
version: result.worker.version,
|
||||
runtime: result.worker.runtime ?? undefined,
|
||||
},
|
||||
// TODO: use a discriminated union schema to differentiate between dequeued runs in dev and in deployed environments.
|
||||
// Would help make the typechecking stricter
|
||||
|
||||
@@ -94,7 +94,7 @@ Examples:
|
||||
)
|
||||
.option(
|
||||
"-r, --runtime <runtime>",
|
||||
"Which runtime to use for the project. Currently only supports node and bun",
|
||||
"Which runtime to use for the project. Supported: node, node-22, bun",
|
||||
"node"
|
||||
)
|
||||
.option("--skip-package-install", "Skip installing the @trigger.dev/sdk package")
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { loadConfig } from "./config.js";
|
||||
|
||||
const projectDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(projectDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
async function createProject(runtime?: string) {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "trigger-runtime-config-"));
|
||||
projectDirs.push(cwd);
|
||||
|
||||
await mkdir(join(cwd, "trigger"));
|
||||
await writeFile(join(cwd, "package.json"), JSON.stringify({ name: "runtime-config-test" }));
|
||||
await writeFile(join(cwd, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n");
|
||||
await writeFile(
|
||||
join(cwd, "trigger.config.ts"),
|
||||
`export default {
|
||||
project: "proj_runtime_config_test",
|
||||
maxDuration: 60,
|
||||
dirs: ["./trigger"],
|
||||
${runtime === undefined ? "" : `runtime: ${JSON.stringify(runtime)},`}
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
return cwd;
|
||||
}
|
||||
|
||||
describe("loadConfig runtime", () => {
|
||||
it.each([
|
||||
["experimental-node-24", "node-24"],
|
||||
["experimental-node-26", "node-26"],
|
||||
] as const)("normalizes %s before returning the resolved config", async (runtime, expected) => {
|
||||
const cwd = await createProject(runtime);
|
||||
|
||||
await expect(loadConfig({ cwd, warn: false })).resolves.toMatchObject({ runtime: expected });
|
||||
});
|
||||
|
||||
it("keeps node as the default", async () => {
|
||||
const cwd = await createProject();
|
||||
|
||||
await expect(loadConfig({ cwd, warn: false })).resolves.toMatchObject({ runtime: "node" });
|
||||
});
|
||||
|
||||
it.each(["node-24", "node-26", "node-23"])(
|
||||
"rejects unsupported public runtime %s while loading config",
|
||||
async (runtime) => {
|
||||
const cwd = await createProject(runtime);
|
||||
|
||||
await expect(loadConfig({ cwd, warn: false })).rejects.toThrowError(
|
||||
new RegExp(`Unsupported runtime "${runtime}" in trigger\\.config`)
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -6,7 +6,11 @@ import type {
|
||||
TriggerConfig,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import type { ResolvedConfig } from "@trigger.dev/core/v3/build";
|
||||
import { DEFAULT_RUNTIME } from "@trigger.dev/core/v3/build";
|
||||
import {
|
||||
DEFAULT_RUNTIME,
|
||||
isExperimentalConfigRuntime,
|
||||
resolveBuildRuntime,
|
||||
} from "@trigger.dev/core/v3/build";
|
||||
import * as c12 from "c12";
|
||||
import { defu } from "defu";
|
||||
import type * as esbuild from "esbuild";
|
||||
@@ -170,6 +174,24 @@ async function resolveConfig(
|
||||
);
|
||||
}
|
||||
|
||||
const config =
|
||||
"config" in result.config ? (result.config.config as TriggerConfig) : result.config;
|
||||
|
||||
const features = featuresFromCompatibilityFlags(
|
||||
["run_engine_v2" as const].concat(config.compatibilityFlags ?? [])
|
||||
);
|
||||
const defaultRuntime: BuildRuntime = features.run_engine_v2 ? "node" : DEFAULT_RUNTIME;
|
||||
const configuredRuntime = overrides?.runtime ?? config.runtime ?? defaultRuntime;
|
||||
const runtime = resolveBuildRuntime(configuredRuntime);
|
||||
|
||||
if (warn && isExperimentalConfigRuntime(configuredRuntime)) {
|
||||
prettyWarning(
|
||||
`The "${configuredRuntime}" runtime is experimental and may change before general availability.`
|
||||
);
|
||||
}
|
||||
|
||||
validateConfig(config, warn);
|
||||
|
||||
const packageJsonPath = await resolvePackageJSON(cwd);
|
||||
const tsconfigPath = await safeResolveTsConfig(cwd);
|
||||
const lockfilePath = await resolveLockfile(cwd);
|
||||
@@ -181,24 +203,14 @@ async function resolveConfig(
|
||||
? dirname(packageJsonPath)
|
||||
: cwd;
|
||||
|
||||
const config =
|
||||
"config" in result.config ? (result.config.config as TriggerConfig) : result.config;
|
||||
|
||||
validateConfig(config, warn);
|
||||
|
||||
let dirs = config.dirs ? config.dirs : await autoDetectDirs(workingDir);
|
||||
|
||||
dirs = dirs.map((dir) => resolveTriggerDir(dir, workingDir));
|
||||
|
||||
const features = featuresFromCompatibilityFlags(
|
||||
["run_engine_v2" as const].concat(config.compatibilityFlags ?? [])
|
||||
);
|
||||
|
||||
const defaultRuntime: BuildRuntime = features.run_engine_v2 ? "node" : DEFAULT_RUNTIME;
|
||||
|
||||
const mergedConfig = defu(
|
||||
{
|
||||
workingDir,
|
||||
runtime,
|
||||
configFile: result.configFile,
|
||||
packageJsonPath,
|
||||
tsconfigPath,
|
||||
@@ -230,7 +242,7 @@ async function resolveConfig(
|
||||
...mergedConfig,
|
||||
dirs: Array.from(new Set(dirs)),
|
||||
instrumentedPackageNames: getInstrumentedPackageNames(mergedConfig),
|
||||
runtime: mergedConfig.runtime,
|
||||
runtime,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { BuildRuntime } from "@trigger.dev/core/v3/schemas";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { generateContainerfile } from "./buildImage.js";
|
||||
|
||||
const nodeImages: Array<[BuildRuntime, string]> = [
|
||||
[
|
||||
"node-24",
|
||||
"node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d",
|
||||
],
|
||||
[
|
||||
"node-26",
|
||||
"node:26.4.0-bookworm-slim@sha256:ec82d089a8ae2cf02628da7b34ea57dc357b24db724d557fe2d240e6beb659c1",
|
||||
],
|
||||
];
|
||||
|
||||
describe("generateContainerfile", () => {
|
||||
it.each(nodeImages)("selects the pinned multiplatform image for %s", async (runtime, image) => {
|
||||
const containerfile = await generateContainerfile({
|
||||
runtime,
|
||||
build: {},
|
||||
image: undefined,
|
||||
indexScript: "index.js",
|
||||
entrypoint: "entrypoint.js",
|
||||
});
|
||||
|
||||
expect(containerfile).toContain(`FROM ${image} AS base`);
|
||||
});
|
||||
});
|
||||
@@ -692,6 +692,10 @@ const BASE_IMAGE: Record<BuildRuntime, string> = {
|
||||
node: "node:21.7.3-bookworm-slim@sha256:dfc05dee209a1d7adf2ef189bd97396daad4e97c6eaa85778d6f75205ba1b0fb",
|
||||
"node-22":
|
||||
"node:22.16.0-bookworm-slim@sha256:048ed02c5fd52e86fda6fbd2f6a76cf0d4492fd6c6fee9e2c463ed5108da0e34",
|
||||
"node-24":
|
||||
"node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d",
|
||||
"node-26":
|
||||
"node:26.4.0-bookworm-slim@sha256:ec82d089a8ae2cf02628da7b34ea57dc357b24db724d557fe2d240e6beb659c1",
|
||||
};
|
||||
|
||||
const DEFAULT_PACKAGES = ["busybox", "ca-certificates", "dumb-init", "git", "openssl"];
|
||||
@@ -699,7 +703,9 @@ const DEFAULT_PACKAGES = ["busybox", "ca-certificates", "dumb-init", "git", "ope
|
||||
export async function generateContainerfile(options: GenerateContainerfileOptions) {
|
||||
switch (options.runtime) {
|
||||
case "node":
|
||||
case "node-22": {
|
||||
case "node-22":
|
||||
case "node-24":
|
||||
case "node-26": {
|
||||
return await generateNodeContainerfile(options);
|
||||
}
|
||||
case "bun": {
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import { type Defu } from "defu";
|
||||
import type { Prettify } from "ts-essentials";
|
||||
import { CompatibilityFlag, CompatibilityFlagFeatures, TriggerConfig } from "../config.js";
|
||||
import { BuildRuntime } from "../schemas/build.js";
|
||||
import { ResolveEnvironmentVariablesFunction } from "../types/index.js";
|
||||
import type { CompatibilityFlag, CompatibilityFlagFeatures, TriggerConfig } from "../config.js";
|
||||
import type { BuildRuntime } from "../schemas/build.js";
|
||||
import type { ResolveEnvironmentVariablesFunction } from "../types/index.js";
|
||||
|
||||
export type ResolvedConfig = Prettify<
|
||||
Defu<
|
||||
TriggerConfig,
|
||||
[
|
||||
{},
|
||||
{
|
||||
runtime: BuildRuntime;
|
||||
dirs: string[];
|
||||
tsconfig: string;
|
||||
build: {
|
||||
jsx: { factory: string; fragment: string; automatic: true };
|
||||
} & Omit<NonNullable<TriggerConfig["build"]>, "jsx">;
|
||||
compatibilityFlags: CompatibilityFlag[];
|
||||
features: CompatibilityFlagFeatures;
|
||||
},
|
||||
]
|
||||
Omit<
|
||||
Defu<
|
||||
TriggerConfig,
|
||||
[
|
||||
{},
|
||||
{
|
||||
runtime: BuildRuntime;
|
||||
dirs: string[];
|
||||
tsconfig: string;
|
||||
build: {
|
||||
jsx: { factory: string; fragment: string; automatic: true };
|
||||
} & Omit<NonNullable<TriggerConfig["build"]>, "jsx">;
|
||||
compatibilityFlags: CompatibilityFlag[];
|
||||
features: CompatibilityFlagFeatures;
|
||||
},
|
||||
]
|
||||
>,
|
||||
"runtime"
|
||||
> & {
|
||||
runtime: BuildRuntime;
|
||||
workingDir: string;
|
||||
workspaceDir: string;
|
||||
packageJsonPath: string;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { BuildManifest, BuildRuntime, ConfigRuntime, WorkerManifest } from "../schemas/build.js";
|
||||
import { isExperimentalConfigRuntime, resolveBuildRuntime } from "./runtime.js";
|
||||
|
||||
describe("runtime configuration", () => {
|
||||
it.each(["node", "node-22", "experimental-node-24", "experimental-node-26", "bun"] as const)(
|
||||
"accepts %s as a public config runtime",
|
||||
(runtime) => {
|
||||
expect(ConfigRuntime.parse(runtime)).toBe(runtime);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
["experimental-node-24", "node-24"],
|
||||
["experimental-node-26", "node-26"],
|
||||
["node", "node"],
|
||||
["node-22", "node-22"],
|
||||
["bun", "bun"],
|
||||
] as const)("normalizes %s to %s", (runtime, expected) => {
|
||||
expect(resolveBuildRuntime(runtime)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each(["node-24", "node-26"] as const)(
|
||||
"keeps internal runtime %s out of the public config schema",
|
||||
(runtime) => {
|
||||
expect(ConfigRuntime.safeParse(runtime).success).toBe(false);
|
||||
expect(BuildRuntime.safeParse(runtime).success).toBe(true);
|
||||
expect(() => resolveBuildRuntime(runtime)).toThrowError(/Unsupported runtime/);
|
||||
}
|
||||
);
|
||||
|
||||
it("rejects unsupported config runtimes with a clear error", () => {
|
||||
expect(() => resolveBuildRuntime("node-23")).toThrowError(
|
||||
/Unsupported runtime "node-23" in trigger\.config\. Supported runtimes:/
|
||||
);
|
||||
});
|
||||
|
||||
it.each(["experimental-node-24", "experimental-node-26"] as const)(
|
||||
"keeps %s out of internal runtime schemas",
|
||||
(runtime) => {
|
||||
expect(BuildRuntime.safeParse(runtime).success).toBe(false);
|
||||
expect(BuildManifest.shape.runtime.safeParse(runtime).success).toBe(false);
|
||||
expect(WorkerManifest.shape.runtime.safeParse(runtime).success).toBe(false);
|
||||
expect(isExperimentalConfigRuntime(runtime)).toBe(true);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1,15 +1,48 @@
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "url";
|
||||
import { BuildRuntime } from "../schemas/build.js";
|
||||
import { BuildRuntime, ConfigRuntime } from "../schemas/build.js";
|
||||
import { dedupFlags } from "./flags.js";
|
||||
import { homedir } from "node:os";
|
||||
|
||||
export const DEFAULT_RUNTIME = "node" satisfies BuildRuntime;
|
||||
|
||||
export type ExperimentalConfigRuntime = "experimental-node-24" | "experimental-node-26";
|
||||
|
||||
export function isExperimentalConfigRuntime(
|
||||
runtime: unknown
|
||||
): runtime is ExperimentalConfigRuntime {
|
||||
return runtime === "experimental-node-24" || runtime === "experimental-node-26";
|
||||
}
|
||||
|
||||
export function resolveBuildRuntime(runtime: unknown): BuildRuntime {
|
||||
const parsedRuntime = ConfigRuntime.safeParse(runtime);
|
||||
|
||||
if (!parsedRuntime.success) {
|
||||
const value = typeof runtime === "string" ? `"${runtime}"` : String(runtime);
|
||||
|
||||
throw new Error(
|
||||
`Unsupported runtime ${value} in trigger.config. Supported runtimes: ${ConfigRuntime.options.join(
|
||||
", "
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
|
||||
switch (parsedRuntime.data) {
|
||||
case "experimental-node-24":
|
||||
return "node-24";
|
||||
case "experimental-node-26":
|
||||
return "node-26";
|
||||
default:
|
||||
return parsedRuntime.data;
|
||||
}
|
||||
}
|
||||
|
||||
export function binaryForRuntime(runtime: BuildRuntime): string {
|
||||
switch (runtime) {
|
||||
case "node":
|
||||
case "node-22":
|
||||
case "node-24":
|
||||
case "node-26":
|
||||
return "node";
|
||||
case "bun":
|
||||
return "bun";
|
||||
@@ -22,6 +55,8 @@ export function execPathForRuntime(runtime: BuildRuntime): string {
|
||||
switch (runtime) {
|
||||
case "node":
|
||||
case "node-22":
|
||||
case "node-24":
|
||||
case "node-26":
|
||||
return process.execPath;
|
||||
case "bun":
|
||||
if (typeof process.env.BUN_INSTALL === "string") {
|
||||
@@ -50,7 +85,9 @@ export function execOptionsForRuntime(
|
||||
): string {
|
||||
switch (runtime) {
|
||||
case "node":
|
||||
case "node-22": {
|
||||
case "node-22":
|
||||
case "node-24":
|
||||
case "node-26": {
|
||||
const importEntryPoint = options.loaderEntryPoint
|
||||
? `--import=${pathToFileURL(options.loaderEntryPoint).href}`
|
||||
: undefined;
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
AnyOnInitHookFunction,
|
||||
AnyOnStartHookFunction,
|
||||
AnyOnSuccessHookFunction,
|
||||
BuildRuntime,
|
||||
ConfigRuntime,
|
||||
RetryOptions,
|
||||
} from "./index.js";
|
||||
import type { LogLevel } from "./logger/taskLogger.js";
|
||||
@@ -44,7 +44,7 @@ export type TriggerConfig = {
|
||||
/**
|
||||
* @default "node"
|
||||
*/
|
||||
runtime?: BuildRuntime;
|
||||
runtime?: ConfigRuntime;
|
||||
|
||||
/**
|
||||
* Specify the project ref for your trigger.dev tasks. This is the project ref that you get when you create a new project in the trigger.dev dashboard.
|
||||
|
||||
@@ -13,7 +13,17 @@ export const BuildTarget = z.enum(["dev", "deploy", "unmanaged"]);
|
||||
|
||||
export type BuildTarget = z.infer<typeof BuildTarget>;
|
||||
|
||||
export const BuildRuntime = z.enum(["node", "node-22", "bun"]);
|
||||
export const ConfigRuntime = z.enum([
|
||||
"node",
|
||||
"node-22",
|
||||
"experimental-node-24",
|
||||
"experimental-node-26",
|
||||
"bun",
|
||||
]);
|
||||
|
||||
export type ConfigRuntime = z.infer<typeof ConfigRuntime>;
|
||||
|
||||
export const BuildRuntime = z.enum(["node", "node-22", "node-24", "node-26", "bun"]);
|
||||
|
||||
export type BuildRuntime = z.infer<typeof BuildRuntime>;
|
||||
|
||||
|
||||
@@ -272,6 +272,8 @@ export const DequeuedMessage = z.object({
|
||||
id: z.string(),
|
||||
friendlyId: z.string(),
|
||||
version: z.string(),
|
||||
// Canonical runtime identifier (e.g. "node", "node-22", "node-24", "bun")
|
||||
runtime: z.string().optional(),
|
||||
}),
|
||||
deployment: z.object({
|
||||
id: z.string().optional(),
|
||||
|
||||
Reference in New Issue
Block a user