Use locked package versions when resolving dependencies in deployed workers

This commit is contained in:
Eric Allam
2024-05-01 15:51:39 +01:00
parent 2dfe191c55
commit c37c822314
8 changed files with 529 additions and 128 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Use locked package versions when resolving dependencies in deployed workers
+18 -9
View File
@@ -1283,6 +1283,8 @@ async function compileProject(
const dependencies = await gatherRequiredDependencies(allImports, config, javascriptProject);
logger.debug("gatherRequiredDependencies()", { dependencies });
const packageJsonContents = {
name: "trigger-worker",
version: "0.0.0",
@@ -1547,6 +1549,7 @@ async function gatherRequiredDependencies(
project: JavascriptProject
) {
const dependencies: Record<string, string> = {};
const resolvablePackageNames = new Set<string>();
for (const file of imports) {
if ((file.kind !== "require-call" && file.kind !== "dynamic-import") || !file.external) {
@@ -1555,26 +1558,32 @@ async function gatherRequiredDependencies(
const packageName = detectPackageNameFromImportPath(file.path);
if (dependencies[packageName]) {
if (!packageName) {
continue;
}
const externalDependencyVersion = await project.resolve(packageName);
resolvablePackageNames.add(packageName);
}
if (externalDependencyVersion) {
dependencies[packageName] = stripWorkspaceFromVersion(externalDependencyVersion);
continue;
}
const resolvedPackageVersions = await project.resolveAll(Array.from(resolvablePackageNames));
const missingPackages = Array.from(resolvablePackageNames).filter(
(packageName) => !resolvedPackageVersions[packageName]
);
for (const missingPackage of missingPackages) {
const internalDependencyVersion =
(packageJson.dependencies as Record<string, string>)[packageName] ??
detectDependencyVersion(packageName);
(packageJson.dependencies as Record<string, string>)[missingPackage] ??
detectDependencyVersion(missingPackage);
if (internalDependencyVersion) {
dependencies[packageName] = stripWorkspaceFromVersion(internalDependencyVersion);
dependencies[missingPackage] = stripWorkspaceFromVersion(internalDependencyVersion);
}
}
for (const [packageName, version] of Object.entries(resolvedPackageVersions)) {
dependencies[packageName] = version;
}
if (config.additionalPackages) {
for (const packageName of config.additionalPackages) {
if (dependencies[packageName]) {
@@ -5,51 +5,10 @@ import { logger } from "./logger";
import { PackageManager, getUserPackageManager } from "./getUserPackageManager";
import { PackageJson } from "type-fest";
import { assertExhaustive } from "./assertExhaustive";
import { builtinModules } from "node:module";
export type ResolveOptions = { allowDev: boolean };
const BuiltInModules = new Set([
"assert",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"http",
"http2",
"https",
"inspector",
"module",
"net",
"os",
"path",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"repl",
"stream",
"string_decoder",
"timers",
"tls",
"trace_events",
"tty",
"url",
"util",
"v8",
"vm",
"worker_threads",
"zlib",
]);
export class JavascriptProject {
private _packageJson?: PackageJson;
private _packageManager?: PackageManager;
@@ -84,27 +43,73 @@ export class JavascriptProject {
}
}
async resolveAll(
packageNames: string[],
options?: ResolveOptions
): Promise<Record<string, string>> {
const externalPackages = packageNames.filter((packageName) => !isBuiltInModule(packageName));
const opts = { allowDev: false, ...options };
const command = await this.#getCommand();
try {
const versions = await command.resolveDependencyVersions(externalPackages, {
cwd: this.projectPath,
});
if (versions) {
logger.debug(`Resolved [${externalPackages.join(", ")}] version using ${command.name}`, {
versions,
});
}
// Merge the resolved versions with the package.json dependencies
const missingPackages = externalPackages.filter((packageName) => !versions[packageName]);
const missingPackageVersions: Record<string, string> = {};
for (const packageName of missingPackages) {
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
if (typeof packageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using package.json`, {
packageJsonVersion,
});
missingPackageVersions[packageName] = packageJsonVersion;
}
if (opts.allowDev) {
const devPackageJsonVersion = this.packageJson.devDependencies?.[packageName];
if (typeof devPackageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using devDependencies`, {
devPackageJsonVersion,
});
missingPackageVersions[packageName] = devPackageJsonVersion;
}
}
}
return { ...versions, ...missingPackageVersions };
} catch (error) {
logger.debug(`Failed to resolve dependency versions using ${command.name}`, {
packageNames,
error,
});
return {};
}
}
async resolve(packageName: string, options?: ResolveOptions): Promise<string | undefined> {
if (BuiltInModules.has(packageName)) {
if (isBuiltInModule(packageName)) {
return undefined;
}
const opts = { allowDev: false, ...options };
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
if (typeof packageJsonVersion === "string") {
return packageJsonVersion;
}
if (opts.allowDev) {
const devPackageJsonVersion = this.packageJson.devDependencies?.[packageName];
if (typeof devPackageJsonVersion === "string") {
return devPackageJsonVersion;
}
}
const command = await this.#getCommand();
try {
@@ -113,8 +118,30 @@ export class JavascriptProject {
});
if (version) {
logger.debug(`Resolved ${packageName} version using ${command.name}`, { version });
return version;
}
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
if (typeof packageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using package.json`, { packageJsonVersion });
return packageJsonVersion;
}
if (opts.allowDev) {
const devPackageJsonVersion = this.packageJson.devDependencies?.[packageName];
if (typeof devPackageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using devDependencies`, {
devPackageJsonVersion,
});
return devPackageJsonVersion;
}
}
} catch (error) {
logger.debug(`Failed to resolve dependency version using ${command.name}`, {
packageName,
@@ -176,6 +203,11 @@ interface PackageManagerCommands {
packageName: string,
options: PackageManagerOptions
): Promise<string | undefined>;
resolveDependencyVersions(
packageNames: string[],
options: PackageManagerOptions
): Promise<Record<string, string>>;
}
class PNPMCommands implements PackageManagerCommands {
@@ -197,7 +229,7 @@ class PNPMCommands implements PackageManagerCommands {
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} list ${packageName} -r --json`;
const result = JSON.parse(stdout) as PnpmList;
logger.debug(`Resolving ${packageName} version using ${this.name}`, { result });
logger.debug(`Resolving ${packageName} version using ${this.name}`);
// Return the first dependency version that matches the package name
for (const dep of result) {
@@ -208,6 +240,31 @@ class PNPMCommands implements PackageManagerCommands {
}
}
}
async resolveDependencyVersions(
packageNames: string[],
options: PackageManagerOptions
): Promise<Record<string, string>> {
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} list ${packageNames} -r --json`;
const result = JSON.parse(stdout) as PnpmList;
logger.debug(`Resolving ${packageNames.join(" ")} version using ${this.name}`);
const results: Record<string, string> = {};
// Return the first dependency version that matches the package name
for (const dep of result) {
for (const packageName of packageNames) {
const dependency = dep.dependencies?.[packageName];
if (dependency) {
results[packageName] = dependency.version;
}
}
}
return results;
}
}
type NpmDependency = {
@@ -246,6 +303,28 @@ class NPMCommands implements PackageManagerCommands {
return this.#recursivelySearchDependencies(output.dependencies, packageName);
}
async resolveDependencyVersions(
packageNames: string[],
options: PackageManagerOptions
): Promise<Record<string, string>> {
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} list ${packageNames} --json`;
const output = JSON.parse(stdout) as NpmListOutput;
logger.debug(`Resolving ${packageNames.join(" ")} version using ${this.name}`, { output });
const results: Record<string, string> = {};
for (const packageName of packageNames) {
const version = this.#recursivelySearchDependencies(output.dependencies, packageName);
if (version) {
results[packageName] = version;
}
}
return results;
}
#recursivelySearchDependencies(
dependencies: Record<string, NpmDependency>,
packageName: string
@@ -286,7 +365,7 @@ class YarnCommands implements PackageManagerCommands {
const lines = stdout.split("\n");
logger.debug(`Resolving ${packageName} version using ${this.name}`, { lines });
logger.debug(`Resolving ${packageName} version using ${this.name}`);
for (const line of lines) {
const json = JSON.parse(line);
@@ -296,4 +375,54 @@ class YarnCommands implements PackageManagerCommands {
}
}
}
async resolveDependencyVersions(
packageNames: string[],
options: PackageManagerOptions
): Promise<Record<string, string>> {
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} info ${packageNames} --json`;
const lines = stdout.split("\n");
logger.debug(`Resolving ${packageNames.join(" ")} version using ${this.name}`);
const results: Record<string, string> = {};
for (const line of lines) {
const json = JSON.parse(line);
const packageName = this.#parseYarnValueIntoPackageName(json.value);
if (packageNames.includes(packageName)) {
results[packageName] = json.children.Version;
}
}
return results;
}
// The "value" when doing yarn info is formatted like this:
// "package-name@npm:version" or "package-name@workspace:version"
// This function will parse the value into just the package name.
// This correctly handles scoped packages as well e.g. @scope/package-name@npm:version
#parseYarnValueIntoPackageName(value: string): string {
const parts = value.split("@");
// If the value does not contain an "@" symbol, then it's just the package name
if (parts.length === 3) {
return parts[1] as string;
}
// If the value contains an "@" symbol, then the package name is the first part
return parts[0] as string;
}
}
function isBuiltInModule(module: string): boolean {
// if the module has node: prefix, it's a built-in module
if (module.startsWith("node:")) {
return true;
}
return builtinModules.includes(module);
}
+283 -59
View File
@@ -3083,6 +3083,12 @@ importers:
'@opentelemetry/api':
specifier: ^1.8.0
version: 1.8.0
'@react-email/components':
specifier: ^0.0.17
version: 0.0.17(@types/react@18.3.1)(react@18.2.0)
'@react-email/render':
specifier: ^0.0.7
version: 0.0.7
'@sindresorhus/slugify':
specifier: ^2.2.1
version: 2.2.1
@@ -3110,6 +3116,12 @@ importers:
pg:
specifier: ^8.11.5
version: 8.11.5
react:
specifier: ^18.2.0
version: 18.2.0
react-email:
specifier: ^1.6.1
version: 1.6.1(react@18.2.0)
reflect-metadata:
specifier: ^0.1.13
version: 0.1.13
@@ -3129,6 +3141,9 @@ importers:
'@types/node':
specifier: 20.4.2
version: 20.4.2
'@types/react':
specifier: ^18.3.1
version: 18.3.1
trigger.dev:
specifier: workspace:*
version: link:../../packages/cli-v3
@@ -8771,7 +8786,7 @@ packages:
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
'@npmcli/git': 4.1.0
glob: 10.3.3
glob: 10.3.10
hosted-git-info: 6.1.1
json-parse-even-better-errors: 3.0.0
normalize-package-data: 5.0.0
@@ -9717,6 +9732,20 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.1)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@types/react': 18.3.1
react: 18.2.0
dev: false
/@radix-ui/react-context@1.0.0(react@18.2.0):
resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==}
peerDependencies:
@@ -10293,6 +10322,21 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.3.1)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0)
'@types/react': 18.3.1
react: 18.2.0
dev: false
/@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==}
peerDependencies:
@@ -11206,24 +11250,138 @@ packages:
react: 18.2.0
dev: false
/@react-email/body@0.0.8(react@18.2.0):
resolution: {integrity: sha512-gqdkNYlIaIw0OdpWu8KjIcQSIFvx7t2bZpXVxMMvBS859Ia1+1X3b5RNbjI3S1ZqLddUf7owOHkO4MiXGE+nxg==}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/button@0.0.15(react@18.2.0):
resolution: {integrity: sha512-9Zi6SO3E8PoHYDfcJTecImiHLyitYWmIRs0HE3Ogra60ZzlWP2EXu+AZqwQnhXuq+9pbgwBWNWxB5YPetNPTNA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/button@0.0.4:
resolution: {integrity: sha512-6leTjsbf37fBb54AKSTOp1lzDsEFgJ/RdoBAy9MhuiciOqnUhJ5BRHZd4LJkt46FsBrILs8Ob50Y4quzV4kP0Q==}
dependencies:
react: 18.2.0
dev: false
/@react-email/code-block@0.0.4(react@18.2.0):
resolution: {integrity: sha512-xjVLi/9dFNJ70N7hYme+21eQWa3b9/kgp4V+FKQJkQCuIMobxPRCIGM5jKD/0Vo2OqrE5chYv/dkg/aP8a8sPg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
prismjs: 1.29.0
react: 18.2.0
dev: false
/@react-email/code-inline@0.0.2(react@18.2.0):
resolution: {integrity: sha512-0cmgbbibFeOJl0q04K9jJlPDuJ+SEiX/OG6m3Ko7UOkG3TqjRD8Dtvkij6jNDVfUh/zESpqJCP2CxrCLLMUjdA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/column@0.0.10(react@18.2.0):
resolution: {integrity: sha512-MnP8Mnwipr0X3XtdD6jMLckb0sI5/IlS6Kl/2F6/rsSWBJy5Gg6nizlekTdkwDmy0kNSe3/1nGU0Zqo98pl63Q==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/components@0.0.17(@types/react@18.3.1)(react@18.2.0):
resolution: {integrity: sha512-x5gGQaK0QchbwHvUrCBVnE8GCWdO5osTVuTSA54Fwzels6ZDeNTHEYRx9gI3Nwcf/dkoVYkVH4rzWST0SF0MLA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
'@react-email/body': 0.0.8(react@18.2.0)
'@react-email/button': 0.0.15(react@18.2.0)
'@react-email/code-block': 0.0.4(react@18.2.0)
'@react-email/code-inline': 0.0.2(react@18.2.0)
'@react-email/column': 0.0.10(react@18.2.0)
'@react-email/container': 0.0.12(react@18.2.0)
'@react-email/font': 0.0.6(react@18.2.0)
'@react-email/head': 0.0.8(react@18.2.0)
'@react-email/heading': 0.0.12(@types/react@18.3.1)(react@18.2.0)
'@react-email/hr': 0.0.8(react@18.2.0)
'@react-email/html': 0.0.8(react@18.2.0)
'@react-email/img': 0.0.8(react@18.2.0)
'@react-email/link': 0.0.8(react@18.2.0)
'@react-email/markdown': 0.0.10(react@18.2.0)
'@react-email/preview': 0.0.9(react@18.2.0)
'@react-email/render': 0.0.13
'@react-email/row': 0.0.8(react@18.2.0)
'@react-email/section': 0.0.12(react@18.2.0)
'@react-email/tailwind': 0.0.16(react@18.2.0)
'@react-email/text': 0.0.8(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
dev: false
/@react-email/container@0.0.12(react@18.2.0):
resolution: {integrity: sha512-HFu8Pu5COPFfeZxSL+wKv/TV5uO/sp4zQ0XkRCdnGkj/xoq0lqOHVDL4yC2Pu6fxXF/9C3PHDA++5uEYV5WVJw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/container@0.0.4:
resolution: {integrity: sha512-NwyppwXFfj+jzociP/jXIVkxCB6q9hNyoyMPIOnSbZDl6A16WaEi4ABo+i8BG3dzqQCJ357KPvMRkn8PJ/pWjA==}
dependencies:
react: 18.2.0
dev: false
/@react-email/font@0.0.6(react@18.2.0):
resolution: {integrity: sha512-sZZFvEZ4U3vNCAZ8wXqIO3DuGJR2qE/8m2fEH+tdqwa532zGO3zW+UlCTg0b9455wkJSzEBeaWik0IkNvjXzxw==}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/head@0.0.2:
resolution: {integrity: sha512-DLPDRPsqxOKTUYMy7TouldjGZ6/ZAnfmQuRFceliL2YyP4QPQyR+cWaYUItw026+wIJ4eNqLmVgb6M6UOFzB9w==}
dependencies:
react: 18.2.0
dev: false
/@react-email/head@0.0.8(react@18.2.0):
resolution: {integrity: sha512-8/NI0gtQmLIilAe6rebK1TWw3IXHxtrR02rInkQq8yQ7zKbYbzx7Q/FhmsJgAk+uYh2Er/KhgYJ0sHZyDhfMTQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/heading@0.0.12(@types/react@18.3.1)(react@18.2.0):
resolution: {integrity: sha512-eB7mpnAvDmwvQLoPuwEiPRH4fPXWe6ltz6Ptbry2BlI88F0a2k11Ghb4+sZHBqg7vVw/MKbqEgtLqr3QJ/KfCQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
'@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
dev: false
/@react-email/heading@0.0.5:
resolution: {integrity: sha512-guSPzy6x+za+O4ch+3XeRkhgu82rT6FvcDtpuUwAqrFrrY4RTRIZ4jlRWO/hMC9EYGd76PjpsRG7deb+YcwmcQ==}
dependencies:
@@ -11238,28 +11396,83 @@ packages:
react: 18.2.0
dev: false
/@react-email/hr@0.0.8(react@18.2.0):
resolution: {integrity: sha512-JLVvpCg2wYKEB+n/PGCggWG9fRU5e4lxsGdpK5SDLsCL0ic3OLKSpHMfeE+ZSuw0GixAVVQN7F64PVJHQkd4MQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/html@0.0.2:
resolution: {integrity: sha512-j6r7PdBUuH0G5OhiIskCtDerdowkiOia8nzAMbKt2S3vkRkgIIULIZwWA1mEjHXyTP5ZAOeCaqFTILt4Wn1seA==}
dev: false
/@react-email/html@0.0.8(react@18.2.0):
resolution: {integrity: sha512-arII3wBNLpeJtwyIJXPaILm5BPKhA+nvdC1F9QkuKcOBJv2zXctn8XzPqyGqDfdplV692ulNJP7XY55YqbKp6w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/img@0.0.2:
resolution: {integrity: sha512-ULvl6OnKlifj0kcQ3U0WL2Ita2e6FOqjZb9PFYGzu82ZphceiK3aMetn9tK+WJCsQA1LEKt9fYr5o3Yz8MF58A==}
dependencies:
react: 18.2.0
dev: false
/@react-email/img@0.0.8(react@18.2.0):
resolution: {integrity: sha512-jx/rPuKo31tV18fu7P5rRqelaH5wkhg83Dq7uLwJpfqhbi4KFBGeBfD0Y3PiLPPoh+WvYf+Adv9W2ghNW8nOMQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/link@0.0.2:
resolution: {integrity: sha512-HhUGxOfoBJW6G/7dWs7LIKwkGBn+PfTYcewpkxBoA+IBc6YTwu1KL0gyB3VLTB0qeEXDlT9lNM+qc/EZNsRoaQ==}
dependencies:
react: 18.2.0
dev: false
/@react-email/link@0.0.8(react@18.2.0):
resolution: {integrity: sha512-nVikuTi8WJHa6Baad4VuRUbUCa/7EtZ1Qy73TRejaCHn+vhetc39XGqHzKLNh+Z/JFL8Hv9g+4AgG16o2R0ogQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/markdown@0.0.10(react@18.2.0):
resolution: {integrity: sha512-MH0xO+NJ4IuJcx9nyxbgGKAMXyudFjCZ0A2GQvuWajemW9qy2hgnJ3mW3/z5lwcenG+JPn7JyO/iZpizQ7u1tA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
md-to-react-email: 5.0.2(react@18.2.0)
react: 18.2.0
dev: false
/@react-email/preview@0.0.2:
resolution: {integrity: sha512-qUe4n5R3z999O12H1IGGzq/zCFKKrzRoi30PLCpxqN4e1CWB+e/s0w20UDFJRy7rRpaZYuLTwTQ5w4T6XHNHIg==}
dependencies:
react: 18.2.0
dev: false
/@react-email/preview@0.0.9(react@18.2.0):
resolution: {integrity: sha512-2fyAA/zzZYfYmxfyn3p2YOIU30klyA6Dq4ytyWq4nfzQWWglt5hNDE0cMhObvRtfjM9ghMSVtoELAb0MWiF/kw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/render@0.0.12:
resolution: {integrity: sha512-S8WRv/PqECEi6x0QJBj0asnAb5GFtJaHlnByxLETLkgJjc76cxMYDH4r9wdbuJ4sjkcbpwP3LPnVzwS+aIjT7g==}
engines: {node: '>=18.0.0'}
@@ -11270,6 +11483,16 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-email/render@0.0.13:
resolution: {integrity: sha512-lmBizrV+rQeSa3GjiL8/kPU0gENqO/wv+4xrlWANabp9UY3lTLXzy7HMRSE8YFBES9AbxP5VX1iRKuEnsoBDew==}
engines: {node: '>=18.0.0'}
dependencies:
html-to-text: 9.0.5
js-beautify: 1.15.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-email/render@0.0.4(react@18.2.0):
resolution: {integrity: sha512-HPWjgw1TKcvYUPh74BjVC8Xq0ZU4nqb/tKsTa/VK/Ln4yXqnakBXNYl/UdHeJs57Ic9RMXSA9iOciQEHLgFWOg==}
engines: {node: '>=16.0.0'}
@@ -11301,18 +11524,54 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@react-email/row@0.0.8(react@18.2.0):
resolution: {integrity: sha512-JsB6pxs/ZyjYpEML3nbwJRGAerjcN/Pa/QG48XUwnT/MioDWrUuyQuefw+CwCrSUZ2P1IDrv2tUD3/E3xzcoKw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/section@0.0.1:
resolution: {integrity: sha512-Pb2HcGNhTOtFRJVB+Kq2vTUnd8M+LuZBuHfpUD16ua/m2/mbW5WeXAfSuJ2Tl6qspzvDKLHqVX+o6HBc1qgl2Q==}
dependencies:
react: 18.2.0
dev: false
/@react-email/section@0.0.12(react@18.2.0):
resolution: {integrity: sha512-UCD/N/BeOTN4h3VZBUaFdiSem6HnpuxD1Q51TdBFnqeNqS5hBomp8LWJJ9s4gzwHWk1XPdNfLA3I/fJwulJshg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/tailwind@0.0.16(react@18.2.0):
resolution: {integrity: sha512-uMifPxCEHaHLhpS1kVCMGyTeEL+aMYzHT4bgj8CkgCiBoF9wNNfIVMUlHGzHUTv4ZTEPaMfZgC/Hi8RqzL/Ogw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-email/text@0.0.2:
resolution: {integrity: sha512-BUOl5/2nsImBLSBC9XE7Khj3KWpubni73l0jT1weLzXG+GY3cZARTEErSit3BC5/KAG9lVISwFTTOfnVjhVDBA==}
dependencies:
react: 18.2.0
dev: false
/@react-email/text@0.0.8(react@18.2.0):
resolution: {integrity: sha512-uvN2TNWMrfC9wv/LLmMLbbEN1GrMWZb9dBK14eYxHHAEHCeyvGb5ePZZ2MPyzO7Y5yTC+vFEnCEr76V+hWMxCQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.2.0
dependencies:
react: 18.2.0
dev: false
/@react-stately/calendar@3.4.3(react@18.2.0):
resolution: {integrity: sha512-OrEcdskszDjnjVnFuSiDC2PVBJ6lWMCJROD5s6W1LUehUtBp8LX9wPavAGHV43LbhN9ldj560sxaQ4WCddrRCA==}
peerDependencies:
@@ -14246,6 +14505,12 @@ packages:
'@types/scheduler': 0.16.2
csstype: 3.1.1
/@types/react@18.3.1:
resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==}
dependencies:
'@types/prop-types': 15.7.5
csstype: 3.1.1
/@types/request@2.48.12:
resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==}
dependencies:
@@ -17318,6 +17583,7 @@ packages:
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
@@ -18436,16 +18702,6 @@ packages:
safe-buffer: 5.2.1
dev: false
/editorconfig@0.15.3:
resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==}
hasBin: true
dependencies:
commander: 2.20.3
lru-cache: 4.1.5
semver: 5.7.1
sigmund: 1.0.1
dev: false
/editorconfig@1.0.4:
resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
engines: {node: '>=14'}
@@ -21429,18 +21685,6 @@ packages:
minipass: 7.0.3
path-scurry: 1.10.1
/glob@10.3.3:
resolution: {integrity: sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.0
minimatch: 9.0.3
minipass: 7.0.3
path-scurry: 1.10.1
dev: true
/glob@7.1.6:
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
dependencies:
@@ -21493,17 +21737,6 @@ packages:
once: 1.4.0
dev: false
/glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.2
once: 1.4.0
dev: false
/glob@9.3.5:
resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -23031,15 +23264,6 @@ packages:
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
engines: {node: '>=6'}
/jackspeak@2.3.0:
resolution: {integrity: sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
dev: true
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
@@ -23721,17 +23945,6 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
/js-beautify@1.14.7:
resolution: {integrity: sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
config-chain: 1.1.13
editorconfig: 0.15.3
glob: 8.1.0
nopt: 6.0.0
dev: false
/js-beautify@1.15.1:
resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
engines: {node: '>=14'}
@@ -24471,6 +24684,12 @@ packages:
hasBin: true
dev: false
/marked@7.0.4:
resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==}
engines: {node: '>= 16'}
hasBin: true
dev: false
/match-sorter@6.3.1:
resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==}
dependencies:
@@ -24478,6 +24697,15 @@ packages:
remove-accents: 0.4.2
dev: false
/md-to-react-email@5.0.2(react@18.2.0):
resolution: {integrity: sha512-x6kkpdzIzUhecda/yahltfEl53mH26QdWu4abUF9+S0Jgam8P//Ciro8cdhyMHnT5MQUJYrIbO6ORM2UxPiNNA==}
peerDependencies:
react: 18.x
dependencies:
marked: 7.0.4
react: 18.2.0
dev: false
/md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
dependencies:
@@ -27950,7 +28178,7 @@ packages:
dependencies:
condense-newlines: 0.2.1
extend-shallow: 2.0.1
js-beautify: 1.14.7
js-beautify: 1.15.1
dev: false
/printable-characters@1.0.42:
@@ -29852,10 +30080,6 @@ packages:
/siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
/sigmund@1.0.1:
resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==}
dev: false
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+5
View File
@@ -10,6 +10,8 @@
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@ffprobe-installer/ffprobe": "^2.1.2",
"@opentelemetry/api": "^1.8.0",
"@react-email/components": "^0.0.17",
"@react-email/render": "^0.0.7",
"@sindresorhus/slugify": "^2.2.1",
"@traceloop/instrumentation-openai": "^0.3.9",
"@trigger.dev/core": "workspace:^3.0.0-beta.0",
@@ -19,6 +21,8 @@
"msw": "^2.2.1",
"openai": "^4.28.0",
"pg": "^8.11.5",
"react": "^18.2.0",
"react-email": "^1.6.1",
"reflect-metadata": "^0.1.13",
"stripe": "^12.14.0",
"typeorm": "^0.3.20",
@@ -27,6 +31,7 @@
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
"@types/node": "20.4.2",
"@types/react": "^18.3.1",
"trigger.dev": "workspace:*",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
@@ -0,0 +1,13 @@
import { task } from "@trigger.dev/sdk/v3";
import { ExampleEmail } from "./emails";
import { render } from "@react-email/render";
export const emailTask = task({
id: "email",
run: async () => {
return {
subject: "Example email",
react: render(<ExampleEmail />),
};
},
});
@@ -0,0 +1,15 @@
import { Button, Html } from "@react-email/components";
import * as React from "react";
export function ExampleEmail(props: {}) {
return (
<Html>
<Button
href="https://example.com"
style={{ background: "#000", color: "#fff", padding: "12px 20px" }}
>
Click me
</Button>
</Html>
);
}
+2 -1
View File
@@ -1,7 +1,8 @@
{
"extends": "@trigger.dev/tsconfig/node18.json",
"include": ["./src/**/*.ts", "trigger.config.ts"],
"include": ["./src/**/*.ts", "trigger.config.ts", "src/trigger/email.tsx"],
"compilerOptions": {
"jsx": "react-jsx",
"baseUrl": ".",
"lib": ["DOM", "DOM.Iterable"],
"paths": {