diff --git a/.changeset/clever-carrots-travel.md b/.changeset/clever-carrots-travel.md new file mode 100644 index 000000000..1e4afac37 --- /dev/null +++ b/.changeset/clever-carrots-travel.md @@ -0,0 +1,7 @@ +--- +"@trigger.dev/sdk": patch +"trigger.dev": patch +"@trigger.dev/core": patch +--- + +Implement task.onSuccess/onFailure and config.onSuccess/onFailure diff --git a/.changeset/mighty-flowers-train.md b/.changeset/mighty-flowers-train.md new file mode 100644 index 000000000..32d4ff8e4 --- /dev/null +++ b/.changeset/mighty-flowers-train.md @@ -0,0 +1,83 @@ +--- +"trigger.dev": patch +"@trigger.dev/core": patch +--- + +Adds support for `emitDecoratorMetadata: true` and `experimentalDecorators: true` in your tsconfig using the [`@anatine/esbuild-decorators`](https://github.com/anatine/esbuildnx/tree/main/packages/esbuild-decorators) package. This allows you to use libraries like TypeORM: + +```ts orm/index.ts +import "reflect-metadata"; +import { DataSource } from "typeorm"; +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity() +export class Photo { + @PrimaryColumn() + id!: number; + + @Column() + name!: string; + + @Column() + description!: string; + + @Column() + filename!: string; + + @Column() + views!: number; + + @Column() + isPublished!: boolean; +} + +export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 5432, + username: "postgres", + password: "postgres", + database: "v3-catalog", + entities: [Photo], + synchronize: true, + logging: false, +}); +``` + +And then in your trigger.config.ts file you can initialize the datasource using the new `init` option: + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; +import { AppDataSource } from "@/trigger/orm"; + +export const config: TriggerConfig = { + // ... other options here + init: async (payload, { ctx }) => { + await AppDataSource.initialize(); + }, +}; +``` + +Now you are ready to use this in your tasks: + +```ts +import { task } from "@trigger.dev/sdk/v3"; +import { AppDataSource, Photo } from "./orm"; + +export const taskThatUsesDecorators = task({ + id: "taskThatUsesDecorators", + run: async (payload: { message: string }) => { + console.log("Creating a photo..."); + + const photo = new Photo(); + photo.id = 2; + photo.name = "Me and Bears"; + photo.description = "I am near polar bears"; + photo.filename = "photo-with-bears.jpg"; + photo.views = 1; + photo.isPublished = true; + + await AppDataSource.manager.save(photo); + }, +}); +``` diff --git a/.changeset/pre.json b/.changeset/pre.json index 8ea9d6b98..197e7c8ee 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -50,6 +50,7 @@ "chilled-hornets-move", "clean-pianos-listen", "clever-apes-collect", + "clever-carrots-travel", "cool-glasses-bake", "cuddly-feet-approve", "dry-walls-check", @@ -68,6 +69,7 @@ "loud-actors-remember", "many-ligers-pump", "mighty-camels-joke", + "mighty-flowers-train", "nasty-jars-pump", "new-rivers-tell", "ninety-pets-travel", @@ -75,6 +77,7 @@ "polite-ducks-switch", "polite-rockets-matter", "poor-flowers-cross", + "purple-garlics-shop", "rare-roses-float", "real-planets-stare", "rich-kangaroos-unite", diff --git a/.changeset/purple-garlics-shop.md b/.changeset/purple-garlics-shop.md new file mode 100644 index 000000000..0d4be024f --- /dev/null +++ b/.changeset/purple-garlics-shop.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Add typescript as a dependency so the esbuild-decorator will work even when running in npx diff --git a/apps/webapp/app/components/SetupCommands.tsx b/apps/webapp/app/components/SetupCommands.tsx index fa9ec55d1..8b3bb274c 100644 --- a/apps/webapp/app/components/SetupCommands.tsx +++ b/apps/webapp/app/components/SetupCommands.tsx @@ -206,3 +206,39 @@ export function TriggerDevStepV3() { ); } + +export function TriggerLoginStepV3() { + return ( + + + npm + pnpm + yarn + + + + + + + + + + + + ); +} diff --git a/apps/webapp/app/components/runs/v3/SpanTitle.tsx b/apps/webapp/app/components/runs/v3/SpanTitle.tsx index 8b50047c6..d065131a7 100644 --- a/apps/webapp/app/components/runs/v3/SpanTitle.tsx +++ b/apps/webapp/app/components/runs/v3/SpanTitle.tsx @@ -73,14 +73,7 @@ export function SpanCodePathAccessory({ > {accessory.items.map((item, index) => ( - - {item.text} - + {item.text} {index < accessory.items.length - 1 && ( diff --git a/apps/webapp/app/components/runs/v3/TaskPath.tsx b/apps/webapp/app/components/runs/v3/TaskPath.tsx index 16fe0caac..54fbad9ee 100644 --- a/apps/webapp/app/components/runs/v3/TaskPath.tsx +++ b/apps/webapp/app/components/runs/v3/TaskPath.tsx @@ -27,7 +27,7 @@ type TaskFunctionNameProps = { export function TaskFunctionName({ variant, functionName, className }: TaskFunctionNameProps) { return ( - + {`${functionName}()`} ); diff --git a/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx b/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx index 970df8189..e785ccc4a 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunStatus.tsx @@ -30,6 +30,14 @@ const taskRunStatusDescriptions: Record = { CRASHED: "Task has crashed and won't be retried", }; +export const QUEUED_STATUSES: TaskRunStatus[] = ["PENDING", "WAITING_FOR_DEPLOY"]; + +export const RUNNING_STATUSES: TaskRunStatus[] = [ + "EXECUTING", + "RETRYING_AFTER_FAILURE", + "WAITING_TO_RESUME", +]; + export function descriptionForTaskRunStatus(status: TaskRunStatus): string { return taskRunStatusDescriptions[status]; } diff --git a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx index f53f61569..2502c9f65 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx @@ -36,7 +36,6 @@ type RunsTableProps = { showJob?: boolean; runs: RunListItem[]; isLoading?: boolean; - currentUser: User; }; export function TaskRunsTable({ @@ -45,7 +44,6 @@ export function TaskRunsTable({ filters, runs, isLoading = false, - currentUser, }: RunsTableProps) { const organization = useOrganization(); const project = useProject(); @@ -78,15 +76,16 @@ export function TaskRunsTable({ ) : ( runs.map((run) => { const path = v3RunSpanPath(organization, project, run, { spanId: run.spanId }); - const usernameForEnv = - currentUser.id !== run.environment.userId ? run.environment.userName : undefined; return ( #{run.number} {run.taskIdentifier} {run.version ?? "–"} - + diff --git a/apps/webapp/app/db.server.ts b/apps/webapp/app/db.server.ts index 2f429e277..3c36ae83f 100644 --- a/apps/webapp/app/db.server.ts +++ b/apps/webapp/app/db.server.ts @@ -70,23 +70,21 @@ export { Prisma }; export const prisma = singleton("prisma", getClient); +export const $replica: Omit = singleton( + "replica", + () => getReplicaClient() ?? prisma +); + function getClient() { const { DATABASE_URL } = process.env; invariant(typeof DATABASE_URL === "string", "DATABASE_URL env var not set"); - const databaseUrl = new URL(DATABASE_URL); + const databaseUrl = extendQueryParams(DATABASE_URL, { + connection_limit: env.DATABASE_CONNECTION_LIMIT.toString(), + pool_timeout: env.DATABASE_POOL_TIMEOUT.toString(), + }); - // We need to add the connection_limit and pool_timeout query params to the url, in a way that works if the DATABASE_URL already has query params - const query = databaseUrl.searchParams; - query.set("connection_limit", env.DATABASE_CONNECTION_LIMIT.toString()); - query.set("pool_timeout", env.DATABASE_POOL_TIMEOUT.toString()); - databaseUrl.search = query.toString(); - - // Remove the username:password in the url and print that to the console - const urlWithoutCredentials = new URL(databaseUrl.href); - urlWithoutCredentials.password = ""; - - console.log(`🔌 setting up prisma client to ${urlWithoutCredentials.toString()}`); + console.log(`🔌 setting up prisma client to ${redactUrlSecrets(databaseUrl)}`); const client = new PrismaClient({ datasources: { @@ -134,6 +132,68 @@ function getClient() { return client; } +function getReplicaClient() { + if (!env.DATABASE_READ_REPLICA_URL) { + console.log(`🔌 No database replica, using the regular client`); + return; + } + + const replicaUrl = extendQueryParams(env.DATABASE_READ_REPLICA_URL, { + connection_limit: env.DATABASE_CONNECTION_LIMIT.toString(), + pool_timeout: env.DATABASE_POOL_TIMEOUT.toString(), + }); + + console.log(`🔌 setting up read replica connection to ${redactUrlSecrets(replicaUrl)}`); + + const replicaClient = new PrismaClient({ + datasources: { + db: { + url: replicaUrl.href, + }, + }, + log: [ + { + emit: "stdout", + level: "error", + }, + { + emit: "stdout", + level: "info", + }, + { + emit: "stdout", + level: "warn", + }, + ], + }); + + // connect eagerly + replicaClient.$connect(); + + console.log(`🔌 read replica connected`); + + return replicaClient; +} + +function extendQueryParams(hrefOrUrl: string | URL, queryParams: Record) { + const url = new URL(hrefOrUrl); + const query = url.searchParams; + + for (const [key, val] of Object.entries(queryParams)) { + query.set(key, val); + } + + url.search = query.toString(); + + return url; +} + +function redactUrlSecrets(hrefOrUrl: string | URL) { + const url = new URL(hrefOrUrl); + url.password = ""; + return url.href; +} + export type { PrismaClient } from "@trigger.dev/database"; export const PrismaErrorSchema = z.object({ diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index a67394c5c..cde3aeb77 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -19,6 +19,7 @@ const EnvironmentSchema = z.object({ isValidDatabaseUrl, "DIRECT_URL is invalid, for details please check the additional output above this message." ), + DATABASE_READ_REPLICA_URL: z.string().optional(), SESSION_SECRET: z.string(), MAGIC_LINK_SECRET: z.string(), ENCRYPTION_KEY: z.string(), diff --git a/apps/webapp/app/hooks/useEnvironments.ts b/apps/webapp/app/hooks/useEnvironments.ts index 08ebaf457..ccec438cc 100644 --- a/apps/webapp/app/hooks/useEnvironments.ts +++ b/apps/webapp/app/hooks/useEnvironments.ts @@ -10,20 +10,3 @@ export function useEnvironments(matches?: UIMatch[]) { return project.environments; } - -export function useDevEnvironment(matches?: UIMatch[]) { - const user = useUser(); - const environments = useEnvironments(matches); - if (!environments) return; - - return environments.find( - (environment) => environment.type === "DEVELOPMENT" && environment.userId === user.id - ); -} - -export function useProdEnvironment(matches?: UIMatch[]) { - const environments = useEnvironments(matches); - if (!environments) return; - - return environments.find((environment) => environment.type === "PRODUCTION"); -} diff --git a/apps/webapp/app/models/organization.server.ts b/apps/webapp/app/models/organization.server.ts index b773617c6..e2dd1d87d 100644 --- a/apps/webapp/app/models/organization.server.ts +++ b/apps/webapp/app/models/organization.server.ts @@ -87,8 +87,8 @@ export async function createOrganization( } export async function createEnvironment( - organization: Organization, - project: Project, + organization: Pick, + project: Pick, type: RuntimeEnvironment["type"], member?: OrgMember, prismaClient: PrismaClientOrTransaction = prisma diff --git a/apps/webapp/app/models/project.server.ts b/apps/webapp/app/models/project.server.ts index 576d245ed..47d9097ca 100644 --- a/apps/webapp/app/models/project.server.ts +++ b/apps/webapp/app/models/project.server.ts @@ -87,7 +87,7 @@ export async function createProject( // Create the dev and prod environments await createEnvironment(organization, project, "PRODUCTION"); - if (project.version === "V2") { + if (version === "v2") { await createEnvironment(organization, project, "STAGING"); } diff --git a/apps/webapp/app/models/runtimeEnvironment.server.ts b/apps/webapp/app/models/runtimeEnvironment.server.ts index 6c5258024..29a938a1c 100644 --- a/apps/webapp/app/models/runtimeEnvironment.server.ts +++ b/apps/webapp/app/models/runtimeEnvironment.server.ts @@ -1,5 +1,6 @@ -import type { RuntimeEnvironment } from "@trigger.dev/database"; +import type { Prisma, RuntimeEnvironment } from "@trigger.dev/database"; import { prisma } from "~/db.server"; +import { getUsername } from "~/utils/username"; export type { RuntimeEnvironment }; @@ -118,3 +119,36 @@ export async function disconnectSession(environmentId: string) { return session; }); } + +type DisplayableInputEnvironment = Prisma.RuntimeEnvironmentGetPayload<{ + select: { + id: true; + type: true; + orgMember: { + select: { + user: { + select: { + id: true; + name: true; + displayName: true; + }; + }; + }; + }; + }; +}>; + +export function displayableEnvironments( + environment: DisplayableInputEnvironment, + userId: string | undefined +) { + return { + id: environment.id, + type: environment.type, + userName: environment.orgMember + ? environment.orgMember.user.id === userId + ? undefined + : getUsername(environment.orgMember.user) + : undefined, + }; +} diff --git a/apps/webapp/app/presenters/ProjectPresenter.server.ts b/apps/webapp/app/presenters/ProjectPresenter.server.ts index f8e31ea09..7f61825e1 100644 --- a/apps/webapp/app/presenters/ProjectPresenter.server.ts +++ b/apps/webapp/app/presenters/ProjectPresenter.server.ts @@ -1,6 +1,8 @@ import { PrismaClient, prisma } from "~/db.server"; import { Project } from "~/models/project.server"; +import { displayableEnvironments } from "~/models/runtimeEnvironment.server"; import { User } from "~/models/user.server"; +import { sortEnvironments } from "~/services/environmentSort.server"; export class ProjectPresenter { #prismaClient: PrismaClient; @@ -49,7 +51,13 @@ export class ProjectPresenter { type: true, orgMember: { select: { - userId: true, + user: { + select: { + id: true, + name: true, + displayName: true, + }, + }, }, }, apiKey: true, @@ -76,13 +84,12 @@ export class ProjectPresenter { hasInactiveExternalTriggers: project._count.sources > 0, jobCount: project._count.jobs, httpEndpointCount: project._count.httpEndpoints, - environments: project.environments.map((environment) => ({ - id: environment.id, - slug: environment.slug, - type: environment.type, - apiKey: environment.apiKey, - userId: environment.orgMember?.userId, - })), + environments: sortEnvironments( + project.environments.map((environment) => ({ + ...displayableEnvironments(environment, userId), + userId: environment.orgMember?.user.id, + })) + ), }; } } diff --git a/apps/webapp/app/presenters/v3/ApiKeysPresenter.server.ts b/apps/webapp/app/presenters/v3/ApiKeysPresenter.server.ts index 2c783b0f5..406bb13b2 100644 --- a/apps/webapp/app/presenters/v3/ApiKeysPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/ApiKeysPresenter.server.ts @@ -74,6 +74,7 @@ export class ApiKeysPresenter { environmentVariableCount: environment._count.environmentVariableValues, })) ), + hasStaging: environments.some((environment) => environment.type === "STAGING"), }; } } diff --git a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts index d44678b7e..d203d7f28 100644 --- a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts @@ -1,10 +1,12 @@ import { Prisma, TaskRunStatus } from "@trigger.dev/database"; import { Direction } from "~/components/runs/RunStatuses"; import { sqlDatabaseSchema, PrismaClient, prisma } from "~/db.server"; +import { displayableEnvironments } from "~/models/runtimeEnvironment.server"; import { getUsername } from "~/utils/username"; import { CANCELLABLE_STATUSES } from "~/v3/services/cancelTaskRun.server"; type RunListOptions = { + userId?: string; projectSlug: string; //filters tasks?: string[]; @@ -34,6 +36,7 @@ export class RunListPresenter { } public async call({ + userId, projectSlug, tasks, versions, @@ -231,12 +234,7 @@ export class RunListPresenter { attempts: Number(run.attempts), isReplayable: true, isCancellable: CANCELLABLE_STATUSES.includes(run.status), - environment: { - type: environment.type, - slug: environment.slug, - userId: environment.orgMember?.user.id, - userName: getUsername(environment.orgMember?.user), - }, + environment: displayableEnvironments(environment, userId), }; }), pagination: { diff --git a/apps/webapp/app/presenters/v3/TaskListPresenter.server.ts b/apps/webapp/app/presenters/v3/TaskListPresenter.server.ts index 0d019eb84..bf57f1a26 100644 --- a/apps/webapp/app/presenters/v3/TaskListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/TaskListPresenter.server.ts @@ -1,19 +1,42 @@ -import { Prisma, TaskRunStatus, TaskTriggerSource } from "@trigger.dev/database"; +import { + Prisma, + RuntimeEnvironmentType, + TaskRunStatus, + TaskTriggerSource, +} from "@trigger.dev/database"; import { PrismaClient, prisma, sqlDatabaseSchema } from "~/db.server"; import { Organization } from "~/models/organization.server"; import { Project } from "~/models/project.server"; import { User } from "~/models/user.server"; +import { sortEnvironments } from "~/services/environmentSort.server"; +import { logger } from "~/services/logger.server"; import { getUsername } from "~/utils/username"; +import { BasePresenter } from "./basePresenter.server"; +import { QUEUED_STATUSES, RUNNING_STATUSES } from "~/components/runs/v3/TaskRunStatus"; +import { displayableEnvironments } from "~/models/runtimeEnvironment.server"; -export type Task = Awaited>[0]; +export type Task = { + slug: string; + exportName: string; + filePath: string; + createdAt: Date; + triggerSource: TaskTriggerSource; + environments: { + id: string; + type: RuntimeEnvironmentType; + userName?: string; + }[]; + latestRun?: { + createdAt: Date; + status: TaskRunStatus; + }; +}; -export class TaskListPresenter { - #prismaClient: PrismaClient; +type Return = Awaited>; - constructor(prismaClient: PrismaClient = prisma) { - this.#prismaClient = prismaClient; - } +export type TaskActivity = Awaited[string]; +export class TaskListPresenter extends BasePresenter { public async call({ userId, projectSlug, @@ -23,7 +46,7 @@ export class TaskListPresenter { projectSlug: Project["slug"]; organizationSlug: Organization["slug"]; }) { - const project = await this.#prismaClient.project.findFirstOrThrow({ + const project = await this._replica.project.findFirstOrThrow({ select: { id: true, environments: { @@ -53,7 +76,7 @@ export class TaskListPresenter { }, }); - const tasks = await this.#prismaClient.$queryRaw< + const tasks = await this._replica.$queryRaw< { id: string; slug: string; @@ -64,73 +87,243 @@ export class TaskListPresenter { triggerSource: TaskTriggerSource; }[] >` - SELECT DISTINCT ON(bwt.slug, bwt."runtimeEnvironmentId") - bwt.slug, - bwt.id, - bwt."exportName", - bwt."filePath", - bwt."runtimeEnvironmentId", - bwt."createdAt", - bwt."triggerSource" - FROM - ${sqlDatabaseSchema}."BackgroundWorkerTask" as bwt - WHERE bwt."projectId" = ${project.id} - ORDER BY - bwt.slug, - bwt."runtimeEnvironmentId", - bwt."createdAt" DESC;`; + WITH workers AS ( + SELECT DISTINCT ON ("runtimeEnvironmentId") id, "runtimeEnvironmentId", version + FROM ${sqlDatabaseSchema}."BackgroundWorker" + WHERE "runtimeEnvironmentId" IN (${Prisma.join(project.environments.map((e) => e.id))}) + ORDER BY "runtimeEnvironmentId", "createdAt" DESC + ) + SELECT tasks.id, slug, "filePath", "exportName", "triggerSource", tasks."runtimeEnvironmentId", tasks."createdAt" + FROM workers + JOIN ${sqlDatabaseSchema}."BackgroundWorkerTask" tasks ON tasks."workerId" = workers.id + ORDER BY slug ASC;`; let latestRuns = [] as { createdAt: Date; status: TaskRunStatus; - lockedById: string; + taskIdentifier: string; }[]; if (tasks.length > 0) { - latestRuns = await this.#prismaClient.$queryRaw< + const uniqueTaskSlugs = new Set(tasks.map((t) => t.slug)); + latestRuns = await this._replica.$queryRaw< { createdAt: Date; status: TaskRunStatus; - lockedById: string; + taskIdentifier: string; }[] >` SELECT * FROM ( SELECT "createdAt", "status", - "lockedById", - ROW_NUMBER() OVER (PARTITION BY "lockedById" ORDER BY "updatedAt" DESC) AS rn + "taskIdentifier", + ROW_NUMBER() OVER (PARTITION BY "taskIdentifier" ORDER BY "updatedAt" DESC) AS rn FROM ${sqlDatabaseSchema}."TaskRun" WHERE - "lockedById" IN(${Prisma.join(tasks.map((t) => t.id))}) - ) t - WHERE rn = 1;`; + "taskIdentifier" IN(${Prisma.join(Array.from(uniqueTaskSlugs))}) + AND "projectId" = ${project.id} + ) t + WHERE rn = 1;`; } - return tasks.map((task) => { - const latestRun = latestRuns.find((r) => r.lockedById === task.id); + //group by the task identifier (task.slug). Add the latestRun and add all the environments. + const outputTasks = tasks.reduce((acc, task) => { + const latestRun = latestRuns.find((r) => r.taskIdentifier === task.slug); const environment = project.environments.find((env) => env.id === task.runtimeEnvironmentId); if (!environment) { throw new Error(`Environment not found for TaskRun ${task.id}`); } - return { - ...task, - environment: { - id: environment.id, - type: environment.type, - slug: environment.slug, - userId: environment.orgMember?.user.id, - userName: getUsername(environment.orgMember?.user), - }, - latestRun: latestRun - ? { - createdAt: latestRun.createdAt, - status: latestRun.status, - } - : undefined, - }; - }); + let existingTask = acc.find((t) => t.slug === task.slug); + + if (!existingTask) { + existingTask = { + ...task, + environments: [], + }; + acc.push(existingTask); + } + + existingTask.environments.push(displayableEnvironments(environment, userId)); + + //order the environments + existingTask.environments = sortEnvironments(existingTask.environments); + + existingTask.latestRun = latestRun + ? { + createdAt: latestRun.createdAt, + status: latestRun.status, + } + : undefined; + + return acc; + }, [] as Task[]); + + //then get the activity for each task + const activity = this.#getActivity( + outputTasks.map((t) => t.slug), + project.id + ); + + const runningStats = this.#getRunningStats( + outputTasks.map((t) => t.slug), + project.id + ); + + const durations = this.#getAverageDurations( + outputTasks.map((t) => t.slug), + project.id + ); + + const userEnvironment = project.environments.find((e) => e.orgMember?.user.id === userId); + const userHasTasks = userEnvironment + ? outputTasks.some((t) => t.environments.some((e) => e.id === userEnvironment.id)) + : false; + + return { tasks: outputTasks, userHasTasks, activity, runningStats, durations }; + } + + async #getActivity(tasks: string[], projectId: string) { + const activity = await this._replica.$queryRaw< + { + taskIdentifier: string; + status: TaskRunStatus; + day: Date; + count: BigInt; + }[] + >` + SELECT + tr."taskIdentifier", + tr."status", + DATE(tr."createdAt") as day, + COUNT(*) + FROM + ${sqlDatabaseSchema}."TaskRun" as tr + WHERE + tr."taskIdentifier" IN (${Prisma.join(tasks)}) + AND tr."projectId" = ${projectId} + AND tr."createdAt" >= (current_date - interval '6 days') + GROUP BY + tr."taskIdentifier", + tr."status", + day + ORDER BY + tr."taskIdentifier" ASC, + day ASC, + tr."status" ASC;`; + + //today with no time + const today = new Date(); + today.setUTCHours(0, 0, 0, 0); + + return activity.reduce((acc, a) => { + let existingTask = acc[a.taskIdentifier]; + + if (!existingTask) { + existingTask = []; + //populate the array with the past 7 days + for (let i = 6; i >= 0; i--) { + const day = new Date(today); + day.setUTCDate(today.getDate() - i); + day.setUTCHours(0, 0, 0, 0); + + existingTask.push({ + day: day.toISOString(), + [TaskRunStatus.COMPLETED_SUCCESSFULLY]: 0, + } as { day: string } & Record); + } + + acc[a.taskIdentifier] = existingTask; + } + + const dayString = a.day.toISOString(); + const day = existingTask.find((d) => d.day === dayString); + + if (!day) { + logger.warn(`Day not found for TaskRun`, { + day: dayString, + taskIdentifier: a.taskIdentifier, + existingTask, + }); + return acc; + } + + day[a.status] = Number(a.count); + + return acc; + }, {} as Record)[]>); + } + + async #getRunningStats(tasks: string[], projectId: string) { + const statuses = await this._replica.$queryRaw< + { + taskIdentifier: string; + status: TaskRunStatus; + count: BigInt; + }[] + >` + SELECT + tr."taskIdentifier", + tr."status", + COUNT(*) + FROM + ${sqlDatabaseSchema}."TaskRun" as tr + WHERE + tr."taskIdentifier" IN (${Prisma.join(tasks)}) + AND tr."projectId" = ${projectId} + AND tr."status" IN ('PENDING', 'WAITING_FOR_DEPLOY', 'EXECUTING', 'RETRYING_AFTER_FAILURE', 'WAITING_TO_RESUME') + GROUP BY + tr."taskIdentifier", + tr."status" + ORDER BY + tr."taskIdentifier" ASC, + tr."status" ASC;`; + + return statuses.reduce((acc, a) => { + let existingTask = acc[a.taskIdentifier]; + + if (!existingTask) { + existingTask = { + queued: 0, + running: 0, + }; + + acc[a.taskIdentifier] = existingTask; + } + + if (QUEUED_STATUSES.includes(a.status)) { + existingTask.queued += Number(a.count); + } + if (RUNNING_STATUSES.includes(a.status)) { + existingTask.running += Number(a.count); + } + + return acc; + }, {} as Record); + } + + async #getAverageDurations(tasks: string[], projectId: string) { + const durations = await this._replica.$queryRaw< + { + taskIdentifier: string; + duration: Number; + }[] + >` + SELECT + tr."taskIdentifier", + AVG(EXTRACT(EPOCH FROM (tr."updatedAt" - tr."lockedAt"))) as duration + FROM + ${sqlDatabaseSchema}."TaskRun" as tr + WHERE + tr."taskIdentifier" IN (${Prisma.join(tasks)}) + AND tr."projectId" = ${projectId} + AND tr."createdAt" >= (current_date - interval '6 days') + AND tr."status" IN ('COMPLETED_SUCCESSFULLY', 'COMPLETED_WITH_ERRORS') + GROUP BY + tr."taskIdentifier";`; + + return Object.fromEntries(durations.map((s) => [s.taskIdentifier, Number(s.duration)])); } } diff --git a/apps/webapp/app/presenters/v3/basePresenter.server.ts b/apps/webapp/app/presenters/v3/basePresenter.server.ts index 1820494dc..39298c9da 100644 --- a/apps/webapp/app/presenters/v3/basePresenter.server.ts +++ b/apps/webapp/app/presenters/v3/basePresenter.server.ts @@ -1,10 +1,13 @@ import { Span, SpanKind } from "@opentelemetry/api"; -import { PrismaClientOrTransaction, prisma } from "~/db.server"; +import { $replica, PrismaClientOrTransaction, prisma } from "~/db.server"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { attributesFromAuthenticatedEnv, tracer } from "../../v3/tracer.server"; export abstract class BasePresenter { - constructor(protected readonly _prisma: PrismaClientOrTransaction = prisma) {} + constructor( + protected readonly _prisma: PrismaClientOrTransaction = prisma, + protected readonly _replica: PrismaClientOrTransaction = $replica + ) {} protected async traceWithEnv( trace: string, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.astro/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.astro/route.tsx index c07ca0e46..f2aa69f43 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.astro/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.astro/route.tsx @@ -1,5 +1,4 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; -import invariant from "tiny-invariant"; import { AstroLogo } from "~/assets/logos/AstroLogo"; import { Feedback } from "~/components/Feedback"; import { InitCommand, RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; @@ -11,18 +10,17 @@ import { Header1 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import { StepNumber } from "~/components/primitives/StepNumber"; import { useAppOrigin } from "~/hooks/useAppOrigin"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; export default function SetUpAstro() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); - invariant(devEnvironment, "Dev environment must be defined"); + const { apiKey } = useV2OnboardingApiKey(); const appOrigin = useAppOrigin(); return ( @@ -67,7 +65,7 @@ export default function SetUpAstro() { title="Run the CLI 'init' command in an existing Astro project" /> - + You’ll notice a new folder in your project called 'jobs'. We’ve added a very simple diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.express/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.express/route.tsx index 1950d078b..ede841fdd 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.express/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.express/route.tsx @@ -1,5 +1,4 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; -import invariant from "tiny-invariant"; import { ExpressLogo } from "~/assets/logos/ExpressLogo"; import { Feedback } from "~/components/Feedback"; import { RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; @@ -11,20 +10,17 @@ import { ClipboardField } from "~/components/primitives/ClipboardField"; import { Header1 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import { StepNumber } from "~/components/primitives/StepNumber"; -import { useAppOrigin } from "~/hooks/useAppOrigin"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; export default function Page() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); - invariant(devEnvironment, "Dev environment must be defined"); - const appOrigin = useAppOrigin(); + const { apiKey } = useV2OnboardingApiKey(); return (
@@ -73,7 +69,7 @@ export default function Page() { Server} /> diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx index 17d4c630c..e4929d27d 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx @@ -1,5 +1,4 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; -import invariant from "tiny-invariant"; import { NestjsLogo } from "~/assets/logos/NestjsLogo"; import { Feedback } from "~/components/Feedback"; import { TriggerDevStep } from "~/components/SetupCommands"; @@ -11,12 +10,12 @@ import { Header1 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import { StepNumber } from "~/components/primitives/StepNumber"; import { useAppOrigin } from "~/hooks/useAppOrigin"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; import { CodeBlock } from "../../components/code/CodeBlock"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; const AppModuleCode = ` import { Module } from '@nestjs/common'; @@ -114,11 +113,9 @@ export default function SetupNestJS() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); + const { apiKey } = useV2OnboardingApiKey(); const appOrigin = useAppOrigin(); - invariant(devEnvironment, "devEnvironment is required"); - return (
@@ -161,7 +158,7 @@ export default function SetupNestJS() { diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nextjs/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nextjs/route.tsx index e5de3efa0..c01c599b6 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nextjs/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nextjs/route.tsx @@ -1,6 +1,5 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; import { useState } from "react"; -import invariant from "tiny-invariant"; import { NextjsLogo } from "~/assets/logos/NextjsLogo"; import { Feedback } from "~/components/Feedback"; import { InitCommand, RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; @@ -21,11 +20,11 @@ import { Paragraph } from "~/components/primitives/Paragraph"; import { RadioGroup, RadioGroupItem } from "~/components/primitives/RadioButton"; import { StepNumber } from "~/components/primitives/StepNumber"; import { useAppOrigin } from "~/hooks/useAppOrigin"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; type SelectionChoices = "use-existing-project" | "create-new-next-app"; @@ -33,12 +32,10 @@ export default function SetupNextjs() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); + const { apiKey } = useV2OnboardingApiKey(); const appOrigin = useAppOrigin(); const [selectedValue, setSelectedValue] = useState(null); - invariant(devEnvironment, "devEnvironment is required"); - return (
@@ -152,7 +149,7 @@ export default function SetupNextjs() { title="Run the CLI 'init' command in your new Next.js project" /> - + You’ll notice a new folder in your project called 'jobs'. We’ve added a very simple example Job in examples.ts{" "} @@ -179,7 +176,7 @@ export default function SetupNextjs() { title="Run the CLI 'init' command in an existing Next.js project" /> - + You’ll notice a new folder in your project called 'jobs'. We’ve added a very diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.remix/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.remix/route.tsx index f1f20e7fb..620a09c4c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.remix/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.remix/route.tsx @@ -1,5 +1,4 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; -import invariant from "tiny-invariant"; import { RemixLogo } from "~/assets/logos/RemixLogo"; import { Feedback } from "~/components/Feedback"; import { InitCommand, RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; @@ -11,18 +10,17 @@ import { Header1 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import { StepNumber } from "~/components/primitives/StepNumber"; import { useAppOrigin } from "~/hooks/useAppOrigin"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; export default function SetUpRemix() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); - invariant(devEnvironment, "Dev environment must be defined"); + const { apiKey } = useV2OnboardingApiKey(); const appOrigin = useAppOrigin(); return ( @@ -67,7 +65,7 @@ export default function SetUpRemix() { title="Run the CLI 'init' command in an existing Remix project" /> - + You’ll notice a new folder in your project called 'jobs'. We’ve added a very simple diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx index 88eeafaa5..47da85ab7 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx @@ -1,5 +1,4 @@ import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; -import invariant from "tiny-invariant"; import { SvelteKitLogo } from "~/assets/logos/SveltekitLogo"; import { Feedback } from "~/components/Feedback"; import { RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; @@ -11,18 +10,18 @@ import { ClipboardField } from "~/components/primitives/ClipboardField"; import { Header1 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import { StepNumber } from "~/components/primitives/StepNumber"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { projectSetupPath } from "~/utils/pathBuilder"; +import { useV2OnboardingApiKey } from "../_app.orgs.$organizationSlug.projects.$projectParam.setup/route"; export default function SetUpSveltekit() { const organization = useOrganization(); const project = useProject(); useProjectSetupComplete(); - const devEnvironment = useDevEnvironment(); - invariant(devEnvironment, "Dev environment must be defined"); + const { apiKey } = useV2OnboardingApiKey(); + return (
@@ -70,7 +69,7 @@ export default function SetUpSveltekit() { Server} /> diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup/route.tsx index 23c24e589..031e790ac 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup/route.tsx @@ -1,4 +1,48 @@ import { Outlet } from "@remix-run/react"; +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { typedjson } from "remix-typedjson"; +import { prisma } from "~/db.server"; +import { useTypedMatchData, useTypedMatchesData } from "~/hooks/useTypedMatchData"; +import { requireUserId } from "~/services/session.server"; +import { ProjectParamSchema } from "~/utils/pathBuilder"; + +export const loader = async ({ request, params }: LoaderFunctionArgs) => { + const userId = await requireUserId(request); + const { organizationSlug, projectParam } = ProjectParamSchema.parse(params); + + const environment = await prisma.runtimeEnvironment.findFirst({ + where: { + organization: { + slug: organizationSlug, + }, + project: { + slug: projectParam, + }, + orgMember: { + userId, + }, + }, + }); + + if (!environment) { + throw new Response("Not Found", { status: 404 }); + } + + return typedjson({ + apiKey: environment.apiKey, + }); +}; + +export function useV2OnboardingApiKey() { + const routeMatch = useTypedMatchesData({ + id: "routes/_app.orgs.$organizationSlug.projects.$projectParam.setup", + }); + if (!routeMatch) { + throw new Error("Route match not found"); + } + + return routeMatch; +} export default function Page() { return ( diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam._index/route.tsx index a7f2cac49..7a0753de3 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam._index/route.tsx @@ -1,21 +1,24 @@ -import { ChatBubbleLeftRightIcon } from "@heroicons/react/20/solid"; +import { ChatBubbleLeftRightIcon, ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/20/solid"; import { useRevalidator } from "@remix-run/react"; import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database"; -import { useEffect } from "react"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import invariant from "tiny-invariant"; +import { formatDuration, formatDurationMilliseconds } from "@trigger.dev/core/v3"; +import { TaskRunStatus } from "@trigger.dev/database"; +import { Fragment, Suspense, useEffect, useState } from "react"; +import { Bar, BarChart, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts"; +import { TypedAwait, typeddefer, useTypedLoaderData } from "remix-typedjson"; import { Feedback } from "~/components/Feedback"; -import { InitCommandV3, TriggerDevStepV3 } from "~/components/SetupCommands"; +import { InitCommandV3, TriggerDevStepV3, TriggerLoginStepV3 } from "~/components/SetupCommands"; import { StepContentContainer } from "~/components/StepContentContainer"; import { InlineCode } from "~/components/code/InlineCode"; import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; import { Button } from "~/components/primitives/Buttons"; -import { DateTime } from "~/components/primitives/DateTime"; -import { Header1 } from "~/components/primitives/Headers"; +import { Callout } from "~/components/primitives/Callout"; +import { DateTime, formatDateTime } from "~/components/primitives/DateTime"; +import { Header1, Header2, Header3 } from "~/components/primitives/Headers"; import { NavBar, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; +import { Spinner } from "~/components/primitives/Spinner"; import { StepNumber } from "~/components/primitives/StepNumber"; import { Table, @@ -28,18 +31,22 @@ import { TableRow, } from "~/components/primitives/Table"; import { SimpleTooltip } from "~/components/primitives/Tooltip"; -import { TaskFunctionName, TaskPath } from "~/components/runs/v3/TaskPath"; -import { TaskRunStatusCombo } from "~/components/runs/v3/TaskRunStatus"; +import { TaskFunctionName } from "~/components/runs/v3/TaskPath"; +import { + TaskRunStatusCombo, + TaskRunStatusIcon, + runStatusClassNameColor, + runStatusTitle, +} from "~/components/runs/v3/TaskRunStatus"; import { TaskTriggerSourceIcon, taskTriggerSourceDescription, } from "~/components/runs/v3/TaskTriggerSource"; -import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useEventSource } from "~/hooks/useEventSource"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { useUser } from "~/hooks/useUser"; -import { TaskListPresenter } from "~/presenters/v3/TaskListPresenter.server"; +import { TaskActivity, TaskListPresenter } from "~/presenters/v3/TaskListPresenter.server"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; import { ProjectParamSchema, v3RunsPath, v3TasksStreamingPath } from "~/utils/pathBuilder"; @@ -50,14 +57,18 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { try { const presenter = new TaskListPresenter(); - const tasks = await presenter.call({ + const { tasks, userHasTasks, activity, runningStats, durations } = await presenter.call({ userId, organizationSlug, projectSlug: projectParam, }); - return typedjson({ + return typeddefer({ tasks, + userHasTasks, + activity, + runningStats, + durations, }); } catch (error) { console.error(error); @@ -71,8 +82,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { export default function Page() { const organization = useOrganization(); const project = useProject(); - const user = useUser(); - const { tasks } = useTypedLoaderData(); + const { tasks, userHasTasks, activity, runningStats, durations } = + useTypedLoaderData(); const hasTasks = tasks.length > 0; //live reload the page when the tasks change @@ -97,35 +108,30 @@ export default function Page() {
{hasTasks ? ( -
+
+ {!userHasTasks && } Task ID Task - Path - Environment + Running + Queued + Activity (7d) + Avg. duration + Environments Last run - -
Last run status
-
- Created at Go to page
{tasks.length > 0 ? ( tasks.map((task) => { - const usernameForEnv = - user.id !== task.environment.userId - ? task.environment.userName - : undefined; const path = v3RunsPath(organization, project, { tasks: [task.slug], - environments: [task.environment.id], }); return ( - +
{task.slug}
- + - {task.filePath} + + + + + } + > + + {(data) => { + const taskData = data[task.slug]; + return taskData?.running ?? "0"; + }} + + + + + }> + + {(data) => { + const taskData = data[task.slug]; + return taskData?.queued ?? "0"; + }} + + + + + }> + + {(data) => { + const taskData = data[task.slug]; + return ( + <> + {taskData !== undefined ? ( +
+ +
+ ) : ( + + )} + + ); + }} +
+
+
+ + }> + + {(data) => { + const taskData = data[task.slug]; + return taskData + ? formatDurationMilliseconds(taskData * 1000, { + style: "short", + }) + : "–"; + }} + + + - +
+ {task.environments.map((environment) => ( + + ))} +
- {task.latestRun ? (
+
) : ( "Never run" )}
- - {task.latestRun ? ( - - ) : ( - "–" - )} - - - -
); @@ -188,7 +251,9 @@ export default function Page() {
) : ( - + + + )}
@@ -197,19 +262,9 @@ export default function Page() { ); } -function classForTaskRunStatus(status: TaskRunStatus) { - switch (status) { - case "SYSTEM_FAILURE": - case "COMPLETED_WITH_ERRORS": - return "text-error"; - default: - return ""; - } -} - function CreateTaskInstructions() { return ( - +
Get setup in 3 minutes
@@ -240,6 +295,150 @@ function CreateTaskInstructions() { This page will automatically refresh. - +
); } + +function UserHasNoTasks() { + const [open, setOpen] = useState(false); + + return ( + setOpen((o) => !o)} + > + {open ? "Close" : "Setup your dev environment"} + + } + > + {open ? ( +
+ Get setup in 3 minutes + + + + You'll need to open a terminal at the root of your project. + + + + + + + + + + + + This page will automatically refresh. + +
+ ) : ( + "Your DEV environment isn't setup yet." + )} +
+ ); +} + +function TaskActivityGraph({ activity }: { activity: TaskActivity }) { + return ( + + + } + allowEscapeViewBox={{ x: true, y: true }} + wrapperStyle={{ zIndex: 1000 }} + /> + {/* The background */} + + + + + + + + + + + + + + + + ); +} + +function TaskActivityBlankState() { + return ( +
+ {[...Array(7)].map((_, i) => ( +
+ ))} +
+ ); +} + +const CustomTooltip = ({ active, payload, label }: TooltipProps) => { + if (active && payload) { + const items = payload.map((p) => ({ + status: p.dataKey as TaskRunStatus, + value: p.value, + })); + const title = payload[0].payload.day as string; + const formattedDate = formatDateTime(new Date(title), "UTC", [], false, false); + return ( +
+ {formattedDate} +
+ {items.map((item) => ( + + +

{item.value}

+
+ ))} +
+
+ ); + } + + return null; +}; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.apikeys/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.apikeys/route.tsx index f837c2bd6..a151ca312 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.apikeys/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.apikeys/route.tsx @@ -1,10 +1,12 @@ import { BookOpenIcon } from "@heroicons/react/20/solid"; -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { Form } from "@remix-run/react"; +import { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { EnvironmentLabel, environmentTitle } from "~/components/environments/EnvironmentLabel"; import { RegenerateApiKeyModal } from "~/components/environments/RegenerateApiKeyModal"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { LinkButton } from "~/components/primitives/Buttons"; +import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { Callout } from "~/components/primitives/Callout"; import { ClipboardField } from "~/components/primitives/ClipboardField"; import { DateTime } from "~/components/primitives/DateTime"; import { Header3 } from "~/components/primitives/Headers"; @@ -20,11 +22,15 @@ import { TableRow, } from "~/components/primitives/Table"; import { TextLink } from "~/components/primitives/TextLink"; +import { prisma } from "~/db.server"; +import { useFeatures } from "~/hooks/useFeatures"; import { useProject } from "~/hooks/useProject"; +import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server"; +import { createEnvironment } from "~/models/organization.server"; import { ApiKeysPresenter } from "~/presenters/v3/ApiKeysPresenter.server"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; -import { ProjectParamSchema, docsPath } from "~/utils/pathBuilder"; +import { ProjectParamSchema, docsPath, v3ApiKeysPath } from "~/utils/pathBuilder"; export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); @@ -32,13 +38,14 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { try { const presenter = new ApiKeysPresenter(); - const { environments } = await presenter.call({ + const { environments, hasStaging } = await presenter.call({ userId, projectSlug: projectParam, }); return typedjson({ environments, + hasStaging, }); } catch (error) { console.error(error); @@ -49,9 +56,76 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { } }; +export const action = async ({ request, params }: ActionFunctionArgs) => { + const userId = await requireUserId(request); + const { organizationSlug, projectParam } = ProjectParamSchema.parse(params); + + if (request.method.toUpperCase() !== "POST") { + return { status: 405, body: "Method Not Allowed" }; + } + + const project = await prisma.project.findUnique({ + where: { + slug: params.projectParam, + organization: { + members: { + some: { + userId, + }, + }, + }, + }, + select: { + id: true, + organizationId: true, + environments: { + select: { + type: true, + }, + }, + }, + }); + + if (!project) { + return redirectWithErrorMessage( + v3ApiKeysPath({ slug: organizationSlug }, { slug: projectParam }), + request, + "Project not found" + ); + } + + if (project.environments.some((env) => env.type === "STAGING")) { + return redirectWithErrorMessage( + v3ApiKeysPath({ slug: organizationSlug }, { slug: projectParam }), + request, + "You already have a staging environment" + ); + } + + const environment = await createEnvironment( + { id: project.organizationId }, + { id: project.id }, + "STAGING" + ); + + if (!environment) { + return redirectWithErrorMessage( + v3ApiKeysPath({ slug: organizationSlug }, { slug: projectParam }), + request, + "Failed to create staging environment" + ); + } + + return redirectWithSuccessMessage( + v3ApiKeysPath({ slug: organizationSlug }, { slug: projectParam }), + request, + "Staging environment created" + ); +}; + export default function Page() { - const { environments } = useTypedLoaderData(); - const project = useProject(); + const { environments, hasStaging } = useTypedLoaderData(); + const { isManagedCloud } = useFeatures(); return ( @@ -127,6 +201,21 @@ export default function Page() { ))} + + {!hasStaging && ( + + + + } + > + {isManagedCloud + ? "The Staging environment will be a paid feature when we add billing. In the interim you can enable it for free." + : "You can add a Staging environment to your project."} + + )}
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx index b9fc82245..2502c4586 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables.new/route.tsx @@ -208,7 +208,7 @@ export default function Page() { - Dev environment variables specified here will be overriden by ones in your{" "} + Dev environment variables specified here will be overridden by ones in your{" "} .env file when running locally. diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables/route.tsx index 1f1866cbd..ef9712496 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables/route.tsx @@ -247,7 +247,7 @@ export default function Page() { - Dev environment variables specified here will be overriden by ones in your .env file + Dev environment variables specified here will be overridden by ones in your .env file when running locally.
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs._index/route.tsx index 4e0fd2001..976390e46 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs._index/route.tsx @@ -34,6 +34,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { const presenter = new RunListPresenter(); const list = await presenter.call({ + userId, projectSlug: projectParam, tasks, versions, @@ -88,7 +89,6 @@ export default function Page() { filters={list.filters} runs={list.runs} isLoading={isLoading} - currentUser={user} />
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules.$scheduleParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules.$scheduleParam/route.tsx index 4de4deafd..760cbe783 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules.$scheduleParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules.$scheduleParam/route.tsx @@ -247,7 +247,6 @@ export default function Page() { }} runs={schedule.runs} isLoading={false} - currentUser={user} />
diff --git a/apps/webapp/app/services/environmentSort.server.ts b/apps/webapp/app/services/environmentSort.server.ts index 71dab2703..a1e22d70f 100644 --- a/apps/webapp/app/services/environmentSort.server.ts +++ b/apps/webapp/app/services/environmentSort.server.ts @@ -1,4 +1,5 @@ import { RuntimeEnvironmentType } from "@trigger.dev/database"; +import { logger } from "./logger.server"; const environmentSortOrder: RuntimeEnvironmentType[] = [ "DEVELOPMENT", @@ -9,12 +10,23 @@ const environmentSortOrder: RuntimeEnvironmentType[] = [ type SortType = { type: RuntimeEnvironmentType; + userName?: string | null; }; export function sortEnvironments(environments: T[]): T[] { return environments.sort((a, b) => { const aIndex = environmentSortOrder.indexOf(a.type); const bIndex = environmentSortOrder.indexOf(b.type); - return aIndex - bIndex; + + const difference = aIndex - bIndex; + + if (difference === 0) { + //same environment so sort by name + const usernameA = a.userName || ""; + const usernameB = b.userName || ""; + return usernameA.localeCompare(usernameB); + } + + return difference; }); } diff --git a/apps/webapp/app/services/runs/continueRun.server.ts b/apps/webapp/app/services/runs/continueRun.server.ts index 044040ae4..844b4a3c3 100644 --- a/apps/webapp/app/services/runs/continueRun.server.ts +++ b/apps/webapp/app/services/runs/continueRun.server.ts @@ -32,6 +32,14 @@ export class ContinueRunService { throw new Error("Run is not resumable"); } + // Delete any tasks that are errored + const erroredTasks = await tx.task.findMany({ + where: { + runId: runId, + status: "ERRORED", + }, + }); + await tx.jobRun.update({ where: { id: runId }, data: { @@ -45,17 +53,15 @@ export class ContinueRunService { }, }); - // Delete any tasks that are errored - await tx.task.deleteMany({ - where: { - runId: runId, - status: "ERRORED", - }, - }); + for (const task of erroredTasks) { + await tx.task.delete({ + where: { id: task.id }, + }); + } await ResumeRunService.enqueue(run, tx); }, - { timeout: 10000 } + { timeout: 30_000 } ); } } diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 0fefac87e..3f1f54e1e 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -139,7 +139,7 @@ "react-resizable-panels": "^2.0.9", "react-stately": "^3.29.1", "react-use": "^17.4.0", - "recharts": "^2.8.0", + "recharts": "^2.12.6", "remix-auth": "^3.6.0", "remix-auth-email-link": "2.0.2", "remix-auth-github": "^1.6.0", diff --git a/docs/v3/tasks-overview.mdx b/docs/v3/tasks-overview.mdx index ab81fb82e..09ec23ab7 100644 --- a/docs/v3/tasks-overview.mdx +++ b/docs/v3/tasks-overview.mdx @@ -79,7 +79,7 @@ export const taskWithRetries = task({ maxTimeoutInMs: 30_000, randomize: false, }, - run: async ({ payload, ctx }) => { + run: async (payload: any, { ctx }) => { //... }, }); @@ -99,7 +99,7 @@ export const oneAtATime = task({ queue: { concurrencyLimit: 1, }, - run: async ({ payload, ctx }) => { + run: async (payload: any, { ctx }) => { //... }, }); @@ -116,7 +116,7 @@ export const heavyTask = task({ cpu: 2, memory: 4, }, - run: async ({ payload, ctx }) => { + run: async (payload: any, { ctx }) => { //... }, }); @@ -124,27 +124,143 @@ export const heavyTask = task({ ### `init` function -This function is called before a run attempt. +This function is called before a run attempt: + +```ts /trigger/init.ts +export const taskWithInit = task({ + id: "task-with-init", + init: async (payload, { ctx }) => { + //... + }, + run: async (payload: any, { ctx }) => { + //... + }, +}); +``` + +You can also return data from the `init` function that will be available in the params of the `run`, `cleanup`, `onSuccess`, and `onFailure` functions. + +```ts /trigger/init-return.ts +export const taskWithInitReturn = task({ + id: "task-with-init-return", + init: async (payload, { ctx }) => { + return { someData: "someValue" }; + }, + run: async (payload: any, { ctx, init }) => { + console.log(init.someData); // "someValue" + }, +}); +``` ### `cleanup` function -This function is called after a run attempt has succeeded or failed. +This function is called after the `run` function is executed, regardless of whether the run was successful or not. It's useful for cleaning up resources, logging, or other side effects. + +```ts /trigger/cleanup.ts +export const taskWithCleanup = task({ + id: "task-with-cleanup", + cleanup: async (payload, { ctx }) => { + //... + }, + run: async (payload: any, { ctx }) => { + //... + }, +}); +``` ### `middleware` function This function is called before the `run` function, it allows you to wrap the run function with custom code. For more information [read the guide](/v3/middleware). +### `onStart` function + +When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects. This function will only be called one per run (not per retry). If you want to run code before each retry, use the `init` function. + +```ts /trigger/on-start.ts +export const taskWithOnStart = task({ + id: "task-with-on-start", + onStart: async (payload, { ctx }) => { + //... + }, + run: async (payload: any, { ctx }) => { + //... + }, +}); +``` + +You can also define an `onStart` function in your `trigger.config.ts` file to get notified when any task starts. + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + +export const config: TriggerConfig = { + onStart: async (payload, { ctx }) => { + console.log("Task started", ctx.task.id); + }, +}; +``` + ### `onSuccess` function -When a task attempt succeeds, the `onSuccess` function is called. It's useful for sending notifications, logging, or other side effects. +When a task run succeeds, the `onSuccess` function is called. It's useful for sending notifications, logging, syncing state to your database, or other side effects. - +```ts /trigger/on-success.ts +export const taskWithOnSuccess = task({ + id: "task-with-on-success", + onSuccess: async (payload, output, { ctx }) => { + //... + }, + run: async (payload: any, { ctx }) => { + //... + }, +}); +``` -### `onError` function +You can also define an `onSuccess` function in your `trigger.config.ts` file to get notified when any task succeeds. -When a task attempt fails, the `onError` function is called. It's useful for sending notifications, logging, or other side effects. +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; - +export const config: TriggerConfig = { + onSuccess: async (payload, output, { ctx }) => { + console.log("Task succeeded", ctx.task.id); + }, +}; +``` + +### `onFailure` function + +When a task run fails, the `onFailure` function is called. It's useful for sending notifications, logging, or other side effects. It will only be executed once the task run has exhausted all its retries. + +```ts /trigger/on-failure.ts +export const taskWithOnFailure = task({ + id: "task-with-on-failure", + onFailure: async (payload, error, { ctx }) => { + //... + }, + run: async (payload: any, { ctx }) => { + //... + }, +}); +``` + +You can also define an `onFailure` function in your `trigger.config.ts` file to get notified when any task fails. + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + +export const config: TriggerConfig = { + onFailure: async (payload, error, { ctx }) => { + console.log("Task failed", ctx.task.id); + }, +}; +``` + +### `handleError` functions + +You can define a function that will be called when an error is thrown in the `run` function, that allows you to control how the error is handled and whether the task should be retried. + +Read more about `handleError` in our [Errors and Retrying guide](/v3/errors-retrying). ## Next steps diff --git a/docs/v3/trigger-config.mdx b/docs/v3/trigger-config.mdx index 7d74070ab..2cff40c29 100644 --- a/docs/v3/trigger-config.mdx +++ b/docs/v3/trigger-config.mdx @@ -31,6 +31,46 @@ export const config: TriggerConfig = { Most of the time you don't need to change anything in this file, or if you do then we will tell you when you the run the CLI command. +## Global initialization + +You can run code before any task is run by adding a `init` function to your `trigger.config.ts` file. + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + +export const config: TriggerConfig = { + //..other stuff + init: async (payload, { ctx }) => { + console.log("I run before any task is run"); + }, +}; +``` + +You'll have access to the run payload and the context object. Currently you cannot return anything from this function. + +## Lifecycle functions + +You can add lifecycle functions to get notified when any task starts, succeeds, or fails using `onStart`, `onSuccess` and `onFailure`: + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + +export const config: TriggerConfig = { + //..other stuff + onSuccess: async (payload, output, { ctx }) => { + console.log("Task succeeded", ctx.task.id); + }, + onFailure: async (payload, error, { ctx }) => { + console.log("Task failed", ctx.task.id); + }, + onStart: async (payload, { ctx }) => { + console.log("Task started", ctx.task.id); + }, +}; +``` + +Read more about task lifecycle functions in the [tasks overview](/v3/tasks-overview). + ## Instrumentations We use OpenTelemetry (OTEL) for our run logs. This means you get a lot of information about your tasks with no effort. But you probably want to add more information to your logs. For example, here's all the Prisma calls automatically logged: @@ -92,7 +132,6 @@ Prisma works by generating a client from your `prisma.schema` file. This means y - ```json default path @@ -140,6 +179,87 @@ Prisma works by generating a client from your `prisma.schema` file. This means y +## TypeORM support + +We support using TypeORM with Trigger. You can use decorators in your entities and then use them in your tasks. Here's an example: + +```ts orm/index.ts +import "reflect-metadata"; +import { DataSource } from "typeorm"; +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity() +export class Photo { + @PrimaryColumn() + id!: number; + + @Column() + name!: string; + + @Column() + description!: string; + + @Column() + filename!: string; + + @Column() + views!: number; + + @Column() + isPublished!: boolean; +} + +export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 5432, + username: "postgres", + password: "postgres", + database: "my-database", + entities: [Photo], + synchronize: true, + logging: false, +}); +``` + +And then in your trigger.config.ts file you can initialize the datasource using the `onStart` lifecycle function option: + +```ts trigger.config.ts +import type { TriggerConfig } from "@trigger.dev/sdk/v3"; +import { AppDataSource } from "@/trigger/orm"; + +export const config: TriggerConfig = { + // ... other options here + onStart: async (payload, { ctx }) => { + await AppDataSource.initialize(); + }, +}; +``` + +Now you are ready to use this in your tasks: + +```ts +import { task } from "@trigger.dev/sdk/v3"; +import { AppDataSource, Photo } from "./orm"; + +export const taskThatUsesDecorators = task({ + id: "task-that-uses-decorators", + run: async (payload: { message: string }) => { + console.log("Creating a photo..."); + + const photo = new Photo(); + photo.id = 2; + photo.name = "Me and Bears"; + photo.description = "I am near polar bears"; + photo.filename = "photo-with-bears.jpg"; + photo.views = 1; + photo.isPublished = true; + + await AppDataSource.manager.save(photo); + }, +}); +``` + ## Troubleshooting If you have an issue with bundling let us know on [Discord](https://trigger.dev/discord) or [via email](https://trigger.dev/contact). diff --git a/integrations/airtable/CHANGELOG.md b/integrations/airtable/CHANGELOG.md index 16fe54065..75cb63363 100644 --- a/integrations/airtable/CHANGELOG.md +++ b/integrations/airtable/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/airtable +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/airtable/package.json b/integrations/airtable/package.json index 0b6d83504..c70928999 100644 --- a/integrations/airtable/package.json +++ b/integrations/airtable/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/airtable", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for airtable", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,8 +25,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "airtable": "^0.12.1", "zod": "3.22.3" }, diff --git a/integrations/github/CHANGELOG.md b/integrations/github/CHANGELOG.md index dc3515a4e..241ca7ea4 100644 --- a/integrations/github/CHANGELOG.md +++ b/integrations/github/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/github +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/github/package.json b/integrations/github/package.json index 2c5be9895..bf145fd6a 100644 --- a/integrations/github/package.json +++ b/integrations/github/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/github", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official GitHub integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -30,8 +30,8 @@ "@octokit/request-error": "^5.0.1", "@octokit/webhooks": "^12.0.10", "octokit": "^3.1.2", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "zod": "3.22.3" }, "engines": { diff --git a/integrations/linear/CHANGELOG.md b/integrations/linear/CHANGELOG.md index 625c5d218..98bc8ab34 100644 --- a/integrations/linear/CHANGELOG.md +++ b/integrations/linear/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/linear +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/linear/package.json b/integrations/linear/package.json index 3687084eb..f141adf89 100644 --- a/integrations/linear/package.json +++ b/integrations/linear/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/linear", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for @linear/sdk", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,8 +26,8 @@ }, "dependencies": { "@linear/sdk": "^8.0.0", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "zod": "3.22.3" }, "engines": { diff --git a/integrations/openai/CHANGELOG.md b/integrations/openai/CHANGELOG.md index 7a3e36cb5..3e38b31e7 100644 --- a/integrations/openai/CHANGELOG.md +++ b/integrations/openai/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/slack +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/openai/package.json b/integrations/openai/package.json index 1cd751eb7..601ba3a54 100644 --- a/integrations/openai/package.json +++ b/integrations/openai/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/openai", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official OpenAI integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -42,8 +42,8 @@ }, "dependencies": { "openai": "^4.16.1", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22" }, "engines": { "node": ">=18.0.0" diff --git a/integrations/plain/CHANGELOG.md b/integrations/plain/CHANGELOG.md index 1fd3ebdb5..8d2f9b2ea 100644 --- a/integrations/plain/CHANGELOG.md +++ b/integrations/plain/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/plain +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/plain/package.json b/integrations/plain/package.json index 79bad93c5..afe2ca44a 100644 --- a/integrations/plain/package.json +++ b/integrations/plain/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/plain", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official Plain.com integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -24,8 +24,8 @@ "build:tsup": "tsup" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "@team-plain/typescript-sdk": "^2.7.0" }, "engines": { diff --git a/integrations/replicate/CHANGELOG.md b/integrations/replicate/CHANGELOG.md index 60c916f5d..861f43242 100644 --- a/integrations/replicate/CHANGELOG.md +++ b/integrations/replicate/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/replicate +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/replicate/package.json b/integrations/replicate/package.json index 077e2385d..a6040dcf2 100644 --- a/integrations/replicate/package.json +++ b/integrations/replicate/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/replicate", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for replicate", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,8 +25,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "replicate": "^0.18.1", "zod": "3.22.3" }, diff --git a/integrations/resend/CHANGELOG.md b/integrations/resend/CHANGELOG.md index f33d7c942..e599fbe41 100644 --- a/integrations/resend/CHANGELOG.md +++ b/integrations/resend/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/resend +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/resend/package.json b/integrations/resend/package.json index 2561c0075..6d3262a8b 100644 --- a/integrations/resend/package.json +++ b/integrations/resend/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/resend", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official Resend.com integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -24,8 +24,8 @@ "build:tsup": "tsup" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "resend": "^2.1.0" }, "engines": { diff --git a/integrations/sendgrid/CHANGELOG.md b/integrations/sendgrid/CHANGELOG.md index 08e04baef..3df4e79e3 100644 --- a/integrations/sendgrid/CHANGELOG.md +++ b/integrations/sendgrid/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/sendgrid +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/sendgrid/package.json b/integrations/sendgrid/package.json index 2c532e16b..1305840e4 100644 --- a/integrations/sendgrid/package.json +++ b/integrations/sendgrid/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sendgrid", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for @sendgrid/mail", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,8 +26,8 @@ }, "dependencies": { "@sendgrid/mail": "^7.7.0", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/shopify/CHANGELOG.md b/integrations/shopify/CHANGELOG.md index 3e49bbc0b..c7e1d62a3 100644 --- a/integrations/shopify/CHANGELOG.md +++ b/integrations/shopify/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/shopify +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/shopify/package.json b/integrations/shopify/package.json index a5e9c669e..39e005a13 100644 --- a/integrations/shopify/package.json +++ b/integrations/shopify/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/shopify", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for @shopify/shopify-api", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,8 +26,8 @@ }, "dependencies": { "@shopify/shopify-api": "^8.0.2", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", "zod": "3.22.3" }, "engines": { diff --git a/integrations/slack/CHANGELOG.md b/integrations/slack/CHANGELOG.md index 98fbfa9ec..bba779c61 100644 --- a/integrations/slack/CHANGELOG.md +++ b/integrations/slack/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/slack +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/slack/package.json b/integrations/slack/package.json index 665af2fcf..745a2af45 100644 --- a/integrations/slack/package.json +++ b/integrations/slack/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/slack", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official Slack integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,7 +25,7 @@ }, "dependencies": { "@slack/web-api": "^6.8.1", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "zod": "3.22.3" }, "engines": { diff --git a/integrations/stripe/CHANGELOG.md b/integrations/stripe/CHANGELOG.md index 253a51760..6f2defde9 100644 --- a/integrations/stripe/CHANGELOG.md +++ b/integrations/stripe/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/stripe +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/stripe/package.json b/integrations/stripe/package.json index 41acf648c..810e0f555 100644 --- a/integrations/stripe/package.json +++ b/integrations/stripe/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/stripe", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for stripe", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,8 +25,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "stripe": "^12.14.0", "zod": "3.22.3" }, diff --git a/integrations/supabase/CHANGELOG.md b/integrations/supabase/CHANGELOG.md index 0d67bfc51..ae0425f1a 100644 --- a/integrations/supabase/CHANGELOG.md +++ b/integrations/supabase/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/supabase +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/supabase/package.json b/integrations/supabase/package.json index 1051b3b32..3907d8a0e 100644 --- a/integrations/supabase/package.json +++ b/integrations/supabase/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/supabase", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev integration for @supabase/supabase-js", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,8 +26,8 @@ }, "dependencies": { "@supabase/supabase-js": "^2.26.0", - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "supabase-management-js": "^1.0.0", "zod": "3.22.3" }, diff --git a/integrations/typeform/CHANGELOG.md b/integrations/typeform/CHANGELOG.md index 2ecd4d671..9c7e3d8e3 100644 --- a/integrations/typeform/CHANGELOG.md +++ b/integrations/typeform/CHANGELOG.md @@ -1,5 +1,20 @@ # @trigger.dev/typeform +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/integration-kit@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/integration-kit@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/integrations/typeform/package.json b/integrations/typeform/package.json index a1ed5ca72..ee266d7c1 100644 --- a/integrations/typeform/package.json +++ b/integrations/typeform/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/typeform", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The official Typeform integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -24,8 +24,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.20", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/integration-kit": "workspace:^3.0.0-beta.22", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "@typeform/api-client": "^1.8.0", "zod": "3.22.3" }, diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index cd1321c6e..00f9c61b6 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/astro +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index 45b7da9ef..6ebbc82fa 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,7 +1,7 @@ { "name": "@trigger.dev/astro", "description": "An Astro-native integration for Trigger.dev background jobs platform", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ @@ -20,7 +20,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22" }, "devDependencies": { "astro": "^3.0.12", diff --git a/packages/cli-v3/CHANGELOG.md b/packages/cli-v3/CHANGELOG.md index 1927aa80f..52c728180 100644 --- a/packages/cli-v3/CHANGELOG.md +++ b/packages/cli-v3/CHANGELOG.md @@ -1,5 +1,100 @@ # trigger.dev +## 3.0.0-beta.22 + +### Patch Changes + +- ebeb79052: Add typescript as a dependency so the esbuild-decorator will work even when running in npx + - @trigger.dev/core@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- 9491a1649: Implement task.onSuccess/onFailure and config.onSuccess/onFailure +- 9491a1649: Adds support for `emitDecoratorMetadata: true` and `experimentalDecorators: true` in your tsconfig using the [`@anatine/esbuild-decorators`](https://github.com/anatine/esbuildnx/tree/main/packages/esbuild-decorators) package. This allows you to use libraries like TypeORM: + + ```ts orm/index.ts + import "reflect-metadata"; + import { DataSource } from "typeorm"; + import { Entity, Column, PrimaryColumn } from "typeorm"; + + @Entity() + export class Photo { + @PrimaryColumn() + id!: number; + + @Column() + name!: string; + + @Column() + description!: string; + + @Column() + filename!: string; + + @Column() + views!: number; + + @Column() + isPublished!: boolean; + } + + export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 5432, + username: "postgres", + password: "postgres", + database: "v3-catalog", + entities: [Photo], + synchronize: true, + logging: false, + }); + ``` + + And then in your trigger.config.ts file you can initialize the datasource using the new `init` option: + + ```ts trigger.config.ts + import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + import { AppDataSource } from "@/trigger/orm"; + + export const config: TriggerConfig = { + // ... other options here + init: async (payload, { ctx }) => { + await AppDataSource.initialize(); + }, + }; + ``` + + Now you are ready to use this in your tasks: + + ```ts + import { task } from "@trigger.dev/sdk/v3"; + import { AppDataSource, Photo } from "./orm"; + + export const taskThatUsesDecorators = task({ + id: "taskThatUsesDecorators", + run: async (payload: { message: string }) => { + console.log("Creating a photo..."); + + const photo = new Photo(); + photo.id = 2; + photo.name = "Me and Bears"; + photo.description = "I am near polar bears"; + photo.filename = "photo-with-bears.jpg"; + photo.views = 1; + photo.isPublished = true; + + await AppDataSource.manager.save(photo); + }, + }); + ``` + +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/core@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 5533d068d..73bec575e 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -1,6 +1,6 @@ { "name": "trigger.dev", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "A Command-Line Interface for Trigger.dev (v3) projects", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -71,6 +71,7 @@ "test": "vitest" }, "dependencies": { + "@anatine/esbuild-decorators": "^0.2.19", "@clack/prompts": "^0.7.0", "@depot/cli": "0.0.1-cli.2.55.0", "@opentelemetry/api": "^1.8.0", @@ -85,7 +86,7 @@ "@opentelemetry/sdk-trace-base": "^1.22.0", "@opentelemetry/sdk-trace-node": "^1.22.0", "@opentelemetry/semantic-conventions": "^1.22.0", - "@trigger.dev/core": "workspace:3.0.0-beta.20", + "@trigger.dev/core": "workspace:3.0.0-beta.22", "@types/degit": "^2.8.3", "chalk": "^5.2.0", "chokidar": "^3.5.3", @@ -124,7 +125,8 @@ "url": "^0.11.1", "ws": "^8.12.0", "zod": "3.22.3", - "zod-validation-error": "^1.5.0" + "zod-validation-error": "^1.5.0", + "typescript": "^5.4.0" }, "engines": { "node": ">=18.0.0" diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index 55193c349..6ca164878 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -8,14 +8,13 @@ import { flattenAttributes, } from "@trigger.dev/core/v3"; import { recordSpanException } from "@trigger.dev/core/v3/workers"; -import chalk from "chalk"; import { Command, Option as CommandOption } from "commander"; import { Metafile, build } from "esbuild"; import { execa } from "execa"; import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, join, relative, posix } from "node:path"; +import { dirname, join, posix, relative } from "node:path"; import { setTimeout } from "node:timers/promises"; import terminalLink from "terminal-link"; import invariant from "tiny-invariant"; @@ -32,7 +31,7 @@ import { wrapCommandAction, } from "../cli/common.js"; import { readConfig } from "../utilities/configFiles.js"; -import { createTempDir, readJSONFile, writeJSONFile } from "../utilities/fileSystem"; +import { createTempDir, writeJSONFile } from "../utilities/fileSystem"; import { printStandloneInitialBanner } from "../utilities/initialBanner.js"; import { detectPackageNameFromImportPath, @@ -43,6 +42,7 @@ import { logger } from "../utilities/logger.js"; import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles"; import { login } from "./login"; +import { esbuildDecorators } from "@anatine/esbuild-decorators"; import { Glob, GlobOptions } from "glob"; import type { SetOptional } from "type-fest"; import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build"; @@ -53,12 +53,12 @@ import { parseBuildErrorStack, parseNpmInstallError, } from "../utilities/deployErrors"; -import { safeJsonParse } from "../utilities/safeJsonParse"; import { JavascriptProject } from "../utilities/javascriptProject"; +import { docs, getInTouch } from "../utilities/links"; import { cliRootPath } from "../utilities/resolveInternalFilePath"; +import { safeJsonParse } from "../utilities/safeJsonParse"; import { escapeImportPath, spinner } from "../utilities/windows"; import { updateTriggerPackages } from "./update"; -import { docs, getInTouch } from "../utilities/links"; const DeployCommandOptions = CommonCommandOptions.extend({ skipTypecheck: z.boolean().default(false), @@ -1137,6 +1137,11 @@ async function compileProject( config.tsconfigPath ), workerSetupImportConfigPlugin(configPath), + esbuildDecorators({ + tsconfig: config.tsconfigPath, + tsx: true, + force: false, + }), ], }); diff --git a/packages/cli-v3/src/commands/dev.tsx b/packages/cli-v3/src/commands/dev.tsx index 33516e797..b182b535b 100644 --- a/packages/cli-v3/src/commands/dev.tsx +++ b/packages/cli-v3/src/commands/dev.tsx @@ -53,6 +53,7 @@ import { findUp, pathExists } from "find-up"; import { cliRootPath } from "../utilities/resolveInternalFilePath"; import { escapeImportPath } from "../utilities/windows"; import { updateTriggerPackages } from "./update"; +import { esbuildDecorators } from "@anatine/esbuild-decorators"; let apiClient: CliApiClient | undefined; @@ -409,6 +410,11 @@ function useDev({ config.tsconfigPath ), workerSetupImportConfigPlugin(configPath), + esbuildDecorators({ + tsconfig: config.tsconfigPath, + tsx: true, + force: false, + }), { name: "trigger.dev v3", setup(build) { diff --git a/packages/cli-v3/src/utilities/configFiles.ts b/packages/cli-v3/src/utilities/configFiles.ts index 87ed14779..5725e0e67 100644 --- a/packages/cli-v3/src/utilities/configFiles.ts +++ b/packages/cli-v3/src/utilities/configFiles.ts @@ -10,6 +10,7 @@ import { createTempDir, readJSONFileSync } from "./fileSystem.js"; import { logger } from "./logger.js"; import { findTriggerDirectories, resolveTriggerDirectories } from "./taskFiles.js"; import { build } from "esbuild"; +import { esbuildDecorators } from "@anatine/esbuild-decorators"; function getGlobalConfigFolderPath() { const configDir = xdgAppPaths("trigger").config(); @@ -172,6 +173,13 @@ export async function readConfig( target: ["es2018", "node18"], outfile: builtConfigFilePath, logLevel: "silent", + plugins: [ + esbuildDecorators({ + cwd: absoluteDir, + tsx: false, + force: false, + }), + ], }); // import the config file diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 99120fac9..d2ad9ebcb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,21 @@ # create-trigger +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/core@3.0.0-beta.22 +- @trigger.dev/yalt@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/core@3.0.0-beta.21 + - @trigger.dev/yalt@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 66b38f4d7..0e4c12447 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/cli", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "The Trigger.dev CLI", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/core-apps/CHANGELOG.md b/packages/core-apps/CHANGELOG.md index 9bf77c902..9eeb4ea27 100644 --- a/packages/core-apps/CHANGELOG.md +++ b/packages/core-apps/CHANGELOG.md @@ -1,5 +1,9 @@ # @trigger.dev/core-apps +## 3.0.0-beta.22 + +## 3.0.0-beta.21 + ## 3.0.0-beta.20 ## 3.0.0-beta.19 diff --git a/packages/core-apps/package.json b/packages/core-apps/package.json index 89e1ca054..85c444372 100644 --- a/packages/core-apps/package.json +++ b/packages/core-apps/package.json @@ -1,7 +1,7 @@ { "name": "@trigger.dev/core-apps", "description": "Backend core code used across apps", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "private": true, "license": "MIT", "main": "./dist/index.js", diff --git a/packages/core-backend/CHANGELOG.md b/packages/core-backend/CHANGELOG.md index 2cb476ac9..42d69e538 100644 --- a/packages/core-backend/CHANGELOG.md +++ b/packages/core-backend/CHANGELOG.md @@ -1,5 +1,9 @@ # @trigger.dev/core-backend +## 3.0.0-beta.22 + +## 3.0.0-beta.21 + ## 3.0.0-beta.20 ## 3.0.0-beta.19 diff --git a/packages/core-backend/package.json b/packages/core-backend/package.json index e46f1eb7a..e9c365beb 100644 --- a/packages/core-backend/package.json +++ b/packages/core-backend/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/core-backend", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Core code used across `@trigger.dev/sdk` and Trigger.dev server", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 52304949e..5116d795d 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,91 @@ # internal-platform +## 3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- 9491a1649: Implement task.onSuccess/onFailure and config.onSuccess/onFailure +- 9491a1649: Adds support for `emitDecoratorMetadata: true` and `experimentalDecorators: true` in your tsconfig using the [`@anatine/esbuild-decorators`](https://github.com/anatine/esbuildnx/tree/main/packages/esbuild-decorators) package. This allows you to use libraries like TypeORM: + + ```ts orm/index.ts + import "reflect-metadata"; + import { DataSource } from "typeorm"; + import { Entity, Column, PrimaryColumn } from "typeorm"; + + @Entity() + export class Photo { + @PrimaryColumn() + id!: number; + + @Column() + name!: string; + + @Column() + description!: string; + + @Column() + filename!: string; + + @Column() + views!: number; + + @Column() + isPublished!: boolean; + } + + export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 5432, + username: "postgres", + password: "postgres", + database: "v3-catalog", + entities: [Photo], + synchronize: true, + logging: false, + }); + ``` + + And then in your trigger.config.ts file you can initialize the datasource using the new `init` option: + + ```ts trigger.config.ts + import type { TriggerConfig } from "@trigger.dev/sdk/v3"; + import { AppDataSource } from "@/trigger/orm"; + + export const config: TriggerConfig = { + // ... other options here + init: async (payload, { ctx }) => { + await AppDataSource.initialize(); + }, + }; + ``` + + Now you are ready to use this in your tasks: + + ```ts + import { task } from "@trigger.dev/sdk/v3"; + import { AppDataSource, Photo } from "./orm"; + + export const taskThatUsesDecorators = task({ + id: "taskThatUsesDecorators", + run: async (payload: { message: string }) => { + console.log("Creating a photo..."); + + const photo = new Photo(); + photo.id = 2; + photo.name = "Me and Bears"; + photo.description = "I am near polar bears"; + photo.filename = "photo-with-bears.jpg"; + photo.views = 1; + photo.isPublished = true; + + await AppDataSource.manager.save(photo); + }, + }); + ``` + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 181ace193..b4a8ddcf3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/core", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Core code used across the Trigger.dev SDK and platform", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/core/src/v3/types/config.ts b/packages/core/src/v3/types/config.ts index e2fe65242..81786576d 100644 --- a/packages/core/src/v3/types/config.ts +++ b/packages/core/src/v3/types/config.ts @@ -1,3 +1,4 @@ +import { FailureFnParams, InitFnParams, StartFnParams, SuccessFnParams } from "."; import { LogLevel } from "../logger/taskLogger"; import { RetryOptions } from "../schemas"; import type { InstrumentationOption } from "@opentelemetry/instrumentation"; @@ -16,7 +17,7 @@ export interface ProjectConfig { * List of additional files to include in your trigger.dev bundle. e.g. ["./prisma/schema.prisma"] * * Supports glob patterns. - * + * * Note: The path separator for glob patterns is `/`, even on Windows! */ additionalFiles?: string[]; @@ -48,4 +49,24 @@ export interface ProjectConfig { * Enable console logging while running the dev CLI. This will print out logs from console.log, console.warn, and console.error. By default all logs are sent to the trigger.dev backend, and not logged to the console. */ enableConsoleLogging?: boolean; + + /** + * Run before a task is executed, for all tasks. This is useful for setting up any global state that is needed for all tasks. + */ + init?: (payload: unknown, params: InitFnParams) => void | Promise; + + /** + * onSuccess is called after the run function has successfully completed. + */ + onSuccess?: (payload: unknown, output: unknown, params: SuccessFnParams) => Promise; + + /** + * onFailure is called after a task run has failed (meaning the run function threw an error and won't be retried anymore) + */ + onFailure?: (payload: unknown, error: unknown, params: FailureFnParams) => Promise; + + /** + * onStart is called the first time a task is executed in a run (not before every retry) + */ + onStart?: (payload: unknown, params: StartFnParams) => Promise; } diff --git a/packages/core/src/v3/types/index.ts b/packages/core/src/v3/types/index.ts index f6072f8f4..e1a79157f 100644 --- a/packages/core/src/v3/types/index.ts +++ b/packages/core/src/v3/types/index.ts @@ -22,12 +22,15 @@ export type InitFnParams = Prettify<{ ctx: Context; }>; +export type StartFnParams = Prettify<{ + ctx: Context; +}>; + export type Context = TaskRunContext; -export type SuccessFnParams = RunFnParams & - Prettify<{ - output: TOutput; - }>; +export type SuccessFnParams = RunFnParams; + +export type FailureFnParams = RunFnParams; export type HandleErrorFnParams = RunFnParams & Prettify<{ @@ -74,5 +77,8 @@ export type TaskMetadataWithFunctions = TaskMetadata & { error: unknown, params: HandleErrorFnParams ) => HandleErrorResult; + onSuccess?: (payload: any, output: any, params: SuccessFnParams) => Promise; + onFailure?: (payload: any, error: unknown, params: FailureFnParams) => Promise; + onStart?: (payload: any, params: StartFnParams) => Promise; }; }; diff --git a/packages/core/src/v3/workers/taskExecutor.ts b/packages/core/src/v3/workers/taskExecutor.ts index 48fd1abd1..16a396ede 100644 --- a/packages/core/src/v3/workers/taskExecutor.ts +++ b/packages/core/src/v3/workers/taskExecutor.ts @@ -90,10 +90,16 @@ export class TaskExecutor { parsedPayload = await parsePacket(payloadPacket); - initOutput = await this.#callTaskInit(parsedPayload, ctx); + if (execution.attempt.number === 1) { + await this.#callOnStartFunctions(parsedPayload, ctx); + } + + initOutput = await this.#callInitFunctions(parsedPayload, ctx); const output = await this.#callRun(parsedPayload, ctx, initOutput); + await this.#callOnSuccessFunctions(parsedPayload, output, ctx, initOutput); + try { const stringifiedOutput = await stringifyIO(output); @@ -148,6 +154,15 @@ export class TaskExecutor { recordSpanException(span, handleErrorResult.error ?? runError); + if (handleErrorResult.status !== "retry") { + await this.#callOnFailureFunctions( + parsedPayload, + handleErrorResult.error ?? runError, + ctx, + initOutput + ); + } + return { id: execution.run.id, ok: false, @@ -218,16 +233,194 @@ export class TaskExecutor { return middlewareFn(payload, { ctx, next: async () => runFn(payload, { ctx, init }) }); } - async #callTaskInit(payload: unknown, ctx: TaskRunContext) { + async #callInitFunctions(payload: unknown, ctx: TaskRunContext) { + await this.#callConfigInit(payload, ctx); + const initFn = this.task.fns.init; if (!initFn) { return {}; } - return this._tracer.startActiveSpan("init", async (span) => { - return await initFn(payload, { ctx }); - }); + return this._tracer.startActiveSpan( + "init", + async (span) => { + return await initFn(payload, { ctx }); + }, + { + attributes: { + [SemanticInternalAttributes.STYLE_ICON]: "function", + }, + } + ); + } + + async #callConfigInit(payload: unknown, ctx: TaskRunContext) { + const initFn = this._importedConfig?.init; + + if (!initFn) { + return {}; + } + + return this._tracer.startActiveSpan( + "config.init", + async (span) => { + return await initFn(payload, { ctx }); + }, + { + attributes: { + [SemanticInternalAttributes.STYLE_ICON]: "function", + }, + } + ); + } + + async #callOnSuccessFunctions( + payload: unknown, + output: any, + ctx: TaskRunContext, + initOutput: any + ) { + await this.#callOnSuccessFunction( + this.task.fns.onSuccess, + "task.onSuccess", + payload, + output, + ctx, + initOutput + ); + + await this.#callOnSuccessFunction( + this._importedConfig?.onSuccess, + "config.onSuccess", + payload, + output, + ctx, + initOutput + ); + } + + async #callOnSuccessFunction( + onSuccessFn: TaskMetadataWithFunctions["fns"]["onSuccess"], + name: string, + payload: unknown, + output: any, + ctx: TaskRunContext, + initOutput: any + ) { + if (!onSuccessFn) { + return; + } + + try { + await this._tracer.startActiveSpan( + name, + async (span) => { + return await onSuccessFn(payload, output, { ctx, init: initOutput }); + }, + { + attributes: { + [SemanticInternalAttributes.STYLE_ICON]: "function", + }, + } + ); + } catch { + // Ignore errors from onSuccess functions + } + } + + async #callOnFailureFunctions( + payload: unknown, + error: unknown, + ctx: TaskRunContext, + initOutput: any + ) { + await this.#callOnFailureFunction( + this.task.fns.onFailure, + "task.onFailure", + payload, + error, + ctx, + initOutput + ); + + await this.#callOnFailureFunction( + this._importedConfig?.onFailure, + "config.onFailure", + payload, + error, + ctx, + initOutput + ); + } + + async #callOnFailureFunction( + onFailureFn: TaskMetadataWithFunctions["fns"]["onFailure"], + name: string, + payload: unknown, + error: unknown, + ctx: TaskRunContext, + initOutput: any + ) { + if (!onFailureFn) { + return; + } + + try { + return await this._tracer.startActiveSpan( + name, + async (span) => { + return await onFailureFn(payload, error, { ctx, init: initOutput }); + }, + { + attributes: { + [SemanticInternalAttributes.STYLE_ICON]: "function", + }, + } + ); + } catch (e) { + // Ignore errors from onFailure functions + } + } + + async #callOnStartFunctions(payload: unknown, ctx: TaskRunContext) { + await this.#callOnStartFunction( + this._importedConfig?.onStart, + "config.onStart", + payload, + ctx, + {} + ); + + await this.#callOnStartFunction(this.task.fns.onStart, "task.onStart", payload, ctx, {}); + } + + async #callOnStartFunction( + onStartFn: TaskMetadataWithFunctions["fns"]["onStart"], + name: string, + payload: unknown, + ctx: TaskRunContext, + initOutput: any + ) { + if (!onStartFn) { + return; + } + + try { + await this._tracer.startActiveSpan( + name, + async (span) => { + return await onStartFn(payload, { ctx }); + }, + { + attributes: { + [SemanticInternalAttributes.STYLE_ICON]: "function", + }, + } + ); + } catch { + // Ignore errors from onStart functions + } } async #callTaskCleanup(payload: unknown, ctx: TaskRunContext, init: unknown) { diff --git a/packages/database/prisma/migrations/20240424210540_change_on_delete_ref_action_on_run_execution/migration.sql b/packages/database/prisma/migrations/20240424210540_change_on_delete_ref_action_on_run_execution/migration.sql new file mode 100644 index 000000000..56977e0b9 --- /dev/null +++ b/packages/database/prisma/migrations/20240424210540_change_on_delete_ref_action_on_run_execution/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "JobRunExecution" DROP CONSTRAINT "JobRunExecution_resumeTaskId_fkey"; + +-- AddForeignKey +ALTER TABLE "JobRunExecution" ADD CONSTRAINT "JobRunExecution_resumeTaskId_fkey" FOREIGN KEY ("resumeTaskId") REFERENCES "Task"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/database/prisma/migrations/20240425114134_task_run_compound_index_for_project_id_created_at_and_task_identifier/migration.sql b/packages/database/prisma/migrations/20240425114134_task_run_compound_index_for_project_id_created_at_and_task_identifier/migration.sql new file mode 100644 index 000000000..500138b5e --- /dev/null +++ b/packages/database/prisma/migrations/20240425114134_task_run_compound_index_for_project_id_created_at_and_task_identifier/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "TaskRun_projectId_createdAt_taskIdentifier_idx" ON "TaskRun"("projectId", "createdAt", "taskIdentifier"); diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 27264c81f..1afa7b1c3 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -991,7 +991,7 @@ model JobRunExecution { reason JobRunExecutionReason @default(EXECUTE_JOB) status JobRunExecutionStatus @default(PENDING) - resumeTask Task? @relation(fields: [resumeTaskId], references: [id], onDelete: Cascade, onUpdate: Cascade) + resumeTask Task? @relation(fields: [resumeTaskId], references: [id], onDelete: SetNull, onUpdate: Cascade) resumeTaskId String? graphileJobId String? @@ -1636,6 +1636,8 @@ model TaskRun { scheduleId String? @@unique([runtimeEnvironmentId, idempotencyKey]) + // Task activity graph + @@index([projectId, createdAt, taskIdentifier]) } enum TaskRunStatus { diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 841a877ca..cf5d78174 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,9 @@ # @trigger.dev/eslint-plugin +## 3.0.0-beta.22 + +## 3.0.0-beta.21 + ## 3.0.0-beta.20 ## 3.0.0-beta.19 diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index bcb271872..b9e34bf32 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/eslint-plugin", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "ESLint plugin with trigger.dev best practices", "keywords": [ "eslint", diff --git a/packages/express/CHANGELOG.md b/packages/express/CHANGELOG.md index 1cfb78989..e7c8d92be 100644 --- a/packages/express/CHANGELOG.md +++ b/packages/express/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/express +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/express/package.json b/packages/express/package.json index 3845f42be..b1c010cbf 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/express", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Official Express adapter for Trigger.dev", "license": "MIT", "main": "./dist/index.js", @@ -23,7 +23,7 @@ "./package.json": "./package.json" }, "devDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "@trigger.dev/tsconfig": "workspace:*", "@types/debug": "^4.1.7", "@types/express": "^4.17.13", @@ -39,7 +39,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22" }, "dependencies": { "debug": "^4.3.4", diff --git a/packages/hono/CHANGELOG.md b/packages/hono/CHANGELOG.md index a131b3a28..fb9258602 100644 --- a/packages/hono/CHANGELOG.md +++ b/packages/hono/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/hono +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/hono/package.json b/packages/hono/package.json index 6db8a0e78..71c066982 100644 --- a/packages/hono/package.json +++ b/packages/hono/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/hono", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "A Trigger.dev adapter for Hono.dev", "license": "MIT", "main": "./dist/index.js", @@ -32,7 +32,7 @@ }, "peerDependencies": { "hono": "3.x", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22" }, "devDependencies": { "@trigger.dev/tsconfig": "workspace:*", diff --git a/packages/integration-kit/CHANGELOG.md b/packages/integration-kit/CHANGELOG.md index 50d0971e8..c2b111017 100644 --- a/packages/integration-kit/CHANGELOG.md +++ b/packages/integration-kit/CHANGELOG.md @@ -1,5 +1,19 @@ # @trigger.dev/integration-kit +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/core@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/core@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/integration-kit/package.json b/packages/integration-kit/package.json index 5bcc0609c..83caa6082 100644 --- a/packages/integration-kit/package.json +++ b/packages/integration-kit/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/integration-kit", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev Integration Kit has helpers to make creating integrations easier", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/nestjs/CHANGELOG.md b/packages/nestjs/CHANGELOG.md index b794bb2b5..362305090 100644 --- a/packages/nestjs/CHANGELOG.md +++ b/packages/nestjs/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/nestjs +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 801ae66f2..1cf08a410 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/nestjs", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Official NestJS adapter for Trigger.dev", "license": "MIT", "main": "./dist/index.js", @@ -23,7 +23,7 @@ "./package.json": "./package.json" }, "devDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "@trigger.dev/tsconfig": "workspace:*", "@types/debug": "^4.1.7", "@types/express": "^4.17.13", @@ -41,7 +41,7 @@ }, "peerDependencies": { "@nestjs/common": ">=10.0.0", - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22" }, "dependencies": { "@nestjs/common": "^10.2.4", diff --git a/packages/nextjs/CHANGELOG.md b/packages/nextjs/CHANGELOG.md index 59583e365..3e5126f04 100644 --- a/packages/nextjs/CHANGELOG.md +++ b/packages/nextjs/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/nextjs +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3cd1cc25a..2f077310d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/nextjs", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev Next.js integration", "license": "MIT", "main": "./dist/index.js", @@ -41,7 +41,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "next": ">=12.0.0" }, "dependencies": { diff --git a/packages/otlp-importer/CHANGELOG.md b/packages/otlp-importer/CHANGELOG.md index 4daf086f9..6c7cf0c48 100644 --- a/packages/otlp-importer/CHANGELOG.md +++ b/packages/otlp-importer/CHANGELOG.md @@ -1,5 +1,9 @@ # @trigger.dev/otlp-importer +## 3.0.0-beta.22 + +## 3.0.0-beta.21 + ## 3.0.0-beta.20 ## 3.0.0-beta.19 diff --git a/packages/otlp-importer/package.json b/packages/otlp-importer/package.json index 92c1f56cd..f8df22b92 100644 --- a/packages/otlp-importer/package.json +++ b/packages/otlp-importer/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/otlp-importer", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "OpenTelemetry OTLP Importer for Node.js written in TypeScript", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 8caaa6cf7..94871ecec 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,19 @@ # @trigger.dev/react +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/core@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/core@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index 9ed35ab1c..12b27e7b0 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/react", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev React SDK", "license": "MIT", "types": "dist/index.d.ts", @@ -27,7 +27,7 @@ }, "dependencies": { "@tanstack/react-query": "5.0.0-beta.2", - "@trigger.dev/core": "workspace:^3.0.0-beta.20", + "@trigger.dev/core": "workspace:^3.0.0-beta.22", "debug": "^4.3.4", "zod": "3.22.3" }, diff --git a/packages/remix/CHANGELOG.md b/packages/remix/CHANGELOG.md index 7c34bb281..aef9cde1a 100644 --- a/packages/remix/CHANGELOG.md +++ b/packages/remix/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/remix +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/remix/package.json b/packages/remix/package.json index 95c6f77ff..bea20bb4c 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/remix", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev Remix integration", "license": "MIT", "main": "./dist/index.js", @@ -39,7 +39,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20", + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22", "@remix-run/server-runtime": ">1.19.0" }, "dependencies": { diff --git a/packages/sveltekit/CHANGELOG.md b/packages/sveltekit/CHANGELOG.md index 3ef158f1b..dbd580306 100644 --- a/packages/sveltekit/CHANGELOG.md +++ b/packages/sveltekit/CHANGELOG.md @@ -1,5 +1,18 @@ # @trigger.dev/sveltekit +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 792e7e960..4746f628e 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sveltekit", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "Trigger.dev svelteKit integration", "license": "MIT", "main": "./dist/index.js", @@ -39,7 +39,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^3.0.0-beta.20" + "@trigger.dev/sdk": "workspace:^3.0.0-beta.22" }, "dependencies": { "debug": "^4.3.4" diff --git a/packages/testing/CHANGELOG.md b/packages/testing/CHANGELOG.md index 5defb6b0e..f78c82e17 100644 --- a/packages/testing/CHANGELOG.md +++ b/packages/testing/CHANGELOG.md @@ -1,5 +1,21 @@ # @trigger.dev/testing +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/core@3.0.0-beta.22 +- @trigger.dev/sdk@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/sdk@3.0.0-beta.21 + - @trigger.dev/core@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/testing/package.json b/packages/testing/package.json index 88cba9627..f8bbb1632 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,7 +1,7 @@ { "name": "@trigger.dev/testing", "description": "A collection of useful tools to write tests for Trigger.dev.", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/trigger-sdk/CHANGELOG.md b/packages/trigger-sdk/CHANGELOG.md index ddf2e3ee8..7aafd9e52 100644 --- a/packages/trigger-sdk/CHANGELOG.md +++ b/packages/trigger-sdk/CHANGELOG.md @@ -1,5 +1,22 @@ # @trigger.dev/sdk +## 3.0.0-beta.22 + +### Patch Changes + +- @trigger.dev/core@3.0.0-beta.22 +- @trigger.dev/core-backend@3.0.0-beta.22 + +## 3.0.0-beta.21 + +### Patch Changes + +- 9491a1649: Implement task.onSuccess/onFailure and config.onSuccess/onFailure +- Updated dependencies [9491a1649] +- Updated dependencies [9491a1649] + - @trigger.dev/core@3.0.0-beta.21 + - @trigger.dev/core-backend@3.0.0-beta.21 + ## 3.0.0-beta.20 ### Patch Changes diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index bea323fca..2475542d7 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sdk", - "version": "3.0.0-beta.20", + "version": "3.0.0-beta.22", "description": "trigger.dev Node.JS SDK", "license": "MIT", "main": "./dist/index.js", @@ -48,8 +48,8 @@ "@opentelemetry/api": "^1.8.0", "@opentelemetry/api-logs": "^0.48.0", "@opentelemetry/semantic-conventions": "^1.22.0", - "@trigger.dev/core": "workspace:3.0.0-beta.20", - "@trigger.dev/core-backend": "workspace:3.0.0-beta.20", + "@trigger.dev/core": "workspace:3.0.0-beta.22", + "@trigger.dev/core-backend": "workspace:3.0.0-beta.22", "chalk": "^5.2.0", "cronstrue": "^2.21.0", "debug": "^4.3.4", diff --git a/packages/trigger-sdk/src/v3/shared.ts b/packages/trigger-sdk/src/v3/shared.ts index aa950897a..ff1121f6a 100644 --- a/packages/trigger-sdk/src/v3/shared.ts +++ b/packages/trigger-sdk/src/v3/shared.ts @@ -6,6 +6,7 @@ import { } from "@opentelemetry/semantic-conventions"; import { BatchTaskRunExecutionResult, + FailureFnParams, HandleErrorFnParams, HandleErrorResult, InitFnParams, @@ -17,6 +18,7 @@ import { RetryOptions, RunFnParams, SemanticInternalAttributes, + StartFnParams, SuccessFnParams, TaskRunContext, TaskRunExecutionResult, @@ -143,15 +145,67 @@ export type TaskOptions< * @param params - Metadata about the run. */ run: (payload: TPayload, params: RunFnParams) => Promise; + + /** + * init is called before the run function is called. It's useful for setting up any global state. + */ init?: (payload: TPayload, params: InitFnParams) => Promise; + + /** + * cleanup is called after the run function has completed. + */ + cleanup?: (payload: TPayload, params: RunFnParams) => Promise; + + /** + * handleError is called when the run function throws an error. It can be used to modify the error or return new retry options. + */ handleError?: ( payload: TPayload, error: unknown, params: HandleErrorFnParams ) => HandleErrorResult; - cleanup?: (payload: TPayload, params: RunFnParams) => Promise; + + /** + * middleware allows you to run code "around" the run function. This can be useful for logging, metrics, or other cross-cutting concerns. + * + * When writing middleware, you should always call `next()` to continue the execution of the task: + * + * ```ts + * export const middlewareTask = task({ + * id: "middleware-task", + * middleware: async (payload, { ctx, next }) => { + * console.log("Before run"); + * await next(); + * console.log("After run"); + * }, + * run: async (payload, { ctx }) => {} + * }); + * ``` + */ middleware?: (payload: TPayload, params: MiddlewareFnParams) => Promise; - onSuccess?: (payload: TPayload, params: SuccessFnParams) => Promise; + + /** + * onStart is called the first time a task is executed in a run (not before every retry) + */ + onStart?: (payload: TPayload, params: StartFnParams) => Promise; + + /** + * onSuccess is called after the run function has successfully completed. + */ + onSuccess?: ( + payload: TPayload, + output: TOutput, + params: SuccessFnParams + ) => Promise; + + /** + * onFailure is called after a task run has failed (meaning the run function threw an error and won't be retried anymore) + */ + onFailure?: ( + payload: TPayload, + error: unknown, + params: FailureFnParams + ) => Promise; }; type InvokeHandle = { @@ -248,6 +302,14 @@ export interface Task { batchTriggerAndWait: (items: Array>) => Promise>; } +export type TaskPayload = TTask extends Task + ? TInput + : never; + +export type TaskOutput = TTask extends Task + ? TOutput + : never; + type TaskRunOptions = { idempotencyKey?: string; maxAttempts?: number; @@ -624,6 +686,9 @@ export function createTask=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} @@ -13130,6 +13150,10 @@ packages: - supports-color dev: false + /@sqltools/formatter@1.2.5: + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + dev: false + /@supabase/functions-js@2.1.2: resolution: {integrity: sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==} dependencies: @@ -14524,7 +14548,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@5.59.6(eslint@8.42.0)(typescript@5.3.3): + /@typescript-eslint/parser@5.59.6(eslint@8.42.0)(typescript@5.4.5): resolution: {integrity: sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -14536,10 +14560,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.59.6 '@typescript-eslint/types': 5.59.6 - '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) eslint: 8.42.0 - typescript: 5.3.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -14811,7 +14835,7 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@5.59.6(typescript@5.3.3): + /@typescript-eslint/typescript-estree@5.59.6(typescript@5.4.5): resolution: {integrity: sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -14826,8 +14850,8 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.3.3) - typescript: 5.3.3 + tsutils: 3.21.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -14913,7 +14937,7 @@ packages: - typescript dev: true - /@typescript-eslint/utils@5.59.6(eslint@8.45.0)(typescript@5.3.3): + /@typescript-eslint/utils@5.59.6(eslint@8.45.0)(typescript@5.4.5): resolution: {integrity: sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -14924,7 +14948,7 @@ packages: '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.59.6 '@typescript-eslint/types': 5.59.6 - '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.4.5) eslint: 8.45.0 eslint-scope: 5.1.1 semver: 7.5.4 @@ -15437,7 +15461,6 @@ packages: /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - dev: true /acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} @@ -15649,6 +15672,11 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 + /app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + dev: false + /append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} @@ -17042,10 +17070,6 @@ packages: typescript: 5.2.2 dev: false - /classnames@2.3.2: - resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} - dev: false - /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -17073,6 +17097,19 @@ packages: dependencies: restore-cursor: 4.0.0 + /cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + dev: false + /cli-spinners@2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} @@ -17869,6 +17906,10 @@ packages: '@babel/runtime': 7.22.5 dev: true + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: false + /deasync@0.1.28: resolution: {integrity: sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==} engines: {node: '>=0.11.0'} @@ -18255,10 +18296,11 @@ packages: resolution: {integrity: sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==} dev: true - /dom-helpers@3.4.0: - resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==} + /dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: '@babel/runtime': 7.22.5 + csstype: 3.1.1 dev: false /dom-serializer@2.0.0: @@ -19174,7 +19216,7 @@ packages: - supports-color dev: true - /eslint-config-next@13.4.6(eslint@8.42.0)(typescript@5.3.3): + /eslint-config-next@13.4.6(eslint@8.42.0)(typescript@5.4.5): resolution: {integrity: sha512-nlv4FYish1RYYHILbQwM5/rD37cOvEqtMfDjtQCYbXdE2O3MggqHu2q6IDeLE2Z6u8ZJyNPgWOA6OimWcxj3qw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 @@ -19185,7 +19227,7 @@ packages: dependencies: '@next/eslint-plugin-next': 13.4.6 '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.3.3) + '@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.4.5) eslint: 8.42.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.42.0) @@ -19193,7 +19235,7 @@ packages: eslint-plugin-jsx-a11y: 6.7.1(eslint@8.42.0) eslint-plugin-react: 7.32.2(eslint@8.42.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.42.0) - typescript: 5.3.3 + typescript: 5.4.5 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color @@ -19235,14 +19277,14 @@ packages: eslint-plugin-turbo: 1.13.0(eslint@8.31.0) dev: true - /eslint-doc-generator@1.4.3(eslint@8.45.0)(typescript@5.3.3): + /eslint-doc-generator@1.4.3(eslint@8.45.0)(typescript@5.4.5): resolution: {integrity: sha512-cn9KXE7xuKlxKi/9VbirR3cbz7W1geRObwWzZjJAnpTeNBoqA8Rj+lD8/HHHJ7PnOdaTrRyhhoYdCtxqq3U7Bw==} engines: {node: ^14.18.0 || ^16.0.0 || >=18.0.0} hasBin: true peerDependencies: eslint: '>= 7' dependencies: - '@typescript-eslint/utils': 5.59.6(eslint@8.45.0)(typescript@5.3.3) + '@typescript-eslint/utils': 5.59.6(eslint@8.45.0)(typescript@5.4.5) ajv: 8.12.0 boolean: 3.2.0 commander: 10.0.1 @@ -21385,6 +21427,7 @@ packages: minimatch: 9.0.3 minipass: 7.0.3 path-scurry: 1.10.1 + dev: true /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -21881,6 +21924,10 @@ packages: engines: {node: '>=8'} dev: true + /highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + dev: false + /highlight.run@7.3.4: resolution: {integrity: sha512-Rgx+gy0tb2tH4hNzxYi/VK5pL/msaAtaQBIy8XsPHLujdSgo5OPWO6vOdjjB7ufM1l/CI2RLmlQ+L2QZOuHBjw==} dev: false @@ -22979,6 +23026,7 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + dev: true /jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} @@ -23679,7 +23727,7 @@ packages: dependencies: config-chain: 1.1.13 editorconfig: 1.0.4 - glob: 10.3.3 + glob: 10.3.10 js-cookie: 3.0.5 nopt: 7.2.0 dev: false @@ -25340,6 +25388,12 @@ packages: engines: {node: '>=10'} hasBin: true + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mlly@1.4.2: resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: @@ -26886,6 +26940,16 @@ packages: engines: {node: '>=6'} dev: true + /parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + dependencies: + parse5: 6.0.1 + dev: false + + /parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + dev: false + /parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} @@ -27021,10 +27085,20 @@ packages: is-reference: 3.0.1 dev: true + /pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + requiresBuild: true + dev: false + optional: true + /pg-connection-string@2.5.0: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} dev: false + /pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + dev: false + /pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -27038,10 +27112,22 @@ packages: pg: 8.10.0 dev: false + /pg-pool@3.6.2(pg@8.11.5): + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + dependencies: + pg: 8.11.5 + dev: false + /pg-protocol@1.6.0: resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} dev: false + /pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + dev: false + /pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} @@ -27071,6 +27157,24 @@ packages: pgpass: 1.0.5 dev: false + /pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + dependencies: + pg-connection-string: 2.6.4 + pg-pool: 3.6.2(pg@8.11.5) + pg-protocol: 1.6.1 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + dev: false + /pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} dependencies: @@ -28252,10 +28356,6 @@ packages: /react-is@18.1.0: resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==} - /react-lifecycles-compat@3.0.4: - resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} - dev: false - /react-query@3.39.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} peerDependencies: @@ -28336,17 +28436,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-resize-detector@8.1.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==} - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - dependencies: - lodash: 4.17.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - /react-router-dom@6.14.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==} engines: {node: '>=14'} @@ -28391,18 +28480,17 @@ packages: '@remix-run/router': 1.10.0 react: 18.2.0 - /react-smooth@2.0.4(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OkFsrrMBTvQUwEJthE1KXSOj79z57yvEWeFefeXPib+RmQEI9B1Ub1PgzlzzUyBOvl/TjXt5nF2hmD4NsgAh8A==} + /react-smooth@4.0.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==} peerDependencies: - prop-types: ^15.6.0 - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: fast-equals: 5.0.1 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-transition-group: 2.9.0(react-dom@18.2.0)(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false /react-stately@3.29.1(react@18.2.0): @@ -28453,18 +28541,18 @@ packages: tslib: 2.6.2 dev: false - /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: - react: '>=15.0.0' - react-dom: '>=15.0.0' + react: '>=16.6.0' + react-dom: '>=16.6.0' dependencies: - dom-helpers: 3.4.0 + '@babel/runtime': 7.22.5 + dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-lifecycles-compat: 3.0.4 dev: false /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.5.0): @@ -28624,25 +28712,22 @@ packages: decimal.js-light: 2.5.1 dev: false - /recharts@2.8.0(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-nciXqQDh3aW8abhwUlA4EBOBusRHLNiKHfpRZiG/yjups1x+auHb2zWPuEcTn/IMiN47vVMMuF8Sr+vcQJtsmw==} - engines: {node: '>=12'} + /recharts@2.12.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==} + engines: {node: '>=14'} peerDependencies: - prop-types: ^15.6.0 react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: - classnames: 2.3.2 + clsx: 2.0.0 eventemitter3: 4.0.7 lodash: 4.17.21 - prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-is: 16.13.1 - react-resize-detector: 8.1.0(react-dom@18.2.0)(react@18.2.0) - react-smooth: 2.0.4(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + react-smooth: 4.0.1(react-dom@18.2.0)(react@18.2.0) recharts-scale: 0.4.5 - reduce-css-calc: 2.1.8 + tiny-invariant: 1.3.1 victory-vendor: 36.6.11 dev: false @@ -28682,6 +28767,10 @@ packages: /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + dev: false + /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} engines: {node: '>=4'} @@ -29655,6 +29744,14 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + /sharp@0.32.6: resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} engines: {node: '>=14.15.0'} @@ -30633,8 +30730,8 @@ packages: picocolors: 1.0.0 sade: 1.8.1 svelte: 4.2.1 - svelte-preprocess: 5.0.4(postcss@8.4.29)(svelte@4.2.1)(typescript@5.3.3) - typescript: 5.3.3 + svelte-preprocess: 5.0.4(postcss@8.4.29)(svelte@4.2.1)(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - '@babel/core' - coffeescript @@ -30673,7 +30770,7 @@ packages: svelte: 4.2.1 dev: true - /svelte-preprocess@5.0.4(postcss@8.4.29)(svelte@4.2.1)(typescript@5.3.3): + /svelte-preprocess@5.0.4(postcss@8.4.29)(svelte@4.2.1)(typescript@5.4.5): resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} engines: {node: '>= 14.10.0'} requiresBuild: true @@ -30718,7 +30815,7 @@ packages: sorcery: 0.11.0 strip-indent: 3.0.0 svelte: 4.2.1 - typescript: 5.3.3 + typescript: 5.4.5 dev: true /svelte@4.2.1: @@ -31500,7 +31597,6 @@ packages: typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true /ts-poet@6.6.0: resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==} @@ -31748,6 +31844,47 @@ packages: dev: true patched: true + /tsup@8.0.1(patch_hash=a5ztaafw5l4qfghy2hjjuynb34)(postcss@8.4.27)(typescript@5.4.5): + resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 4.0.1(esbuild@0.19.11) + cac: 6.7.14 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@8.1.1) + esbuild: 0.19.11 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss: 8.4.27 + postcss-load-config: 4.0.1(postcss@8.4.27) + resolve-from: 5.0.0 + rollup: 4.6.1 + source-map: 0.8.0-beta.0 + sucrase: 3.32.0 + tree-kill: 1.2.2 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + patched: true + /tsup@8.0.2(postcss@8.4.27)(typescript@5.3.2): resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} engines: {node: '>=18'} @@ -31827,14 +31964,14 @@ packages: typescript: 5.2.2 dev: true - /tsutils@3.21.0(typescript@5.3.3): + /tsutils@3.21.0(typescript@5.4.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.3.3 + typescript: 5.4.5 dev: true /tsx@3.12.2: @@ -32061,6 +32198,85 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + /typeorm@0.3.20(pg@8.11.5)(ts-node@10.9.2): + resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==} + engines: {node: '>=16.13.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.8.0 + mssql: ^9.1.1 || ^10.0.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + dependencies: + '@sqltools/formatter': 1.2.5 + app-root-path: 3.1.0 + buffer: 6.0.3 + chalk: 4.1.2 + cli-highlight: 2.1.11 + dayjs: 1.11.10 + debug: 4.3.4(supports-color@8.1.1) + dotenv: 16.4.5 + glob: 10.3.10 + mkdirp: 2.1.6 + pg: 8.11.5 + reflect-metadata: 0.2.2 + sha.js: 2.4.11 + ts-node: 10.9.2(@types/node@20.4.2)(typescript@5.3.3) + tslib: 2.6.2 + uuid: 9.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: false + /typescript@4.9.4: resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} engines: {node: '>=4.2.0'} @@ -32097,6 +32313,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + /ufo@1.3.0: resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} diff --git a/references/v3-catalog/package.json b/references/v3-catalog/package.json index 06199b4aa..a6e915e7b 100644 --- a/references/v3-catalog/package.json +++ b/references/v3-catalog/package.json @@ -18,7 +18,10 @@ "execa": "^8.0.1", "msw": "^2.2.1", "openai": "^4.28.0", + "pg": "^8.11.5", + "reflect-metadata": "^0.1.13", "stripe": "^12.14.0", + "typeorm": "^0.3.20", "yt-dlp-wrap": "^2.3.12" }, "devDependencies": { diff --git a/references/v3-catalog/src/handleError.ts b/references/v3-catalog/src/handleError.ts index 1dbec25bb..261f7e589 100644 --- a/references/v3-catalog/src/handleError.ts +++ b/references/v3-catalog/src/handleError.ts @@ -2,6 +2,4 @@ import { logger, type HandleErrorFunction } from "@trigger.dev/sdk/v3"; export const handleError: HandleErrorFunction = async (payload, error, { ctx, retry }) => { logger.log("handling error", { error }); - - return { skipRetrying: true }; }; diff --git a/references/v3-catalog/src/trigger/decorators.ts b/references/v3-catalog/src/trigger/decorators.ts new file mode 100644 index 000000000..fd7e9cf02 --- /dev/null +++ b/references/v3-catalog/src/trigger/decorators.ts @@ -0,0 +1,32 @@ +import { logger, task } from "@trigger.dev/sdk/v3"; +import { AppDataSource, Photo } from "./orm"; + +export const taskThatUsesDecorators = task({ + id: "taskThatUsesDecorators", + run: async (payload: { message: string }) => { + console.log("Creating a photo..."); + + const photo = new Photo(); + photo.id = 2; + photo.name = "Me and Bears"; + photo.description = "I am near polar bears"; + photo.filename = "photo-with-bears.jpg"; + photo.views = 1; + photo.isPublished = true; + + await AppDataSource.manager.save(photo); + + if (Math.random() > 0.5) { + throw new Error("Failed to create photo"); + } + }, + onSuccess: async (payload, output, { ctx }) => { + logger.log("Photo created successfully", { output, ctx }); + }, + onFailure: async (payload, error, { ctx }) => { + logger.error("Failed to create photo", { error, ctx }); + }, + onStart: async (payload, { ctx }) => { + logger.log("Starting to create photo", { ctx }); + }, +}); diff --git a/references/v3-catalog/src/trigger/orm/index.ts b/references/v3-catalog/src/trigger/orm/index.ts new file mode 100644 index 000000000..766822049 --- /dev/null +++ b/references/v3-catalog/src/trigger/orm/index.ts @@ -0,0 +1,36 @@ +import "reflect-metadata"; +import { DataSource } from "typeorm"; +import { Entity, Column, PrimaryColumn } from "typeorm"; + +@Entity() +export class Photo { + @PrimaryColumn() + id!: number; + + @Column() + name!: string; + + @Column() + description!: string; + + @Column() + filename!: string; + + @Column() + views!: number; + + @Column() + isPublished!: boolean; +} + +export const AppDataSource = new DataSource({ + type: "postgres", + host: "localhost", + port: 5432, + username: "postgres", + password: "postgres", + database: "v3-catalog", + entities: [Photo], + synchronize: true, + logging: false, +}); diff --git a/references/v3-catalog/trigger.config.ts b/references/v3-catalog/trigger.config.ts index 2c1bd722d..84cfc710f 100644 --- a/references/v3-catalog/trigger.config.ts +++ b/references/v3-catalog/trigger.config.ts @@ -1,5 +1,6 @@ import type { TriggerConfig } from "@trigger.dev/sdk/v3"; import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai"; +import { AppDataSource } from "@/trigger/orm"; export { handleError } from "./src/handleError"; @@ -21,4 +22,16 @@ export const config: TriggerConfig = { instrumentations: [new OpenAIInstrumentation()], logLevel: "log", enableConsoleLogging: true, + onStart: async (payload, { ctx }) => { + if (ctx.organization.id === "clsylhs0v0002dyx75xx4pod1") { + console.log("Initializing the app data source"); + + await AppDataSource.initialize(); + } + }, + onFailure: async (payload, error, { ctx }) => { + console.log(`Task ${ctx.task.id} failed ${ctx.run.id}`); + + throw error; + }, }; diff --git a/references/v3-catalog/tsconfig.json b/references/v3-catalog/tsconfig.json index 822dd4f3e..11f740215 100644 --- a/references/v3-catalog/tsconfig.json +++ b/references/v3-catalog/tsconfig.json @@ -10,6 +10,8 @@ "@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"], "@trigger.dev/sdk/v3": ["../../packages/trigger-sdk/src/v3/index"], "@trigger.dev/sdk/v3/*": ["../../packages/trigger-sdk/src/v3/*"] - } + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true } } diff --git a/scripts/publish-prerelease.sh b/scripts/publish-prerelease.sh new file mode 100755 index 000000000..4e1c774a4 --- /dev/null +++ b/scripts/publish-prerelease.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +set -e + +# Use the first argument as version or 'v3-prerelease' if not available +version=${1:-'v3-prerelease'} + +# Ensure git stage is clear +if [[ $(git status --porcelain) ]]; then + echo "Your git status is not clean."; + exit 1; +else + echo "Git status is clean. Proceeding with the script."; +fi + +# Run your commands +rm .changeset/pre.json + +echo "Running: pnpm exec changeset version --snapshot $version" +pnpm exec changeset version --snapshot $version + +echo "Running: pnpm run build --filter \"@trigger.dev/*\" --filter \"trigger.dev\"" +pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev" + +echo "Going to run: pnpm exec changeset publish --no-git-tag --snapshot --tag $version" +read -p "Do you wish to continue? (Y/n): " prompt +if [[ $prompt =~ [yY](es)* ]]; then + pnpm exec changeset publish --no-git-tag --snapshot --tag $version +else + echo "Publish command aborted by the user." + exit 1; +fi + +# If there were no errors, clear the git stage +echo "Commands ran successfully. Clearing the git stage." +git reset --hard HEAD