chore: upgrade to TypeScript 7 (#4318)

## Summary

Upgrade the monorepo to TypeScript 7.0.2 and update package build
tooling for compatibility with the native compiler.

## Design

Package builds now use `tshy` 4, while the packages still using `tsup`
move to `tsdown`. The few scripts that depend on the legacy TypeScript
compiler API use an explicit TypeScript 6 alias; declaration portability
coverage invokes the TypeScript 7 CLI directly.

Turbo is updated so workspace tasks can read the regenerated pnpm
lockfile.

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Chris Arderne
2026-08-05 15:49:44 +01:00
committed by GitHub
parent c01a4f18f4
commit 85f5b37c68
30 changed files with 1456 additions and 669 deletions
+13
View File
@@ -0,0 +1,13 @@
---
"trigger.dev": patch
"@trigger.dev/build": patch
"@trigger.dev/core": patch
"@trigger.dev/python": patch
"@trigger.dev/react-hooks": patch
"@trigger.dev/redis-worker": patch
"@trigger.dev/rsc": patch
"@trigger.dev/schema-to-json": patch
"@trigger.dev/sdk": patch
---
Refresh package builds for TypeScript 7 compatibility while preserving existing runtime entry points. TypeScript remains an optional peer for the decorator metadata build extension, so installing the Trigger.dev CLI does not install an additional compiler.
+1 -1
View File
@@ -104,7 +104,7 @@ export function createSearchParams<TParams extends ParamType>(
return { success: true, params: new SearchParams<TParams>(parsed.params as TParams, schema) };
}
function parseSearchParams<TParams extends ParamType>(params: TParams, schema: ZodType<TParams>) {
function parseSearchParams<TParams>(params: unknown, schema: ZodType<TParams>) {
const parsedParams = schema.safeParse(params);
if (!parsedParams.success) {
+2
View File
@@ -269,6 +269,8 @@
"tailwindcss": "^4.3.1",
"tsconfig-paths": "^3.14.1",
"tsx": "^4.20.6",
"typescript": "catalog:",
"typescript-legacy-api": "npm:typescript@6.0.3",
"vite-tsconfig-paths": "^5.1.4",
"vite": "^6.4.2"
},
+1 -1
View File
@@ -12,7 +12,7 @@
* pnpm --filter webapp run guard:runops-legacy # regenerate baseline
* pnpm --filter webapp run guard:runops-legacy -- --check # CI gate
*/
import ts from "typescript";
import ts from "typescript-legacy-api";
import * as fs from "node:fs";
import * as path from "node:path";
@@ -6,12 +6,13 @@
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"dependencies": {
"typescript": "catalog:"
"@typescript/typescript6": "6.0.2"
},
"devDependencies": {
"@types/node": "^24.13.3",
"rimraf": "6.0.1",
"tsx": "^4.19.2",
"typescript": "catalog:",
"vitest": "4.1.7"
},
"scripts": {
@@ -1,4 +1,4 @@
import ts from "typescript";
import ts from "@typescript/typescript6";
import { readFileSync, readdirSync } from "node:fs";
import { join, resolve } from "node:path";
import { CHECKS } from "./checks/index.js";
@@ -1,4 +1,4 @@
import ts from "typescript";
import ts from "@typescript/typescript6";
/**
* Source-to-source mutations for the tree-scale corpus in `mutationCorpus.test.ts`. Why they are text
@@ -1,4 +1,4 @@
import ts from "typescript";
import ts from "@typescript/typescript6";
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import type { CatchEvidence, EntryPoint, LogCall } from "./types.js";
@@ -1,4 +1,4 @@
import ts from "typescript";
import ts from "@typescript/typescript6";
import { CHECKS } from "./checks/index.js";
const KNOWN_CHECK_IDS = new Set(CHECKS.map((c) => c.id));
@@ -1,4 +1,4 @@
import ts from "typescript";
import ts from "@typescript/typescript6";
import { readdirSync, readFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { AUDIT_SYMBOLS } from "./checks/auditTrail.js";
+1 -3
View File
@@ -68,7 +68,7 @@
"pkg-pr-new": "0.0.75",
"pkg-types": "1.1.3",
"tsx": "^3.7.1",
"turbo": "^1.10.3",
"turbo": "^1.13.4",
"typescript": "catalog:",
"vite-tsconfig-paths": "^4.0.5",
"vitest": "4.1.7"
@@ -91,12 +91,10 @@
"antlr4ts@0.5.0-alpha.4": "patches/antlr4ts@0.5.0-alpha.4.patch",
"@window-splitter/state@1.1.3": "patches/@window-splitter__state@1.1.3.patch",
"streamdown@2.5.0": "patches/streamdown@2.5.0.patch",
"tsup@8.4.0": "patches/tsup@8.4.0.patch",
"@remix-run/router@1.23.3": "patches/@remix-run__router@1.23.3.patch",
"@clickhouse/client-common@1.12.1": "patches/@clickhouse__client-common@1.12.1.patch"
},
"overrides": {
"typescript": "catalog:",
"@types/node": "24.13.3",
"react@^18": "18.3.1",
"react-dom@^18": "18.3.1",
+11 -2
View File
@@ -91,8 +91,17 @@
"@types/resolve": "^1.20.6",
"esbuild": "^0.23.0",
"rimraf": "6.0.1",
"tshy": "^3.0.2",
"tsx": "4.17.0"
"tshy": "^4.1.3",
"tsx": "4.17.0",
"typescript": "6.0.3"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": ">=18.20.0"
+1 -1
View File
@@ -67,7 +67,7 @@
"find-up": "^7.0.0",
"rimraf": "^6.0.1",
"ts-essentials": "10.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"tsx": "4.17.0"
},
"scripts": {
+1 -1
View File
@@ -244,7 +244,7 @@
"rimraf": "^6.0.1",
"superjson": "^2.2.1",
"ts-essentials": "10.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"tsx": "4.17.0"
},
"engines": {
+3 -3
View File
@@ -19,14 +19,14 @@
},
"scripts": {
"clean": "rimraf dist .turbo",
"build": "tsup",
"dev": "tsup --watch",
"build": "tsdown",
"dev": "tsdown --watch",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "^24.13.3",
"rimraf": "6.0.1",
"tsup": "^8.4.0",
"tsdown": "0.22.10",
"typescript": "catalog:"
},
"engines": {
@@ -1,10 +1,10 @@
import { defineConfig } from "tsup";
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
format: ["cjs", "esm"],
fixedExtension: false,
dts: true,
splitting: false,
sourcemap: true,
clean: true,
treeshake: true,
+1 -1
View File
@@ -51,7 +51,7 @@
"devDependencies": {
"@types/node": "^24.13.3",
"rimraf": "6.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"typescript": "catalog:",
"tsx": "4.17.0",
"esbuild": "^0.23.0",
+1 -1
View File
@@ -45,7 +45,7 @@
"@types/react": "*",
"@types/react-dom": "*",
"rimraf": "^6.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"tsx": "4.17.0"
},
"peerDependencies": {
+4 -3
View File
@@ -17,8 +17,8 @@
],
"scripts": {
"clean": "rimraf dist .turbo",
"build": "tsup",
"dev": "tsup --watch",
"build": "tsdown",
"dev": "tsdown --watch",
"typecheck": "tsc --noEmit -p tsconfig.src.json",
"test": "vitest --sequence.concurrent=false --no-file-parallelism"
},
@@ -37,8 +37,9 @@
"@internal/tracing": "workspace:*",
"@types/lodash.omit": "^4.5.7",
"@types/seedrandom": "^3.0.8",
"esbuild": "^0.23.0",
"rimraf": "6.0.1",
"tsup": "^8.4.0",
"tsdown": "0.22.10",
"tsx": "4.17.0"
},
"engines": {
+5 -4
View File
@@ -19,7 +19,7 @@ import { nanoid } from "nanoid";
import pLimit from "p-limit";
import { z } from "zod";
import { type AnyQueueItem, SimpleQueue } from "./queue.js";
import { parseExpression } from "cron-parser";
import cronParser from "cron-parser";
export const CronSchema = z.object({
cron: z.string(),
@@ -1130,9 +1130,10 @@ class Worker<TCatalog extends WorkerCatalog> {
}
private calculateNextScheduledAt(cron: string, lastTimestamp?: Date): Date {
const scheduledAt = parseExpression(cron, {
currentDate: lastTimestamp,
})
const scheduledAt = cronParser
.parseExpression(cron, {
currentDate: lastTimestamp,
})
.next()
.toDate();
@@ -0,0 +1,44 @@
import { build } from "esbuild";
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { spawnSync } from "node:child_process";
import { afterAll, expect, test } from "vitest";
const packageDir = fileURLToPath(new URL("../", import.meta.url));
const tempDir = mkdtempSync(join(tmpdir(), "redis-worker-build-output-"));
const require = createRequire(import.meta.url);
afterAll(() => {
rmSync(tempDir, { recursive: true, force: true });
});
test("the ESM output can be rebundled and loaded as CommonJS", async () => {
const buildResult = spawnSync("pnpm", ["exec", "tsdown"], {
cwd: packageDir,
encoding: "utf8",
});
const buildOutput = [buildResult.stdout, buildResult.stderr].filter(Boolean).join("\n");
expect(buildResult.error, buildOutput).toBeUndefined();
expect(buildResult.status, buildOutput).toBe(0);
const esmPath = join(packageDir, "dist/index.js");
const esmOutput = readFileSync(esmPath, "utf8");
expect(esmOutput).not.toMatch(/\b[\w$]*createRequire[\w$]*\s*\(\s*import\.meta\.url\s*\)/);
const cjsPath = join(tempDir, "index.cjs");
await build({
entryPoints: [esmPath],
outfile: cjsPath,
bundle: true,
format: "cjs",
platform: "node",
logLevel: "silent",
});
expect(() => require(cjsPath)).not.toThrow();
}, 30_000);
+87
View File
@@ -0,0 +1,87 @@
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
format: ["cjs", "esm"],
fixedExtension: false,
tsconfig: "tsconfig.src.json",
dts: {
sourcemap: false,
},
sourcemap: true,
// The CJS declarations are emitted in a separate pass, so their output can
// disable source maps without affecting the runtime bundle.
outputOptions(options, _format, { cjsDts }) {
if (!cjsDts) return;
return {
...options,
sourcemap: false,
};
},
clean: true,
treeshake: true,
minify: false,
deps: {
onlyBundle: false,
alwaysBundle: [
// Always bundle internal packages
/^@internal/,
// Always bundle ESM-only packages
"nanoid",
"p-limit",
],
dts: {
// The TypeScript 7 declaration emitter consumes referenced package declarations.
neverBundle: [/^@internal/],
},
},
// rolldown injects its own `__require` helper in the ESM output as
// `createRequire(import.meta.url)` with no fallback. When this ESM bundle is
// re-bundled into a CJS consumer (e.g. the webapp server), esbuild replaces
// `import.meta.url` with `undefined`, so `createRequire(undefined)` throws at
// startup. Patch the helper to fall back to a valid path, matching the
// fallback the previous tsup banner provided.
plugins: [
// The ESM declarations share an output pass with the runtime bundle.
// Remove their dangling map comment while retaining runtime source maps.
{
name: "strip-declaration-sourcemap-comments",
generateBundle(_options, bundle) {
for (const output of Object.values(bundle)) {
if (output.type !== "chunk" || !/\.d\.(?:c|m)?ts$/.test(output.fileName)) {
continue;
}
output.code = output.code.replace(/\n?\/\/# sourceMappingURL=.*$/m, "");
}
},
},
{
name: "resilient-create-require",
renderChunk(code: string) {
const unsafeCreateRequire = /(\b[\w$]*createRequire[\w$]*\s*\()\s*import\.meta\.url\s*\)/g;
const patched = code.replace(
unsafeCreateRequire,
"$1import.meta.url || process.cwd() + '/index.js')"
);
if (patched === code) return null;
return {
code: patched,
map: null,
};
},
generateBundle(_options, bundle) {
const unsafeCreateRequire = /\b[\w$]*createRequire[\w$]*\s*\(\s*import\.meta\.url\s*\)/;
for (const output of Object.values(bundle)) {
if (output.type === "chunk" && unsafeCreateRequire.test(output.code)) {
throw new Error(`Unsafe createRequire helper remained in ${output.fileName}`);
}
}
},
},
],
});
-27
View File
@@ -1,27 +0,0 @@
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["cjs", "esm"],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
treeshake: true,
bundle: true,
minify: false,
noExternal: [
// Always bundle internal packages
/^@internal/,
// Always bundle ESM-only packages
"nanoid",
"p-limit",
],
banner: ({ format }) => {
if (format !== "esm") return;
return {
js: `import { createRequire } from 'module'; const require = createRequire(import.meta.url || process.cwd() + '/index.js');`,
};
},
});
+1 -1
View File
@@ -49,7 +49,7 @@
"@types/react": "*",
"@types/react-dom": "*",
"rimraf": "^6.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"tsx": "4.17.0"
},
"engines": {
+1 -1
View File
@@ -51,7 +51,7 @@
"arktype": "^2.0.0",
"runtypes": "^6.7.0",
"superstruct": "^2.0.2",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"@sinclair/typebox": "^0.34.3",
"valibot": "^1.1.0",
"yup": "^1.7.0",
+2 -1
View File
@@ -98,9 +98,10 @@
"ai-v7": "npm:ai@7.0.0-canary.159",
"encoding": "^0.1.13",
"rimraf": "^6.0.1",
"tshy": "^3.0.2",
"tshy": "^4.1.3",
"tsx": "4.17.0",
"typed-emitter": "^2.1.0",
"typescript": "catalog:",
"zod": "3.25.76"
},
"peerDependencies": {
@@ -1,16 +1,19 @@
import { spawnSync } from "node:child_process";
import { createRequire } from "node:module";
import {
cpSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { dirname, join, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import ts from "typescript";
/**
* Regression test for declaration-emit portability (customer TS2742).
@@ -76,40 +79,39 @@ describe("declaration emit portability", () => {
}
const fixturePath = join(consumerDir, "agent.ts");
ts.sys.writeFile(fixturePath, FIXTURE_SOURCE);
const emitted = new Map<string, string>();
const host = ts.createCompilerHost({});
host.writeFile = (fileName, text) => emitted.set(fileName, text);
const program = ts.createProgram({
rootNames: [fixturePath],
options: {
target: ts.ScriptTarget.ES2022,
module: ts.ModuleKind.NodeNext,
moduleResolution: ts.ModuleResolutionKind.NodeNext,
strict: true,
declaration: true,
emitDeclarationOnly: true,
skipLibCheck: true,
outDir: join(consumerDir, "out"),
rootDir: consumerDir,
},
host,
});
const emitResult = program.emit();
const diagnostics = [
...ts.getPreEmitDiagnostics(program),
...emitResult.diagnostics,
].filter((d) => d.category === ts.DiagnosticCategory.Error);
const formatted = diagnostics.map((d) =>
ts.flattenDiagnosticMessageText(d.messageText, "\n")
writeFileSync(fixturePath, FIXTURE_SOURCE);
writeFileSync(
join(consumerDir, "tsconfig.json"),
JSON.stringify({
compilerOptions: {
target: "ES2022",
module: "NodeNext",
moduleResolution: "NodeNext",
strict: true,
declaration: true,
emitDeclarationOnly: true,
skipLibCheck: true,
outDir: "out",
rootDir: ".",
},
files: ["agent.ts"],
})
);
expect(formatted).toEqual([]);
const dts = [...emitted.entries()].find(([name]) => name.endsWith("agent.d.ts"))?.[1];
expect(dts).toBeDefined();
const require = createRequire(import.meta.url);
const typescriptRoot = dirname(require.resolve("typescript/package.json"));
const result = spawnSync(
process.execPath,
[join(typescriptRoot, "bin", "tsc"), "-p", consumerDir],
{
encoding: "utf8",
}
);
const compilerOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
expect(result.error, compilerOutput).toBeUndefined();
expect(result.status, compilerOutput).toBe(0);
const dts = readFileSync(join(consumerDir, "out", "agent.d.ts"), "utf8");
// The payload generic must be named via the public subpath, and the
// emit must not fall back to file paths into the package.
expect(dts).toContain('import("@trigger.dev/sdk/chat").ChatTaskWirePayload');
-12
View File
@@ -1,12 +0,0 @@
diff --git a/dist/rollup.js b/dist/rollup.js
index cc82363..924f57f 100644
--- a/dist/rollup.js
+++ b/dist/rollup.js
@@ -6791,7 +6791,6 @@ var getRollupConfig = async (options) => {
tsconfig: options.tsconfig,
compilerOptions: {
...compilerOptions,
- baseUrl: compilerOptions.baseUrl || ".",
// Ensure ".d.ts" modules are generated
declaration: true,
// Skip ".js" generation
+1229 -563
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -6,7 +6,7 @@ packages:
- "!packages/cli-v3/e2e/**"
catalog:
typescript: 6.0.3
typescript: 7.0.2
minimumReleaseAge: 4320
minimumReleaseAgeExclude:
@@ -14,6 +14,7 @@ minimumReleaseAgeExclude:
- "next"
- "@next/*"
- "agentcrumbs"
- "tsdown"
- "secure-exec"
- "@secure-exec/*"
preferOffline: true