-
-
-
-
- {" "}
- Environment
-
-
- {environments.map((environment) => (
-
-
-
- ))}
-
-
-
+
+
- )}
-
+
+
+ Learn more about running tests
+
+
+ {payload.error ? (
+
{payload.error}
+ ) : (
+
+ )}
+
+
+
+ Environment
+
+
+ {environments.map((environment) => (
+
+
+
+ ))}
+
+
+
+
+ Run test
+
+
+
+
+
+
);
}
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 255f31340..d13b547a0 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,21 +1,220 @@
-import { NestjsLogo } from "~/assets/logos/NestjsLogo";
-import { FrameworkComingSoon } from "~/components/frameworks/FrameworkComingSoon";
+import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid";
+import invariant from "tiny-invariant";
+import { Feedback } from "~/components/Feedback";
+import { PageGradient } from "~/components/PageGradient";
+import { StepContentContainer } from "~/components/StepContentContainer";
+import { InlineCode } from "~/components/code/InlineCode";
+import { InstallPackages } from "~/components/code/InstallPackages";
import { BreadcrumbLink } from "~/components/navigation/NavBar";
+import { Button, LinkButton } from "~/components/primitives/Buttons";
+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 { Handle } from "~/utils/handle";
-import { trimTrailingSlash } from "~/utils/pathBuilder";
+import { projectSetupPath, trimTrailingSlash } from "~/utils/pathBuilder";
+import { CodeBlock } from "../../components/code/CodeBlock";
+import { TriggerDevStep } from "~/components/SetupCommands";
export const handle: Handle = {
- breadcrumb: (match) =>
,
+ breadcrumb: (match) =>
,
};
-export default function Page() {
+const AppModuleCode = `
+import { Module } from '@nestjs/common';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { TriggerDevModule } from '@trigger.dev/nestjs';
+
+@Module({
+ imports: [
+ ConfigModule.forRoot({
+ isGlobal: true,
+ }),
+ TriggerDevModule.registerAsync({
+ inject: [ConfigService],
+ useFactory: (config: ConfigService) => ({
+ id: 'my-nest-app',
+ apiKey: config.getOrThrow('TRIGGER_API_KEY'),
+ apiUrl: config.getOrThrow('TRIGGER_API_URL'),
+ verbose: false,
+ ioLogLocalEnabled: true,
+ }),
+ }),
+ ],
+})
+export class AppModule {}
+`;
+
+const JobControllerCode = `
+import { Controller, Get } from '@nestjs/common';
+import { InjectTriggerDevClient } from '@trigger.dev/nestjs';
+import { eventTrigger, TriggerClient } from '@trigger.dev/sdk';
+
+@Controller()
+export class JobController {
+ constructor(
+ @InjectTriggerDevClient() private readonly client: TriggerClient,
+ ) {
+ this.client.defineJob({
+ id: 'test-job',
+ name: 'Test Job One',
+ version: '0.0.1',
+ trigger: eventTrigger({
+ name: 'test.event',
+ }),
+ run: async (payload, io, ctx) => {
+ await io.logger.info('Hello world!', { payload });
+
+ return {
+ message: 'Hello world!',
+ };
+ },
+ });
+ }
+
+ @Get()
+ getHello(): string {
+ return \`Running Trigger.dev with client-id \${this.client.id}\`;
+ }
+}`;
+
+const AppModuleWithControllerCode = `
+import { Module } from '@nestjs/common';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { TriggerDevModule } from '@trigger.dev/nestjs';
+import { JobController } from './job.controller';
+
+@Module({
+ imports: [
+ ConfigModule.forRoot({
+ isGlobal: true,
+ }),
+ TriggerDevModule.registerAsync({
+ inject: [ConfigService],
+ useFactory: (config: ConfigService) => ({
+ id: 'my-nest-app',
+ apiKey: config.getOrThrow('TRIGGER_API_KEY'),
+ apiUrl: config.getOrThrow('TRIGGER_API_URL'),
+ verbose: false,
+ ioLogLocalEnabled: true,
+ }),
+ }),
+ ],
+ controllers: [
+ //...existingControllers,
+ JobController
+ ],
+})
+export class AppModule {}
+`;
+
+const packageJsonCode = `"trigger.dev": {
+ "endpointId": "my-nest-app"
+}`;
+
+export default function SetupNestJS() {
+ const organization = useOrganization();
+ const project = useProject();
+ useProjectSetupComplete();
+ const devEnvironment = useDevEnvironment();
+ const appOrigin = useAppOrigin();
+
+ invariant(devEnvironment, "devEnvironment is required");
+
return (
-
-
-
+
+
+
+
+ Get setup in 2 minutes
+
+
+
+ Choose a different framework
+
+
+ I'm stuck!
+
+ }
+ defaultValue="help"
+ />
+
+
+ <>
+
+
+
+
+
+
+
+ Inside your .env file, create the following env variables:
+
+
+
+
+
+
+ Now, go to your app.module.ts and add the{" "}
+ TriggerDevModule :
+
+
+
+
+
+
+ Create a controller called{" "}
+ job.controller.ts and add the following code:
+
+
+
+
+
+
+ Now, add the new controller to your{" "}
+ app.module.ts :
+
+
+
+
+
+
+ Now, add this to the top-level of your package.json :
+
+
+
+
+
+
+ Finally, run your project with npm run start :
+
+
+
+
+
+
+
+
+ This page will automatically refresh.
+
+ >
+
+
);
}
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 8083db44b..eb47e5fdf 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,23 +1,113 @@
-import { SvelteKitLogo } from "~/assets/logos/SveltekitLogo";
-import { FrameworkComingSoon } from "~/components/frameworks/FrameworkComingSoon";
+import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid";
+import invariant from "tiny-invariant";
+import { Feedback } from "~/components/Feedback";
+import { PageGradient } from "~/components/PageGradient";
+import { RunDevCommand, TriggerDevStep } from "~/components/SetupCommands";
+import { StepContentContainer } from "~/components/StepContentContainer";
+import { InlineCode } from "~/components/code/InlineCode";
import { BreadcrumbLink } from "~/components/navigation/NavBar";
+import { Button, LinkButton } from "~/components/primitives/Buttons";
+import { ClipboardField } from "~/components/primitives/ClipboardField";
+import { Header1 } from "~/components/primitives/Headers";
+import { NamedIcon } from "~/components/primitives/NamedIcon";
+import { Paragraph } from "~/components/primitives/Paragraph";
+import { StepNumber } from "~/components/primitives/StepNumber";
+import { useAppOrigin } from "~/hooks/useAppOrigin";
+import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete";
+import { useDevEnvironment } from "~/hooks/useEnvironments";
+import { useOrganization } from "~/hooks/useOrganizations";
+import { useProject } from "~/hooks/useProject";
import { Handle } from "~/utils/handle";
-import { trimTrailingSlash } from "~/utils/pathBuilder";
-
+import { projectSetupPath, trimTrailingSlash } from "~/utils/pathBuilder";
+import { Callout } from "~/components/primitives/Callout";
+import { Badge } from "~/components/primitives/Badge";
export const handle: Handle = {
breadcrumb: (match) => (
),
};
-export default function Page() {
+export default function SetUpSveltekit() {
+ const organization = useOrganization();
+ const project = useProject();
+ useProjectSetupComplete();
+ const devEnvironment = useDevEnvironment();
+ invariant(devEnvironment, "Dev environment must be defined");
return (
-
-
-
+
+
+
+
+ Get setup in 5 minutes
+
+
+
+ Choose a different framework
+
+
+ I'm stuck!
+
+ }
+ defaultValue="help"
+ />
+
+
+
+
+ Trigger.dev has full support for serverless. We will be adding support for long-running
+ servers soon.
+
+
+
+
+ Copy your server API Key to your clipboard:
+
+ Server}
+ />
+
+ Now follow this guide:
+
+ Manual installation guide
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This page will automatically refresh.
+
+
+
+
+
);
}
diff --git a/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts
new file mode 100644
index 000000000..fcf271b21
--- /dev/null
+++ b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts
@@ -0,0 +1,68 @@
+import { ActionArgs, json } from "@remix-run/server-runtime";
+import {
+ EndpointIndexErrorSchema,
+ GetEndpointIndexResponse,
+ GetEndpointIndexResponseSchema,
+} from "@trigger.dev/core";
+import { z } from "zod";
+import { prisma } from "~/db.server";
+import { authenticateApiRequest } from "~/services/apiAuth.server";
+import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server";
+import { logger } from "~/services/logger.server";
+
+const ParamsSchema = z.object({
+ indexId: z.string(),
+});
+
+export async function loader({ request, params }: ActionArgs) {
+ if (request.method.toUpperCase() !== "GET") {
+ return { status: 405, body: "Method Not Allowed" };
+ }
+
+ const parsedParams = ParamsSchema.safeParse(params);
+
+ if (!parsedParams.success) {
+ return json({ error: "Invalid params" }, { status: 400 });
+ }
+
+ // Next authenticate the request
+ const authenticationResult = await authenticateApiRequest(request);
+ if (!authenticationResult) {
+ logger.info("Invalid or missing api key", { url: request.url });
+ return json({ error: "Invalid or Missing API key" }, { status: 401 });
+ }
+
+ const authenticatedEnv = authenticationResult.environment;
+
+ const { indexId } = parsedParams.data;
+
+ const endpointIndex = await prisma.endpointIndex.findUnique({
+ where: {
+ id: indexId,
+ endpoint: {
+ environmentId: authenticatedEnv.id,
+ },
+ },
+ });
+
+ if (!endpointIndex) {
+ logger.info("EndpointIndex not found", { url: request.url });
+ return json({ error: "EndpointIndex not found" }, { status: 404 });
+ }
+
+ const parsed = GetEndpointIndexResponseSchema.safeParse(endpointIndex);
+
+ if (!parsed.success) {
+ logger.info("EndpointIndex failed parsing", { errors: parsed.error.issues, endpointIndex });
+ const parseFailResult: GetEndpointIndexResponse = {
+ status: "FAILURE",
+ error: {
+ message: "Invalid endpoint index",
+ },
+ updatedAt: new Date(),
+ };
+ return json(parseFailResult, { status: 500 });
+ }
+
+ return json(parsed.data);
+}
diff --git a/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts b/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts
index 5429b9b21..0ccd1ed99 100644
--- a/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts
+++ b/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts
@@ -1,7 +1,7 @@
import { ActionArgs, LoaderArgs, json } from "@remix-run/server-runtime";
import { RuntimeEnvironmentType } from "@trigger.dev/database";
import { z } from "zod";
-import { PrismaClient, prisma } from "~/db.server";
+import { $transaction, PrismaClient, prisma } from "~/db.server";
import { logger } from "~/services/logger.server";
import { workerQueue } from "~/services/worker.server";
import { safeJsonParse } from "~/utils/json";
@@ -93,43 +93,53 @@ export class TriggerEndpointIndexHookService {
body,
});
- const endpoint = await this.#prismaClient.endpoint.findUnique({
- where: {
- environmentId_slug: {
- environmentId,
- slug: endpointSlug,
+ await $transaction(this.#prismaClient, async (tx) => {
+ const endpoint = await tx.endpoint.findUnique({
+ where: {
+ environmentId_slug: {
+ environmentId,
+ slug: endpointSlug,
+ },
},
- },
- include: {
- environment: true,
- },
- });
+ include: {
+ environment: true,
+ },
+ });
- if (!endpoint) {
- throw new Error("Endpoint not found");
- }
-
- if (endpoint.indexingHookIdentifier !== indexHookIdentifier) {
- throw new Error("Index hook identifier is invalid");
- }
-
- const reason = parseReasonFromBody(body);
-
- // Index the endpoint in 5 seconds from now
- await workerQueue.enqueue(
- "indexEndpoint",
- {
- id: endpoint.id,
- source: "HOOK",
- reason,
- sourceData: body,
- },
- {
- runAt: new Date(Date.now() + 5000),
- maxAttempts:
- endpoint.environment.type === RuntimeEnvironmentType.DEVELOPMENT ? 1 : undefined,
+ if (!endpoint) {
+ throw new Error("Endpoint not found");
}
- );
+
+ if (endpoint.indexingHookIdentifier !== indexHookIdentifier) {
+ throw new Error("Index hook identifier is invalid");
+ }
+
+ const reason = parseReasonFromBody(body);
+
+ const index = await tx.endpointIndex.create({
+ data: {
+ endpointId: endpoint.id,
+ status: "PENDING",
+ source: "HOOK",
+ reason,
+ sourceData: body,
+ },
+ });
+
+ // Index the endpoint in 5 seconds from now
+ await workerQueue.enqueue(
+ "performEndpointIndexing",
+ {
+ id: index.id,
+ },
+ {
+ runAt: new Date(Date.now() + 5000),
+ maxAttempts:
+ endpoint.environment.type === RuntimeEnvironmentType.DEVELOPMENT ? 1 : undefined,
+ tx,
+ }
+ );
+ });
}
}
diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts b/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts
new file mode 100644
index 000000000..876d22d84
--- /dev/null
+++ b/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts
@@ -0,0 +1,71 @@
+import type { ActionArgs } from "@remix-run/server-runtime";
+import { json } from "@remix-run/server-runtime";
+import { PrismaErrorSchema } from "~/db.server";
+import { z } from "zod";
+import { authenticateApiRequest } from "~/services/apiAuth.server";
+import { CancelRunService } from "~/services/runs/cancelRun.server";
+import { ApiRunPresenter } from "~/presenters/ApiRunPresenter.server";
+
+const ParamsSchema = z.object({
+ runId: z.string(),
+});
+
+export async function action({ request, params }: ActionArgs) {
+ // Ensure this is a POST request
+ if (request.method.toUpperCase() !== "POST") {
+ return { status: 405, body: "Method Not Allowed" };
+ }
+
+ // Authenticate the request
+ const authenticationResult = await authenticateApiRequest(request);
+
+ if (!authenticationResult) {
+ return json({ error: "Invalid or Missing API Key" }, { status: 401 });
+ }
+
+ const parsed = ParamsSchema.safeParse(params);
+
+ if (!parsed.success) {
+ return json({ error: "Invalid or Missing runId" }, { status: 400 });
+ }
+
+ const { runId } = parsed.data;
+
+ const service = new CancelRunService();
+ try {
+ await service.call({ runId });
+ } catch (error) {
+ const prismaError = PrismaErrorSchema.safeParse(error);
+ // Record not found in the database
+ if (prismaError.success && prismaError.data.code === "P2005") {
+ return json({ error: "Run not found" }, { status: 404 });
+ } else {
+ return json({ error: "Internal Server Error" }, { status: 500 });
+ }
+ }
+
+ const presenter = new ApiRunPresenter();
+ const jobRun = await presenter.call({
+ runId: runId,
+ });
+
+ if (!jobRun) {
+ return json({ message: "Run not found" }, { status: 404 });
+ }
+
+ return json({
+ id: jobRun.id,
+ status: jobRun.status,
+ startedAt: jobRun.startedAt,
+ updatedAt: jobRun.updatedAt,
+ completedAt: jobRun.completedAt,
+ output: jobRun.output,
+ tasks: jobRun.tasks,
+ statuses: jobRun.statuses.map((s) => ({
+ ...s,
+ state: s.state ?? undefined,
+ data: s.data ?? undefined,
+ history: s.history ?? undefined,
+ })),
+ });
+}
diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts
new file mode 100644
index 000000000..5c1425e2a
--- /dev/null
+++ b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts
@@ -0,0 +1,124 @@
+import type { ActionArgs } from "@remix-run/server-runtime";
+import { json } from "@remix-run/server-runtime";
+import { RuntimeEnvironmentType } from "@trigger.dev/database";
+import { z } from "zod";
+import { $transaction, PrismaClient, PrismaClientOrTransaction, prisma } from "~/db.server";
+import { enqueueRunExecutionV2 } from "~/models/jobRunExecution.server";
+import { logger } from "~/services/logger.server";
+
+const ParamsSchema = z.object({
+ runId: z.string(),
+ id: z.string(),
+ secret: z.string(),
+});
+
+export async function action({ request, params }: ActionArgs) {
+ // Ensure this is a POST request
+ if (request.method.toUpperCase() !== "POST") {
+ return { status: 405, body: "Method Not Allowed" };
+ }
+
+ const { runId, id } = ParamsSchema.parse(params);
+
+ // Parse body as JSON (no schema parsing)
+ const body = await request.json();
+
+ const service = new CallbackRunTaskService();
+
+ try {
+ // Complete task with request body as output
+ await service.call(runId, id, body, request.url);
+
+ return json({ success: true });
+ } catch (error) {
+ if (error instanceof Error) {
+ logger.error("Error while processing task callback:", { error });
+ }
+
+ return json({ error: "Something went wrong" }, { status: 500 });
+ }
+}
+
+export class CallbackRunTaskService {
+ #prismaClient: PrismaClient;
+
+ constructor(prismaClient: PrismaClient = prisma) {
+ this.#prismaClient = prismaClient;
+ }
+
+ public async call(runId: string, id: string, taskBody: any, callbackUrl: string): Promise
{
+ const task = await findTask(prisma, id);
+
+ if (!task) {
+ return;
+ }
+
+ if (task.runId !== runId) {
+ return;
+ }
+
+ if (task.status !== "WAITING") {
+ return;
+ }
+
+ if (!task.callbackUrl) {
+ return;
+ }
+
+ if (new URL(task.callbackUrl).pathname !== new URL(callbackUrl).pathname) {
+ logger.error("Callback URLs don't match", { runId, taskId: id, callbackUrl });
+ return;
+ }
+
+ logger.debug("CallbackRunTaskService.call()", { task });
+
+ await this.#resumeTask(task, taskBody);
+ }
+
+ async #resumeTask(task: NonNullable, output: any) {
+ await $transaction(this.#prismaClient, async (tx) => {
+ await tx.taskAttempt.updateMany({
+ where: {
+ taskId: task.id,
+ status: "PENDING",
+ },
+ data: {
+ status: "COMPLETED",
+ },
+ });
+
+ await tx.task.update({
+ where: { id: task.id },
+ data: {
+ status: "COMPLETED",
+ completedAt: new Date(),
+ output: output ? output : undefined,
+ },
+ });
+
+ await this.#resumeRunExecution(task, tx);
+ });
+ }
+
+ async #resumeRunExecution(task: NonNullable, prisma: PrismaClientOrTransaction) {
+ await enqueueRunExecutionV2(task.run, prisma, {
+ skipRetrying: task.run.environment.type === RuntimeEnvironmentType.DEVELOPMENT,
+ });
+ }
+}
+
+type FoundTask = Awaited>;
+
+async function findTask(prisma: PrismaClientOrTransaction, id: string) {
+ return prisma.task.findUnique({
+ where: { id },
+ include: {
+ run: {
+ include: {
+ environment: true,
+ queue: true,
+ },
+ },
+ },
+ });
+}
diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts
index 0e5eaecd9..871a48d92 100644
--- a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts
+++ b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts
@@ -1,14 +1,22 @@
import type { ActionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { TaskStatus } from "@trigger.dev/database";
-import { RunTaskBodyOutput, RunTaskBodyOutputSchema, ServerTask } from "@trigger.dev/core";
+import {
+ API_VERSIONS,
+ RunTaskBodyOutput,
+ RunTaskBodyOutputSchema,
+ RunTaskResponseWithCachedTasksBody,
+ ServerTask,
+} from "@trigger.dev/core";
import { z } from "zod";
import { $transaction, PrismaClient, prisma } from "~/db.server";
-import { taskWithAttemptsToServerTask } from "~/models/task.server";
+import { prepareTasksForCaching, taskWithAttemptsToServerTask } from "~/models/task.server";
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { ulid } from "~/services/ulid.server";
import { workerQueue } from "~/services/worker.server";
+import { generateSecret } from "~/services/sources/utils.server";
+import { env } from "~/env.server";
const ParamsSchema = z.object({
runId: z.string(),
@@ -16,6 +24,8 @@ const ParamsSchema = z.object({
const HeadersSchema = z.object({
"idempotency-key": z.string(),
+ "trigger-version": z.string().optional().nullable(),
+ "x-cached-tasks-cursor": z.string().optional().nullable(),
});
export async function action({ request, params }: ActionArgs) {
@@ -37,7 +47,11 @@ export async function action({ request, params }: ActionArgs) {
return json({ error: "Invalid or Missing idempotency key" }, { status: 400 });
}
- const { "idempotency-key": idempotencyKey } = headers.data;
+ const {
+ "idempotency-key": idempotencyKey,
+ "trigger-version": triggerVersion,
+ "x-cached-tasks-cursor": cachedTasksCursor,
+ } = headers.data;
const { runId } = ParamsSchema.parse(params);
@@ -48,6 +62,8 @@ export async function action({ request, params }: ActionArgs) {
body: anyBody,
runId,
idempotencyKey,
+ triggerVersion,
+ cachedTasksCursor,
});
const body = RunTaskBodyOutputSchema.safeParse(anyBody);
@@ -71,6 +87,26 @@ export async function action({ request, params }: ActionArgs) {
return json({ error: "Something went wrong" }, { status: 500 });
}
+ if (triggerVersion === API_VERSIONS.LAZY_LOADED_CACHED_TASKS) {
+ const requestMigration = new ChangeRequestLazyLoadedCachedTasks();
+
+ const responseBody = await requestMigration.call(runId, task, cachedTasksCursor);
+
+ logger.debug(
+ "RunTaskService.call() response migrating with ChangeRequestLazyLoadedCachedTasks",
+ {
+ responseBody,
+ cachedTasksCursor,
+ }
+ );
+
+ return json(responseBody, {
+ headers: {
+ "trigger-version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS,
+ },
+ });
+ }
+
return json(task);
} catch (error) {
if (error instanceof Error) {
@@ -81,6 +117,51 @@ export async function action({ request, params }: ActionArgs) {
}
}
+class ChangeRequestLazyLoadedCachedTasks {
+ #prismaClient: PrismaClient;
+
+ constructor(prismaClient: PrismaClient = prisma) {
+ this.#prismaClient = prismaClient;
+ }
+
+ public async call(
+ runId: string,
+ task: ServerTask,
+ cursor?: string | null
+ ): Promise {
+ if (!cursor) {
+ return {
+ task,
+ };
+ }
+
+ // We need to limit the cached tasks to not be too large >2MB when serialized
+ const TOTAL_CACHED_TASK_BYTE_LIMIT = 2000000;
+
+ const nextTasks = await this.#prismaClient.task.findMany({
+ where: {
+ runId,
+ status: "COMPLETED",
+ noop: false,
+ },
+ take: 250,
+ cursor: {
+ id: cursor,
+ },
+ orderBy: {
+ id: "asc",
+ },
+ });
+
+ const preparedTasks = prepareTasksForCaching(nextTasks, TOTAL_CACHED_TASK_BYTE_LIMIT);
+
+ return {
+ task,
+ cachedTasks: preparedTasks,
+ };
+ }
+}
+
export class RunTaskService {
#prismaClient: PrismaClient;
@@ -106,10 +187,13 @@ export class RunTaskService {
},
});
+ const delayUntilInFuture = taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now();
+ const callbackEnabled = taskBody.callback?.enabled;
+
if (existingTask) {
if (existingTask.status === "CANCELED") {
const existingTaskStatus =
- (taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now()) || taskBody.trigger
+ delayUntilInFuture || callbackEnabled || taskBody.trigger
? "WAITING"
: taskBody.noop
? "COMPLETED"
@@ -154,16 +238,21 @@ export class RunTaskService {
status = "CANCELED";
} else {
status =
- (taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now()) || taskBody.trigger
+ delayUntilInFuture || callbackEnabled || taskBody.trigger
? "WAITING"
: taskBody.noop
? "COMPLETED"
: "RUNNING";
}
+ const taskId = ulid();
+ const callbackUrl = callbackEnabled
+ ? `${env.APP_ORIGIN}/api/v1/runs/${runId}/tasks/${taskId}/callback/${generateSecret(12)}`
+ : undefined;
+
const task = await tx.task.create({
data: {
- id: ulid(),
+ id: taskId,
idempotencyKey,
displayKey: taskBody.displayKey,
runConnection: taskBody.connectionKey
@@ -191,9 +280,10 @@ export class RunTaskService {
noop: taskBody.noop,
delayUntil: taskBody.delayUntil,
params: taskBody.params ?? undefined,
- properties: taskBody.properties ?? undefined,
+ properties: this.#filterProperties(taskBody.properties) ?? undefined,
redact: taskBody.redact ?? undefined,
operation: taskBody.operation,
+ callbackUrl,
style: taskBody.style ?? { style: "normal" },
attempts: {
create: {
@@ -215,8 +305,19 @@ export class RunTaskService {
{
id: task.id,
},
- { tx, runAt: task.delayUntil ?? undefined }
+ { tx, runAt: task.delayUntil ?? undefined, jobKey: `operation:${task.id}` }
);
+ } else if (task.status === "WAITING" && callbackUrl && taskBody.callback) {
+ if (taskBody.callback.timeoutInSeconds > 0) {
+ // We need to schedule the callback timeout
+ await workerQueue.enqueue(
+ "processCallbackTimeout",
+ {
+ id: task.id,
+ },
+ { tx, runAt: new Date(Date.now() + taskBody.callback.timeoutInSeconds * 1000) }
+ );
+ }
}
return task;
@@ -224,4 +325,14 @@ export class RunTaskService {
return task ? taskWithAttemptsToServerTask(task) : undefined;
}
+
+ #filterProperties(properties: RunTaskBodyOutput["properties"]): RunTaskBodyOutput["properties"] {
+ if (!properties) return;
+
+ return properties.filter((property) => {
+ if (!property) return false;
+
+ return typeof property.label === "string" && typeof property.text === "string";
+ });
+ }
}
diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.ts b/apps/webapp/app/routes/api.v1.runs.$runId.ts
index e1273654f..12fbaffc9 100644
--- a/apps/webapp/app/routes/api.v1.runs.$runId.ts
+++ b/apps/webapp/app/routes/api.v1.runs.$runId.ts
@@ -1,7 +1,7 @@
import type { LoaderArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { z } from "zod";
-import { prisma } from "~/db.server";
+import { ApiRunPresenter } from "~/presenters/ApiRunPresenter.server";
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { apiCors } from "~/utils/apiCors";
import { taskListToTree } from "~/utils/taskListToTree";
@@ -51,51 +51,15 @@ export async function loader({ request, params }: LoaderArgs) {
const query = parsedQuery.data;
const showTaskDetails = query.taskdetails && authenticationResult.type === "PRIVATE";
-
const take = Math.min(query.take, 50);
- const jobRun = await prisma.jobRun.findUnique({
- where: {
- id: runId,
- },
- select: {
- id: true,
- status: true,
- startedAt: true,
- updatedAt: true,
- completedAt: true,
- environmentId: true,
- output: true,
- tasks: {
- select: {
- id: true,
- parentId: true,
- displayKey: true,
- status: true,
- name: true,
- icon: true,
- startedAt: true,
- completedAt: true,
- params: showTaskDetails,
- output: showTaskDetails,
- },
- where: {
- parentId: query.subtasks ? undefined : null,
- },
- orderBy: {
- id: "asc",
- },
- take: take + 1,
- cursor: query.cursor
- ? {
- id: query.cursor,
- }
- : undefined,
- },
- statuses: {
- select: { key: true, label: true, state: true, data: true, history: true },
- },
- },
+ const presenter = new ApiRunPresenter();
+ const jobRun = await presenter.call({
+ runId: runId,
+ maxTasks: take,
+ taskDetails: showTaskDetails,
+ subTasks: query.subtasks,
+ cursor: query.cursor,
});
if (!jobRun) {
diff --git a/apps/webapp/app/routes/api.v1.sources.http.$id.ts b/apps/webapp/app/routes/api.v1.sources.http.$id.ts
index 27092e881..c89a0a891 100644
--- a/apps/webapp/app/routes/api.v1.sources.http.$id.ts
+++ b/apps/webapp/app/routes/api.v1.sources.http.$id.ts
@@ -6,11 +6,24 @@ import { HandleHttpSourceService } from "~/services/sources/handleHttpSource.ser
export async function action({ request, params }: ActionArgs) {
logger.info("Handling http source", { url: request.url });
- const { id } = z.object({ id: z.string() }).parse(params);
+ try {
+ const { id } = z.object({ id: z.string() }).parse(params);
+ const service = new HandleHttpSourceService();
+ const result = await service.call(id, request);
- const service = new HandleHttpSourceService();
-
- return await service.call(id, request);
+ return new Response(undefined, {
+ status: result.status,
+ });
+ } catch (e) {
+ if (e instanceof Error) {
+ logger.error("Error handling http source", { error: e.message });
+ } else {
+ logger.error("Error handling http source", { error: JSON.stringify(e) });
+ }
+ return new Response(undefined, {
+ status: 500,
+ });
+ }
}
export async function loader({ request, params }: LoaderArgs) {
diff --git a/apps/webapp/app/routes/login._index/route.tsx b/apps/webapp/app/routes/login._index/route.tsx
index 20bab4c24..ed85ab189 100644
--- a/apps/webapp/app/routes/login._index/route.tsx
+++ b/apps/webapp/app/routes/login._index/route.tsx
@@ -60,16 +60,29 @@ export default function LoginPage() {
-
+
+
+ Create an account or login
+
{data.showGithubAuth && (
-
+
Continue with GitHub
)}
-
+
= ({ parentsData }) => ({
title: `Login to Trigger.dev${appEnvTitleTag(parentsData?.root.appEnv)}`,
@@ -32,10 +32,26 @@ export async function loader({ request }: LoaderArgs) {
});
const session = await getUserSession(request);
+ const error = session.get("auth:error");
- return typedjson({
- magicLinkSent: session.has("triggerdotdev:magiclink"),
- });
+ let magicLinkError: string | undefined;
+ if (error) {
+ if ("message" in error) {
+ magicLinkError = error.message;
+ } else {
+ magicLinkError = JSON.stringify(error, null, 2);
+ }
+ }
+
+ return typedjson(
+ {
+ magicLinkSent: session.has("triggerdotdev:magiclink"),
+ magicLinkError,
+ },
+ {
+ headers: { "Set-Cookie": await commitSession(session) },
+ }
+ );
}
export async function action({ request }: ActionArgs) {
@@ -50,7 +66,7 @@ export async function action({ request }: ActionArgs) {
.parse(payload);
if (action === "send") {
- await authenticator.authenticate("email-link", request, {
+ return authenticator.authenticate("email-link", request, {
successRedirect: "/login/magic",
failureRedirect: "/login/magic",
});
@@ -67,13 +83,13 @@ export async function action({ request }: ActionArgs) {
}
export default function LoginMagicLinkPage() {
- const { magicLinkSent } = useTypedLoaderData();
- const transition = useTransition();
+ const { magicLinkSent, magicLinkError } = useTypedLoaderData();
+ const navigate = useNavigation();
const isLoading =
- (transition.state === "loading" || transition.state === "submitting") &&
- transition.type === "actionSubmission" &&
- transition.submission.formData.get("action") === "send";
+ (navigate.state === "loading" || navigate.state === "submitting") &&
+ navigate.formAction !== undefined &&
+ navigate.formData?.get("action") === "send";
return (
@@ -102,12 +118,17 @@ export default function LoginMagicLinkPage() {
variant="tertiary/small"
LeadingIcon="arrow-left"
leadingIconClassName="text-dimmed group-hover:text-bright transition"
+ data-action="re-enter email"
>
Re-enter email
}
confirmButton={
-
+
Log in using another option
}
@@ -116,7 +137,10 @@ export default function LoginMagicLinkPage() {
>
) : (
<>
-
+
+
+ Create an account or login using your email
+
Your email address
@@ -137,6 +161,7 @@ export default function LoginMagicLinkPage() {
variant="primary/medium"
disabled={isLoading}
fullWidth
+ data-action="send a magic link"
>
{isLoading ? "Sendingβ¦" : "Send a magic link"}
+ {magicLinkError && {magicLinkError} }
By logging in with your email you agree to our{" "}
@@ -162,11 +188,28 @@ export default function LoginMagicLinkPage() {
variant={"tertiary/small"}
LeadingIcon={"arrow-left"}
leadingIconClassName="text-dimmed group-hover:text-bright transition"
+ data-action="all login options"
>
All login options
>
)}
+
+
+ Having login issues?
+
+
+ Ensure the Magic Link email isn't in your spam folder. If the problem persists,{" "}
+
+ drop us an email
+ {" "}
+ or let us know on{" "}
+
+ Discord
+
+ .
+
+
diff --git a/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts b/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts
index 297595319..df8fcf927 100644
--- a/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts
+++ b/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts
@@ -1,11 +1,5 @@
-import { parse } from "@conform-to/zod";
import { ActionArgs, json } from "@remix-run/server-runtime";
import { z } from "zod";
-import { prisma } from "~/db.server";
-import {
- CreateEndpointError,
- CreateEndpointService,
-} from "~/services/endpoints/createEndpoint.server";
import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server";
import { requireUserId } from "~/services/session.server";
diff --git a/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts b/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts
index 0412919ec..ece220cb7 100644
--- a/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts
+++ b/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts
@@ -2,28 +2,15 @@ import { parse } from "@conform-to/zod";
import { ActionArgs, json } from "@remix-run/server-runtime";
import { z } from "zod";
import { prisma } from "~/db.server";
-import {
- CreateEndpointError,
- CreateEndpointService,
-} from "~/services/endpoints/createEndpoint.server";
-import { requireUserId } from "~/services/session.server";
-import { RuntimeEnvironmentTypeSchema } from "@trigger.dev/core";
-import { env } from "process";
+import { CreateEndpointError } from "~/services/endpoints/createEndpoint.server";
import { ValidateCreateEndpointService } from "~/services/endpoints/validateCreateEndpoint.server";
-const ParamsSchema = z.object({
- projectId: z.string(),
-});
-
export const bodySchema = z.object({
environmentId: z.string(),
url: z.string().url("Must be a valid URL"),
});
-export async function action({ request, params }: ActionArgs) {
- const userId = await requireUserId(request);
- const { projectId } = ParamsSchema.parse(params);
-
+export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const submission = parse(formData, { schema: bodySchema });
@@ -48,7 +35,7 @@ export async function action({ request, params }: ActionArgs) {
}
const service = new ValidateCreateEndpointService();
- const result = await service.call({
+ await service.call({
url: submission.value.url,
environment,
});
diff --git a/apps/webapp/app/services/email.server.ts b/apps/webapp/app/services/email.server.ts
index bc8014fcc..04672d8be 100644
--- a/apps/webapp/app/services/email.server.ts
+++ b/apps/webapp/app/services/email.server.ts
@@ -1,4 +1,4 @@
-import type { DeliverEmail } from "emails";
+import type { DeliverEmail, SendPlainTextOptions } from "emails";
import { EmailClient } from "emails";
import type { SendEmailOptions } from "remix-auth-email-link";
import { redirect } from "remix-typedjson";
@@ -6,6 +6,7 @@ import { env } from "~/env.server";
import type { User } from "~/models/user.server";
import type { AuthUser } from "./authUser";
import { workerQueue } from "./worker.server";
+import { logger } from "./logger.server";
const client = new EmailClient({
apikey: env.RESEND_API_KEY,
@@ -20,11 +21,22 @@ export async function sendMagicLinkEmail(options: SendEmailOptions): P
throw redirect(options.magicLink);
}
- return client.send({
- email: "magic_link",
- to: options.emailAddress,
- magicLink: options.magicLink,
- });
+ logger.debug("Sending magic link email", { emailAddress: options.emailAddress });
+
+ try {
+ return await client.send({
+ email: "magic_link",
+ to: options.emailAddress,
+ magicLink: options.magicLink,
+ });
+ } catch (error) {
+ logger.error("Error sending magic link email", { error: JSON.stringify(error) });
+ throw error;
+ }
+}
+
+export async function sendPlainTextEmail(options: SendPlainTextOptions) {
+ return client.sendPlainText(options);
}
export async function scheduleWelcomeEmail(user: User) {
diff --git a/apps/webapp/app/services/emailAuth.server.tsx b/apps/webapp/app/services/emailAuth.server.tsx
index ff9ea4813..e48d405dd 100644
--- a/apps/webapp/app/services/emailAuth.server.tsx
+++ b/apps/webapp/app/services/emailAuth.server.tsx
@@ -5,6 +5,7 @@ import { findOrCreateUser } from "~/models/user.server";
import { env } from "~/env.server";
import { sendMagicLinkEmail } from "~/services/email.server";
import { postAuthentication } from "./postAuth.server";
+import { logger } from "./logger.server";
let secret = env.MAGIC_LINK_SECRET;
if (!secret) throw new Error("Missing MAGIC_LINK_SECRET env variable.");
@@ -25,6 +26,8 @@ const emailStrategy = new EmailLinkStrategy(
form: FormData;
magicLinkVerify: boolean;
}) => {
+ logger.info("Magic link user authenticated", { email, magicLinkVerify });
+
try {
const { user, isNewUser } = await findOrCreateUser({
email,
@@ -35,6 +38,7 @@ const emailStrategy = new EmailLinkStrategy(
return { userId: user.id };
} catch (error) {
+ logger.debug("Magic link user failed to authenticate", { error: JSON.stringify(error) });
throw error;
}
}
diff --git a/apps/webapp/app/services/endpointApi.server.ts b/apps/webapp/app/services/endpointApi.server.ts
index 8cce435fa..708a62ee7 100644
--- a/apps/webapp/app/services/endpointApi.server.ts
+++ b/apps/webapp/app/services/endpointApi.server.ts
@@ -1,7 +1,9 @@
import {
+ API_VERSIONS,
ApiEventLog,
DeliverEventResponseSchema,
DeserializedJson,
+ EndpointHeadersSchema,
ErrorWithStackSchema,
HttpSourceRequest,
HttpSourceResponseSchema,
@@ -89,10 +91,20 @@ export class EndpointApi {
};
}
+ const headers = EndpointHeadersSchema.safeParse(Object.fromEntries(response.headers.entries()));
+
+ if (headers.success && headers.data["trigger-version"]) {
+ return {
+ ...pongResponse.data,
+ triggerVersion: headers.data["trigger-version"],
+ };
+ }
+
return pongResponse.data;
}
async indexEndpoint() {
+ const startTimeInMs = performance.now();
const response = await safeFetch(this.url, {
method: "POST",
headers: {
@@ -102,66 +114,13 @@ export class EndpointApi {
},
});
- if (!response) {
- throw new Error(`Could not connect to endpoint ${this.url}`);
- }
-
- if (response.status === 401) {
- const body = await safeBodyFromResponse(response, ErrorWithStackSchema);
-
- if (body) {
- return {
- ok: false,
- error: body.message,
- } as const;
- }
-
- return {
- ok: false,
- error: `Trigger API key is invalid`,
- } as const;
- }
-
- if (!response.ok) {
- throw new Error(`Could not connect to endpoint ${this.url}. Status code: ${response.status}`);
- }
-
- const anyBody = await response.json();
-
- const data = IndexEndpointResponseSchema.parse(anyBody);
-
return {
- ok: true,
- data,
- } as const;
- }
-
- async deliverEvent(event: ApiEventLog) {
- const response = await safeFetch(this.url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "x-trigger-api-key": this.apiKey,
- "x-trigger-action": "DELIVER_EVENT",
- },
- body: JSON.stringify(event),
- });
-
- if (!response) {
- throw new Error(`Could not connect to endpoint ${this.url}`);
- }
-
- if (!response.ok) {
- throw new Error(`Could not connect to endpoint ${this.url}. Status code: ${response.status}`);
- }
-
- const anyBody = await response.json();
-
- logger.debug("deliverEvent() response from endpoint", {
- body: anyBody,
- });
-
- return DeliverEventResponseSchema.parse(anyBody);
+ response,
+ headerParser: EndpointHeadersSchema,
+ parser: IndexEndpointResponseSchema,
+ errorParser: ErrorWithStackSchema,
+ durationInMs: Math.floor(performance.now() - startTimeInMs),
+ };
}
async executeJobRequest(options: RunJobBody) {
@@ -338,6 +297,15 @@ export class EndpointApi {
};
}
+ const headers = EndpointHeadersSchema.safeParse(Object.fromEntries(response.headers.entries()));
+
+ if (headers.success && headers.data["trigger-version"]) {
+ return {
+ ...validateResponse.data,
+ triggerVersion: headers.data["trigger-version"],
+ };
+ }
+
return validateResponse.data;
}
}
@@ -359,6 +327,7 @@ function addStandardRequestOptions(options: RequestInit) {
headers: {
...options.headers,
"user-agent": "triggerdotdev-server/2.0.0",
+ "x-trigger-version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS,
},
};
}
diff --git a/apps/webapp/app/services/endpoints/createEndpoint.server.ts b/apps/webapp/app/services/endpoints/createEndpoint.server.ts
index bd1839222..a77bb844f 100644
--- a/apps/webapp/app/services/endpoints/createEndpoint.server.ts
+++ b/apps/webapp/app/services/endpoints/createEndpoint.server.ts
@@ -74,18 +74,27 @@ export class CreateEndpointService {
slug: id,
url: endpointUrl,
indexingHookIdentifier: indexingHookIdentifier(),
+ version: pong.triggerVersion,
},
update: {
url: endpointUrl,
+ version: pong.triggerVersion,
+ },
+ });
+
+ const endpointIndex = await tx.endpointIndex.create({
+ data: {
+ endpointId: endpoint.id,
+ status: "PENDING",
+ source: "INTERNAL",
},
});
// Kick off process to fetch the jobs for this endpoint
await workerQueue.enqueue(
- "indexEndpoint",
+ "performEndpointIndexing",
{
- id: endpoint.id,
- source: "INTERNAL",
+ id: endpointIndex.id,
},
{
tx,
@@ -94,7 +103,7 @@ export class CreateEndpointService {
}
);
- return endpoint;
+ return { ...endpoint, endpointIndex };
});
return result;
diff --git a/apps/webapp/app/services/endpoints/indexEndpoint.server.ts b/apps/webapp/app/services/endpoints/indexEndpoint.server.ts
index bc529fe0c..27df22c4f 100644
--- a/apps/webapp/app/services/endpoints/indexEndpoint.server.ts
+++ b/apps/webapp/app/services/endpoints/indexEndpoint.server.ts
@@ -1,23 +1,9 @@
import type { EndpointIndexSource } from "@trigger.dev/database";
import { PrismaClient, prisma } from "~/db.server";
-import { findEndpoint } from "~/models/endpoint.server";
-import { EndpointApi } from "../endpointApi.server";
-import { RegisterJobService } from "../jobs/registerJob.server";
-import { logger } from "../logger.server";
-import { RegisterSourceServiceV1 } from "../sources/registerSourceV1.server";
-import { RegisterDynamicScheduleService } from "../triggers/registerDynamicSchedule.server";
-import { RegisterDynamicTriggerService } from "../triggers/registerDynamicTrigger.server";
-import { DisableJobService } from "../jobs/disableJob.server";
-import { RegisterSourceServiceV2 } from "../sources/registerSourceV2.server";
+import { PerformEndpointIndexService } from "./performEndpointIndexService";
export class IndexEndpointService {
#prismaClient: PrismaClient;
- #registerJobService = new RegisterJobService();
- #disableJobService = new DisableJobService();
- #registerSourceServiceV1 = new RegisterSourceServiceV1();
- #registerSourceServiceV2 = new RegisterSourceServiceV2();
- #registerDynamicTriggerService = new RegisterDynamicTriggerService();
- #registerDynamicScheduleService = new RegisterDynamicScheduleService();
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
@@ -29,207 +15,17 @@ export class IndexEndpointService {
reason?: string,
sourceData?: any
) {
- const endpoint = await findEndpoint(id);
-
- // Make a request to the endpoint to fetch a list of jobs
- const client = new EndpointApi(endpoint.environment.apiKey, endpoint.url);
-
- const indexResponse = await client.indexEndpoint();
-
- if (!indexResponse.ok) {
- throw new Error(indexResponse.error);
- }
-
- const { jobs, sources, dynamicTriggers, dynamicSchedules } = indexResponse.data;
-
- logger.debug("Indexing endpoint", {
- endpointId: endpoint.id,
- endpointUrl: endpoint.url,
- endpointSlug: endpoint.slug,
- source: source,
- sourceData: sourceData,
- stats: {
- jobs: jobs.length,
- sources: sources.length,
- dynamicTriggers: dynamicTriggers.length,
- dynamicSchedules: dynamicSchedules.length,
- },
- });
-
- const indexStats = {
- jobs: 0,
- sources: 0,
- dynamicTriggers: 0,
- dynamicSchedules: 0,
- disabledJobs: 0,
- };
-
- const existingJobs = await this.#prismaClient.job.findMany({
- where: {
- projectId: endpoint.projectId,
- deletedAt: null,
- },
- include: {
- aliases: {
- where: {
- name: "latest",
- environmentId: endpoint.environmentId,
- },
- include: {
- version: true,
- },
- take: 1,
- },
- },
- });
-
- for (const job of jobs) {
- if (!job.enabled) {
- const disabledJob = await this.#disableJobService
- .call(endpoint, { slug: job.id, version: job.version })
- .catch((error) => {
- logger.error("Failed to disable job", {
- endpointId: endpoint.id,
- job,
- error,
- });
-
- return;
- });
-
- if (disabledJob) {
- indexStats.disabledJobs++;
- }
- } else {
- try {
- const registeredVersion = await this.#registerJobService.call(endpoint, job);
-
- if (registeredVersion) {
- indexStats.jobs++;
- }
- } catch (error) {
- logger.error("Failed to register job", {
- endpointId: endpoint.id,
- job,
- error,
- });
- }
- }
- }
-
- // TODO: we need to do this for sources, dynamic triggers, and dynamic schedules
- const missingJobs = existingJobs.filter((job) => {
- return !jobs.find((j) => j.id === job.slug);
- });
-
- if (missingJobs.length > 0) {
- logger.debug("Disabling missing jobs", {
- endpointId: endpoint.id,
- missingJobIds: missingJobs.map((job) => job.slug),
- });
-
- for (const job of missingJobs) {
- const latestVersion = job.aliases[0]?.version;
-
- if (!latestVersion) {
- continue;
- }
-
- const disabledJob = await this.#disableJobService
- .call(endpoint, {
- slug: job.slug,
- version: latestVersion.version,
- })
- .catch((error) => {
- logger.error("Failed to disable job", {
- endpointId: endpoint.id,
- job,
- error,
- });
-
- return;
- });
-
- if (disabledJob) {
- indexStats.disabledJobs++;
- }
- }
- }
-
- for (const source of sources) {
- try {
- switch (source.version) {
- default:
- case "1": {
- await this.#registerSourceServiceV1.call(endpoint, source);
- break;
- }
- case "2": {
- await this.#registerSourceServiceV2.call(endpoint, source);
- break;
- }
- }
-
- indexStats.sources++;
- } catch (error) {
- logger.error("Failed to register source", {
- endpointId: endpoint.id,
- source,
- error,
- });
- }
- }
-
- for (const dynamicTrigger of dynamicTriggers) {
- try {
- await this.#registerDynamicTriggerService.call(endpoint, dynamicTrigger);
-
- indexStats.dynamicTriggers++;
- } catch (error) {
- logger.error("Failed to register dynamic trigger", {
- endpointId: endpoint.id,
- dynamicTrigger,
- error,
- });
- }
- }
-
- for (const dynamicSchedule of dynamicSchedules) {
- try {
- await this.#registerDynamicScheduleService.call(endpoint, dynamicSchedule);
-
- indexStats.dynamicSchedules++;
- } catch (error) {
- logger.error("Failed to register dynamic schedule", {
- endpointId: endpoint.id,
- dynamicSchedule,
- error,
- });
- }
- }
-
- logger.debug("Endpoint indexing complete", {
- endpointId: endpoint.id,
- indexStats,
- source,
- sourceData,
- reason,
- });
-
- return await this.#prismaClient.endpointIndex.create({
+ const endpointIndex = await this.#prismaClient.endpointIndex.create({
data: {
- endpointId: endpoint.id,
- stats: indexStats,
- data: {
- jobs,
- sources,
- dynamicTriggers,
- dynamicSchedules,
- },
+ endpointId: id,
+ status: "PENDING",
source,
- sourceData,
reason,
+ sourceData,
},
});
+
+ const performEndpointIndexService = new PerformEndpointIndexService();
+ return await performEndpointIndexService.call(endpointIndex.id);
}
}
diff --git a/apps/webapp/app/services/endpoints/performEndpointIndexService.ts b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts
new file mode 100644
index 000000000..ca1c0bcd1
--- /dev/null
+++ b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts
@@ -0,0 +1,338 @@
+import type { EndpointIndexSource } from "@trigger.dev/database";
+import { PrismaClient, prisma } from "~/db.server";
+import { findEndpoint } from "~/models/endpoint.server";
+import { EndpointApi } from "../endpointApi.server";
+import { RegisterJobService } from "../jobs/registerJob.server";
+import { logger } from "../logger.server";
+import { RegisterSourceServiceV1 } from "../sources/registerSourceV1.server";
+import { RegisterDynamicScheduleService } from "../triggers/registerDynamicSchedule.server";
+import { RegisterDynamicTriggerService } from "../triggers/registerDynamicTrigger.server";
+import { DisableJobService } from "../jobs/disableJob.server";
+import { RegisterSourceServiceV2 } from "../sources/registerSourceV2.server";
+import { EndpointIndexError } from "@trigger.dev/core";
+import { safeBodyFromResponse } from "~/utils/json";
+import { fromZodError } from "zod-validation-error";
+import { IndexEndpointStats } from "@trigger.dev/core";
+
+export class PerformEndpointIndexService {
+ #prismaClient: PrismaClient;
+ #registerJobService = new RegisterJobService();
+ #disableJobService = new DisableJobService();
+ #registerSourceServiceV1 = new RegisterSourceServiceV1();
+ #registerSourceServiceV2 = new RegisterSourceServiceV2();
+ #registerDynamicTriggerService = new RegisterDynamicTriggerService();
+ #registerDynamicScheduleService = new RegisterDynamicScheduleService();
+
+ constructor(prismaClient: PrismaClient = prisma) {
+ this.#prismaClient = prismaClient;
+ }
+
+ public async call(id: string) {
+ const endpointIndex = await this.#prismaClient.endpointIndex.update({
+ where: {
+ id,
+ },
+ data: {
+ status: "STARTED",
+ },
+ include: {
+ endpoint: {
+ include: {
+ environment: {
+ include: {
+ organization: true,
+ project: true,
+ },
+ },
+ },
+ },
+ },
+ });
+
+ logger.debug("Performing endpoint index", endpointIndex);
+
+ // Make a request to the endpoint to fetch a list of jobs
+ const client = new EndpointApi(
+ endpointIndex.endpoint.environment.apiKey,
+ endpointIndex.endpoint.url
+ );
+ const { response, parser, headerParser, errorParser } = await client.indexEndpoint();
+
+ if (!response) {
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: `Could not connect to endpoint ${endpointIndex.endpoint.url}`,
+ });
+ }
+
+ if (response.status === 401) {
+ const body = await safeBodyFromResponse(response, errorParser);
+
+ if (body) {
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: body.message,
+ });
+ }
+
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: "Trigger API key is invalid",
+ });
+ }
+
+ if (!response.ok) {
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: `Could not connect to endpoint ${endpointIndex.endpoint.url}. Status code: ${response.status}`,
+ });
+ }
+
+ const anyBody = await response.json();
+ const bodyResult = parser.safeParse(anyBody);
+
+ if (!bodyResult.success) {
+ const issues: string[] = [];
+ bodyResult.error.issues.forEach((issue) => {
+ if (issue.path.at(0) === "jobs") {
+ const jobIndex = issue.path.at(1) as number;
+ const job = (anyBody as any).jobs[jobIndex];
+
+ if (job) {
+ issues.push(`Job "${job.id}": ${issue.message} at "${issue.path.slice(2).join(".")}".`);
+ }
+ }
+ });
+
+ let friendlyError: string | undefined;
+ if (issues.length > 0) {
+ friendlyError = `Your Jobs have issues:\n${issues.map((issue) => `- ${issue}`).join("\n")}`;
+ } else {
+ friendlyError = fromZodError(bodyResult.error, {
+ prefix: "There's an issue with the format of your Jobs",
+ }).message;
+ }
+
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: friendlyError,
+ raw: bodyResult.error.issues,
+ });
+ }
+
+ const headerResult = headerParser.safeParse(Object.fromEntries(response.headers.entries()));
+ if (!headerResult.success) {
+ const friendlyError = fromZodError(headerResult.error, {
+ prefix: "Your headers are invalid",
+ });
+ return updateEndpointIndexWithError(this.#prismaClient, id, {
+ message: friendlyError.message,
+ raw: headerResult.error.issues,
+ });
+ }
+
+ const { jobs, sources, dynamicTriggers, dynamicSchedules } = bodyResult.data;
+ const { "trigger-version": triggerVersion } = headerResult.data;
+ const { endpoint } = endpointIndex;
+
+ if (triggerVersion && triggerVersion !== endpoint.version) {
+ await this.#prismaClient.endpoint.update({
+ where: {
+ id: endpoint.id,
+ },
+ data: {
+ version: triggerVersion,
+ },
+ });
+ }
+
+ const indexStats: IndexEndpointStats = {
+ jobs: 0,
+ sources: 0,
+ dynamicTriggers: 0,
+ dynamicSchedules: 0,
+ disabledJobs: 0,
+ };
+
+ const existingJobs = await this.#prismaClient.job.findMany({
+ where: {
+ projectId: endpoint.projectId,
+ deletedAt: null,
+ },
+ include: {
+ aliases: {
+ where: {
+ name: "latest",
+ environmentId: endpoint.environmentId,
+ },
+ include: {
+ version: true,
+ },
+ take: 1,
+ },
+ },
+ });
+
+ for (const job of jobs) {
+ if (!job.enabled) {
+ const disabledJob = await this.#disableJobService
+ .call(endpoint, { slug: job.id, version: job.version })
+ .catch((error) => {
+ logger.error("Failed to disable job", {
+ endpointId: endpoint.id,
+ job,
+ error,
+ });
+
+ return;
+ });
+
+ if (disabledJob) {
+ indexStats.disabledJobs++;
+ }
+ } else {
+ try {
+ const registeredVersion = await this.#registerJobService.call(endpoint, job);
+
+ if (registeredVersion) {
+ if (!job.internal) {
+ indexStats.jobs++;
+ }
+ }
+ } catch (error) {
+ logger.error("Failed to register job", {
+ endpointId: endpoint.id,
+ job,
+ error,
+ });
+ }
+ }
+ }
+
+ // TODO: we need to do this for sources, dynamic triggers, and dynamic schedules
+ const missingJobs = existingJobs.filter((job) => {
+ return !jobs.find((j) => j.id === job.slug);
+ });
+
+ if (missingJobs.length > 0) {
+ logger.debug("Disabling missing jobs", {
+ endpointId: endpoint.id,
+ missingJobIds: missingJobs.map((job) => job.slug),
+ });
+
+ for (const job of missingJobs) {
+ const latestVersion = job.aliases[0]?.version;
+
+ if (!latestVersion) {
+ continue;
+ }
+
+ const disabledJob = await this.#disableJobService
+ .call(endpoint, {
+ slug: job.slug,
+ version: latestVersion.version,
+ })
+ .catch((error) => {
+ logger.error("Failed to disable job", {
+ endpointId: endpoint.id,
+ job,
+ error,
+ });
+
+ return;
+ });
+
+ if (disabledJob) {
+ indexStats.disabledJobs++;
+ }
+ }
+ }
+
+ for (const source of sources) {
+ try {
+ switch (source.version) {
+ default:
+ case "1": {
+ await this.#registerSourceServiceV1.call(endpoint, source);
+ break;
+ }
+ case "2": {
+ await this.#registerSourceServiceV2.call(endpoint, source);
+ break;
+ }
+ }
+
+ indexStats.sources++;
+ } catch (error) {
+ logger.error("Failed to register source", {
+ endpointId: endpoint.id,
+ source,
+ error,
+ });
+ }
+ }
+
+ for (const dynamicTrigger of dynamicTriggers) {
+ try {
+ await this.#registerDynamicTriggerService.call(endpoint, dynamicTrigger);
+
+ indexStats.dynamicTriggers++;
+ } catch (error) {
+ logger.error("Failed to register dynamic trigger", {
+ endpointId: endpoint.id,
+ dynamicTrigger,
+ error,
+ });
+ }
+ }
+
+ for (const dynamicSchedule of dynamicSchedules) {
+ try {
+ await this.#registerDynamicScheduleService.call(endpoint, dynamicSchedule);
+
+ indexStats.dynamicSchedules++;
+ } catch (error) {
+ logger.error("Failed to register dynamic schedule", {
+ endpointId: endpoint.id,
+ dynamicSchedule,
+ error,
+ });
+ }
+ }
+
+ logger.debug("Endpoint indexing complete", {
+ endpointId: endpoint.id,
+ indexStats,
+ source: endpointIndex.source,
+ sourceData: endpointIndex.sourceData,
+ reason: endpointIndex.reason,
+ });
+
+ return await this.#prismaClient.endpointIndex.update({
+ where: {
+ id,
+ },
+ data: {
+ status: "SUCCESS",
+ stats: indexStats,
+ data: {
+ jobs,
+ sources,
+ dynamicTriggers,
+ dynamicSchedules,
+ },
+ },
+ });
+ }
+}
+
+async function updateEndpointIndexWithError(
+ prismaClient: PrismaClient,
+ id: string,
+ error: EndpointIndexError
+) {
+ return await prismaClient.endpointIndex.update({
+ where: {
+ id,
+ },
+ data: {
+ status: "FAILURE",
+ error,
+ },
+ });
+}
diff --git a/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts b/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts
index 15bc6b015..72658c769 100644
--- a/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts
+++ b/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts
@@ -17,7 +17,9 @@ export class RecurringEndpointIndexService {
const endpoints = await this.#prismaClient.endpoint.findMany({
where: {
environment: {
- type: RuntimeEnvironmentType.PRODUCTION,
+ type: {
+ in: [RuntimeEnvironmentType.PRODUCTION, RuntimeEnvironmentType.STAGING],
+ },
},
indexings: {
none: {
@@ -32,12 +34,18 @@ export class RecurringEndpointIndexService {
logger.debug("Found endpoints that haven't been indexed in the last 10 minutes", {
count: endpoints.length,
});
-
// Enqueue each endpoint for indexing
for (const endpoint of endpoints) {
- await workerQueue.enqueue("indexEndpoint", {
- id: endpoint.id,
- source: "INTERNAL",
+ const index = await this.#prismaClient.endpointIndex.create({
+ data: {
+ endpointId: endpoint.id,
+ status: "PENDING",
+ source: "INTERNAL",
+ },
+ });
+
+ await workerQueue.enqueue("performEndpointIndexing", {
+ id: index.id,
});
}
}
diff --git a/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts b/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts
index d578a26d9..84baa4c22 100644
--- a/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts
+++ b/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts
@@ -58,18 +58,23 @@ export class ValidateCreateEndpointService {
slug: validationResult.endpointId,
url: endpointUrl,
indexingHookIdentifier: indexingHookIdentifier(),
+ version: validationResult.triggerVersion,
},
update: {
url: endpointUrl,
+ version: validationResult.triggerVersion,
},
});
- // Kick off process to fetch the jobs for this endpoint
+ const index = await tx.endpointIndex.create({
+ data: { endpointId: endpoint.id, status: "PENDING", source: "INTERNAL" },
+ });
+
+ // Kick off process to fetch the jobs for this index
await workerQueue.enqueue(
- "indexEndpoint",
+ "performEndpointIndexing",
{
- id: endpoint.id,
- source: "INTERNAL",
+ id: index.id,
},
{
tx,
diff --git a/apps/webapp/app/services/events/ingestSendEvent.server.ts b/apps/webapp/app/services/events/ingestSendEvent.server.ts
index b1de0e2a9..9e4a254bb 100644
--- a/apps/webapp/app/services/events/ingestSendEvent.server.ts
+++ b/apps/webapp/app/services/events/ingestSendEvent.server.ts
@@ -3,6 +3,25 @@ import { $transaction, PrismaClientOrTransaction, PrismaErrorSchema, prisma } fr
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { workerQueue } from "~/services/worker.server";
import { logger } from "../logger.server";
+import { EventRecord, ExternalAccount } from "@trigger.dev/database";
+
+type UpdateEventInput = {
+ tx: PrismaClientOrTransaction;
+ existingEventLog: EventRecord;
+ reqEvent: RawEvent;
+ deliverAt?: Date;
+};
+
+type CreateEventInput = {
+ tx: PrismaClientOrTransaction;
+ event: RawEvent;
+ environment: AuthenticatedEnvironment;
+ deliverAt?: Date;
+ sourceContext?: { id: string; metadata?: any };
+ externalAccount?: ExternalAccount;
+};
+
+const EVENT_UPDATE_THRESHOLD_WINDOW_IN_MSECS = 5 * 1000; // 5 seconds
export class IngestSendEvent {
#prismaClient: PrismaClientOrTransaction;
@@ -52,34 +71,25 @@ export class IngestSendEvent {
})
: undefined;
- // Create a new event in the database
- const eventLog = await tx.eventRecord.create({
- data: {
- organizationId: environment.organizationId,
- projectId: environment.projectId,
- environmentId: environment.id,
- eventId: event.id,
- name: event.name,
- timestamp: event.timestamp ?? new Date(),
- payload: event.payload ?? {},
- context: event.context ?? {},
- source: event.source ?? "trigger.dev",
- sourceContext,
- deliverAt: deliverAt,
- externalAccountId: externalAccount ? externalAccount.id : undefined,
+ const existingEventLog = await tx.eventRecord.findUnique({
+ where: {
+ eventId_environmentId: {
+ eventId: event.id,
+ environmentId: environment.id,
+ },
},
});
- if (this.deliverEvents) {
- // Produce a message to the event bus
- await workerQueue.enqueue(
- "deliverEvent",
- {
- id: eventLog.id,
- },
- { runAt: eventLog.deliverAt, tx, jobKey: `event:${eventLog.id}` }
- );
- }
+ const eventLog = await (existingEventLog
+ ? this.updateEvent({ tx, existingEventLog, reqEvent: event, deliverAt })
+ : this.createEvent({
+ tx,
+ event,
+ environment,
+ deliverAt,
+ sourceContext,
+ externalAccount,
+ }));
return eventLog;
});
@@ -95,21 +105,81 @@ export class IngestSendEvent {
throw error;
}
- // If the error is a Prisma unique constraint error, it means that the event already exists
- if (prismaError.success && prismaError.data.code === "P2002") {
- logger.debug("Event already exists, finding and returning", { event, environment });
-
- return this.#prismaClient.eventRecord.findUniqueOrThrow({
- where: {
- eventId_environmentId: {
- eventId: event.id,
- environmentId: environment.id,
- },
- },
- });
- }
-
throw error;
}
}
+
+ private async createEvent({
+ tx,
+ event,
+ environment,
+ deliverAt,
+ sourceContext,
+ externalAccount,
+ }: CreateEventInput) {
+ const eventLog = await tx.eventRecord.create({
+ data: {
+ organizationId: environment.organizationId,
+ projectId: environment.projectId,
+ environmentId: environment.id,
+ eventId: event.id,
+ name: event.name,
+ timestamp: event.timestamp ?? new Date(),
+ payload: event.payload ?? {},
+ context: event.context ?? {},
+ source: event.source ?? "trigger.dev",
+ sourceContext,
+ deliverAt: deliverAt,
+ externalAccountId: externalAccount ? externalAccount.id : undefined,
+ },
+ });
+
+ await this.enqueueWorkerEvent(tx, eventLog);
+
+ return eventLog;
+ }
+
+ private async updateEvent({ tx, existingEventLog, reqEvent, deliverAt }: UpdateEventInput) {
+ if (!this.shouldUpdateEvent(existingEventLog)) {
+ logger.debug(`not updating event for event id: ${existingEventLog.eventId}`);
+ return existingEventLog;
+ }
+
+ const updatedEventLog = await tx.eventRecord.update({
+ where: {
+ eventId_environmentId: {
+ eventId: existingEventLog.eventId,
+ environmentId: existingEventLog.environmentId,
+ },
+ },
+ data: {
+ payload: reqEvent.payload ?? existingEventLog.payload,
+ context: reqEvent.context ?? existingEventLog.context,
+ deliverAt: deliverAt ?? new Date(),
+ },
+ });
+
+ await this.enqueueWorkerEvent(tx, updatedEventLog);
+
+ return updatedEventLog;
+ }
+
+ private shouldUpdateEvent(eventLog: EventRecord) {
+ const thresholdTime = new Date(Date.now() + EVENT_UPDATE_THRESHOLD_WINDOW_IN_MSECS);
+
+ return eventLog.deliverAt >= thresholdTime;
+ }
+
+ private async enqueueWorkerEvent(tx: PrismaClientOrTransaction, eventLog: EventRecord) {
+ if (this.deliverEvents) {
+ // Produce a message to the event bus
+ await workerQueue.enqueue(
+ "deliverEvent",
+ {
+ id: eventLog.id,
+ },
+ { runAt: eventLog.deliverAt, tx, jobKey: `event:${eventLog.id}` }
+ );
+ }
+ }
}
diff --git a/apps/webapp/app/services/externalApis/integrationCatalog.server.ts b/apps/webapp/app/services/externalApis/integrationCatalog.server.ts
index b86c2e496..3c13f154a 100644
--- a/apps/webapp/app/services/externalApis/integrationCatalog.server.ts
+++ b/apps/webapp/app/services/externalApis/integrationCatalog.server.ts
@@ -3,6 +3,7 @@ import { github } from "./integrations/github";
import { linear } from "./integrations/linear";
import { openai } from "./integrations/openai";
import { plain } from "./integrations/plain";
+import { replicate } from "./integrations/replicate";
import { resend } from "./integrations/resend";
import { sendgrid } from "./integrations/sendgrid";
import { slack } from "./integrations/slack";
@@ -37,6 +38,7 @@ export const integrationCatalog = new IntegrationCatalog({
linear,
openai,
plain,
+ replicate,
resend,
slack,
stripe,
diff --git a/apps/webapp/app/services/externalApis/integrations/replicate.ts b/apps/webapp/app/services/externalApis/integrations/replicate.ts
new file mode 100644
index 000000000..74f20cdaf
--- /dev/null
+++ b/apps/webapp/app/services/externalApis/integrations/replicate.ts
@@ -0,0 +1,50 @@
+import type { HelpSample, Integration } from "../types";
+
+function usageSample(hasApiKey: boolean): HelpSample {
+ const apiKeyPropertyName = "apiKey";
+
+ return {
+ title: "Using the client",
+ code: `
+import { Replicate } from "@trigger.dev/replicate";
+
+const replicate = new Replicate({
+ id: "__SLUG__",${hasApiKey ? `,\n ${apiKeyPropertyName}: process.env.REPLICATE_API_KEY!` : ""}
+});
+
+client.defineJob({
+ id: "replicate-create-prediction",
+ name: "Replicate - Create Prediction",
+ version: "0.1.0",
+ integrations: { replicate },
+ trigger: eventTrigger({
+ name: "replicate.predict",
+ schema: z.object({
+ prompt: z.string(),
+ version: z.string(),
+ }),
+ }),
+ run: async (payload, io, ctx) => {
+ return io.replicate.predictions.createAndAwait("await-prediction", {
+ version: payload.version,
+ input: { prompt: payload.prompt },
+ });
+ },
+});
+ `,
+ };
+}
+
+export const replicate: Integration = {
+ identifier: "replicate",
+ name: "Replicate",
+ packageName: "@trigger.dev/replicate@latest",
+ authenticationMethods: {
+ apikey: {
+ type: "apikey",
+ help: {
+ samples: [usageSample(true)],
+ },
+ },
+ },
+};
diff --git a/apps/webapp/app/services/jobs/registerJob.server.ts b/apps/webapp/app/services/jobs/registerJob.server.ts
index 401c69c8a..8e75e5376 100644
--- a/apps/webapp/app/services/jobs/registerJob.server.ts
+++ b/apps/webapp/app/services/jobs/registerJob.server.ts
@@ -4,14 +4,7 @@ import {
SCHEDULED_EVENT,
TriggerMetadata,
} from "@trigger.dev/core";
-import type {
- Endpoint,
- Integration,
- Job,
- JobIntegration,
- JobIntegrationPayload,
- JobVersion,
-} from "@trigger.dev/database";
+import type { Endpoint, Integration, Job, JobIntegration, JobVersion } from "@trigger.dev/database";
import { DEFAULT_MAX_CONCURRENT_RUNS } from "~/consts";
import type { PrismaClient } from "~/db.server";
import { prisma } from "~/db.server";
@@ -229,7 +222,7 @@ export class RegisterJobService {
},
update: {
name: example.name,
- icon: example.icon,
+ icon: example.icon ?? null,
payload: example.payload,
},
});
diff --git a/apps/webapp/app/services/logger.server.ts b/apps/webapp/app/services/logger.server.ts
index fdc886479..75bb91ca1 100644
--- a/apps/webapp/app/services/logger.server.ts
+++ b/apps/webapp/app/services/logger.server.ts
@@ -8,3 +8,10 @@ export const logger = new Logger(
["examples", "output", "connectionString", "payload"],
sensitiveDataReplacer
);
+
+export const workerLogger = new Logger(
+ "worker",
+ (process.env.APP_LOG_LEVEL ?? "debug") as LogLevel,
+ ["examples", "output", "connectionString"],
+ sensitiveDataReplacer
+);
diff --git a/apps/webapp/app/services/runs/createRun.server.ts b/apps/webapp/app/services/runs/createRun.server.ts
index a3c789193..7c1089772 100644
--- a/apps/webapp/app/services/runs/createRun.server.ts
+++ b/apps/webapp/app/services/runs/createRun.server.ts
@@ -70,6 +70,7 @@ export class CreateRunService {
? eventRecord.externalAccountId
: undefined,
isTest: eventRecord.isTest,
+ internal: job.internal,
},
});
diff --git a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
index 591f6f00b..c67a9dd18 100644
--- a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
+++ b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
@@ -263,6 +263,7 @@ export class PerformRunExecutionV1Service {
.flat()
.filter(Boolean)
.map((t) => CachedTaskSchema.parse(t)),
+ yieldedExecutions: run.yieldedExecutions,
});
if (!response) {
@@ -354,6 +355,11 @@ export class PerformRunExecutionV1Service {
break;
}
+ case "YIELD_EXECUTION": {
+ await this.#resumeYieldedExecution(execution, safeBody.data.key);
+
+ break;
+ }
default: {
const _exhaustiveCheck: never = status;
throw new Error(`Non-exhaustive match for value: ${status}`);
@@ -393,6 +399,40 @@ export class PerformRunExecutionV1Service {
});
}
+ async #resumeYieldedExecution(execution: FoundRunExecution, key: string) {
+ const { run } = execution;
+
+ return await $transaction(this.#prismaClient, async (tx) => {
+ await tx.jobRunExecution.update({
+ where: {
+ id: execution.id,
+ },
+ data: {
+ status: "SUCCESS",
+ completedAt: new Date(),
+ run: {
+ update: {
+ yieldedExecutions: {
+ push: key,
+ },
+ },
+ },
+ },
+ });
+
+ const newJobExecution = await tx.jobRunExecution.create({
+ data: {
+ runId: run.id,
+ reason: "EXECUTE_JOB",
+ status: "PENDING",
+ retryLimit: EXECUTE_JOB_RETRY_LIMIT,
+ },
+ });
+
+ await enqueueRunExecutionV1(newJobExecution, run.queue.id, run.queue.maxJobs, tx);
+ });
+ }
+
async #resumeRunWithTask(execution: FoundRunExecution, data: RunJobResumeWithTask) {
const { run } = execution;
@@ -409,7 +449,9 @@ export class PerformRunExecutionV1Service {
// If the task has an operation, then the next performRunExecution will occur
// when that operation has finished
- if (!data.task.operation) {
+ // Tasks with callbacks enabled will also get processed separately, i.e. when
+ // they time out, or on valid requests to their callbackUrl
+ if (!data.task.operation && !data.task.callbackUrl) {
const newJobExecution = await tx.jobRunExecution.create({
data: {
runId: run.id,
diff --git a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
index 61feab018..f22d13edb 100644
--- a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
+++ b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
@@ -1,12 +1,17 @@
import {
- CachedTask,
+ API_VERSIONS,
+ BloomFilter,
+ ConnectionAuth,
+ EndpointHeadersSchema,
RunJobError,
RunJobInvalidPayloadError,
RunJobResumeWithTask,
RunJobRetryWithTask,
RunJobSuccess,
RunJobUnresolvedAuthError,
+ RunSourceContext,
RunSourceContextSchema,
+ supportsFeature,
} from "@trigger.dev/core";
import { RuntimeEnvironmentType, type Task } from "@trigger.dev/database";
import { generateErrorMessage } from "zod-error";
@@ -18,10 +23,17 @@ import { formatError } from "~/utils/formatErrors.server";
import { safeJsonZodParse } from "~/utils/json";
import { EndpointApi } from "../endpointApi.server";
import { logger } from "../logger.server";
+import { prepareTasksForCaching, prepareTasksForCachingLegacy } from "~/models/task.server";
+import { MAX_RUN_YIELDED_EXECUTIONS } from "~/consts";
+import { ApiEventLog } from "@trigger.dev/core";
+import { RunJobBody } from "@trigger.dev/core";
type FoundRun = NonNullable>>;
type FoundTask = FoundRun["tasks"][number];
+// We need to limit the cached tasks to not be too large >3.5MB when serialized
+const TOTAL_CACHED_TASK_BYTE_LIMIT = 3500000;
+
export type PerformRunExecutionV2Input = {
id: string;
reason: "PREPROCESS" | "EXECUTE_JOB";
@@ -230,38 +242,19 @@ export class PerformRunExecutionV2Service {
const sourceContext = RunSourceContextSchema.safeParse(run.event.sourceContext);
- const { response, parser, errorParser, durationInMs } = await client.executeJobRequest({
+ const executionBody = await this.#createExecutionBody(
+ run,
+ [run.tasks, resumedTask].flat().filter(Boolean),
+ startedAt,
+ isRetry,
+ connections.auth,
event,
- job: {
- id: run.version.job.slug,
- version: run.version.version,
- },
- run: {
- id: run.id,
- isTest: run.isTest,
- startedAt,
- isRetry,
- },
- environment: {
- id: run.environment.id,
- slug: run.environment.slug,
- type: run.environment.type,
- },
- organization: {
- id: run.organization.id,
- slug: run.organization.slug,
- title: run.organization.title,
- },
- account: run.externalAccount
- ? {
- id: run.externalAccount.identifier,
- metadata: run.externalAccount.metadata,
- }
- : undefined,
- connections: connections.auth,
- source: sourceContext.success ? sourceContext.data : undefined,
- tasks: prepareTasksForRun([run.tasks, resumedTask].flat().filter(Boolean)),
- });
+ sourceContext.success ? sourceContext.data : undefined
+ );
+
+ const { response, parser, errorParser, durationInMs } = await client.executeJobRequest(
+ executionBody
+ );
if (!response) {
return await this.#failRunExecutionWithRetry({
@@ -269,6 +262,25 @@ export class PerformRunExecutionV2Service {
});
}
+ // Update the endpoint version if it has changed
+ const rawHeaders = Object.fromEntries(response.headers.entries());
+ const headers = EndpointHeadersSchema.safeParse(rawHeaders);
+
+ if (
+ headers.success &&
+ headers.data["trigger-version"] &&
+ headers.data["trigger-version"] !== run.endpoint.version
+ ) {
+ await this.#prismaClient.endpoint.update({
+ where: {
+ id: run.endpoint.id,
+ },
+ data: {
+ version: headers.data["trigger-version"],
+ },
+ });
+ }
+
const rawBody = await response.text();
if (!response.ok) {
@@ -389,6 +401,10 @@ export class PerformRunExecutionV2Service {
break;
}
+ case "YIELD_EXECUTION": {
+ await this.#resumeYieldedRun(run, safeBody.data.key, isRetry, durationInMs, executionCount);
+ break;
+ }
default: {
const _exhaustiveCheck: never = status;
throw new Error(`Non-exhaustive match for value: ${status}`);
@@ -396,6 +412,91 @@ export class PerformRunExecutionV2Service {
}
}
+ async #createExecutionBody(
+ run: FoundRun,
+ tasks: FoundTask[],
+ startedAt: Date,
+ isRetry: boolean,
+ connections: Record,
+ event: ApiEventLog,
+ source?: RunSourceContext
+ ): Promise {
+ if (supportsFeature("lazyLoadedCachedTasks", run.endpoint.version)) {
+ const preparedTasks = prepareTasksForCaching(tasks, TOTAL_CACHED_TASK_BYTE_LIMIT);
+
+ return {
+ event,
+ job: {
+ id: run.version.job.slug,
+ version: run.version.version,
+ },
+ run: {
+ id: run.id,
+ isTest: run.isTest,
+ startedAt,
+ isRetry,
+ },
+ environment: {
+ id: run.environment.id,
+ slug: run.environment.slug,
+ type: run.environment.type,
+ },
+ organization: {
+ id: run.organization.id,
+ slug: run.organization.slug,
+ title: run.organization.title,
+ },
+ account: run.externalAccount
+ ? {
+ id: run.externalAccount.identifier,
+ metadata: run.externalAccount.metadata,
+ }
+ : undefined,
+ connections,
+ source,
+ tasks: preparedTasks.tasks,
+ cachedTaskCursor: preparedTasks.cursor,
+ noopTasksSet: prepareNoOpTasksBloomFilter(tasks),
+ yieldedExecutions: run.yieldedExecutions,
+ };
+ }
+
+ const preparedTasks = prepareTasksForCachingLegacy(tasks, TOTAL_CACHED_TASK_BYTE_LIMIT);
+
+ return {
+ event,
+ job: {
+ id: run.version.job.slug,
+ version: run.version.version,
+ },
+ run: {
+ id: run.id,
+ isTest: run.isTest,
+ startedAt,
+ isRetry,
+ },
+ environment: {
+ id: run.environment.id,
+ slug: run.environment.slug,
+ type: run.environment.type,
+ },
+ organization: {
+ id: run.organization.id,
+ slug: run.organization.slug,
+ title: run.organization.title,
+ },
+ account: run.externalAccount
+ ? {
+ id: run.externalAccount.identifier,
+ metadata: run.externalAccount.metadata,
+ }
+ : undefined,
+ connections,
+ source,
+ tasks: preparedTasks.tasks,
+ };
+ }
+
async #completeRunWithSuccess(run: FoundRun, data: RunJobSuccess, durationInMs: number) {
await this.#prismaClient.jobRun.update({
where: { id: run.id },
@@ -429,7 +530,9 @@ export class PerformRunExecutionV2Service {
// If the task has an operation, then the next performRunExecution will occur
// when that operation has finished
- if (!data.task.operation) {
+ // Tasks with callbacks enabled will also get processed separately, i.e. when
+ // they time out, or on valid requests to their callbackUrl
+ if (!data.task.operation && !data.task.callbackUrl) {
await enqueueRunExecutionV2(run, tx, {
runAt: data.task.delayUntil ?? undefined,
resumeTaskId: data.task.id,
@@ -501,6 +604,56 @@ export class PerformRunExecutionV2Service {
});
}
+ async #resumeYieldedRun(
+ run: FoundRun,
+ key: string,
+ isRetry: boolean,
+ durationInMs: number,
+ executionCount: number
+ ) {
+ await $transaction(this.#prismaClient, async (tx) => {
+ if (run.yieldedExecutions.length + 1 > MAX_RUN_YIELDED_EXECUTIONS) {
+ return await this.#failRunExecution(
+ tx,
+ "EXECUTE_JOB",
+ run,
+ {
+ message: `Run has yielded too many times, the maximum is ${MAX_RUN_YIELDED_EXECUTIONS}`,
+ },
+ "FAILURE",
+ durationInMs
+ );
+ }
+
+ await tx.jobRun.update({
+ where: {
+ id: run.id,
+ },
+ data: {
+ executionDuration: {
+ increment: durationInMs,
+ },
+ executionCount: {
+ increment: 1,
+ },
+ yieldedExecutions: {
+ push: key,
+ },
+ },
+ select: {
+ yieldedExecutions: true,
+ executionCount: true,
+ },
+ });
+
+ await enqueueRunExecutionV2(run, tx, {
+ isRetry,
+ skipRetrying: run.environment.type === RuntimeEnvironmentType.DEVELOPMENT,
+ executionCount,
+ });
+ });
+ }
+
async #retryRunWithTask(
run: FoundRun,
data: RunJobRetryWithTask,
@@ -686,69 +839,16 @@ export class PerformRunExecutionV2Service {
}
}
-function prepareTasksForRun(possibleTasks: FoundTask[]): CachedTask[] {
- const tasks = possibleTasks.filter((task) => task.status === "COMPLETED");
+function prepareNoOpTasksBloomFilter(possibleTasks: FoundTask[]): string {
+ const tasks = possibleTasks.filter((task) => task.status === "COMPLETED" && task.noop);
- // We need to limit the cached tasks to not be too large >3.5MB when serialized
- const TOTAL_CACHED_TASK_BYTE_LIMIT = 3500000;
+ const filter = new BloomFilter(BloomFilter.NOOP_TASK_SET_SIZE);
- const cachedTasks = new Map(); // Cache for prepared tasks
- const cachedTaskSizes = new Map(); // Cache for calculated task sizes
-
- // Helper function to get the cached prepared task, or prepare and cache if not already cached
- function getCachedTask(task: FoundTask): CachedTask {
- const taskId = task.id;
- if (!cachedTasks.has(taskId)) {
- cachedTasks.set(taskId, prepareTaskForRun(task));
- }
- return cachedTasks.get(taskId)!;
+ for (const task of tasks) {
+ filter.add(task.idempotencyKey);
}
- // Helper function to get the cached task size, or calculate and cache if not already cached
- function getCachedTaskSize(task: CachedTask): number {
- const taskId = task.id;
- if (!cachedTaskSizes.has(taskId)) {
- cachedTaskSizes.set(taskId, calculateCachedTaskSize(task));
- }
- return cachedTaskSizes.get(taskId)!;
- }
-
- // Prepare tasks and calculate their sizes
- const availableTasks = tasks.map((task) => {
- const cachedTask = getCachedTask(task);
- return { task: cachedTask, size: getCachedTaskSize(cachedTask) };
- });
-
- // Sort tasks in ascending order by size
- availableTasks.sort((a, b) => a.size - b.size);
-
- // Select tasks using greedy approach
- const tasksToRun: CachedTask[] = [];
- let remainingSize = TOTAL_CACHED_TASK_BYTE_LIMIT;
-
- for (const { task, size } of availableTasks) {
- if (size <= remainingSize) {
- tasksToRun.push(task);
- remainingSize -= size;
- }
- }
-
- return tasksToRun;
-}
-
-function prepareTaskForRun(task: FoundTask): CachedTask {
- return {
- id: task.idempotencyKey, // We should eventually move this back to task.id
- status: task.status,
- idempotencyKey: task.idempotencyKey,
- noop: task.noop,
- output: task.output as any,
- parentId: task.parentId,
- };
-}
-
-function calculateCachedTaskSize(task: CachedTask): number {
- return JSON.stringify(task).length;
+ return filter.serialize();
}
async function findRun(prisma: PrismaClientOrTransaction, id: string) {
@@ -783,6 +883,9 @@ async function findRun(prisma: PrismaClientOrTransaction, id: string) {
output: true,
parentId: true,
},
+ orderBy: {
+ id: "asc",
+ },
},
event: true,
version: {
diff --git a/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts b/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts
index 85f0f9811..762227624 100644
--- a/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts
+++ b/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts
@@ -36,7 +36,7 @@ export class NextScheduledEventService {
const scheduleTime = calculateNextScheduledEvent(
schedule.data,
- scheduleSource.lastEventTimestamp
+ scheduleSource.lastEventTimestamp ?? scheduleSource.createdAt
);
logger.debug("enqueuing scheduled event", {
@@ -67,6 +67,7 @@ export class NextScheduledEventService {
},
data: {
workerJobId: workerJob.id,
+ nextEventTimestamp: scheduleTime,
},
});
diff --git a/apps/webapp/app/services/sources/utils.server.ts b/apps/webapp/app/services/sources/utils.server.ts
index 127ca4b7a..4c2bc7ae7 100644
--- a/apps/webapp/app/services/sources/utils.server.ts
+++ b/apps/webapp/app/services/sources/utils.server.ts
@@ -1,5 +1,5 @@
import crypto from "node:crypto";
-export function generateSecret(): string {
- return crypto.randomBytes(32).toString("hex");
+export function generateSecret(sizeInBytes = 32): string {
+ return crypto.randomBytes(sizeInBytes).toString("hex");
}
diff --git a/apps/webapp/app/services/tasks/performTaskOperation.server.ts b/apps/webapp/app/services/tasks/performTaskOperation.server.ts
index fb5a6304a..987cb56fa 100644
--- a/apps/webapp/app/services/tasks/performTaskOperation.server.ts
+++ b/apps/webapp/app/services/tasks/performTaskOperation.server.ts
@@ -1,5 +1,3 @@
-import { env } from "process";
-import { Run } from "~/presenters/RunPresenter.server";
import {
FetchOperationSchema,
FetchRequestInit,
diff --git a/apps/webapp/app/services/tasks/processCallbackTimeout.ts b/apps/webapp/app/services/tasks/processCallbackTimeout.ts
new file mode 100644
index 000000000..948691990
--- /dev/null
+++ b/apps/webapp/app/services/tasks/processCallbackTimeout.ts
@@ -0,0 +1,76 @@
+import { RuntimeEnvironmentType } from "@trigger.dev/database";
+import { $transaction, PrismaClient, PrismaClientOrTransaction, prisma } from "~/db.server";
+import { enqueueRunExecutionV2 } from "~/models/jobRunExecution.server";
+import { logger } from "../logger.server";
+
+type FoundTask = Awaited>;
+
+export class ProcessCallbackTimeoutService {
+ #prismaClient: PrismaClient;
+
+ constructor(prismaClient: PrismaClient = prisma) {
+ this.#prismaClient = prismaClient;
+ }
+
+ public async call(id: string) {
+ const task = await findTask(this.#prismaClient, id);
+
+ if (!task) {
+ return;
+ }
+
+ if (task.status !== "WAITING" || !task.callbackUrl) {
+ return;
+ }
+
+ logger.debug("ProcessCallbackTimeoutService.call", { task });
+
+ return await this.#failTask(task, "Remote callback timeout - no requests received");
+ }
+
+ async #failTask(task: NonNullable, error: string) {
+ await $transaction(this.#prismaClient, async (tx) => {
+ await tx.taskAttempt.updateMany({
+ where: {
+ taskId: task.id,
+ status: "PENDING",
+ },
+ data: {
+ status: "ERRORED",
+ error
+ },
+ });
+
+ await tx.task.update({
+ where: { id: task.id },
+ data: {
+ status: "ERRORED",
+ completedAt: new Date(),
+ output: error,
+ },
+ });
+
+ await this.#resumeRunExecution(task, tx);
+ });
+ }
+
+ async #resumeRunExecution(task: NonNullable, prisma: PrismaClientOrTransaction) {
+ await enqueueRunExecutionV2(task.run, prisma, {
+ skipRetrying: task.run.environment.type === RuntimeEnvironmentType.DEVELOPMENT,
+ });
+ }
+}
+
+async function findTask(prisma: PrismaClient, id: string) {
+ return prisma.task.findUnique({
+ where: { id },
+ include: {
+ run: {
+ include: {
+ environment: true,
+ queue: true,
+ },
+ },
+ },
+ });
+}
diff --git a/apps/webapp/app/services/worker.server.ts b/apps/webapp/app/services/worker.server.ts
index 90b710b3a..ecdfd8a97 100644
--- a/apps/webapp/app/services/worker.server.ts
+++ b/apps/webapp/app/services/worker.server.ts
@@ -1,16 +1,18 @@
import { DeliverEmailSchema } from "@/../../packages/emails/src";
-import { ScheduledPayloadSchema } from "@trigger.dev/core";
+import { ScheduledPayloadSchema, addMissingVersionField } from "@trigger.dev/core";
import { z } from "zod";
import { prisma } from "~/db.server";
import { env } from "~/env.server";
import { ZodWorker } from "~/platform/zodWorker.server";
import { sendEmail } from "./email.server";
import { IndexEndpointService } from "./endpoints/indexEndpoint.server";
+import { PerformEndpointIndexService } from "./endpoints/performEndpointIndexService";
import { RecurringEndpointIndexService } from "./endpoints/recurringEndpointIndex.server";
import { DeliverEventService } from "./events/deliverEvent.server";
import { InvokeDispatcherService } from "./events/invokeDispatcher.server";
import { integrationAuthRepository } from "./externalApis/integrationAuthRepository.server";
import { IntegrationConnectionCreatedService } from "./externalApis/integrationConnectionCreated.server";
+import { logger } from "./logger.server";
import { MissingConnectionCreatedService } from "./runs/missingConnectionCreated.server";
import { PerformRunExecutionV1Service } from "./runs/performRunExecutionV1.server";
import { PerformRunExecutionV2Service } from "./runs/performRunExecutionV2.server";
@@ -19,7 +21,7 @@ import { DeliverScheduledEventService } from "./schedules/deliverScheduledEvent.
import { ActivateSourceService } from "./sources/activateSource.server";
import { DeliverHttpSourceRequestService } from "./sources/deliverHttpSourceRequest.server";
import { PerformTaskOperationService } from "./tasks/performTaskOperation.server";
-import { addMissingVersionField } from "@trigger.dev/core";
+import { ProcessCallbackTimeoutService } from "./tasks/processCallbackTimeout";
const workerCatalog = {
indexEndpoint: z.object({
@@ -28,8 +30,14 @@ const workerCatalog = {
sourceData: z.any().optional(),
reason: z.string().optional(),
}),
+ performEndpointIndexing: z.object({
+ id: z.string(),
+ }),
scheduleEmail: DeliverEmailSchema,
startRun: z.object({ id: z.string() }),
+ processCallbackTimeout: z.object({
+ id: z.string(),
+ }),
performTaskOperation: z.object({
id: z.string(),
}),
@@ -186,6 +194,11 @@ function getWorkerQueue() {
return new ZodWorker({
name: "workerQueue",
prisma,
+ cleanup: {
+ frequencyExpression: "13,27,43 * * * *",
+ ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
+ maxCount: 1000,
+ },
runnerOptions: {
connectionString: env.DATABASE_URL,
concurrency: env.WORKER_CONCURRENCY,
@@ -287,6 +300,7 @@ function getWorkerQueue() {
deliverHttpSourceRequest: {
priority: 1, // smaller number = higher priority
maxAttempts: 14,
+ queueName: (payload) => `sources:${payload.id}`,
handler: async (payload, job) => {
const service = new DeliverHttpSourceRequestService();
@@ -302,9 +316,17 @@ function getWorkerQueue() {
await service.call(payload.id);
},
},
+ processCallbackTimeout: {
+ priority: 0, // smaller number = higher priority
+ maxAttempts: 3,
+ handler: async (payload, job) => {
+ const service = new ProcessCallbackTimeoutService();
+
+ await service.call(payload.id);
+ },
+ },
performTaskOperation: {
priority: 0, // smaller number = higher priority
- queueName: (payload) => `tasks:${payload.id}`,
maxAttempts: 3,
handler: async (payload, job) => {
const service = new PerformTaskOperationService();
@@ -313,7 +335,6 @@ function getWorkerQueue() {
},
},
scheduleEmail: {
- queueName: "internal-queue",
priority: 100,
maxAttempts: 3,
handler: async (payload, job) => {
@@ -325,10 +346,17 @@ function getWorkerQueue() {
maxAttempts: 7,
handler: async (payload, job) => {
const service = new IndexEndpointService();
-
await service.call(payload.id, payload.source, payload.reason, payload.sourceData);
},
},
+ performEndpointIndexing: {
+ priority: 1, // smaller number = higher priority
+ maxAttempts: 7,
+ handler: async (payload, job) => {
+ const service = new PerformEndpointIndexService();
+ await service.call(payload.id);
+ },
+ },
deliverEvent: {
priority: 0, // smaller number = higher priority
maxAttempts: 5,
@@ -340,7 +368,6 @@ function getWorkerQueue() {
},
refreshOAuthToken: {
priority: 8, // smaller number = higher priority
- queueName: "internal-queue",
maxAttempts: 7,
handler: async (payload, job) => {
await integrationAuthRepository.refreshConnection({
diff --git a/apps/webapp/app/utils/icon.ts b/apps/webapp/app/utils/icon.ts
new file mode 100644
index 000000000..d3ee34bcc
--- /dev/null
+++ b/apps/webapp/app/utils/icon.ts
@@ -0,0 +1,9 @@
+import { hasIcon } from "@trigger.dev/companyicons";
+import { iconNames as namedIcons } from "~/components/primitives/NamedIcon";
+
+export const isValidIcon = (icon?: string): boolean => {
+ if (!icon) {
+ return false;
+ }
+ return namedIcons.includes(icon) || hasIcon(icon);
+};
diff --git a/apps/webapp/app/utils/sse.ts b/apps/webapp/app/utils/sse.ts
index 4588cedc5..105911d7f 100644
--- a/apps/webapp/app/utils/sse.ts
+++ b/apps/webapp/app/utils/sse.ts
@@ -47,7 +47,7 @@ export function sse({ request, pingInterval = 1000, updateInterval = 348, run }:
});
}
} else {
- logger.debug("Uknown error sending SSE, aborting", {
+ logger.debug("Unknown error sending SSE, aborting", {
error,
args,
});
diff --git a/apps/webapp/package.json b/apps/webapp/package.json
index 4d4f5ce16..7ee50248a 100644
--- a/apps/webapp/package.json
+++ b/apps/webapp/package.json
@@ -13,7 +13,7 @@
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
"start": "cross-env NODE_ENV=production node --max-old-space-size=8192 ./build/server.js",
"start:local": "cross-env node --max-old-space-size=8192 ./build/server.js",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsc -p ./tsconfig.check.json",
"db:seed": "node prisma/seed.js",
"db:seed:local": "ts-node prisma/seed.ts",
"generate:sourcemaps": "remix build --sourcemap",
@@ -34,6 +34,7 @@
"@codemirror/lang-javascript": "^6.1.1",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/language": "^6.3.1",
+ "@codemirror/lint": "^6.4.2",
"@codemirror/search": "^6.2.3",
"@codemirror/state": "^6.1.3",
"@codemirror/view": "^6.5.0",
@@ -93,7 +94,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.0",
- "react-hotkeys-hook": "^3.4.7",
+ "react-hotkeys-hook": "^4.4.1",
"react-use": "^17.4.0",
"recharts": "^2.8.0",
"remix-auth": "^3.2.2",
@@ -105,13 +106,15 @@
"simple-oauth2": "^5.0.0",
"simplur": "^3.0.1",
"slug": "^6.0.0",
+ "sonner": "^1.0.3",
"tailwind-merge": "^1.12.0",
"tailwind-scrollbar-hide": "^1.1.7",
"tailwindcss-animate": "^1.0.5",
"tiny-invariant": "^1.2.0",
"ulid": "^2.3.0",
- "zod": "3.21.4",
- "zod-error": "1.5.0"
+ "zod": "3.22.3",
+ "zod-error": "1.5.0",
+ "zod-validation-error": "^1.5.0"
},
"devDependencies": {
"@remix-run/dev": "1.19.2-pre.0",
diff --git a/apps/webapp/server.ts b/apps/webapp/server.ts
index 892a99430..55c9fc530 100644
--- a/apps/webapp/server.ts
+++ b/apps/webapp/server.ts
@@ -62,7 +62,23 @@ if (process.env.HTTP_SERVER_DISABLED !== "true") {
});
// Handle shutdowns gracefully
- createTerminus(server, { signals: ["SIGINT", "SIGTERM"], timeout: 5000 });
+ createTerminus(server, {
+ signals: ["SIGINT", "SIGTERM"],
+ timeout: process.env.GRACEFUL_SHUTDOWN_TIMEOUT
+ ? Number(process.env.GRACEFUL_SHUTDOWN_TIMEOUT)
+ : 5000,
+ onSignal: async () => {
+ console.log("[terminus] onSignal: starting cleanup");
+ },
+ onShutdown: async () => {
+ console.log("[terminus] onShutdown: cleanup finished, server is shutting down");
+ },
+ onSendFailureDuringShutdown: async () => {
+ console.log(
+ "[terminus] onSendFailureDuringShutdown: cleanup finished, server is shutting down"
+ );
+ },
+ });
} else {
console.log(`β
app ready (skipping http server)`);
}
diff --git a/apps/webapp/tsconfig.check.json b/apps/webapp/tsconfig.check.json
new file mode 100644
index 000000000..f1adffe51
--- /dev/null
+++ b/apps/webapp/tsconfig.check.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "noEmit": true,
+ "paths": {
+ "~/*": ["./app/*"],
+ "@/*": ["./*"]
+ }
+ }
+}
diff --git a/config-packages/tsconfig/integration.json b/config-packages/tsconfig/integration.json
new file mode 100644
index 000000000..ff9d795e5
--- /dev/null
+++ b/config-packages/tsconfig/integration.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./node18.json",
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2019"],
+ "paths": {
+ "@trigger.dev/tsup/*": ["../../config-packages/tsup/src/*"],
+ "@trigger.dev/tsup": ["../../config-packages/tsup/src/index"],
+ "@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
+ "@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
+ "@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"],
+ "@trigger.dev/integration-kit": ["../../packages/integration-kit/src/index"]
+ },
+ "declaration": false,
+ "declarationMap": false,
+ "baseUrl": ".",
+ "stripInternal": true
+ },
+ "exclude": ["node_modules"]
+}
diff --git a/config-packages/tsup/package.json b/config-packages/tsup/package.json
new file mode 100644
index 000000000..8af32c7ae
--- /dev/null
+++ b/config-packages/tsup/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@trigger.dev/tsup",
+ "version": "0.0.0",
+ "private": true,
+ "license": "MIT",
+ "devDependencies": {
+ "tsup": "7.1.x"
+ }
+}
diff --git a/config-packages/tsup/src/index.ts b/config-packages/tsup/src/index.ts
new file mode 100644
index 000000000..661e53cca
--- /dev/null
+++ b/config-packages/tsup/src/index.ts
@@ -0,0 +1,3 @@
+export { defineConfig } from "tsup";
+export { deepMergeOptions } from "./utils";
+export { options as integrationOptions } from "./integration";
diff --git a/config-packages/tsup/src/integration.ts b/config-packages/tsup/src/integration.ts
new file mode 100644
index 000000000..0fb2a3d7d
--- /dev/null
+++ b/config-packages/tsup/src/integration.ts
@@ -0,0 +1,22 @@
+import { Options, defineConfig } from "tsup";
+
+export const options: Options = {
+ name: "main",
+ entry: ["./src/index.ts"],
+ outDir: "./dist",
+ platform: "node",
+ format: ["cjs"],
+ legacyOutput: true,
+ sourcemap: true,
+ clean: true,
+ bundle: true,
+ splitting: false,
+ dts: true,
+ treeshake: {
+ preset: "smallest",
+ },
+ esbuildPlugins: [],
+ external: ["http", "https", "util", "events", "tty", "os", "timers"],
+};
+
+export default defineConfig(options);
diff --git a/config-packages/tsup/src/utils.ts b/config-packages/tsup/src/utils.ts
new file mode 100644
index 000000000..6e46ff742
--- /dev/null
+++ b/config-packages/tsup/src/utils.ts
@@ -0,0 +1,32 @@
+import { Options } from "tsup";
+
+export const deepMergeOptions = deepMergeRecords;
+
+function deepMergeRecords>(...options: TRecord[]): TRecord {
+ const result = {} as TRecord;
+
+ for (const option of options) {
+ for (const key in option) {
+ if (option.hasOwnProperty(key)) {
+ const optionValue = option[key];
+ const existingValue = result[key];
+
+ if (
+ existingValue &&
+ typeof existingValue === "object" &&
+ typeof optionValue === "object" &&
+ !Array.isArray(existingValue) &&
+ !Array.isArray(optionValue) &&
+ existingValue !== null &&
+ optionValue !== null
+ ) {
+ result[key] = deepMergeRecords(existingValue, optionValue);
+ } else {
+ result[key] = optionValue;
+ }
+ }
+ }
+ }
+
+ return result;
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 2c7d3fc30..cf12ecd6a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -32,7 +32,8 @@ ENV NODE_ENV production
RUN pnpm install --prod --no-frozen-lockfile
COPY --from=pruner --chown=node:node /triggerdotdev/packages/database/prisma/schema.prisma /triggerdotdev/packages/database/prisma/schema.prisma
# RUN pnpm add @prisma/client@5.1.1 -w
-RUN pnpx prisma@4.16.0 generate --schema /triggerdotdev/packages/database/prisma/schema.prisma
+ENV NPM_CONFIG_IGNORE_WORKSPACE_ROOT_CHECK true
+RUN pnpx prisma@5.4.1 generate --schema /triggerdotdev/packages/database/prisma/schema.prisma
## Builder (builds the webapp)
FROM base AS builder
diff --git a/docker/dev-compose.yml b/docker/dev-compose.yml
index b5db14f4b..3f0003a57 100644
--- a/docker/dev-compose.yml
+++ b/docker/dev-compose.yml
@@ -35,6 +35,7 @@ services:
DIRECT_URL: postgres://postgres:postgres@db:5432/postgres?schema=public
SESSION_SECRET: secret123
MAGIC_LINK_SECRET: secret123
+ ENCRYPTION_KEY: secret123
REMIX_APP_PORT: 3030
PORT: 3030
networks:
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 76017f376..41e935745 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -2,6 +2,7 @@ version: "3"
volumes:
database-data:
+ pgadmin-data:
networks:
app_network:
@@ -22,3 +23,21 @@ services:
- app_network
ports:
- 5432:5432
+
+ pgadmin:
+ container_name: pgadmin
+ image: dpage/pgadmin4:7
+ restart: always
+ environment:
+ PGADMIN_DEFAULT_EMAIL: admin@example.com
+ PGADMIN_DEFAULT_PASSWORD: admin
+ PGADMIN_DISABLE_POSTFIX: "true"
+ volumes:
+ - pgadmin-data:/var/lib/pgadmin
+ - ./pgadmin/servers.json:/pgadmin4/servers.json
+ networks:
+ - app_network
+ ports:
+ - 5480:80
+ depends_on:
+ - database
diff --git a/docker/pgadmin/servers.json b/docker/pgadmin/servers.json
new file mode 100644
index 000000000..83f7159bb
--- /dev/null
+++ b/docker/pgadmin/servers.json
@@ -0,0 +1,13 @@
+{
+ "Servers": {
+ "1": {
+ "Name": "Trigger.dev",
+ "Group": "Trigger.dev",
+ "Port": 5432,
+ "Username": "postgres",
+ "Host": "database",
+ "SSLMode": "prefer",
+ "MaintenanceDB": "postgres"
+ }
+ }
+}
diff --git a/docs/_snippets/frameworks/card-astro.mdx b/docs/_snippets/frameworks/card-astro.mdx
new file mode 100644
index 000000000..6db902f93
--- /dev/null
+++ b/docs/_snippets/frameworks/card-astro.mdx
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/astro"
+/>
diff --git a/docs/_snippets/frameworks/card-express.mdx b/docs/_snippets/frameworks/card-express.mdx
new file mode 100644
index 000000000..3fc9f18c1
--- /dev/null
+++ b/docs/_snippets/frameworks/card-express.mdx
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/express"
+/>
diff --git a/docs/_snippets/frameworks/card-fastify.mdx b/docs/_snippets/frameworks/card-fastify.mdx
new file mode 100644
index 000000000..09ad6d051
--- /dev/null
+++ b/docs/_snippets/frameworks/card-fastify.mdx
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/fastify"
+/>
diff --git a/docs/_snippets/frameworks/card-nestjs.mdx b/docs/_snippets/frameworks/card-nestjs.mdx
new file mode 100644
index 000000000..84aa84537
--- /dev/null
+++ b/docs/_snippets/frameworks/card-nestjs.mdx
@@ -0,0 +1,23 @@
+
+
+
+
+ }
+ href="/documentation/quickstarts/nestjs"
+/>
diff --git a/docs/_snippets/frameworks/card-nextjs.mdx b/docs/_snippets/frameworks/card-nextjs.mdx
new file mode 100644
index 000000000..9f6759574
--- /dev/null
+++ b/docs/_snippets/frameworks/card-nextjs.mdx
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+ href="/documentation/quickstarts/nextjs"
+
+/>
diff --git a/docs/_snippets/frameworks/card-nuxt.mdx b/docs/_snippets/frameworks/card-nuxt.mdx
new file mode 100644
index 000000000..ec3561cb6
--- /dev/null
+++ b/docs/_snippets/frameworks/card-nuxt.mdx
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/nuxt"
+/>
diff --git a/docs/_snippets/frameworks/card-redwood.mdx b/docs/_snippets/frameworks/card-redwood.mdx
new file mode 100644
index 000000000..f37a1afa2
--- /dev/null
+++ b/docs/_snippets/frameworks/card-redwood.mdx
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/redwood"
+/>
diff --git a/docs/_snippets/frameworks/card-remix.mdx b/docs/_snippets/frameworks/card-remix.mdx
new file mode 100644
index 000000000..9de8732f6
--- /dev/null
+++ b/docs/_snippets/frameworks/card-remix.mdx
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/remix"
+/>
diff --git a/docs/_snippets/frameworks/card-supabase.mdx b/docs/_snippets/frameworks/card-supabase.mdx
new file mode 100644
index 000000000..84afe8c23
--- /dev/null
+++ b/docs/_snippets/frameworks/card-supabase.mdx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/supabase"
+/>
diff --git a/docs/_snippets/frameworks/card-sveltekit.mdx b/docs/_snippets/frameworks/card-sveltekit.mdx
new file mode 100644
index 000000000..b6f5a768a
--- /dev/null
+++ b/docs/_snippets/frameworks/card-sveltekit.mdx
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ href="/documentation/quickstarts/sveltekit"
+/>
diff --git a/docs/_snippets/manual-setup-nestjs.mdx b/docs/_snippets/manual-setup-nestjs.mdx
new file mode 100644
index 000000000..c053c49d4
--- /dev/null
+++ b/docs/_snippets/manual-setup-nestjs.mdx
@@ -0,0 +1,264 @@
+
+ Create a blank project by installing the NestJS CLI in your terminal:
+
+```bash
+npm i -g @nestjs/cli
+```
+
+Then, create an empty project with:
+
+```bash
+nest new project-name
+```
+
+
+
+## Installing Required Packages
+
+To begin, install the necessary packages in your NestJS project directory. You can choose one of the following package managers:
+
+
+```bash npm
+npm i @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config
+```
+
+```bash pnpm
+pnpm install @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config
+```
+
+```bash yarn
+yarn add @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config
+```
+
+
+
+
+
+Ensure that you execute this command within a NestJS project.
+
+## Obtaining the Development API Key
+
+To locate your development API key, login to the [Trigger.dev
+dashboard](https://cloud.trigger.dev) and select the Project you want to
+connect to. Then click on the Environments & API Keys tab in the left menu.
+You can copy your development API Key from the field at the top of this page.
+(Your development key will start with `tr_dev_`).
+
+## Adding Environment Variables
+
+Create a `.env` file at the root of your project and include your Trigger API key and URL like this:
+
+```bash
+TRIGGER_API_KEY=ENTER_YOUR_DEVELOPMENT_API_KEY_HERE
+TRIGGER_API_URL=https://api.trigger.dev # this line is only necessary if you are self-hosting Trigger
+```
+
+Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step.
+
+
+ This configuration only will be loaded if you use [NestJS
+ Config](https://docs.nestjs.com/techniques/configuration) or
+ [dotenv](https://github.com/motdotla/dotenv).
+
+
+## Adding TriggerDev Module
+
+Open your `app.module.ts`, and add the following inside your `imports`:
+
+```typescript
+import { TriggerDevModule } from "@trigger.dev/nestjs";
+import { Module } from "@nestjs/common";
+
+//you need to load the environment variables from .env, this is one way to do it
+import "dotenv/config";
+
+@Module({
+ imports: [
+ TriggerDevModule.register({
+ id: "my-app",
+ apiKey: process.env.TRIGGER_API_KEY,
+ apiUrl: process.env.TRIGGER_API_URL,
+ }),
+ // if you use NestJS Config, you can do like this:
+ // TriggerDevModule.registerAsync({
+ // useFactory: (configService: ConfigService) => ({
+ // id: 'my-app',
+ // apiKey: configService.get("TRIGGER_API_KEY"),
+ // apiUrl: configService.get("TRIGGER_API_URL"),
+ // }),
+ // inject: [ConfigService],
+ // }),
+ ],
+})
+export class AppModule {
+ //...
+}
+```
+
+Replace **"my-app"** with an appropriate identifier for your project. The **apiKey** and **apiUrl** are obtained from the environment variables you set earlier.
+
+By following these steps, you'll configure the Trigger Client to work with your project.
+
+## Creating the Example Job
+
+When you add `TriggerDevModule` to your project, you will can have access to the `TriggerClient` instance by using the `@InjectTriggerDevClient()` decorator in the constructor.
+
+Now, let's create an example job to test the integration.
+
+1. Create a controller named `job.controller.ts` alongside your `app.module.ts`
+2. Inside that controller, add the following code:
+
+
+
+```typescript job.controller.ts
+import { Controller, Get } from "@nestjs/common";
+import { InjectTriggerDevClient } from "@trigger.dev/nestjs";
+import { eventTrigger, TriggerClient } from "@trigger.dev/sdk";
+
+@Controller()
+export class JobController {
+ constructor(@InjectTriggerDevClient() private readonly client: TriggerClient) {
+ this.client.defineJob({
+ id: "test-job",
+ name: "Test Job One",
+ version: "0.0.1",
+ trigger: eventTrigger({
+ name: "test.event",
+ }),
+ run: async (payload, io, ctx) => {
+ await io.logger.info("Hello world!", { payload });
+
+ return {
+ message: "Hello world!",
+ };
+ },
+ });
+ }
+
+ @Get()
+ getHello(): string {
+ return `Running Trigger.dev with client-id ${this.client.id}`;
+ }
+}
+```
+
+Now, add this controller to your `app.module.ts`:
+
+```typescript app.module.ts
+import { TriggerDevModule } from "@trigger.dev/nestjs";
+import { Module } from "@nestjs/common";
+import { JobController } from "./job.controller";
+
+//you need to load the environment variables from .env, this is one way to do it
+import "dotenv/config";
+
+@Module({
+ controllers: [JobController],
+ imports: [
+ TriggerDevModule.register({
+ id: "my-app",
+ apiKey: process.env.TRIGGER_API_KEY,
+ apiUrl: process.env.TRIGGER_API_URL,
+ }),
+ // if you use NestJS Config, you can do like this:
+ // TriggerDevModule.registerAsync({
+ // useFactory: (configService: ConfigService) => ({
+ // id: 'my-app',
+ // apiKey: configService.get("TRIGGER_API_KEY"),
+ // apiUrl: configService.get("TRIGGER_API_URL"),
+ // }),
+ // inject: [ConfigService],
+ // }),
+ ],
+})
+export class AppModule {
+ //...
+}
+```
+
+
+
+
+
+ You can import the Trigger.dev client inside any `service` or `controller`, we recommend you to
+ create specialized `service` for each job you have for a better maintainability.
+
+
+## Adding Configuration to `package.json`
+
+Inside the `package.json` file, add the following configuration under the root object:
+
+```json
+"trigger.dev": {
+ "endpointId": "my-app"
+}
+```
+
+Your `package.json` file might look something like this:
+
+```json
+{
+ "name": "my-app",
+ "version": "1.0.0",
+ "dependencies": {
+ // ... other dependencies
+ },
+ "trigger.dev": {
+ "endpointId": "my-app"
+ }
+}
+```
+
+Replace **"my-app"** with the appropriate identifier you used during the step for creating the Trigger Client.
+
+## Running
+
+### Run your NestJS app
+
+Run your NestJS app locally, like you normally would. For example:
+
+
+
+```bash npm
+npm run start
+```
+
+```bash pnpm
+pnpm run start
+```
+
+```bash yarn
+yarn run start
+```
+
+
+
+### Run the CLI 'dev' command
+
+In a **_separate terminal window or tab_** run:
+
+
+
+```bash npm
+npx @trigger.dev/cli@latest dev
+```
+
+```bash pnpm
+pnpm dlx @trigger.dev/cli@latest dev
+```
+
+```bash yarn
+yarn dlx @trigger.dev/cli@latest dev
+```
+
+
+
+
+ You can optionally pass the port if you're not running on 3000 by adding
+ `--port 3001` to the end
+
+
+
+ You can optionally pass the hostname if you're not running on localhost by adding
+ `--hostname `. Example, in case your Remix is running on 0.0.0.0: `--hostname 0.0.0.0`.
+
diff --git a/docs/_snippets/manual-setup-sveltekit.mdx b/docs/_snippets/manual-setup-sveltekit.mdx
index 8e299631d..438005689 100644
--- a/docs/_snippets/manual-setup-sveltekit.mdx
+++ b/docs/_snippets/manual-setup-sveltekit.mdx
@@ -1 +1,217 @@
-We're in the process of building support for the SvelteKit framework. You can follow along with progress or contribute via [this GitHub issue](https://github.com/triggerdotdev/trigger.dev/issues).
+## Installing Required Packages
+
+To begin, install the necessary packages in your Sveltekit project directory. You can choose one of the following package managers:
+
+
+
+```bash npm
+npm i @trigger.dev/sdk @trigger.dev/sveltekit
+```
+
+```bash pnpm
+pnpm install @trigger.dev/sdk @trigger.dev/sveltekit
+```
+
+```bash yarn
+yarn add @trigger.dev/sdk @trigger.dev/sveltekit
+```
+
+
+
+
+Ensure that you execute this command within a SvelteKit project.
+## Obtaining the Development API Key
+
+To locate your development API key, login to the [Trigger.dev
+dashboard](https://cloud.trigger.dev) and select the Project you want to
+connect to. Then click on the Environments & API Keys tab in the left menu.
+You can copy your development API Key from the field at the top of this page.
+(Your development key will start with `tr_dev_`).
+
+## Adding Environment Variables
+
+Create a `.env` file at the root of your project and include your Trigger API key and URL like this:
+
+```bash
+TRIGGER_API_KEY=ENTER_YOUR_DEVELOPMENT_API_KEY_HERE
+TRIGGER_API_URL=https://api.trigger.dev # this is only necessary if you are self-hosting
+```
+
+Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step.
+
+## Syncing Environment Variable types (TypeScript)
+
+You will have type errors for your environment variables unless you run this command:
+
+```sh
+npx svelte-kit sync
+```
+
+## Configuring the Trigger Client
+
+Create a file at `/src/trigger.ts` or `/trigger.ts` depending on whether you're using the `src` directory or not. `` represents the root directory of your project.
+
+Next, add the following code to the file which creates and exports a new `TriggerClient`:
+
+```typescript src/trigger.(ts/js)
+// trigger.ts (for TypeScript) or trigger.js (for JavaScript)
+
+import { TriggerClient } from "@trigger.dev/sdk";
+import { TRIGGER_API_KEY, TRIGGER_API_URL } from "$env/static/private";
+
+export const client = new TriggerClient({
+ id: "my-app",
+ apiKey: TRIGGER_API_KEY,
+ apiUrl: TRIGGER_API_URL,
+});
+```
+
+Replace **"my-app"** with an appropriate identifier for your project.
+
+## Creating the API Route
+
+To establish an API route for interacting with Trigger.dev, follow these steps based on your project's file type and structure
+
+Create a new file named `+server.(ts/js)` within the `src/routes/api/trigger` directory, and add the following code:
+
+```typescript
+import { createSvelteRoute } from "@trigger.dev/sveltekit";
+import { client } from "../../../trigger";
+
+//import all jobs
+import "../../../jobs";
+
+// Create the Svelte route handler using the createSvelteRoute function
+const svelteRoute = createSvelteRoute(client);
+
+// Define your API route handler
+export const POST = svelteRoute.POST;
+```
+
+## Creating the Example Job
+
+1. Create a folder named `jobs` alongside your `src` directory
+2. Inside the `jobs` folder, add two files named `example.(ts/js)` and `index.(ts/js)`.
+
+
+
+```typescript src/jobs/example.(ts/js)
+import { eventTrigger } from "@trigger.dev/sdk";
+import { client } from "../trigger";
+
+// your first job
+client.defineJob({
+ id: "example-job",
+ name: "Example Job",
+ version: "0.0.1",
+ trigger: eventTrigger({
+ name: "example.event",
+ }),
+ run: async (payload, io, ctx) => {
+ await io.logger.info("Hello world!", { payload });
+
+ return {
+ message: "Hello world!",
+ };
+ },
+});
+```
+
+```typescript src/jobs/index.(ts/js)
+// export all your job files here
+export * from "./example";
+```
+
+
+
+## Additonal Job Definitions
+
+You can define more job definitions by creating additional files in the `jobs` folder and exporting them in the `src/jobs/index` file.
+
+For example, in `index.(ts/js)`, you can export other job files like this:
+
+```typescript
+// export all your job files here
+export * from "./example";
+export * from "./other-job-file";
+```
+
+## Adding Configuration to `package.json`
+
+Inside the `package.json` file, add the following configuration under the root object:
+
+```json
+"trigger.dev": {
+ "endpointId": "my-app"
+}
+```
+
+Your `package.json` file might look something like this:
+
+```json
+{
+ "name": "my-app",
+ "version": "1.0.0",
+ "dependencies": {
+ // ... other dependencies
+ },
+ "trigger.dev": {
+ "endpointId": "my-app"
+ }
+}
+```
+
+Replace **"my-app"** with the appropriate identifier you used during the step for creating the Trigger Client.
+
+## Running
+
+### Run your Sveltekit app
+
+Run your Sveltekit app locally. You need to use the `--host` flag to allow the Trigger.dev CLI to connect to your app.
+
+For example:
+
+
+
+```bash npm
+npm run dev -- --open --host
+```
+
+```bash pnpm
+pnpm run dev -- --open --host
+```
+
+```bash yarn
+yarn run dev -- --open --host
+```
+
+
+
+### Run the CLI 'dev' command
+
+In a **_separate terminal window or tab_** run:
+
+
+
+```bash npm
+npx @trigger.dev/cli@latest dev --port 5173
+```
+
+```bash pnpm
+pnpm dlx @trigger.dev/cli@latest dev --port 5173
+```
+
+```bash yarn
+yarn dlx @trigger.dev/cli@latest dev --port 5173
+```
+
+
+
+
+ You can optionally pass the port if you're not running on 3000 by adding
+ `--port 5173` to the end
+
+
+ You can optionally pass the hostname if you're not running on localhost by adding
+ `--hostname `. Example, in case your Sveltekit app is running on 0.0.0.0: `--hostname 0.0.0.0`.
+
diff --git a/docs/documentation/concepts/client-adaptors.mdx b/docs/documentation/concepts/client-adaptors.mdx
index 98fc4dc56..7cd7c3a90 100644
--- a/docs/documentation/concepts/client-adaptors.mdx
+++ b/docs/documentation/concepts/client-adaptors.mdx
@@ -24,10 +24,12 @@ Adaptors allows Clients to receive data from the Trigger API. They do this by cr
Each platform has one or more adaptors, see the guides below:
-| Platform | Adaptor |
-| ------------------------------------------------- | -------------------- |
-| [Next.js](/documentation/guides/platforms/nextjs) | `createPagesRoute()` |
-| [Next.js](/documentation/guides/platforms/nextjs) | `createAppRoute()` |
-| [Astro](/documentation/guides/platforms/astro) | `createAstroRoute()` |
-| [Remix](/documentation/guides/platforms/remix) | `createRemixRoute()` |
-| Express | Coming soon |
+| Platform | Adaptor |
+| ------------------------------------------------------ | --------------------- |
+| [Next.js](/documentation/guides/platforms/nextjs) | `createPagesRoute()` |
+| [Next.js](/documentation/guides/platforms/nextjs) | `createAppRoute()` |
+| [NestJS](/documentation/guides/manual/nestjs) | `TriggerDevModule` |
+| [Astro](/documentation/guides/platforms/astro) | `createAstroRoute()` |
+| [Remix](/documentation/guides/platforms/remix) | `createRemixRoute()` |
+| [Sveltekit](/documentation/guides/platforms/sveltekit) | `createSvelteRoute()` |
+| Express | Coming soon |
diff --git a/docs/documentation/concepts/triggers/introduction.mdx b/docs/documentation/concepts/triggers/introduction.mdx
index c626e7852..c4a56cee3 100644
--- a/docs/documentation/concepts/triggers/introduction.mdx
+++ b/docs/documentation/concepts/triggers/introduction.mdx
@@ -1,30 +1,19 @@
---
-title: Introduction
+title: "Triggers: Introduction"
+sidebarTitle: "Introduction"
description: "A Trigger is what starts a Job Run. It can be a webhook, a schedule, or an event."
---
We currently support three types of Triggers: Webhooks, Scheduled, and Events. You can use any of these to start a Job Run.
-
+
Start your Jobs in realtime when events happen in APIs
-
+
Run a Job on a repeating schedule
-
+
Run your Job when you send events with data
+
This only needs to be done once for each environment
@@ -32,11 +29,7 @@ There are two ways to do this:
>
Manually refresh in your Trigger.dev dashboard
-
+
Automatically refresh by using our webhook
diff --git a/docs/documentation/guides/manual/nestjs.mdx b/docs/documentation/guides/manual/nestjs.mdx
new file mode 100644
index 000000000..e29c1ae27
--- /dev/null
+++ b/docs/documentation/guides/manual/nestjs.mdx
@@ -0,0 +1,7 @@
+---
+title: "NestJS"
+sidebarTitle: "NestJS"
+description: "How to manually setup Trigger.dev in your NestJS project"
+---
+
+
diff --git a/docs/documentation/guides/react-hooks.mdx b/docs/documentation/guides/react-hooks.mdx
index 648225a15..0132daae7 100644
--- a/docs/documentation/guides/react-hooks.mdx
+++ b/docs/documentation/guides/react-hooks.mdx
@@ -1,5 +1,6 @@
---
-title: "Overview"
+title: "React hooks: Overview"
+sidebarTitle: "Overview"
description: "How to show the live status of Job Runs in your React app"
---
diff --git a/docs/documentation/guides/testing-jobs.mdx b/docs/documentation/guides/testing-jobs.mdx
index 04ce3bf51..d988ba20f 100644
--- a/docs/documentation/guides/testing-jobs.mdx
+++ b/docs/documentation/guides/testing-jobs.mdx
@@ -14,9 +14,32 @@ There's a tab on the Job page called **Test**. Or you can click the "Test" butto

-1. Select the environment you'd like the test to run against.
-2. Some Triggers provide example payloads that you can select from. This will populate the code editor below.
-3. When you're happy with the payload, click **Run test**.
+
+
+ You will see errors inline if you have any syntax errors. You can use the *Clear* and *Copy*
+ buttons in the corner.
+
+
+ Some Triggers provide example payloads that you can select from. When selected they will
+ populate the code editor below.
+
+
+ If you have previously done Runs, you can select from the most recent payloads. When selected
+ they will populate the code editor below.
+
+
+ If this Job has associated Accounts, enter an Account ID. See [testing with account
+ ids](/documentation/guides/using-integrations-byo-auth#testing-jobs-with-account-id) for more
+ information.
+
+
+ Select the environment you'd like the test to run against.
+
+
+ When you're happy with the payload, click **Run test**. Or press the shortcut key: `Cmd + Enter`
+ on Mac, `Ctrl + Enter` on Windows.
+
+
## Identifying test runs
diff --git a/docs/documentation/guides/using-integrations.mdx b/docs/documentation/guides/using-integrations.mdx
index b2a3dc8bf..45833102e 100644
--- a/docs/documentation/guides/using-integrations.mdx
+++ b/docs/documentation/guides/using-integrations.mdx
@@ -1,7 +1,7 @@
---
-title: "Integrations Overview"
-description: "How to use Trigger.dev Integrations"
+title: "Integrations: Overview"
sidebarTitle: "Overview"
+description: "How to use Trigger.dev Integrations"
---
diff --git a/docs/documentation/introduction.mdx b/docs/documentation/introduction.mdx
index 69527d8ce..a9f68d1bf 100644
--- a/docs/documentation/introduction.mdx
+++ b/docs/documentation/introduction.mdx
@@ -1,5 +1,6 @@
---
-title: Introduction
+title: "Getting Started: Introduction"
+sidebarTitle: "Introduction"
description: "Welcome to the Trigger.dev documentation."
---
diff --git a/docs/documentation/quickstarts/introduction.mdx b/docs/documentation/quickstarts/introduction.mdx
index 48891d398..e5e31232f 100644
--- a/docs/documentation/quickstarts/introduction.mdx
+++ b/docs/documentation/quickstarts/introduction.mdx
@@ -1,5 +1,5 @@
---
-title: "Introduction"
+title: "Quick Starts: Introduction"
sidebarTitle: "Introduction"
---
@@ -8,261 +8,19 @@ sidebarTitle: "Introduction"
## Select a framework to get startedβ¦
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-}
- href="/documentation/quickstarts/nextjs"
-
->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/remix">
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/express">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/redwood">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/astro">
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/nuxt">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/sveltkit">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-} href="/documentation/quickstarts/fastify"/>
-
+
+
+
+
+
+
+
+
+
## Or quickly setup Trigger.dev withβ¦
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- href="/documentation/quickstarts/supabase"
-
->
-
+
diff --git a/docs/documentation/quickstarts/nestjs.mdx b/docs/documentation/quickstarts/nestjs.mdx
new file mode 100644
index 000000000..46bc1b03b
--- /dev/null
+++ b/docs/documentation/quickstarts/nestjs.mdx
@@ -0,0 +1,7 @@
+---
+title: "NestJS Quick Start"
+sidebarTitle: "NestJS"
+description: "Start creating Jobs in 5 minutes in your NestJS project."
+---
+
+
diff --git a/docs/images/test-annotated.png b/docs/images/test-annotated.png
index a224e3fe4..d34b50d33 100644
Binary files a/docs/images/test-annotated.png and b/docs/images/test-annotated.png differ
diff --git a/docs/integrations/apis/github-tasks.mdx b/docs/integrations/apis/github-tasks.mdx
index 410dc1170..2dbc2e33d 100644
--- a/docs/integrations/apis/github-tasks.mdx
+++ b/docs/integrations/apis/github-tasks.mdx
@@ -1,54 +1,263 @@
---
-title: Tasks
+title: GitHub Tasks
+sidebarTitle: Tasks
+---
+
+Tasks are executed after the job is triggered and are the main building blocks of a job. You can string together as many tasks as you want.
+
---
## All tasks
-| Function Name | Description |
-| -------------------------------- | ----------------------------------------------------------- |
-| `createIssue` | Creates a new issue in a repository. |
-| `addIssueAssignees` | Adds assignees to an existing issue. |
-| `addIssueLabels` | Adds labels to an existing issue. |
-| `createIssueComment` | Creates a new comment on an existing issue. |
-| `getRepo` | Retrieves information about a repository. |
-| `createIssueCommentWithReaction` | Creates a new comment on an existing issue with a reaction. |
-| `addIssueCommentReaction` | Adds a reaction to an existing issue comment. |
-| `updateWebhook` | Updates an existing webhook. |
-| `createWebhook` | Creates a new webhook. |
-| `listWebhooks` | Lists the webhooks for a repository. |
-| `updateOrgWebhook` | Updates an existing webhook for an organization. |
-| `createOrgWebhook` | Creates a new webhook for an organization. |
-| `listOrgWebhooks` | Lists the webhooks for an organization. |
+### `createIssue`
-## Usage
+Creates a new issue in a repository. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/issues?apiVersion=2022-11-28#create-an-issue).
+
+```ts example.ts
+await io.github.createIssue("create issue", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ title: "", // the title of the issue
+ body: "", // the contents of the issue
+});
+```
+
+### `addIssueAssignees`
+
+Adds assignees to an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/assignees?apiVersion=2022-11-28#add-assignees-to-an-issue).
+
+```ts example.ts
+await io.github.addIssueAssignees("add assignee", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ issueNumber: , // the number of the issue
+ assignees: [""], // the name(s) of the assignee(s)
+});
+```
+
+### `addIssueLabels`
+
+Adds labels to an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/labels?apiVersion=2022-11-28#add-labels-to-an-issue).
+
+```ts example.ts
+await io.github.addIssueLabels("add label", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ issueNumber: , // the number of the issue
+ labels: [""], // the name(s) of the label(s)
+});
+```
+
+### `createIssueComment`
+
+Creates a new comment on an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment).
+
+```ts example.ts
+await io.github.createIssueComment("create comment", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ issueNumber: , // the number of the issue
+ body: "", // the contents of the comment
+});
+```
+
+### `getRepo`
+
+Retrieves information about a repository. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/repos/repos?apiVersion=2022-11-28#get-a-repository).
+
+```ts example.ts
+const repoInfo = await io.github.getRepo({
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+});
+```
+
+### `createIssueCommentWithReaction`
+
+Creates a new comment on an existing issue with a reaction. [Official GitHub docs](https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment).
+
+```ts example.ts
+await io.github.createIssueCommentWithReaction("create comment with reaction", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ issueNumber: , // the number of the issue
+ body: "", // the contents of the comment
+ content: "", // the type of reaction
+});
+```
+
+### `addIssueCommentReaction`
+
+Adds a reaction to an existing issue comment. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue-comment).
+
+```ts example.ts
+await io.github.addIssueCommentReaction("add reaction", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ commentId: , // the id of the specific comment
+ content: "", // the type of reaction
+});
+```
+
+### `updateWebhook`
+
+Updates an existing webhook. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#update-a-repository-webhook).
+
+```ts example.ts
+await io.github.updateWebhook("update webhook", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ webhookId: , // the unique id of the webhook
+ config: {
+ url: "", // the url to which payloads will be delivered
+ contentType: "json", // the media type used to serialize the payloads
+ secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers.
+ },
+});
+```
+
+### `createWebhook`
+
+Creates a new webhook. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#create-a-repository-webhook).
+
+```ts example.ts
+await io.github.createWebhook("create webhook", {
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+ config: {
+ url: "", // the url to which payloads will be delivered
+ contentType: "json", // the media type used to serialize the payloads
+ secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers.
+ },
+ events: [""], // the events for which the webhook will trigger
+});
+```
+
+### `listWebhooks`
+
+Lists the webhooks for a repository. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#list-repository-webhooks).
+
+```ts example.ts
+const webhooks = await io.github.listWebhooks({
+ owner: "", // the name of the owner of the repository
+ repo: "", // the name of the repository
+});
+```
+
+### `updateOrgWebhook`
+
+Updates an existing webhook for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#update-an-organization-webhook).
+
+```ts
+await io.github.updateOrgWebhook("update org webhook", {
+ org: "", // the name of the organization
+ webhookId: , // the unique id of the webhook
+ config: {
+ url: "", // the url to which payloads will be delivered
+ contentType: "json", // the media type used to serialize the payloads
+ secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers.
+ },
+});
+```
+
+### `createOrgWebhook`
+
+Creates a new webhook for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#create-an-organization-webhook).
+
+```ts example.ts
+await io.github.createOrgWebhook("create org webhook", {
+ org: "", // the name of the organization
+ config: {
+ url: "", // the url to which payloads will be delivered
+ contentType: "json", // the media type used to serialize the payloads
+ secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers.
+ },
+ events: [""], // the events for which the webhook will trigger
+});
+```
+
+### `listOrgWebhooks`
+
+Lists the webhooks for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#list-organization-webhooks).
+
+```ts example.ts
+const orgWebhooks = await io.github.listOrgWebhooks({
+ org: "", // the name of the organization
+ per-page: , // the number of webhooks to return per page (max 100)
+ page: , // Page number of the results to fetch.
+});
+```
+
+## Example usage
+
+In this example we'll create a task that adds an assignee and a label to an issue when it's opened.
```ts
client.defineJob({
id: "github-integration-on-issue-opened",
name: "GitHub Integration - On Issue Opened",
- version: "0.1.0",
+ version: "1.0.0",
integrations: { github },
trigger: github.triggers.repo({
event: events.onIssueOpened,
- owner: "triggerdotdev",
- repo: "empty",
+ owner: "",
+ repo: "",
}),
run: async (payload, io, ctx) => {
await io.github.addIssueAssignees("add assignee", {
owner: payload.repository.owner.login,
repo: payload.repository.name,
issueNumber: payload.issue.number,
- assignees: ["matt-aitken"],
+ assignees: [""],
});
await io.github.addIssueLabels("add label", {
owner: payload.repository.owner.login,
repo: payload.repository.name,
issueNumber: payload.issue.number,
- labels: ["bug"],
+ labels: [""],
});
return { payload, ctx };
},
});
```
+
+## Using the underlying GitHub client
+
+You can access the [Octokit instance](https://github.com/octokit/octokit.js#octokit-api-client) by using the `runTask` method on the integration:
+
+```ts
+const github = new Github({
+ id: "github",
+});
+
+client.defineJob({
+ id: "github-example-1",
+ name: "GitHub Example 1",
+ version: "0.1.0",
+ trigger: eventTrigger({
+ name: "github.example",
+ }),
+ integrations: {
+ github,
+ },
+ run: async (payload, io, ctx) => {
+ const contributors = await io.github.runTask(
+ "get-contributors",
+ async (octokit, task) => {
+ const contributors = await octokit.rest.repos.listContributors({
+ owner: "",
+ repo: "",
+ });
+
+ return contributors;
+ },
+ //this is optional, it will appear on the Run page
+ { name: "List Contributors" }
+ );
+ },
+});
+```
+
+Make sure to pass the `idempotencyKey` to the underlying client to ensure that the API call is only executed once. This is only needed for mutating API calls.
diff --git a/docs/integrations/apis/github-triggers.mdx b/docs/integrations/apis/github-triggers.mdx
index d66e62478..3b21ee59e 100644
--- a/docs/integrations/apis/github-triggers.mdx
+++ b/docs/integrations/apis/github-triggers.mdx
@@ -1,46 +1,3053 @@
---
-title: Triggers
+title: GitHub Triggers & Events
+sidebarTitle: Triggers & Events
---
-## All triggers
+You can use these triggers to start a job when a GitHub event occurs.
-| Function Name | Description |
-| --------------------- | -------------------------------------------------------------------------------- |
-| `onIssue` | When any action is performed on an issue. |
-| `onIssueOpened` | When an issue is opened. |
-| `onIssueAssigned` | When an issue is assigned. |
-| `onIssueComment` | When an issue is commented on. |
-| `onStar` | When a repo is starred or unstarred. |
-| `onNewStar` | When a repo is starred. |
-| `onNewRepository` | When a new repo is created. |
-| `onNewBranchOrTag` | When a new branch or tag is created. |
-| `onNewBranch` | When a new branch is created. |
-| `onPush` | When a push is made to a repo. |
-| `onPullRequest` | When activity occurs on a pull request (excluding reviews, issues, or comments). |
-| `onPullRequestReview` | When a pull request review has activity. |
+---
-## Usage
+### Repo
+
+Repo triggers subscribe to a change in a GitHub repo.
```ts
-import { Github, events } from "@trigger.dev/github";
-
-const github = new Github({
- id: "github",
- token: process.env.GITHUB_TOKEN!,
+github.triggers.repo({
+ event: events.onIssueOpened,
+ owner: "triggerdotdev",
+ repo: "trigger.dev",
});
+```
+
+
+
+ The event to trigger the job on.
+
+
+ The owner of the repo.
+
+
+ The name of the repo.
+
+
+
+
+### Org
+
+Org triggers subscribe to a change across an entire GitHub org.
+
+```ts
+github.triggers.repo({
+ event: events.onIssueOpened,
+ owner: "triggerdotdev",
+});
+```
+
+})
+
+
+
+
+ The event to trigger the job on.
+
+
+ The owner of the repo.
+
+
+
+
+## Events
+
+### `onIssue`
+
+When any action is performed on an issue. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues).
+
+```ts usage.ts
client.defineJob({
- id: "github-integration-on-issue",
- name: "GitHub Integration - On Issue",
+ id: "",
+ name: "",
version: "0.1.0",
trigger: github.triggers.repo({
event: events.onIssue,
- owner: "triggerdotdev",
- repo: "empty",
+ owner: "",
+ repo: "",
}),
run: async (payload, io, ctx) => {
- await io.logger.info("This is a simple log info message");
- //do stuff
+ // Add tasks here
},
});
```
+
+
+````json
+{
+ "issue": {
+ "id": 1754473379,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21",
+ "body": "This is a *big* problem:\r\n\r\n```\r\nconst foo = \"bar\"\r\n```",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "state": "open",
+ "title": "This is a sample issue title #20",
+ "labels": [],
+ "locked": false,
+ "number": 21,
+ "node_id": "I_kwDOI-yZFc5okyOj",
+ "assignee": null,
+ "comments": 0,
+ "html_url": "https://github.com/ericallam/basic-starter-12k/issues/21",
+ "assignees": [],
+ "closed_at": null,
+ "milestone": null,
+ "reactions": {
+ "+1": 0,
+ "-1": 0,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/reactions",
+ "eyes": 0,
+ "heart": 0,
+ "laugh": 0,
+ "hooray": 0,
+ "rocket": 0,
+ "confused": 0,
+ "total_count": 0
+ },
+ "created_at": "2023-06-13T09:42:02Z",
+ "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/events",
+ "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/labels{/name}",
+ "updated_at": "2023-06-13T09:42:02Z",
+ "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/comments",
+ "state_reason": null,
+ "timeline_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/timeline",
+ "repository_url": "https://api.github.com/repos/ericallam/basic-starter-12k",
+ "active_lock_reason": null,
+ "author_association": "NONE",
+ "performed_via_github_app": null
+ },
+ "action": "opened",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 602708245,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k",
+ "fork": false,
+ "name": "basic-starter-12k",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 534,
+ "url": "https://api.github.com/users/ericallam",
+ "type": "User",
+ "login": "ericallam",
+ "node_id": "MDQ6VXNlcjUzNA==",
+ "html_url": "https://github.com/ericallam",
+ "gists_url": "https://api.github.com/users/ericallam/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/ericallam/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/534?v=4",
+ "events_url": "https://api.github.com/users/ericallam/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/ericallam/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/ericallam/followers",
+ "following_url": "https://api.github.com/users/ericallam/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/ericallam/orgs",
+ "subscriptions_url": "https://api.github.com/users/ericallam/subscriptions",
+ "received_events_url": "https://api.github.com/users/ericallam/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/ericallam/basic-starter-12k.git",
+ "license": null,
+ "node_id": "R_kgDOI-yZFQ",
+ "private": false,
+ "ssh_url": "git@github.com:ericallam/basic-starter-12k.git",
+ "svn_url": "https://github.com/ericallam/basic-starter-12k",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/ericallam/basic-starter-12k",
+ "keys_url": "https://api.github.com/repos/ericallam/basic-starter-12k/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/blobs{/sha}",
+ "clone_url": "https://github.com/ericallam/basic-starter-12k.git",
+ "forks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/forks",
+ "full_name": "ericallam/basic-starter-12k",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/hooks",
+ "pulls_url": "https://api.github.com/repos/ericallam/basic-starter-12k/pulls{/number}",
+ "pushed_at": "2023-02-16T19:25:20Z",
+ "teams_url": "https://api.github.com/repos/ericallam/basic-starter-12k/teams",
+ "trees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/trees{/sha}",
+ "created_at": "2023-02-16T19:25:19Z",
+ "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues{/number}",
+ "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/labels{/name}",
+ "merges_url": "https://api.github.com/repos/ericallam/basic-starter-12k/merges",
+ "mirror_url": null,
+ "updated_at": "2023-02-16T19:25:19Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/ericallam/basic-starter-12k/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/ericallam/basic-starter-12k/compare/{base}...{head}",
+ "description": null,
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 21,
+ "branches_url": "https://api.github.com/repos/ericallam/basic-starter-12k/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/comments{/number}",
+ "contents_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/ericallam/basic-starter-12k/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/ericallam/basic-starter-12k/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/ericallam/basic-starter-12k/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/ericallam/basic-starter-12k/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/ericallam/basic-starter-12k/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/deployments",
+ "git_commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscribers",
+ "contributors_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contributors",
+ "issue_events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscription",
+ "collaborators_url": "https://api.github.com/repos/ericallam/basic-starter-12k/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/ericallam/basic-starter-12k/notifications{?since,all,participating}",
+ "open_issues_count": 21,
+ "web_commit_signoff_required": false
+ }
+}
+````
+
+
+### `onIssueOpened`
+
+Occurs when an issue is opened. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.org({
+ event: events.onIssueOpened,
+ owner: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+````json
+{
+ "issue": {
+ "id": 1754473379,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21",
+ "body": "This is a *big* problem:\r\n\r\n```\r\nconst foo = \"bar\"\r\n```",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "state": "open",
+ "title": "This is a sample issue title #20",
+ "labels": [],
+ "locked": false,
+ "number": 21,
+ "node_id": "I_kwDOI-yZFc5okyOj",
+ "assignee": null,
+ "comments": 0,
+ "html_url": "https://github.com/ericallam/basic-starter-12k/issues/21",
+ "assignees": [],
+ "closed_at": null,
+ "milestone": null,
+ "reactions": {
+ "+1": 0,
+ "-1": 0,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/reactions",
+ "eyes": 0,
+ "heart": 0,
+ "laugh": 0,
+ "hooray": 0,
+ "rocket": 0,
+ "confused": 0,
+ "total_count": 0
+ },
+ "created_at": "2023-06-13T09:42:02Z",
+ "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/events",
+ "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/labels{/name}",
+ "updated_at": "2023-06-13T09:42:02Z",
+ "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/comments",
+ "state_reason": null,
+ "timeline_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/timeline",
+ "repository_url": "https://api.github.com/repos/ericallam/basic-starter-12k",
+ "active_lock_reason": null,
+ "author_association": "NONE",
+ "performed_via_github_app": null
+ },
+ "action": "opened",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 602708245,
+ "url": "https://api.github.com/repos/ericallam/basic-starter-12k",
+ "fork": false,
+ "name": "basic-starter-12k",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 534,
+ "url": "https://api.github.com/users/ericallam",
+ "type": "User",
+ "login": "ericallam",
+ "node_id": "MDQ6VXNlcjUzNA==",
+ "html_url": "https://github.com/ericallam",
+ "gists_url": "https://api.github.com/users/ericallam/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/ericallam/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/534?v=4",
+ "events_url": "https://api.github.com/users/ericallam/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/ericallam/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/ericallam/followers",
+ "following_url": "https://api.github.com/users/ericallam/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/ericallam/orgs",
+ "subscriptions_url": "https://api.github.com/users/ericallam/subscriptions",
+ "received_events_url": "https://api.github.com/users/ericallam/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/ericallam/basic-starter-12k.git",
+ "license": null,
+ "node_id": "R_kgDOI-yZFQ",
+ "private": false,
+ "ssh_url": "git@github.com:ericallam/basic-starter-12k.git",
+ "svn_url": "https://github.com/ericallam/basic-starter-12k",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/ericallam/basic-starter-12k",
+ "keys_url": "https://api.github.com/repos/ericallam/basic-starter-12k/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/blobs{/sha}",
+ "clone_url": "https://github.com/ericallam/basic-starter-12k.git",
+ "forks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/forks",
+ "full_name": "ericallam/basic-starter-12k",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/hooks",
+ "pulls_url": "https://api.github.com/repos/ericallam/basic-starter-12k/pulls{/number}",
+ "pushed_at": "2023-02-16T19:25:20Z",
+ "teams_url": "https://api.github.com/repos/ericallam/basic-starter-12k/teams",
+ "trees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/trees{/sha}",
+ "created_at": "2023-02-16T19:25:19Z",
+ "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues{/number}",
+ "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/labels{/name}",
+ "merges_url": "https://api.github.com/repos/ericallam/basic-starter-12k/merges",
+ "mirror_url": null,
+ "updated_at": "2023-02-16T19:25:19Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/ericallam/basic-starter-12k/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/ericallam/basic-starter-12k/compare/{base}...{head}",
+ "description": null,
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 21,
+ "branches_url": "https://api.github.com/repos/ericallam/basic-starter-12k/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/comments{/number}",
+ "contents_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/ericallam/basic-starter-12k/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/ericallam/basic-starter-12k/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/ericallam/basic-starter-12k/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/ericallam/basic-starter-12k/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/ericallam/basic-starter-12k/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/deployments",
+ "git_commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscribers",
+ "contributors_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contributors",
+ "issue_events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscription",
+ "collaborators_url": "https://api.github.com/repos/ericallam/basic-starter-12k/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/ericallam/basic-starter-12k/notifications{?since,all,participating}",
+ "open_issues_count": 21,
+ "web_commit_signoff_required": false
+ }
+}
+````
+
+
+### `onIssueAssigned`
+
+When an issue is assigned. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onAssigned,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "id": "issue.assigned",
+ "name": "Issue assigned",
+ "payload": {
+ "issue": {
+ "id": 1767861922,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4",
+ "body": "This is the bod",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "state": "open",
+ "title": "Fourth time lucky?",
+ "labels": [],
+ "locked": false,
+ "number": 4,
+ "node_id": "I_kwDOJyTwbc5pX26i",
+ "assignee": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM5OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "comments": 1,
+ "html_url": "https://github.com/triggerdotdev/empty/issues/4",
+ "assignees": [
+ {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ }
+ ],
+ "closed_at": null,
+ "milestone": null,
+ "reactions": {
+ "+1": 0,
+ "-1": 0,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/reactions",
+ "eyes": 0,
+ "heart": 0,
+ "laugh": 0,
+ "hooray": 0,
+ "rocket": 0,
+ "confused": 0,
+ "total_count": 0
+ },
+ "created_at": "2023-06-21T15:24:20Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/events",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/labels{/name}",
+ "updated_at": "2023-06-21T15:36:53Z",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/comments",
+ "state_reason": null,
+ "timeline_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/timeline",
+ "repository_url": "https://api.github.com/repos/triggerdotdev/empty",
+ "active_lock_reason": null,
+ "author_association": "MEMBER",
+ "performed_via_github_app": null
+ },
+ "action": "assigned",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "assignee": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM5OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T14:21:08Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T14:21:08Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ }
+ }
+}
+```
+
+
+### `onIssueComment`
+
+When an issue is commented on. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onIssueComment,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "payload": {
+ "issue": {
+ "id": 1767861922,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4",
+ "body": "This is the bod",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "state": "open",
+ "title": "Fourth time lucky?",
+ "labels": [],
+ "locked": false,
+ "number": 4,
+ "node_id": "I_kwDOJyTwbc5pX26i",
+ "assignee": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM5OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "comments": 2,
+ "html_url": "https://github.com/triggerdotdev/empty/issues/4",
+ "assignees": [
+ {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ }
+ ],
+ "closed_at": null,
+ "milestone": null,
+ "reactions": {
+ "+1": 0,
+ "-1": 0,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/reactions",
+ "eyes": 0,
+ "heart": 0,
+ "laugh": 0,
+ "hooray": 0,
+ "rocket": 0,
+ "confused": 0,
+ "total_count": 0
+ },
+ "created_at": "2023-06-21T15:24:20Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/events",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/labels{/name}",
+ "updated_at": "2023-06-21T16:02:28Z",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/comments",
+ "state_reason": null,
+ "timeline_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/timeline",
+ "repository_url": "https://api.github.com/repos/triggerdotdev/empty",
+ "active_lock_reason": null,
+ "author_association": "MEMBER",
+ "performed_via_github_app": null
+ },
+ "action": "created",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "comment": {
+ "id": 1601114728,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments/1601114728",
+ "body": "This is a short comment with short code snippet:\r\n\r\n```\r\nconst rick = \"astley\";\r\n```",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "node_id": "IC_kwDOJyTwbc5fbxJo",
+ "html_url": "https://github.com/triggerdotdev/empty/issues/4#issuecomment-1601114728",
+ "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4",
+ "reactions": {
+ "+1": 0,
+ "-1": 0,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments/1601114728/reactions",
+ "eyes": 0,
+ "heart": 0,
+ "laugh": 0,
+ "hooray": 0,
+ "rocket": 0,
+ "confused": 0,
+ "total_count": 0
+ },
+ "created_at": "2023-06-21T16:02:27Z",
+ "updated_at": "2023-06-21T16:02:27Z",
+ "author_association": "MEMBER",
+ "performed_via_github_app": null
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T14:21:08Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T14:21:08Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ }
+ }
+}
+```
+
+
+### `onStar`
+
+When a repo is starred or unstarred. [Official GitHub docs](https://docs.github.com/en/rest/activity/starring?apiVersion=2022-11-28#list-stargazers).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onStar,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "payload": {
+ "action": "created",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 1,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T14:21:08Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:22:03Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 1,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 1,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "starred_at": "2023-06-21T17:22:03Z",
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ }
+ }
+}
+```
+
+
+### `onNewStar`
+
+When a repo is starred. [Official GitHub docs](https://docs.github.com/en/rest/activity/starring?apiVersion=2022-11-28#list-stargazers).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onNewStar,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "payload": {
+ "action": "created",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 1,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T14:21:08Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:22:03Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 1,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 1,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "starred_at": "2023-06-21T17:22:03Z",
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ }
+ }
+}
+```
+
+
+### `onNewRepository`
+
+When a new repo is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onNewRepository,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+### `onNewBranchOrTag`
+
+When a new branch or tag is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onNewBranchOrTag,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "ref": "test",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "ref_type": "branch",
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T17:54:25Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "pusher_type": "user",
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ },
+ "master_branch": "main"
+}
+```
+
+
+### `onNewBranch`
+
+When a new branch is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onNewBranch,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "ref": "test",
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "ref_type": "branch",
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T17:54:25Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "pusher_type": "user",
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ },
+ "master_branch": "main"
+}
+```
+
+
+### `onPush`
+
+When a push is made to a repo. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onPush,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "ref": "refs/heads/main",
+ "after": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "before": "7f0b6655803858ac09ae05354a679d6dad03120c",
+ "forced": false,
+ "pusher": {
+ "name": "matt-aitken",
+ "email": "matt@mattaitken.com"
+ },
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "commits": [
+ {
+ "id": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "url": "https://github.com/triggerdotdev/empty/commit/1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "added": [],
+ "author": {
+ "name": "Matt Aitken",
+ "email": "matt@mattaitken.com",
+ "username": "matt-aitken"
+ },
+ "message": "Updated the readme",
+ "removed": [],
+ "tree_id": "3a584b2cae2fe34a195e1fe437cc62031ddff447",
+ "distinct": true,
+ "modified": ["README.md"],
+ "committer": {
+ "name": "Matt Aitken",
+ "email": "matt@mattaitken.com",
+ "username": "matt-aitken"
+ },
+ "timestamp": "2023-06-21T19:27:16+01:00"
+ }
+ ],
+ "compare": "https://github.com/triggerdotdev/empty/compare/7f0b66558038...1ca6a91f4a13",
+ "created": false,
+ "deleted": false,
+ "base_ref": null,
+ "repository": {
+ "id": 656732269,
+ "url": "https://github.com/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "name": "triggerdotdev",
+ "type": "Organization",
+ "email": "hello@trigger.dev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": 1687372039,
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": 1687357267,
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "stargazers": 0,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 4,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "organization": "triggerdotdev",
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "master_branch": "main",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 4,
+ "web_commit_signoff_required": false
+ },
+ "head_commit": {
+ "id": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "url": "https://github.com/triggerdotdev/empty/commit/1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "added": [],
+ "author": {
+ "name": "Matt Aitken",
+ "email": "matt@mattaitken.com",
+ "username": "matt-aitken"
+ },
+ "message": "Updated the readme",
+ "removed": [],
+ "tree_id": "3a584b2cae2fe34a195e1fe437cc62031ddff447",
+ "distinct": true,
+ "modified": ["README.md"],
+ "committer": {
+ "name": "Matt Aitken",
+ "email": "matt@mattaitken.com",
+ "username": "matt-aitken"
+ },
+ "timestamp": "2023-06-21T19:27:16+01:00"
+ },
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ }
+}
+```
+
+
+### `onPullRequest`
+
+When activity occurs on a pull request (excluding reviews, issues, or comments). [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onPullRequest,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "action": "opened",
+ "number": 5,
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:38:34Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "web_commit_signoff_required": false
+ },
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ },
+ "pull_request": {
+ "id": 1402223044,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5",
+ "base": {
+ "ref": "main",
+ "sha": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "repo": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:38:34Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "allow_auto_merge": false,
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "allow_merge_commit": true,
+ "allow_rebase_merge": true,
+ "allow_squash_merge": true,
+ "merge_commit_title": "MERGE_MESSAGE",
+ "allow_update_branch": false,
+ "merge_commit_message": "PR_TITLE",
+ "delete_branch_on_merge": false,
+ "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+ "squash_merge_commit_message": "COMMIT_MESSAGES",
+ "web_commit_signoff_required": false,
+ "use_squash_pr_title_as_default": false
+ },
+ "user": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "label": "triggerdotdev:main"
+ },
+ "body": "A bit more added to the readme which could be useful",
+ "head": {
+ "ref": "test",
+ "sha": "073aa42afebca03c4b34361564e3976da5c65d48",
+ "repo": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:38:34Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "allow_auto_merge": false,
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "allow_merge_commit": true,
+ "allow_rebase_merge": true,
+ "allow_squash_merge": true,
+ "merge_commit_title": "MERGE_MESSAGE",
+ "allow_update_branch": false,
+ "merge_commit_message": "PR_TITLE",
+ "delete_branch_on_merge": false,
+ "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+ "squash_merge_commit_message": "COMMIT_MESSAGES",
+ "web_commit_signoff_required": false,
+ "use_squash_pr_title_as_default": false
+ },
+ "user": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "label": "triggerdotdev:test"
+ },
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "draft": false,
+ "state": "open",
+ "title": "Added more to the readme",
+ "_links": {
+ "html": {
+ "href": "https://github.com/triggerdotdev/empty/pull/5"
+ },
+ "self": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5"
+ },
+ "issue": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5"
+ },
+ "commits": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits"
+ },
+ "comments": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments"
+ },
+ "statuses": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48"
+ },
+ "review_comment": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}"
+ },
+ "review_comments": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments"
+ }
+ },
+ "labels": [],
+ "locked": false,
+ "merged": false,
+ "number": 5,
+ "commits": 1,
+ "node_id": "PR_kwDOJyTwbc5TlDnE",
+ "assignee": null,
+ "comments": 0,
+ "diff_url": "https://github.com/triggerdotdev/empty/pull/5.diff",
+ "html_url": "https://github.com/triggerdotdev/empty/pull/5",
+ "additions": 2,
+ "assignees": [],
+ "closed_at": null,
+ "deletions": 0,
+ "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5",
+ "mergeable": null,
+ "merged_at": null,
+ "merged_by": null,
+ "milestone": null,
+ "patch_url": "https://github.com/triggerdotdev/empty/pull/5.patch",
+ "auto_merge": null,
+ "created_at": "2023-06-21T18:39:13Z",
+ "rebaseable": null,
+ "updated_at": "2023-06-21T18:39:13Z",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48",
+ "changed_files": 1,
+ "mergeable_state": "unknown",
+ "requested_teams": [],
+ "review_comments": 0,
+ "merge_commit_sha": null,
+ "active_lock_reason": null,
+ "author_association": "MEMBER",
+ "review_comment_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}",
+ "requested_reviewers": [],
+ "review_comments_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments",
+ "maintainer_can_modify": false
+ }
+}
+```
+
+
+### `onPullRequestReview`
+
+When a pull request review has activity. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review).
+
+```ts usage.ts
+client.defineJob({
+ id: "",
+ name: "",
+ version: "0.1.0",
+ trigger: github.triggers.repo({
+ event: events.onPullRequestReview,
+ owner: "",
+ repo: "",
+ }),
+ run: async (payload, io, ctx) => {
+ // Add tasks here
+ },
+});
+```
+
+
+```json
+{
+ "action": "submitted",
+ "review": {
+ "id": 1491475123,
+ "body": "This needs some work still",
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "state": "commented",
+ "_links": {
+ "html": {
+ "href": "https://github.com/triggerdotdev/empty/pull/5#pullrequestreview-1491475123"
+ },
+ "pull_request": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5"
+ }
+ },
+ "node_id": "PRR_kwDOJyTwbc5Y5hqz",
+ "html_url": "https://github.com/triggerdotdev/empty/pull/5#pullrequestreview-1491475123",
+ "commit_id": "073aa42afebca03c4b34361564e3976da5c65d48",
+ "submitted_at": "2023-06-21T18:47:47Z",
+ "pull_request_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5",
+ "author_association": "MEMBER"
+ },
+ "sender": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "repository": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:39:13Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "web_commit_signoff_required": false
+ },
+ "organization": {
+ "id": 95297378,
+ "url": "https://api.github.com/orgs/triggerdotdev",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks",
+ "repos_url": "https://api.github.com/orgs/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/orgs/triggerdotdev/events",
+ "issues_url": "https://api.github.com/orgs/triggerdotdev/issues",
+ "description": "",
+ "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}",
+ "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}"
+ },
+ "pull_request": {
+ "id": 1402223044,
+ "url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5",
+ "base": {
+ "ref": "main",
+ "sha": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2",
+ "repo": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:39:13Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "allow_auto_merge": false,
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "allow_merge_commit": true,
+ "allow_rebase_merge": true,
+ "allow_squash_merge": true,
+ "merge_commit_title": "MERGE_MESSAGE",
+ "allow_update_branch": false,
+ "merge_commit_message": "PR_TITLE",
+ "delete_branch_on_merge": false,
+ "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+ "squash_merge_commit_message": "COMMIT_MESSAGES",
+ "web_commit_signoff_required": false,
+ "use_squash_pr_title_as_default": false
+ },
+ "user": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "label": "triggerdotdev:main"
+ },
+ "body": "A bit more added to the readme which could be useful",
+ "head": {
+ "ref": "test",
+ "sha": "073aa42afebca03c4b34361564e3976da5c65d48",
+ "repo": {
+ "id": 656732269,
+ "url": "https://api.github.com/repos/triggerdotdev/empty",
+ "fork": false,
+ "name": "empty",
+ "size": 0,
+ "forks": 0,
+ "owner": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "topics": [],
+ "git_url": "git://github.com/triggerdotdev/empty.git",
+ "license": null,
+ "node_id": "R_kgDOJyTwbQ",
+ "private": false,
+ "ssh_url": "git@github.com:triggerdotdev/empty.git",
+ "svn_url": "https://github.com/triggerdotdev/empty",
+ "archived": false,
+ "disabled": false,
+ "has_wiki": true,
+ "homepage": null,
+ "html_url": "https://github.com/triggerdotdev/empty",
+ "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}",
+ "language": null,
+ "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags",
+ "watchers": 0,
+ "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}",
+ "clone_url": "https://github.com/triggerdotdev/empty.git",
+ "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks",
+ "full_name": "triggerdotdev/empty",
+ "has_pages": false,
+ "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks",
+ "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}",
+ "pushed_at": "2023-06-21T18:39:13Z",
+ "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams",
+ "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}",
+ "created_at": "2023-06-21T14:21:07Z",
+ "events_url": "https://api.github.com/repos/triggerdotdev/empty/events",
+ "has_issues": true,
+ "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}",
+ "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}",
+ "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges",
+ "mirror_url": null,
+ "updated_at": "2023-06-21T17:23:26Z",
+ "visibility": "public",
+ "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}",
+ "description": "An empty repo that can be used to test the @trigger.dev/github integration",
+ "forks_count": 0,
+ "is_template": false,
+ "open_issues": 5,
+ "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}",
+ "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}",
+ "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}",
+ "has_projects": true,
+ "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}",
+ "allow_forking": true,
+ "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}",
+ "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads",
+ "has_downloads": true,
+ "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages",
+ "default_branch": "main",
+ "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}",
+ "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers",
+ "watchers_count": 0,
+ "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments",
+ "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}",
+ "has_discussions": false,
+ "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers",
+ "allow_auto_merge": false,
+ "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors",
+ "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}",
+ "stargazers_count": 0,
+ "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription",
+ "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}",
+ "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}",
+ "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}",
+ "open_issues_count": 5,
+ "allow_merge_commit": true,
+ "allow_rebase_merge": true,
+ "allow_squash_merge": true,
+ "merge_commit_title": "MERGE_MESSAGE",
+ "allow_update_branch": false,
+ "merge_commit_message": "PR_TITLE",
+ "delete_branch_on_merge": false,
+ "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+ "squash_merge_commit_message": "COMMIT_MESSAGES",
+ "web_commit_signoff_required": false,
+ "use_squash_pr_title_as_default": false
+ },
+ "user": {
+ "id": 95297378,
+ "url": "https://api.github.com/users/triggerdotdev",
+ "type": "Organization",
+ "login": "triggerdotdev",
+ "node_id": "O_kgDOBa4fYg",
+ "html_url": "https://github.com/triggerdotdev",
+ "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/triggerdotdev/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4",
+ "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/triggerdotdev/followers",
+ "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/triggerdotdev/orgs",
+ "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions",
+ "received_events_url": "https://api.github.com/users/triggerdotdev/received_events"
+ },
+ "label": "triggerdotdev:test"
+ },
+ "user": {
+ "id": 10635986,
+ "url": "https://api.github.com/users/matt-aitken",
+ "type": "User",
+ "login": "matt-aitken",
+ "node_id": "MDQ6VXNlcjEwNjM1OTg2",
+ "html_url": "https://github.com/matt-aitken",
+ "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}",
+ "repos_url": "https://api.github.com/users/matt-aitken/repos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4",
+ "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}",
+ "site_admin": false,
+ "gravatar_id": "",
+ "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}",
+ "followers_url": "https://api.github.com/users/matt-aitken/followers",
+ "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}",
+ "organizations_url": "https://api.github.com/users/matt-aitken/orgs",
+ "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions",
+ "received_events_url": "https://api.github.com/users/matt-aitken/received_events"
+ },
+ "draft": false,
+ "state": "open",
+ "title": "Added more to the readme",
+ "_links": {
+ "html": {
+ "href": "https://github.com/triggerdotdev/empty/pull/5"
+ },
+ "self": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5"
+ },
+ "issue": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5"
+ },
+ "commits": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits"
+ },
+ "comments": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments"
+ },
+ "statuses": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48"
+ },
+ "review_comment": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}"
+ },
+ "review_comments": {
+ "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments"
+ }
+ },
+ "labels": [],
+ "locked": false,
+ "number": 5,
+ "node_id": "PR_kwDOJyTwbc5TlDnE",
+ "assignee": null,
+ "diff_url": "https://github.com/triggerdotdev/empty/pull/5.diff",
+ "html_url": "https://github.com/triggerdotdev/empty/pull/5",
+ "assignees": [],
+ "closed_at": null,
+ "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5",
+ "merged_at": null,
+ "milestone": null,
+ "patch_url": "https://github.com/triggerdotdev/empty/pull/5.patch",
+ "auto_merge": null,
+ "created_at": "2023-06-21T18:39:13Z",
+ "updated_at": "2023-06-21T18:47:47Z",
+ "commits_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits",
+ "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments",
+ "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48",
+ "requested_teams": [],
+ "merge_commit_sha": "f146c57c260778db17300c47c366bce0e2dbd53d",
+ "active_lock_reason": null,
+ "author_association": "MEMBER",
+ "review_comment_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}",
+ "requested_reviewers": [],
+ "review_comments_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments"
+ }
+}
+```
+
diff --git a/docs/integrations/apis/github.mdx b/docs/integrations/apis/github.mdx
index c99e71dc0..4b00021be 100644
--- a/docs/integrations/apis/github.mdx
+++ b/docs/integrations/apis/github.mdx
@@ -1,10 +1,21 @@
---
-title: Introduction
+title: GitHub overview & authentication
+sidebarTitle: Overview & authentication
---
-
+## Overview
-## Installation
+Our GitHub integration allows you to create triggers and tasks that interact with GitHub. For examples of some of the things you can do with it, check out our Jobs Showcase:
+
+
+ Check out pre-built GitHub jobs in our showcase.
+
+
+## Installing the GitHub packages
@@ -24,25 +35,41 @@ yarn add @trigger.dev/github@latest
## Authentication
-GitHub supports Personal Access Tokens and OAuth.
+GitHub supports Personal Access Tokens and OAuth. You can use either of these to authenticate with GitHub.
-```ts
-import { Github } from "@trigger.dev/github";
+### Personal Access Token
+
+To create a personal access token on GitHub, login and [follow the instructions](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). Information on the required scopes can be found [here](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps).
+
+```ts my-job.ts
+import { GitHub } from "@trigger.dev/github";
//create GitHub client using a token
-const github = new Github({
+const github = new GitHub({
id: "github",
token: process.env.GITHUB_TOKEN!,
});
+...
+```
+
+### OAuth
+
+To use OAuth you can connect to GitHub via the Trigger.dev [web app](https://cloud.trigger.dev). Click 'Integrations' in the side panel of any project, and configure GitHub with the ID you want to use in your job and the required [scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps).
+
+```ts my-job.ts
+import { GitHub } from "@trigger.dev/github";
//create GitHub client using OAuth
-const github2 = new Github({
- id: "github2",
+const github = new GitHub({
+ id: "github",
});
+...
```
## Triggers and Tasks
+Once you have set up a GitHub client, you can use it to create triggers and tasks.
+
Trigger Jobs when events happen in GitHub, such as a new commit or a new issue.
@@ -51,51 +78,3 @@ const github2 = new Github({
Perform tasks such as creating a new issue or a new comment.
-
-## Using the underlying client
-
-You can use the underlying client to do anything Octokit supports. In this example we create a project card when a new issue is opened..
-
-
- View [the official GitHub docs](https://docs.github.com/en/rest) for everything that is
- supported{" "}
-
-
-```ts
-import { Github, events } from "@trigger.dev/github";
-
-const github = new Github({
- id: "github",
- token: process.env.GITHUB_TOKEN!,
-});
-
-client.defineJob({
- id: "alert-on-new-github-issues",
- name: "Alert on new GitHub issues",
- version: "0.1.1",
- trigger: github.triggers.repo({
- event: events.onIssueOpened,
- owner: "triggerdotdev",
- repo: "trigger.dev",
- }),
- integrations: {
- github,
- },
- run: async (payload, io, ctx) => {
- //io.github.runTask allows you to use the underlying SDK client
- const { data } = await io.github.runTask(
- "create-card",
- async (client) => {
- return client.rest.projects.createCard({
- column_id: 123,
- note: "test",
- });
- },
- { name: "Create card" }
- );
-
- //log the url of the created card
- await io.logger.info(data.url);
- },
-});
-```
diff --git a/docs/integrations/apis/replicate.mdx b/docs/integrations/apis/replicate.mdx
new file mode 100644
index 000000000..978bb51c1
--- /dev/null
+++ b/docs/integrations/apis/replicate.mdx
@@ -0,0 +1,170 @@
+---
+title: Replicate
+description: "Run machine learning tasks easily at scale"
+---
+
+
+
+## Installation
+
+To get started with the Replicate integration on Trigger.dev, you need to install the `@trigger.dev/replicate` package.
+You can do this using npm, pnpm, or yarn:
+
+
+
+```bash npm
+npm install @trigger.dev/replicate@latest
+```
+
+```bash pnpm
+pnpm add @trigger.dev/replicate@latest
+```
+
+```bash yarn
+yarn add @trigger.dev/replicate@latest
+```
+
+
+
+## Authentication
+
+To use the Replicate API with Trigger.dev, you have to provide an API Key.
+
+### API Key
+
+You can create an API Key in your [Account Settings](https://replicate.com/account/api-tokens).
+
+```ts
+import { Replicate } from "@trigger.dev/replicate";
+
+//this will use the passed in API key (defined in your environment variables)
+const replicate = new Replicate({
+ id: "replicate",
+ apiKey: process.env["REPLICATE_API_KEY"],
+});
+```
+
+## Usage
+
+Include the Replicate integration in your Trigger.dev job.
+
+```ts
+client.defineJob({
+ id: "replicate-cinematic-prompt",
+ name: "Replicate - Cinematic Prompt",
+ version: "0.1.0",
+ integrations: { replicate },
+ trigger: eventTrigger({
+ name: "replicate.cinematic",
+ schema: z.object({
+ prompt: z.string().default("rick astley riding a harley through post-apocalyptic miami"),
+ version: z
+ .string()
+ .default("af1a68a271597604546c09c64aabcd7782c114a63539a4a8d14d1eeda5630c33"),
+ }),
+ }),
+ run: async (payload, io, ctx) => {
+ //wait for prediction completion (uses remote callbacks internally)
+ const prediction = await io.replicate.predictions.createAndAwait("await-prediction", {
+ version: payload.version,
+ input: {
+ prompt: `${payload.prompt}, cinematic, 70mm, anamorphic, bokeh`,
+ width: 1280,
+ height: 720,
+ },
+ });
+ return prediction.output;
+ },
+});
+```
+
+### Pagination
+
+You can paginate responses:
+
+- Using the `getAll` helper
+- Using the `paginate` helper
+
+```ts
+client.defineJob({
+ id: "replicate-pagination",
+ name: "Replicate Pagination",
+ version: "0.1.0",
+ integrations: {
+ replicate,
+ },
+ trigger: eventTrigger({
+ name: "replicate.paginate",
+ }),
+ run: async (payload, io, ctx) => {
+ // getAll - returns an array of all results (uses paginate internally)
+ const all = await io.replicate.getAll(io.replicate.predictions.list, "get-all");
+
+ // paginate - returns an async generator, useful to process one page at a time
+ for await (const predictions of io.replicate.paginate(
+ io.replicate.predictions.list,
+ "paginate-all"
+ )) {
+ await io.logger.info("stats", {
+ total: predictions.length,
+ versions: predictions.map((p) => p.version),
+ });
+ }
+
+ return { count: all.length };
+ },
+});
+```
+
+## Tasks
+
+### Collections
+
+| Function Name | Description |
+| ------------------ | ---------------------------------------------------------------------- |
+| `collections.get` | Gets a collection. |
+| `collections.list` | Returns the first page of all collections. Use with pagination helper. |
+
+### Deployments
+
+| Function Name | Description |
+| ---------------------------------------- | --------------------------------------------------------- |
+| `deployments.predictions.create` | Creates a new prediction with a deployment. |
+| `deployments.predictions.createAndAwait` | Creates and waits for a new prediction with a deployment. |
+
+### Models
+
+| Function Name | Description |
+| ----------------- | ------------------------ |
+| `models.get` | Gets a model. |
+| `models.versions` | Gets a model version. |
+| `models.versions` | Gets all model versions. |
+
+### Predictions
+
+| Function Name | Description |
+| ---------------------------- | ---------------------------------------------------------------------- |
+| `predictions.cancel` | Cancels a prediction. |
+| `predictions.create` | Creates a prediction. |
+| `predictions.createAndAwait` | Creates and waits for a prediction. |
+| `predictions.get` | Gets a prediction. |
+| `predictions.list` | Returns the first page of all predictions. Use with pagination helper. |
+
+### Trainings
+
+| Function Name | Description |
+| -------------------------- | -------------------------------------------------------------------- |
+| `trainings.cancel` | Cancels a training. |
+| `trainings.create` | Creates a training. |
+| `trainings.createAndAwait` | Creates and waits for a training. |
+| `trainings.get` | Gets a training. |
+| `trainings.list` | Returns the first page of all trainings. Use with pagination helper. |
+
+### Misc
+
+| Function Name | Description |
+| ------------- | --------------------------------------------------- |
+| `getAll` | Pagination helper that returns an array of results. |
+| `paginate` | Pagination helper that returns an async generator. |
+| `request` | Sends authenticated requests to the Replicate API. |
+| `run` | Creates and waits for a prediction. |
diff --git a/docs/integrations/apis/stripe.mdx b/docs/integrations/apis/stripe.mdx
index 58901892a..87e2a10f5 100644
--- a/docs/integrations/apis/stripe.mdx
+++ b/docs/integrations/apis/stripe.mdx
@@ -39,6 +39,8 @@ const stripe = new Stripe({
The Stripe integration exposes a number of triggers that can be used on a job, powered by Stripe webhooks.
+We recommend testing Stripe payloads using [Stripe Shell](https://stripe.com/docs/stripe-cli?shell=true), Stripe's browser-based shell with the Stripe CLI pre-installed.
+
```ts
client.defineJob({
id: "stripe-price",
diff --git a/docs/integrations/apis/supabase/introduction.mdx b/docs/integrations/apis/supabase/introduction.mdx
index 92485b6c5..af78305dc 100644
--- a/docs/integrations/apis/supabase/introduction.mdx
+++ b/docs/integrations/apis/supabase/introduction.mdx
@@ -1,5 +1,6 @@
---
-title: Introduction
+title: "Supabase: Introduction"
+sidebarTitle: "Introduction"
---
diff --git a/docs/integrations/apis/supabase/management.mdx b/docs/integrations/apis/supabase/management.mdx
index ec3e151d0..286fc93e7 100644
--- a/docs/integrations/apis/supabase/management.mdx
+++ b/docs/integrations/apis/supabase/management.mdx
@@ -125,6 +125,7 @@ Now, you can use the `db` instance to add a trigger to run a job when a row is i
client.defineJob({
id: "supabase-trigger",
name: "Supabase Trigger",
+ version: "1.0.0",
trigger: db.onInserted({
table: "todos",
}),
@@ -140,6 +141,7 @@ You can add additional filters to the trigger by passing a `filter` object:
client.defineJob({
id: "supabase-trigger",
name: "Supabase Trigger",
+ version: "1.0.0",
trigger: db.onUpdated({
table: "todos",
// Only trigger if the todo is marked as completed
@@ -164,6 +166,7 @@ You can also listen for multiple different events using the `on` trigger:
client.defineJob({
id: "supabase-trigger",
name: "Supabase Trigger",
+ version: "1.0.0",
trigger: db.on({
table: "todos",
events: ["INSERT", "UPDATE"] // Trigger on both insert and update events
@@ -206,6 +209,7 @@ const db = supabase.db("https://.supabase.co");
client.defineJob({
id: "supabase-trigger",
name: "Supabase Trigger",
+ version: "1.0.0",
trigger: db.onUpdated({
table: "todos",
}),
diff --git a/docs/integrations/create-tasks.mdx b/docs/integrations/create-tasks.mdx
index fe5386a18..2ed3b09df 100644
--- a/docs/integrations/create-tasks.mdx
+++ b/docs/integrations/create-tasks.mdx
@@ -24,7 +24,7 @@ export class Github implements TriggerIntegration {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
- return this._io.runTask(
+ return this._io.runTask(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
diff --git a/docs/integrations/create.mdx b/docs/integrations/create.mdx
index 5ae14721d..6d56309ca 100644
--- a/docs/integrations/create.mdx
+++ b/docs/integrations/create.mdx
@@ -1,5 +1,6 @@
---
-title: Introduction
+title: "Create an Integration: Introduction"
+sidebarTitle: "Introduction"
description: "You can create Integrations of your own."
---
diff --git a/docs/integrations/introduction.mdx b/docs/integrations/introduction.mdx
index 30be9c1e3..d307fa403 100644
--- a/docs/integrations/introduction.mdx
+++ b/docs/integrations/introduction.mdx
@@ -1,5 +1,6 @@
---
-title: Introduction
+title: "Integrations: Introduction"
+sidebarTitle: "Introduction"
description: "Integrations make it easy to authenticate and use APIs."
---
@@ -30,14 +31,17 @@ description: "Integrations make it easy to authenticate and use APIs."
Navigate the menu or select Integrations from the table below.
-| API | Description | Webhooks | Tasks |
-| --------------------------------------- | ---------------------------------------------------------------- | -------- | ----- |
-| [GitHub](/integrations/apis/github) | Subscribe to webhooks and perform actions | β
| β
|
-| [Linear](/integrations/apis/linear) | Streamline project and issue tracking | β
| β
|
-| [OpenAI](/integrations/apis/openai) | Generate text and images. Including longer than 30s prompts | N/A | β
|
-| [Plain](/integrations/apis/plain) | Perform customer support using Plain | π | β
|
-| [Resend](/integrations/apis/resend) | Send emails using Resend | π | β
|
-| [SendGrid](/integrations/apis/sendgrid) | Send emails using SendGrid | π | β
|
-| [Slack](/integrations/apis/slack) | Send Slack messages | π | β
|
-| [Supabase](/integrations/apis/supabase) | Interact with your projects and databases | β
| β
|
-| [Typeform](/integrations/apis/typeform) | Interact with the Typeform API and get notified of new responses | β
| β
|
+| API | Description | Webhooks | Tasks |
+| ----------------------------------------- | ---------------------------------------------------------------- | -------- | ----- |
+| [Airtable](/integrations/apis/airtable) | Interact with the Airtable API | π | β
|
+| [GitHub](/integrations/apis/github) | Subscribe to webhooks and perform actions | β
| β
|
+| [Linear](/integrations/apis/linear) | Streamline project and issue tracking | β
| β
|
+| [OpenAI](/integrations/apis/openai) | Generate text and images. Including longer than 30s prompts | N/A | β
|
+| [Plain](/integrations/apis/plain) | Perform customer support using Plain | π | β
|
+| [Replicate](/integrations/apis/replicate) | Run machine learning tasks easily at scale | N/A | β
|
+| [Resend](/integrations/apis/resend) | Send emails using Resend | π | β
|
+| [SendGrid](/integrations/apis/sendgrid) | Send emails using SendGrid | π | β
|
+| [Slack](/integrations/apis/slack) | Send Slack messages | π | β
|
+| [Stripe](/integrations/apis/stripe) | Interact with the Stripe API | β
| β
|
+| [Supabase](/integrations/apis/supabase) | Interact with your projects and databases | β
| β
|
+| [Typeform](/integrations/apis/typeform) | Interact with the Typeform API and get notified of new responses | β
| β
|
diff --git a/docs/mint.json b/docs/mint.json
index 66f51cf94..37110caf5 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -76,6 +76,7 @@
"documentation/quickstarts/express",
"documentation/quickstarts/remix",
"documentation/quickstarts/redwood",
+ "documentation/quickstarts/nestjs",
"documentation/quickstarts/astro",
"documentation/quickstarts/nuxt",
"documentation/quickstarts/sveltekit",
@@ -142,6 +143,7 @@
"group": "Manual setup",
"pages": [
"documentation/guides/manual/nextjs",
+ "documentation/guides/manual/nestjs",
"documentation/guides/manual/express",
"documentation/guides/manual/remix",
"documentation/guides/manual/redwood",
@@ -248,6 +250,7 @@
"integrations/apis/linear",
"integrations/apis/openai",
"integrations/apis/plain",
+ "integrations/apis/replicate",
"integrations/apis/resend",
"integrations/apis/sendgrid",
"integrations/apis/slack",
@@ -317,10 +320,7 @@
"sdk/dynamictrigger/constructor",
{
"group": "Instance methods",
- "pages": [
- "sdk/dynamictrigger/register",
- "sdk/dynamictrigger/unregister"
- ]
+ "pages": ["sdk/dynamictrigger/register", "sdk/dynamictrigger/unregister"]
}
]
},
@@ -331,10 +331,7 @@
"sdk/dynamicschedule/constructor",
{
"group": "Instance methods",
- "pages": [
- "sdk/dynamicschedule/register",
- "sdk/dynamicschedule/unregister"
- ]
+ "pages": ["sdk/dynamicschedule/register", "sdk/dynamicschedule/unregister"]
}
]
},
@@ -355,9 +352,7 @@
},
{
"group": "Overview",
- "pages": [
- "examples/introduction"
- ]
+ "pages": ["examples/introduction"]
}
],
"footerSocials": {
@@ -370,4 +365,4 @@
"apiKey": "phc_hwYmedO564b3Ik8nhA4Csrb5SueY0EwFJWCbseGwWW"
}
}
-}
\ No newline at end of file
+}
diff --git a/docs/sdk/dynamicschedule/overview.mdx b/docs/sdk/dynamicschedule/overview.mdx
index f6faaa0a8..2216bc92d 100644
--- a/docs/sdk/dynamicschedule/overview.mdx
+++ b/docs/sdk/dynamicschedule/overview.mdx
@@ -1,5 +1,6 @@
---
-title: "Overview"
+title: "DynamicSchedule: Overview"
+sidebarTitle: "Overview"
description: "`DynamicSchedule` allows you to define a scheduled trigger that can be configured dynamically at runtime."
---
diff --git a/docs/sdk/dynamictrigger/overview.mdx b/docs/sdk/dynamictrigger/overview.mdx
index b78330cf3..c597b3783 100644
--- a/docs/sdk/dynamictrigger/overview.mdx
+++ b/docs/sdk/dynamictrigger/overview.mdx
@@ -1,5 +1,6 @@
---
-title: "Overview"
+title: "DynamicTrigger: Overview"
+sidebarTitle: "Overview"
description: "`DynamicTrigger` allows you to define a trigger that can be configured dynamically at runtime."
---
diff --git a/docs/sdk/eventtrigger.mdx b/docs/sdk/eventtrigger.mdx
index fe94f8c96..5c7b30c95 100644
--- a/docs/sdk/eventtrigger.mdx
+++ b/docs/sdk/eventtrigger.mdx
@@ -45,6 +45,26 @@ You can have multiple Jobs that subscribe to the same event, they will all trigg
```
+
+ Used to provide example payloads that are accepted by the job.
+
+ This will be available in the dashboard and can be used to trigger test runs.
+
+
+
+ The example's ID.
+
+
+ The name that's displayed in the dashboard.
+
+
+ The payload that's accepted by the job.
+
+
+ The icon to use for this example in the dashboard.
+
+
+
@@ -70,6 +90,19 @@ client.defineJob({
filter: {
tier: ["pro"],
},
+ //(optional) example event object
+ examples: [
+ {
+ id: "issue.opened",
+ name: "Issue opened",
+ payload: {
+ userId: "1234",
+ tier: "free",
+ },
+ //optional
+ icon: "github",
+ },
+ ],
}),
run: async (payload, io, ctx) => {
await io.logger.log("New pro user created", { userId: payload.userId });
diff --git a/docs/sdk/introduction.mdx b/docs/sdk/introduction.mdx
index 1e2c7076e..0d4b28362 100644
--- a/docs/sdk/introduction.mdx
+++ b/docs/sdk/introduction.mdx
@@ -1,5 +1,6 @@
---
-title: "Introduction"
+title: "SDK: Introduction"
+sidebarTitle: "Introduction"
description: "The SDK is how you interact with Trigger.dev"
---
diff --git a/docs/sdk/io/overview.mdx b/docs/sdk/io/overview.mdx
index 3a061e336..c44a34fc6 100644
--- a/docs/sdk/io/overview.mdx
+++ b/docs/sdk/io/overview.mdx
@@ -1,5 +1,6 @@
---
-title: "Overview"
+title: "IO: Overview"
+sidebarTitle: "Overview"
description: "The second parameter in a Job's `run()` function. It holds Integrations and useful actions you can perform."
---
@@ -63,5 +64,13 @@ If you want to send an event from outside a run (e.g. just from your backend) yo
`io.registerTrigger()` allows you to register a [DynamicTrigger](/sdk/dynamictrigger) with the specified trigger data.
+### yield()
+
+`io.yield()` allows you to yield the current run and resume it immediately in a different function execution context. Requires a single argument that defines the yield key which works similar to task keys.
+
+### brb()
+
+`io.brb()` is is alias for `io.yield()`.
+
{/* ### [unregisterTrigger()](/sdk/io/unregistertrigger) */}
{/* `io.unregisterTrigger()` allows you to unregister a [DynamicTrigger](/sdk/dynamictrigger) that was previously registered with `io.registerTrigger()`. */}
diff --git a/docs/sdk/io/runtask.mdx b/docs/sdk/io/runtask.mdx
index 579142998..eca598386 100644
--- a/docs/sdk/io/runtask.mdx
+++ b/docs/sdk/io/runtask.mdx
@@ -1,10 +1,12 @@
---
title: "io.runTask()"
sidebarTitle: "runTask()"
-description: "`io.runTask()` allows you to run a [Task](/documentation/concepts/tasks) from inside a Job run."
+description: "Creates and runs a Task inside a Run."
---
-A Task is a resumable unit of a Run that can be retried, resumed and is logged. [Integrations](/integrations) use Tasks internally to perform their actions.
+A [Task](/documentation/concepts/tasks) is a resumable unit of a Run that can be retried, resumed and is logged. [Integrations](/integrations) use Tasks internally to perform their actions.
+
+The wrappers at `io.integration.runTask()` expose the underlying Integration client as the first callback parameter (see examples on the right). They will have defaults set for options and `onError` handlers, but should otherwise be considered identical to raw `io.runTask()`.
## Parameters
@@ -112,12 +114,29 @@ A Task is a resumable unit of a Run that can be retried, resumed and is logged.
+
+
+ An optional object that exposes settings for the remote callback feature.
+
+ Enabling this feature will expose a `callbackUrl` property on the callback's Task parameter. Additionally, `io.runTask()` will now return a Promise that resolves with the body of the first request sent to that URL.
+
+
+
+ Whether to enable the remote callback feature.
+
+
+ The value of the property.
+
+
+
+
+
An optional callback that will be called when the Task fails. You can perform
- logic in here and optionally return a custom error object. Returning an object with `{ retryAt: Date, error?: Error }` will retry the Task at the specified Date. You can also just return a new `Error` object to throw a new error. Return nothing to rethrow the original error.
+ logic in here and optionally return a custom error object. Returning an object with `{ retryAt: Date, error?: Error }` will retry the Task at the specified Date. You can also just return a new `Error` object to throw a new error. Returning `null` or `undefined` will rethrow the original error. If you want to force retrying to be skipped, return `{ skipRetrying: true }`.
@@ -133,6 +152,8 @@ A Task is a resumable unit of a Run that can be retried, resumed and is logged.
A Promise that resolves with the returned value of the callback.
+If the remote callback feature `options.callback` is enabled, the Promise will instead resolve with the body of the first request sent to `task.callbackUrl`.
+
```typescript Run a task
@@ -150,11 +171,11 @@ client.defineJob({
},
run: async (payload, io, ctx) => {
//runTask
- const response = await io.runTask(
+ const response = await io.github.runTask(
"create-card",
- async () => {
+ async (client) => {
//create a project card using the underlying GitHub Integration client
- return io.github.client.rest.projects.createCard({
+ return client.rest.projects.createCard({
column_id: 123,
note: "test",
});
@@ -201,4 +222,43 @@ client.defineJob({
});
```
+```typescript Remote callbacks
+client.defineJob({
+ id: "remote-callback-example",
+ name: "Remote Callback example",
+ version: "0.1.1",
+ trigger: eventTrigger({ name: "predict" }),
+ integrations: { replicate },
+ run: async (payload, io, ctx) => {
+ //runTask
+ const prediction = await io.replicate.runTask(
+ "create-and-await-prediction",
+ async (client, task) => {
+ //create a prediction using the underlying Replicate Integration client
+ await client.predictions.create({
+ ...payload,
+ webhook: task.callbackUrl ?? "",
+ webhook_events_filter: ["completed"],
+ });
+ //the actual return value will be the data sent to callbackUrl
+ //cast to the exact data type you expect to receive or `any` if unsure
+ return {} as Prediction;
+ },
+ {
+ name: "Create and await Prediction",
+ icon: "replicate",
+ //remote callback settings
+ callback: {
+ enabled: true,
+ timeoutInSeconds: 300,
+ },
+ }
+ );
+
+ //log the prediction output
+ await io.logger.info(prediction.output);
+ },
+});
+```
+
diff --git a/docs/sdk/react/introduction.mdx b/docs/sdk/react/introduction.mdx
index 63c67ec93..5c255d15c 100644
--- a/docs/sdk/react/introduction.mdx
+++ b/docs/sdk/react/introduction.mdx
@@ -1,5 +1,6 @@
---
-title: "Introduction"
+title: "React SDK: Introduction"
+sidebarTitle: "Introduction"
description: "The React SDK allows you to display the status of your Jobs and Runs in your React app."
---
diff --git a/docs/sdk/triggerclient/overview.mdx b/docs/sdk/triggerclient/overview.mdx
index a91880ef3..6d263dd0b 100644
--- a/docs/sdk/triggerclient/overview.mdx
+++ b/docs/sdk/triggerclient/overview.mdx
@@ -1,5 +1,6 @@
---
-title: "Overview"
+title: "TriggerClient: Overview"
+sidebarTitle: "Overview"
description: "TriggerClient is used to create a client that connects to the Trigger.dev platform"
---
diff --git a/integrations/airtable/CHANGELOG.md b/integrations/airtable/CHANGELOG.md
index 9905b5f80..ba4997aab 100644
--- a/integrations/airtable/CHANGELOG.md
+++ b/integrations/airtable/CHANGELOG.md
@@ -1,5 +1,34 @@
# @trigger.dev/airtable
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- 9a187f9e: upgrade zod to 3.22.3
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support.
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
+
## 2.1.7
### Patch Changes
diff --git a/integrations/airtable/package.json b/integrations/airtable/package.json
index 87d688f67..403c2bf95 100644
--- a/integrations/airtable/package.json
+++ b/integrations/airtable/package.json
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/airtable",
- "version": "2.1.7",
+ "version": "2.2.0",
"description": "Trigger.dev integration for airtable",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -26,10 +26,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@trigger.dev/integration-kit": "workspace:^2.1.7",
- "@trigger.dev/sdk": "workspace:^2.1.7",
+ "@trigger.dev/integration-kit": "workspace:^2.2.0",
+ "@trigger.dev/sdk": "workspace:^2.2.0",
"airtable": "^0.12.1",
- "zod": "3.21.4"
+ "zod": "3.22.3"
},
"engines": {
"node": ">=16.8.0"
diff --git a/integrations/airtable/src/index.ts b/integrations/airtable/src/index.ts
index f8c2d99f1..bc9ff2402 100644
--- a/integrations/airtable/src/index.ts
+++ b/integrations/airtable/src/index.ts
@@ -15,6 +15,7 @@ import { Base } from "./base";
import { Webhooks, createWebhookEventSource } from "./webhooks";
export * from "./types";
+export * from "./base";
export type AirtableIntegrationOptions = {
/** An ID for this client */
@@ -92,7 +93,7 @@ export class Airtable implements TriggerIntegration {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
- return this._io.runTask(
+ return this._io.runTask(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
diff --git a/integrations/github/CHANGELOG.md b/integrations/github/CHANGELOG.md
index 0228577ca..405bcc8c9 100644
--- a/integrations/github/CHANGELOG.md
+++ b/integrations/github/CHANGELOG.md
@@ -1,5 +1,34 @@
# @trigger.dev/github
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- 9a187f9e: upgrade zod to 3.22.3
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support.
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
+
## 2.1.7
### Patch Changes
diff --git a/integrations/github/package.json b/integrations/github/package.json
index 1354a1277..d9d9f641d 100644
--- a/integrations/github/package.json
+++ b/integrations/github/package.json
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/github",
- "version": "2.1.7",
+ "version": "2.2.0",
"description": "The official GitHub integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -29,10 +29,10 @@
"@octokit/request": "^6.2.5",
"@octokit/request-error": "^4.0.1",
"@octokit/webhooks": "^10.4.0",
- "@trigger.dev/sdk": "workspace:^2.1.7",
- "@trigger.dev/integration-kit": "workspace:^2.1.7",
+ "@trigger.dev/integration-kit": "workspace:^2.2.0",
+ "@trigger.dev/sdk": "workspace:^2.2.0",
"octokit": "^2.0.14",
- "zod": "3.21.4"
+ "zod": "3.22.3"
},
"engines": {
"node": ">=16.8.0"
diff --git a/integrations/github/src/index.ts b/integrations/github/src/index.ts
index 59e2dc968..0716e849b 100644
--- a/integrations/github/src/index.ts
+++ b/integrations/github/src/index.ts
@@ -138,7 +138,7 @@ export class Github implements TriggerIntegration {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
- return this._io.runTask(
+ return this._io.runTask(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
diff --git a/integrations/linear/CHANGELOG.md b/integrations/linear/CHANGELOG.md
index 9bccb718a..25c7d6a61 100644
--- a/integrations/linear/CHANGELOG.md
+++ b/integrations/linear/CHANGELOG.md
@@ -1,5 +1,35 @@
# @trigger.dev/linear
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- 9a187f9e: upgrade zod to 3.22.3
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support.
+- 81e886a1: Fix `getAll` helper and search function params
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
+
## 2.1.7
### Patch Changes
diff --git a/integrations/linear/package.json b/integrations/linear/package.json
index eabcab053..909c88ee3 100644
--- a/integrations/linear/package.json
+++ b/integrations/linear/package.json
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/linear",
- "version": "2.1.7",
+ "version": "2.2.0",
"description": "Trigger.dev integration for @linear/sdk",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -27,9 +27,9 @@
},
"dependencies": {
"@linear/sdk": "^8.0.0",
- "@trigger.dev/integration-kit": "workspace:^2.1.7",
- "@trigger.dev/sdk": "workspace:^2.1.7",
- "zod": "3.21.4"
+ "@trigger.dev/integration-kit": "workspace:^2.2.0",
+ "@trigger.dev/sdk": "workspace:^2.2.0",
+ "zod": "3.22.3"
},
"engines": {
"node": ">=16.8.0"
diff --git a/integrations/linear/src/index.ts b/integrations/linear/src/index.ts
index f6318ee7a..0f53f9e7a 100644
--- a/integrations/linear/src/index.ts
+++ b/integrations/linear/src/index.ts
@@ -158,7 +158,7 @@ export class Linear implements TriggerIntegration {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
- return this._io.runTask(
+ return this._io.runTask(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
@@ -182,7 +182,7 @@ export class Linear implements TriggerIntegration {
>(
task: TTask,
key: IntegrationTaskKey,
- params: Nullable = {}
+ params: Parameters[1] = {}
): Promise>["nodes"]> {
const boundTask = task.bind(this as any);
@@ -695,7 +695,7 @@ export class Linear implements TriggerIntegration {
key: IntegrationTaskKey,
params: {
term: string;
- variables?: L.SearchDocumentsQueryVariables;
+ variables?: Parameters[1];
}
): LinearReturnType {
return this.runTask(
@@ -862,7 +862,7 @@ export class Linear implements TriggerIntegration {
key: IntegrationTaskKey,
params: {
term: string;
- variables?: L.SearchIssuesQueryVariables;
+ variables?: Parameters[1];
}
): LinearReturnType {
return this.runTask(
@@ -1273,7 +1273,7 @@ export class Linear implements TriggerIntegration {
key: IntegrationTaskKey,
params: {
term: string;
- variables?: L.SearchProjectsQueryVariables;
+ variables?: Parameters[1];
}
): LinearReturnType {
return this.runTask(
diff --git a/integrations/openai/CHANGELOG.md b/integrations/openai/CHANGELOG.md
index b346fa3ae..c9fdbb7df 100644
--- a/integrations/openai/CHANGELOG.md
+++ b/integrations/openai/CHANGELOG.md
@@ -1,5 +1,32 @@
# @trigger.dev/slack
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
+
## 2.1.7
### Patch Changes
diff --git a/integrations/openai/package.json b/integrations/openai/package.json
index ce2ccbbb8..ccd647c9d 100644
--- a/integrations/openai/package.json
+++ b/integrations/openai/package.json
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/openai",
- "version": "2.1.7",
+ "version": "2.2.0",
"description": "The official OpenAI integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -25,8 +25,8 @@
},
"dependencies": {
"openai": "^4.2.0",
- "@trigger.dev/sdk": "workspace:^2.1.7",
- "@trigger.dev/integration-kit": "workspace:^2.1.7"
+ "@trigger.dev/sdk": "workspace:^2.2.0",
+ "@trigger.dev/integration-kit": "workspace:^2.2.0"
},
"engines": {
"node": ">=16.8.0"
diff --git a/integrations/plain/CHANGELOG.md b/integrations/plain/CHANGELOG.md
index 6baddddcb..b3beded98 100644
--- a/integrations/plain/CHANGELOG.md
+++ b/integrations/plain/CHANGELOG.md
@@ -1,5 +1,32 @@
# @trigger.dev/plain
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
+
## 2.1.7
### Patch Changes
diff --git a/integrations/plain/package.json b/integrations/plain/package.json
index 8a2e65509..846dc2376 100644
--- a/integrations/plain/package.json
+++ b/integrations/plain/package.json
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/plain",
- "version": "2.1.7",
+ "version": "2.2.0",
"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:^2.1.7",
- "@trigger.dev/sdk": "workspace:^2.1.7",
+ "@trigger.dev/integration-kit": "workspace:^2.2.0",
+ "@trigger.dev/sdk": "workspace:^2.2.0",
"@team-plain/typescript-sdk": "^2.7.0"
},
"engines": {
diff --git a/integrations/replicate/CHANGELOG.md b/integrations/replicate/CHANGELOG.md
new file mode 100644
index 000000000..be38c5d7b
--- /dev/null
+++ b/integrations/replicate/CHANGELOG.md
@@ -0,0 +1,30 @@
+# @trigger.dev/replicate
+
+## 2.2.0
+
+### Patch Changes
+
+- Updated dependencies [975c5f1d]
+ - @trigger.dev/integration-kit@2.2.0
+ - @trigger.dev/sdk@2.2.0
+
+## 2.1.9
+
+### Patch Changes
+
+- 9a187f9e: upgrade zod to 3.22.3
+- Updated dependencies [9a187f9e]
+- Updated dependencies [2e9452ab]
+ - @trigger.dev/sdk@2.1.9
+ - @trigger.dev/integration-kit@2.1.9
+
+## 2.1.8
+
+### Patch Changes
+
+- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support.
+- Updated dependencies [6a992a19]
+- Updated dependencies [ab9e4a98]
+- Updated dependencies [ab9e4a98]
+ - @trigger.dev/sdk@2.1.8
+ - @trigger.dev/integration-kit@2.1.8
diff --git a/integrations/replicate/README.md b/integrations/replicate/README.md
new file mode 100644
index 000000000..67a4b88e8
--- /dev/null
+++ b/integrations/replicate/README.md
@@ -0,0 +1 @@
+# @trigger.dev/replicate
diff --git a/integrations/replicate/package.json b/integrations/replicate/package.json
new file mode 100644
index 000000000..41c4f76a9
--- /dev/null
+++ b/integrations/replicate/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@trigger.dev/replicate",
+ "version": "2.2.0",
+ "description": "Trigger.dev integration for replicate",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "dist/index.js",
+ "dist/index.d.ts",
+ "dist/index.js.map"
+ ],
+ "devDependencies": {
+ "@trigger.dev/tsconfig": "workspace:*",
+ "@types/node": "16.x",
+ "rimraf": "^3.0.2",
+ "tsup": "7.1.x",
+ "typescript": "4.9.4"
+ },
+ "scripts": {
+ "clean": "rimraf dist",
+ "build": "npm run clean && npm run build:tsup",
+ "build:tsup": "tsup",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@trigger.dev/integration-kit": "workspace:^2.2.0",
+ "@trigger.dev/sdk": "workspace:^2.2.0",
+ "replicate": "^0.18.1",
+ "zod": "3.22.3"
+ },
+ "engines": {
+ "node": ">=16.8.0"
+ }
+}
\ No newline at end of file
diff --git a/integrations/replicate/src/collections.ts b/integrations/replicate/src/collections.ts
new file mode 100644
index 000000000..b6f1c0100
--- /dev/null
+++ b/integrations/replicate/src/collections.ts
@@ -0,0 +1,37 @@
+import { IntegrationTaskKey } from "@trigger.dev/sdk";
+import { Page, Collection } from "replicate";
+
+import { ReplicateRunTask } from "./index";
+import { ReplicateReturnType } from "./types";
+
+export class Collections {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ /** Fetch a model collection. */
+ get(key: IntegrationTaskKey, params: { slug: string }): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.collections.get(params.slug);
+ },
+ {
+ name: "Get Collection",
+ params,
+ properties: [{ label: "Collection Slug", text: params.slug }],
+ }
+ );
+ }
+
+ /** Fetch a list of model collections. */
+ list(key: IntegrationTaskKey): ReplicateReturnType> {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.collections.list();
+ },
+ {
+ name: "List Collections",
+ }
+ );
+ }
+}
diff --git a/integrations/replicate/src/deployments.ts b/integrations/replicate/src/deployments.ts
new file mode 100644
index 000000000..c5c1508d1
--- /dev/null
+++ b/integrations/replicate/src/deployments.ts
@@ -0,0 +1,76 @@
+import { IntegrationTaskKey } from "@trigger.dev/sdk";
+import ReplicateClient, { Prediction } from "replicate";
+
+import { ReplicateRunTask } from "./index";
+import { callbackProperties, createDeploymentProperties } from "./utils";
+import { CallbackTimeout, ReplicateReturnType } from "./types";
+
+export class Deployments {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ get predictions() {
+ return new Predictions(this.runTask);
+ }
+}
+
+class Predictions {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ /** Create a new prediction with a deployment. */
+ create(
+ key: IntegrationTaskKey,
+ params: {
+ deployment_owner: string;
+ deployment_name: string;
+ } & Parameters[2]
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ const { deployment_owner, deployment_name, ...options } = params;
+
+ return client.deployments.predictions.create(deployment_owner, deployment_name, options);
+ },
+ {
+ name: "Create Prediction With Deployment",
+ params,
+ properties: createDeploymentProperties(params),
+ }
+ );
+ }
+
+ /** Create a new prediction with a deployment and await the result. */
+ createAndAwait(
+ key: IntegrationTaskKey,
+ params: {
+ deployment_owner: string;
+ deployment_name: string;
+ } & Omit<
+ Parameters[2],
+ "webhook" | "webhook_events_filter"
+ >,
+ options: CallbackTimeout = { timeoutInSeconds: 3600 }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client, task) => {
+ const { deployment_owner, deployment_name, ...options } = params;
+
+ return client.deployments.predictions.create(deployment_owner, deployment_name, {
+ ...options,
+ webhook: task.callbackUrl ?? "",
+ webhook_events_filter: ["completed"],
+ });
+ },
+ {
+ name: "Create And Await Prediction With Deployment",
+ params,
+ properties: [...createDeploymentProperties(params), ...callbackProperties(options)],
+ callback: {
+ enabled: true,
+ timeoutInSeconds: options.timeoutInSeconds,
+ },
+ }
+ );
+ }
+}
diff --git a/integrations/replicate/src/index.ts b/integrations/replicate/src/index.ts
new file mode 100644
index 000000000..0093be164
--- /dev/null
+++ b/integrations/replicate/src/index.ts
@@ -0,0 +1,280 @@
+import {
+ TriggerIntegration,
+ RunTaskOptions,
+ IO,
+ IOTask,
+ IntegrationTaskKey,
+ RunTaskErrorCallback,
+ Json,
+ retry,
+ ConnectionAuth,
+} from "@trigger.dev/sdk";
+import ReplicateClient, { Page, Prediction } from "replicate";
+
+import { Predictions } from "./predictions";
+import { Models } from "./models";
+import { Trainings } from "./trainings";
+import { Collections } from "./collections";
+import { ReplicateReturnType } from "./types";
+import { Deployments } from "./deployments";
+
+export type ReplicateIntegrationOptions = {
+ id: string;
+ apiKey: string;
+};
+
+export type ReplicateRunTask = InstanceType["runTask"];
+
+export class Replicate implements TriggerIntegration {
+ private _options: ReplicateIntegrationOptions;
+ private _client?: any;
+ private _io?: IO;
+ private _connectionKey?: string;
+
+ constructor(private options: ReplicateIntegrationOptions) {
+ if (Object.keys(options).includes("apiKey") && !options.apiKey) {
+ throw `Can't create Replicate integration (${options.id}) as apiKey was undefined`;
+ }
+
+ this._options = options;
+ }
+
+ get authSource() {
+ return "LOCAL" as const;
+ }
+
+ get id() {
+ return this.options.id;
+ }
+
+ get metadata() {
+ return { id: "replicate", name: "Replicate" };
+ }
+
+ cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
+ const replicate = new Replicate(this._options);
+ replicate._io = io;
+ replicate._connectionKey = connectionKey;
+ replicate._client = this.createClient(auth);
+ return replicate;
+ }
+
+ createClient(auth?: ConnectionAuth) {
+ return new ReplicateClient({
+ auth: this._options.apiKey,
+ });
+ }
+
+ runTask | void>(
+ key: IntegrationTaskKey,
+ callback: (client: ReplicateClient, task: IOTask, io: IO) => Promise,
+ options?: RunTaskOptions,
+ errorCallback?: RunTaskErrorCallback
+ ): Promise {
+ if (!this._io) throw new Error("No IO");
+ if (!this._connectionKey) throw new Error("No connection key");
+
+ return this._io.runTask(
+ key,
+ (task, io) => {
+ if (!this._client) throw new Error("No client");
+ return callback(this._client, task, io);
+ },
+ {
+ icon: "replicate",
+ retry: retry.standardBackoff,
+ ...(options ?? {}),
+ connectionKey: this._connectionKey,
+ },
+ errorCallback ?? onError
+ );
+ }
+
+ get collections() {
+ return new Collections(this.runTask.bind(this));
+ }
+
+ get deployments() {
+ return new Deployments(this.runTask.bind(this));
+ }
+
+ get models() {
+ return new Models(this.runTask.bind(this));
+ }
+
+ get predictions() {
+ return new Predictions(this.runTask.bind(this));
+ }
+
+ get trainings() {
+ return new Trainings(this.runTask.bind(this));
+ }
+
+ /** Paginate through a list of results. */
+ async *paginate(
+ task: (key: string) => Promise>,
+ key: IntegrationTaskKey,
+ counter: number = 0
+ ): AsyncGenerator {
+ const boundTask = task.bind(this as any);
+
+ const page = await boundTask(`${key}-${counter}`);
+ yield page.results;
+
+ if (page.next) {
+ const nextStep = counter++;
+
+ const nextPage = () => {
+ return this.request>(`${key}-${nextStep}`, {
+ route: page.next!,
+ options: { method: "GET" },
+ });
+ };
+
+ yield* this.paginate(nextPage, key, nextStep);
+ }
+ }
+
+ /** Auto-paginate and return all results. */
+ async getAll(
+ task: (key: string) => Promise>,
+ key: IntegrationTaskKey
+ ): ReplicateReturnType {
+ const allResults: T[] = [];
+
+ for await (const results of this.paginate(task, key)) {
+ allResults.push(...results);
+ }
+
+ return allResults;
+ }
+
+ /** Make a request to the Replicate API. */
+ request(
+ key: IntegrationTaskKey,
+ params: {
+ route: string | URL;
+ options: Parameters[1];
+ }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ async (client) => {
+ const response = await client.request(params.route, params.options);
+
+ return response.json();
+ },
+ {
+ name: "Send Request",
+ params,
+ properties: [
+ { label: "Route", text: params.route.toString() },
+ ...(params.options.method ? [{ label: "Method", text: params.options.method }] : []),
+ ],
+ callback: { enabled: true },
+ }
+ );
+ }
+
+ /** Run a model and await the result. */
+ run(
+ key: IntegrationTaskKey,
+ params: {
+ identifier: Parameters[0];
+ } & Omit<
+ Parameters[1],
+ "webhook" | "webhook_events_filter" | "wait" | "signal"
+ >
+ ): ReplicateReturnType {
+ const { identifier, ...paramsWithoutIdentifier } = params;
+
+ // see: https://github.com/replicate/replicate-javascript/blob/4b0d9cb0e226fab3d3d31de5b32261485acf5626/index.js#L102
+
+ const namePattern = /[a-zA-Z0-9]+(?:(?:[._]|__|[-]*)[a-zA-Z0-9]+)*/;
+ const pattern = new RegExp(
+ `^(?${namePattern.source})/(?${namePattern.source}):(?[0-9a-fA-F]+)$`
+ );
+
+ const match = identifier.match(pattern);
+
+ if (!match || !match.groups) {
+ throw new Error('Invalid version. It must be in the format "owner/name:version"');
+ }
+
+ const { version } = match.groups;
+
+ return this.predictions.createAndAwait(key, { ...paramsWithoutIdentifier, version });
+ }
+
+ // TODO: wait(prediction) - needs polling
+}
+
+class ApiError extends Error {
+ constructor(
+ message: string,
+ readonly request: Request,
+ readonly response: Response
+ ) {
+ super(message);
+ this.name = "ApiError";
+ }
+}
+
+function isReplicateApiError(error: unknown): error is ApiError {
+ if (typeof error !== "object" || error === null) {
+ return false;
+ }
+
+ const apiError = error as ApiError;
+
+ return (
+ apiError.name === "ApiError" &&
+ apiError.request instanceof Request &&
+ apiError.response instanceof Response
+ );
+}
+
+function shouldRetry(method: string, status: number) {
+ return status === 429 || (method === "GET" && status >= 500);
+}
+
+export function onError(error: unknown): ReturnType {
+ if (!isReplicateApiError(error)) {
+ return;
+ }
+
+ if (!shouldRetry(error.request.method, error.response.status)) {
+ return {
+ skipRetrying: true,
+ };
+ }
+
+ // see: https://github.com/replicate/replicate-javascript/blob/4b0d9cb0e226fab3d3d31de5b32261485acf5626/lib/util.js#L43
+
+ const retryAfter = error.response.headers.get("retry-after");
+
+ if (retryAfter) {
+ const resetDate = new Date(retryAfter);
+
+ if (!Number.isNaN(resetDate.getTime())) {
+ return {
+ retryAt: resetDate,
+ error,
+ };
+ }
+ }
+
+ const rateLimitRemaining = error.response.headers.get("ratelimit-remaining");
+ const rateLimitReset = error.response.headers.get("ratelimit-reset");
+
+ if (rateLimitRemaining === "0" && rateLimitReset) {
+ const resetDate = new Date(Number(rateLimitReset) * 1000);
+
+ if (!Number.isNaN(resetDate.getTime())) {
+ return {
+ retryAt: resetDate,
+ error,
+ };
+ }
+ }
+}
diff --git a/integrations/replicate/src/models.ts b/integrations/replicate/src/models.ts
new file mode 100644
index 000000000..d4b3a78ac
--- /dev/null
+++ b/integrations/replicate/src/models.ts
@@ -0,0 +1,82 @@
+import { IntegrationTaskKey } from "@trigger.dev/sdk";
+import { Model, ModelVersion } from "replicate";
+
+import { ReplicateRunTask } from "./index";
+import { modelProperties } from "./utils";
+import { ReplicateReturnType } from "./types";
+
+export class Models {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ /** Get information about a model. */
+ get(
+ key: IntegrationTaskKey,
+ params: {
+ model_owner: string;
+ model_name: string;
+ }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.models.get(params.model_owner, params.model_name);
+ },
+ {
+ name: "Get Model",
+ params,
+ properties: modelProperties(params),
+ }
+ );
+ }
+
+ get versions() {
+ return new Versions(this.runTask);
+ }
+}
+
+class Versions {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ /** Get a specific model version. */
+ get(
+ key: IntegrationTaskKey,
+ params: {
+ model_owner: string;
+ model_name: string;
+ version_id: string;
+ }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.models.versions.get(params.model_owner, params.model_name, params.version_id);
+ },
+ {
+ name: "Get Model Version",
+ params,
+ properties: modelProperties(params),
+ }
+ );
+ }
+
+ /** List model versions. */
+ list(
+ key: IntegrationTaskKey,
+ params: {
+ model_owner: string;
+ model_name: string;
+ }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.models.versions.list(params.model_owner, params.model_name);
+ },
+ {
+ name: "List Models",
+ params,
+ properties: modelProperties(params),
+ }
+ );
+ }
+}
diff --git a/integrations/replicate/src/predictions.ts b/integrations/replicate/src/predictions.ts
new file mode 100644
index 000000000..9f6c604fd
--- /dev/null
+++ b/integrations/replicate/src/predictions.ts
@@ -0,0 +1,101 @@
+import { IntegrationTaskKey } from "@trigger.dev/sdk";
+import ReplicateClient, { Page, Prediction } from "replicate";
+
+import { ReplicateRunTask } from "./index";
+import { CallbackTimeout, ReplicateReturnType } from "./types";
+import { callbackProperties, createPredictionProperties } from "./utils";
+
+export class Predictions {
+ constructor(private runTask: ReplicateRunTask) {}
+
+ /** Cancel a prediction. */
+ cancel(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.predictions.cancel(params.id);
+ },
+ {
+ name: "Cancel Prediction",
+ params,
+ properties: [{ label: "Prediction ID", text: params.id }],
+ }
+ );
+ }
+
+ /** Create a new prediction. */
+ create(
+ key: IntegrationTaskKey,
+ params: Parameters[0]
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.predictions.create(params);
+ },
+ {
+ name: "Create Prediction",
+ params,
+ properties: createPredictionProperties(params),
+ }
+ );
+ }
+
+ /** Create a new prediction and await the result. */
+ createAndAwait(
+ key: IntegrationTaskKey,
+ params: Omit<
+ Parameters[0],
+ "webhook" | "webhook_events_filter"
+ >,
+ options: CallbackTimeout = { timeoutInSeconds: 3600 }
+ ): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client, task) => {
+ return client.predictions.create({
+ ...params,
+ webhook: task.callbackUrl ?? "",
+ webhook_events_filter: ["completed"],
+ });
+ },
+ {
+ name: "Create And Await Prediction",
+ params,
+ properties: [...createPredictionProperties(params), ...callbackProperties(options)],
+ callback: {
+ enabled: true,
+ timeoutInSeconds: options.timeoutInSeconds,
+ },
+ }
+ );
+ }
+
+ /** Fetch a prediction. */
+ get(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType {
+ return this.runTask(
+ key,
+ (client) => {
+ return client.predictions.get(params.id);
+ },
+ {
+ name: "Get Prediction",
+ params,
+ properties: [{ label: "Prediction ID", text: params.id }],
+ }
+ );
+ }
+
+ /** List all predictions. */
+ list(key: IntegrationTaskKey): ReplicateReturnType