Implement @trigger.dev/nextjs package and make it work with the Next.js 13 app dir

This commit is contained in:
Eric Allam
2023-06-08 17:11:45 +01:00
parent 58473f1cce
commit 410efe1192
28 changed files with 2365 additions and 1294 deletions
+5 -4
View File
@@ -37,7 +37,7 @@ export class EndpointApi {
async ping() {
const response = await safeFetch(this.#url, {
method: "GET",
method: "POST",
headers: {
"Content-Type": "application/json",
"x-trigger-api-key": this.#apiKey,
@@ -68,10 +68,11 @@ export class EndpointApi {
async getEndpointData() {
const response = await safeFetch(this.#url, {
method: "GET",
method: "POSt",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"x-trigger-api-key": this.#apiKey,
"x-trigger-action": "GET_ENDPOINT_DATA",
},
});
@@ -132,7 +133,7 @@ export class EndpointApi {
const response = await safeFetch(this.#url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"content-type": "application/json",
"x-trigger-api-key": this.#apiKey,
"x-trigger-action": "EXECUTE_JOB",
},
@@ -159,10 +159,10 @@ export class RegisterJobService {
// Upsert the JobVersion
const jobVersion = await this.#prismaClient.jobVersion.upsert({
where: {
jobId_version_endpointId: {
jobId_version_environmentId: {
jobId: job.id,
version: metadata.version,
endpointId: endpoint.id,
environmentId: environment.id,
},
},
create: {
@@ -212,6 +212,11 @@ export class RegisterJobService {
id: jobQueue.id,
},
},
endpoint: {
connect: {
id: endpoint.id,
},
},
},
include: {
integrations: {
@@ -383,7 +383,7 @@ export class PerformRunExecutionService {
data: {
retryCount,
retryDelayInMs,
error: output,
error: JSON.stringify(output),
},
});
@@ -29,6 +29,7 @@ export class DeliverHttpSourceRequestService {
secretReference: true,
dynamicTrigger: true,
externalAccount: true,
apiClient: true,
},
},
},
@@ -89,8 +89,8 @@ export class RegisterSourceService {
const triggerSource = await tx.triggerSource.upsert({
where: {
key_endpointId: {
endpointId: endpoint.id,
key_environmentId: {
environmentId: environment.id,
key,
},
},
@@ -148,7 +148,16 @@ export class RegisterSourceService {
},
},
},
update: {},
update: {
endpoint: {
connect: {
id: endpoint.id,
},
},
apiClient: apiClient
? { connect: { id: apiClient.id } }
: undefined,
},
include: {
events: true,
secretReference: true,
@@ -37,8 +37,8 @@ export class UpdateSourceService {
const triggerSource =
await this.#prismaClient.triggerSource.findUniqueOrThrow({
where: {
key_endpointId: {
endpointId: endpoint.id,
key_environmentId: {
environmentId: environment.id,
key: id,
},
},
@@ -54,6 +54,7 @@ export class UpdateSourceService {
data: {
active: true,
channelData: payload.data as any,
endpointId: endpoint.id,
},
});
@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[jobId,version,environmentId]` on the table `JobVersion` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "JobVersion_jobId_version_endpointId_key";
-- CreateIndex
CREATE UNIQUE INDEX "JobVersion_jobId_version_environmentId_key" ON "JobVersion"("jobId", "version", "environmentId");
@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[key,environmentId]` on the table `TriggerSource` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "TriggerSource_key_endpointId_key";
-- CreateIndex
CREATE UNIQUE INDEX "TriggerSource_key_environmentId_key" ON "TriggerSource"("key", "environmentId");
+2 -2
View File
@@ -356,7 +356,7 @@ model JobVersion {
integrations JobIntegration[]
aliases JobAlias[]
@@unique([jobId, version, endpointId])
@@unique([jobId, version, environmentId])
}
model JobQueue {
@@ -738,7 +738,7 @@ model TriggerSource {
httpDeliveries HttpSourceRequestDelivery[]
registrations DynamicTriggerRegistration[]
@@unique([key, endpointId])
@@unique([key, environmentId])
}
enum TriggerChannel {
+4
View File
@@ -0,0 +1,4 @@
{
"typescript.tsdk": "../../node_modules/.pnpm/typescript@5.0.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
+3
View File
@@ -7,4 +7,7 @@ module.exports = {
"@trigger.dev/github",
"@trigger.dev/internal",
],
experimental: {
appDir: true,
},
};
+1
View File
@@ -11,6 +11,7 @@
"dependencies": {
"@trigger.dev/sdk": "workspace:*",
"@trigger.dev/github": "workspace:*",
"@trigger.dev/nextjs": "workspace:*",
"@types/node": "18.15.13",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
@@ -0,0 +1,42 @@
import { github, slack } from "@/trigger";
import { events } from "@trigger.dev/github";
import { makeAppHandler } from "@trigger.dev/nextjs";
import { Job, TriggerClient } from "@trigger.dev/sdk";
export const client = new TriggerClient({
id: "nextjs-appdir-example",
url: process.env.VERCEL_URL,
apiKey: process.env.TRIGGER_API_KEY,
apiUrl: process.env.TRIGGER_API_URL,
logLevel: "debug",
});
new Job(client, {
id: "appdir-alert-on-new-github-issues",
name: "AppDir: Alert on new GitHub issues",
version: "0.1.1",
enabled: true,
integrations: {
slack,
},
trigger: github.triggers.repo({
event: events.onIssueOpened,
repo: "ericallam/basic-starter-12k",
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a simple log info message");
const response = await io.slack.postMessage("Slack 📝", {
text: `New Issue opened: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
return response;
},
});
export const { POST, dynamic } = makeAppHandler(client, {
path: "/api/v2/trigger",
});
@@ -0,0 +1,20 @@
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Home",
description: "Welcome to Next.js",
};
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
@@ -1,475 +1,10 @@
import {
cronTrigger,
eventTrigger,
DynamicSchedule,
DynamicTrigger,
intervalTrigger,
Job,
missingConnectionNotification,
missingConnectionResolvedNotification,
NormalizedRequest,
TriggerClient,
} from "@trigger.dev/sdk";
import { Github, events } from "@trigger.dev/github";
import { Slack } from "@trigger.dev/slack";
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { client } from "@/trigger";
import { makeHandler } from "@trigger.dev/nextjs";
const github = new Github({ id: "github" });
const githubUser = new Github({ id: "github-user" });
export default makeHandler(client, { path: "/api/trigger" });
// const githubLocal = new Github({
// id: "github-local",
// token: process.env.GITHUB_TOKEN,
// });
const slack = new Slack({ id: "my-slack-new" });
const client = new TriggerClient("nextjs", {
apiKey: process.env.TRIGGER_API_KEY,
apiUrl: "http://localhost:3000",
endpoint: "http://localhost:3001/api/trigger",
logLevel: "debug",
});
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
id: "github-issue-opened",
event: events.onIssueOpened,
source: github.sources.repo,
});
const dynamicUserTrigger = new DynamicTrigger(client, {
id: "dynamic-user-trigger",
event: events.onIssueOpened,
source: githubUser.sources.repo,
});
const dynamicSchedule = new DynamicSchedule(client, {
id: "dynamic-interval",
});
const enabled = true;
new Job(client, {
id: "on-missing-auth-connection",
name: "On missing auth connection",
version: "0.1.1",
enabled,
trigger: missingConnectionNotification([githubUser]),
integrations: {
slack,
export const config = {
api: {
bodyParser: false,
},
run: async (payload, io, ctx) => {
switch (payload.type) {
case "DEVELOPER": {
return await io.slack.postMessage("message", {
text: `Missing developer connection: ${JSON.stringify(payload)}`,
channel: "C04GWUTDC3W",
});
}
case "EXTERNAL": {
return await io.slack.postMessage("message", {
text: `Missing external connection: account: ${JSON.stringify(
payload.account
)}, payload: ${JSON.stringify(payload)}`,
channel: "C04GWUTDC3W",
});
}
}
},
});
new Job(client, {
id: "on-missing-auth-connection-resolved",
name: "On missing auth connection-resolved",
version: "0.1.1",
enabled,
trigger: missingConnectionResolvedNotification([githubUser]),
integrations: {
slack,
},
run: async (payload, io, ctx) => {
switch (payload.type) {
case "DEVELOPER": {
return await io.slack.postMessage("message", {
text: `Missing developer connection resolved: ${JSON.stringify(
payload
)}`,
channel: "C04GWUTDC3W",
});
}
case "EXTERNAL": {
return await io.slack.postMessage("message", {
text: `Missing external connection resolved: ${JSON.stringify(
payload
)}`,
channel: "C04GWUTDC3W",
});
}
}
},
});
new Job(client, {
id: "user-on-issue-opened",
name: "user on issue opened",
version: "0.1.1",
enabled,
trigger: dynamicUserTrigger,
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
return await io.github.getRepo("get.repo", {
repo: payload.repository.full_name,
});
},
});
new Job(client, {
id: "get-user-repo",
name: "Get User Repo",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "get.repo",
schema: z.object({
repo: z.string(),
}),
}),
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
await io.logger.info("This is a log info message", {
payload,
});
await io.wait("wait", 1);
return await io.github.getRepo("get.repo", payload);
},
});
new Job(client, {
id: "get-user-repo-on-schedule",
name: "Get User Repo On Schedule",
version: "0.1.1",
enabled,
trigger: dynamicSchedule,
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
return await io.github.getRepo("get.repo", {
repo: ctx.event.context.source.metadata.repo,
});
},
});
new Job(client, {
id: "register-dynamic-interval",
name: "Register Dynamic Interval",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "dynamic.interval",
schema: z.object({
id: z.string(),
seconds: z.number().int().positive(),
}),
}),
run: async (payload, io, ctx) => {
await io.registerInterval("📆", dynamicSchedule, payload.id, {
seconds: payload.seconds,
});
await io.wait("wait", 60);
await io.unregisterInterval("❌📆", dynamicSchedule, payload.id);
},
});
new Job(client, {
id: "register-dynamic-cron",
name: "Register Dynamic Cron",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "dynamic.cron",
schema: z.object({
id: z.string(),
cron: z.string(),
}),
}),
run: async (payload, io, ctx) => {
await io.registerCron("📆", dynamicSchedule, payload.id, {
cron: payload.cron,
});
await io.wait("wait", 60);
await io.unregisterCron("❌📆", dynamicSchedule, payload.id);
},
});
new Job(client, {
id: "use-dynamic-interval",
name: "Use Dynamic Interval",
version: "0.1.1",
enabled,
trigger: dynamicSchedule,
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
},
});
new Job(client, {
id: "scheduled-job-1",
name: "Scheduled Job 1",
version: "0.1.1",
enabled: true,
trigger: intervalTrigger({
seconds: 60,
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
await io.runTask(
"level 1",
{
name: "Level 1",
},
async () => {
await io.runTask(
"level 2",
{
name: "Level 2",
},
async () => {
await io.runTask(
"level 3",
{
name: "Level 3",
},
async () => {
await io.runTask(
"level 4",
{
name: "Level 4",
},
async () => {
await io.runTask(
"level 5",
{
name: "Level 5",
},
async () => {}
);
}
);
}
);
}
);
}
);
},
});
new Job(client, {
id: "scheduled-job-2",
name: "Scheduled Job 2",
version: "0.1.1",
enabled,
trigger: cronTrigger({
cron: "*/5 * * * *", // every 5 minutes
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
ctx,
});
},
});
new Job(client, {
id: "test-io-functions",
name: "Test IO functions",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "test.io",
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
},
});
new Job(client, {
id: "register-dynamic-trigger-on-new-repo",
name: "Register dynamic trigger on new repo",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "new.repo",
schema: z.object({ repo: z.string() }),
}),
run: async (payload, io, ctx) => {
return await io.registerTrigger(
"register-repo",
dynamicOnIssueOpenedTrigger,
payload.repo,
{
repo: payload.repo,
}
);
},
});
new Job(client, {
id: "listen-for-dynamic-trigger",
name: "Listen for dynamic trigger",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo: ${
payload.issue.html_url
}. \n\n${JSON.stringify(ctx)}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "listen-for-dynamic-trigger-2",
name: "Listen for dynamic trigger-2",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo 2: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "listen-for-dynamic-trigger-3",
name: "Listen for dynamic trigger-3",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo 3: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "alert-on-new-github-issues-6",
name: "Alert on new GitHub issues",
version: "0.1.1",
enabled,
integrations: {
slack,
},
trigger: github.triggers.repo({
event: events.onIssueOpened,
repo: "ericallam/basic-starter-render-test",
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a simple log info message");
const response = await io.slack.postMessage("Slack 📝", {
text: `New Issue opened: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
return response;
},
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const normalizedRequest = normalizeRequest(req);
const response = await client.handleRequest(normalizedRequest);
if (!response) {
res.status(404).json({ error: "Not found" });
return;
}
res.status(response.status).json(response.body);
}
function normalizeRequest(req: NextApiRequest): NormalizedRequest {
const normalizedHeaders = Object.entries(req.headers).reduce(
(acc, [key, value]) => {
acc[key] = value as string;
return acc;
},
{} as Record<string, string>
);
const normalizedQuery = Object.entries(req.query).reduce(
(acc, [key, value]) => {
acc[key] = value as string;
return acc;
},
{} as Record<string, string>
);
return {
body: req.body,
headers: normalizedHeaders,
method: req.method ?? "GET",
query: normalizedQuery,
url: req.url ?? "/",
};
}
};
+431
View File
@@ -0,0 +1,431 @@
import { Github, events } from "@trigger.dev/github";
import {
DynamicSchedule,
DynamicTrigger,
Job,
TriggerClient,
cronTrigger,
eventTrigger,
intervalTrigger,
missingConnectionNotification,
missingConnectionResolvedNotification,
} from "@trigger.dev/sdk";
import { Slack } from "@trigger.dev/slack";
import { z } from "zod";
export const client = new TriggerClient({
id: "nextjs-example",
url: process.env.VERCEL_URL,
apiKey: process.env.TRIGGER_API_KEY,
apiUrl: process.env.TRIGGER_API_URL,
logLevel: "debug",
});
export const github = new Github({ id: "github" });
const githubUser = new Github({ id: "github-user" });
// const githubLocal = new Github({
// id: "github-local",
// token: process.env.GITHUB_TOKEN,
// });
export const slack = new Slack({ id: "my-slack-new" });
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
id: "github-issue-opened",
event: events.onIssueOpened,
source: github.sources.repo,
});
const dynamicUserTrigger = new DynamicTrigger(client, {
id: "dynamic-user-trigger",
event: events.onIssueOpened,
source: githubUser.sources.repo,
});
const dynamicSchedule = new DynamicSchedule(client, {
id: "dynamic-interval",
});
const enabled = true;
new Job(client, {
id: "on-missing-auth-connection",
name: "On missing auth connection",
version: "0.1.1",
enabled,
trigger: missingConnectionNotification([githubUser]),
integrations: {
slack,
},
run: async (payload, io, ctx) => {
switch (payload.type) {
case "DEVELOPER": {
return await io.slack.postMessage("message", {
text: `Missing developer connection: ${JSON.stringify(payload)}`,
channel: "C04GWUTDC3W",
});
}
case "EXTERNAL": {
return await io.slack.postMessage("message", {
text: `Missing external connection: account: ${JSON.stringify(
payload.account
)}, payload: ${JSON.stringify(payload)}`,
channel: "C04GWUTDC3W",
});
}
}
},
});
new Job(client, {
id: "on-missing-auth-connection-resolved",
name: "On missing auth connection-resolved",
version: "0.1.1",
enabled,
trigger: missingConnectionResolvedNotification([githubUser]),
integrations: {
slack,
},
run: async (payload, io, ctx) => {
switch (payload.type) {
case "DEVELOPER": {
return await io.slack.postMessage("message", {
text: `Missing developer connection resolved: ${JSON.stringify(
payload
)}`,
channel: "C04GWUTDC3W",
});
}
case "EXTERNAL": {
return await io.slack.postMessage("message", {
text: `Missing external connection resolved: ${JSON.stringify(
payload
)}`,
channel: "C04GWUTDC3W",
});
}
}
},
});
new Job(client, {
id: "user-on-issue-opened",
name: "user on issue opened",
version: "0.1.1",
enabled,
trigger: dynamicUserTrigger,
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
return await io.github.getRepo("get.repo", {
repo: payload.repository.full_name,
});
},
});
new Job(client, {
id: "get-user-repo",
name: "Get User Repo",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "get.repo",
schema: z.object({
repo: z.string(),
}),
}),
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
await io.logger.info("This is a log info message", {
payload,
});
await io.wait("wait", 1);
return await io.github.getRepo("get.repo", payload);
},
});
new Job(client, {
id: "get-user-repo-on-schedule",
name: "Get User Repo On Schedule",
version: "0.1.1",
enabled,
trigger: dynamicSchedule,
integrations: {
github: githubUser,
},
run: async (payload, io, ctx) => {
return await io.github.getRepo("get.repo", {
repo: ctx.event.context.source.metadata.repo,
});
},
});
new Job(client, {
id: "register-dynamic-interval",
name: "Register Dynamic Interval",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "dynamic.interval",
schema: z.object({
id: z.string(),
seconds: z.number().int().positive(),
}),
}),
run: async (payload, io, ctx) => {
await io.registerInterval("📆", dynamicSchedule, payload.id, {
seconds: payload.seconds,
});
await io.wait("wait", 60);
await io.unregisterInterval("❌📆", dynamicSchedule, payload.id);
},
});
new Job(client, {
id: "register-dynamic-cron",
name: "Register Dynamic Cron",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "dynamic.cron",
schema: z.object({
id: z.string(),
cron: z.string(),
}),
}),
run: async (payload, io, ctx) => {
await io.registerCron("📆", dynamicSchedule, payload.id, {
cron: payload.cron,
});
await io.wait("wait", 60);
await io.unregisterCron("❌📆", dynamicSchedule, payload.id);
},
});
new Job(client, {
id: "use-dynamic-interval",
name: "Use Dynamic Interval",
version: "0.1.1",
enabled,
trigger: dynamicSchedule,
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
},
});
new Job(client, {
id: "scheduled-job-1",
name: "Scheduled Job 1",
version: "0.1.1",
enabled: true,
trigger: intervalTrigger({
seconds: 60,
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
await io.runTask(
"level 1",
{
name: "Level 1",
},
async () => {
await io.runTask(
"level 2",
{
name: "Level 2",
},
async () => {
await io.runTask(
"level 3",
{
name: "Level 3",
},
async () => {
await io.runTask(
"level 4",
{
name: "Level 4",
},
async () => {
await io.runTask(
"level 5",
{
name: "Level 5",
},
async () => {}
);
}
);
}
);
}
);
}
);
},
});
new Job(client, {
id: "scheduled-job-2",
name: "Scheduled Job 2",
version: "0.1.1",
enabled,
trigger: cronTrigger({
cron: "*/5 * * * *", // every 5 minutes
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
ctx,
});
},
});
new Job(client, {
id: "test-io-functions",
name: "Test IO functions",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "test.io",
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a log info message", {
payload,
});
await io.sendEvent("send-event", {
name: "custom.event",
payload,
context: ctx,
});
},
});
new Job(client, {
id: "register-dynamic-trigger-on-new-repo",
name: "Register dynamic trigger on new repo",
version: "0.1.1",
enabled,
trigger: eventTrigger({
name: "new.repo",
schema: z.object({ repo: z.string() }),
}),
run: async (payload, io, ctx) => {
return await io.registerTrigger(
"register-repo",
dynamicOnIssueOpenedTrigger,
payload.repo,
{
repo: payload.repo,
}
);
},
});
new Job(client, {
id: "listen-for-dynamic-trigger",
name: "Listen for dynamic trigger",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo: ${
payload.issue.html_url
}. \n\n${JSON.stringify(ctx)}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "listen-for-dynamic-trigger-2",
name: "Listen for dynamic trigger-2",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo 2: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "listen-for-dynamic-trigger-3",
name: "Listen for dynamic trigger-3",
version: "0.1.1",
enabled,
trigger: dynamicOnIssueOpenedTrigger,
integrations: {
slack,
},
run: async (payload, io, ctx) => {
await io.slack.postMessage("Slack 📝", {
text: `New Issue opened on dynamically triggered repo 3: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
},
});
new Job(client, {
id: "alert-on-new-github-issues-3",
name: "Alert on new GitHub issues",
version: "0.1.1",
enabled,
integrations: {
slack,
},
trigger: github.triggers.repo({
event: events.onIssueOpened,
repo: "ericallam/basic-starter-12k",
}),
run: async (payload, io, ctx) => {
await io.wait("wait", 5); // wait for 5 seconds
await io.logger.info("This is a simple log info message");
const response = await io.slack.postMessage("Slack 📝", {
text: `New Issue opened: ${payload.issue.html_url}`,
channel: "C04GWUTDC3W",
});
return response;
},
});
+53 -13
View File
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -15,17 +19,53 @@
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"],
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
"@trigger.dev/internal": ["../../packages/internal/src/index"],
"@trigger.dev/internal/*": ["../../packages/internal/src/*"],
"@trigger.dev/github": ["../../integrations/github/src/index"],
"@trigger.dev/github/*": ["../../integrations/github/src/*"],
"@trigger.dev/slack": ["../../integrations/slack/src/index"],
"@trigger.dev/slack/*": ["../../integrations/slack/src/*"]
}
"@/*": [
"./src/*"
],
"@trigger.dev/sdk": [
"../../packages/trigger-sdk/src/index"
],
"@trigger.dev/sdk/*": [
"../../packages/trigger-sdk/src/*"
],
"@trigger.dev/nextjs": [
"../../packages/nextjs/src/index"
],
"@trigger.dev/nextjs/*": [
"../../packages/nextjs/src/*"
],
"@trigger.dev/internal": [
"../../packages/internal/src/index"
],
"@trigger.dev/internal/*": [
"../../packages/internal/src/*"
],
"@trigger.dev/github": [
"../../integrations/github/src/index"
],
"@trigger.dev/github/*": [
"../../integrations/github/src/*"
],
"@trigger.dev/slack": [
"../../integrations/slack/src/index"
],
"@trigger.dev/slack/*": [
"../../integrations/slack/src/*"
]
},
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
+84 -77
View File
@@ -3,7 +3,9 @@ import {
IntegrationClient,
ExternalSource,
TriggerIntegration,
HandlerEvent,
} from "@trigger.dev/sdk";
import type { Logger } from "@trigger.dev/sdk";
import { Octokit } from "octokit";
import { z } from "zod";
import { tasks } from "./tasks";
@@ -47,82 +49,7 @@ export function createRepoEventSource(
full_name: [params.repo],
},
}),
handler: async (event, logger) => {
logger.debug("[inside github integration] Handling github repo event");
const { rawEvent: request, source } = event;
if (!request.rawBody) {
logger.debug("[inside github integration] No rawBody found");
return;
}
const deliveryId = request.headers["x-github-delivery"];
const hookId = request.headers["x-github-hook-id"];
const signature = request.headers["x-hub-signature-256"];
if (source.secret && signature) {
const githubWebhooks = new Webhooks({
secret: source.secret,
});
if (!githubWebhooks.verify(request.rawBody, signature)) {
logger.debug(
"[inside github integration] Unable to verify the signature of the rawBody",
{
signature,
secret: source.secret,
}
);
return;
}
}
const name = request.headers["x-github-event"];
const context = omit(request.headers, [
"x-github-event",
"x-github-delivery",
"x-hub-signature-256",
"x-hub-signature",
"content-type",
"content-length",
"accept",
"accept-encoding",
"x-forwarded-proto",
]);
const payload = parseBody(request.rawBody);
if (!payload) {
logger.debug("[inside github integration] Unable to parse the rawBody");
return;
}
logger.debug(
"[inside github integration] Returning an event for the webhook!",
{
name,
payload,
context,
}
);
return {
events: [
{
id: [hookId, deliveryId].join(":"),
source: "github.com",
payload,
name,
context,
},
],
};
},
handler: webhookHandler,
register: async (event, io, ctx) => {
const { params, source: httpSource, events, missingEvents } = event;
@@ -209,7 +136,7 @@ export function createOrgEventSource(
login: [params.org],
},
}),
handler: async (event) => {},
handler: webhookHandler,
register: async (event, io, ctx) => {
const { params, source: httpSource, events, missingEvents } = event;
@@ -316,3 +243,83 @@ function omit<T extends Record<string, unknown>, K extends keyof T>(
return result;
}
async function webhookHandler(event: HandlerEvent<"HTTP">, logger: Logger) {
logger.debug("[inside github integration] Handling github repo event");
const { rawEvent: request, source } = event;
if (!request.body) {
logger.debug("[inside github integration] No body found");
return;
}
const rawBody = await request.text();
const deliveryId = request.headers.get("x-github-delivery");
const hookId = request.headers.get("x-github-hook-id");
const signature = request.headers.get("x-hub-signature-256");
if (source.secret && signature) {
const githubWebhooks = new Webhooks({
secret: source.secret,
});
if (!githubWebhooks.verify(rawBody, signature)) {
logger.debug(
"[inside github integration] Unable to verify the signature of the rawBody",
{
signature,
secret: source.secret,
}
);
return;
}
}
const name = request.headers.get("x-github-event") ?? "unknown";
const allHeaders = Object.fromEntries(request.headers.entries());
const context = omit(allHeaders, [
"x-github-event",
"x-github-delivery",
"x-hub-signature-256",
"x-hub-signature",
"content-type",
"content-length",
"accept",
"accept-encoding",
"x-forwarded-proto",
]);
const payload = parseBody(rawBody);
if (!payload) {
logger.debug("[inside github integration] Unable to parse the rawBody");
return;
}
logger.debug(
"[inside github integration] Returning an event for the webhook!",
{
name,
payload,
context,
}
);
return {
events: [
{
id: [hookId, deliveryId].join(":"),
source: "github.com",
payload,
name,
context,
},
],
};
}
+43
View File
@@ -0,0 +1,43 @@
{
"name": "@trigger.dev/nextjs",
"version": "0.1.0",
"description": "Trigger.dev Next.js integration",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js"
},
"./package.json": "./package.json"
},
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
"@types/debug": "^4.1.7",
"@types/ws": "^8.5.3",
"next": "13.3.1",
"rimraf": "^3.0.2",
"tsup": "^6.5.0",
"tsx": "^3.12.1"
},
"scripts": {
"clean": "rimraf dist",
"build": "npm run clean && npm run build:tsup",
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^1.0.0"
},
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">=18"
}
}
+75
View File
@@ -0,0 +1,75 @@
import type { TriggerClient } from "@trigger.dev/sdk";
import type { NextApiRequest, NextApiResponse } from "next";
import { NextResponse } from "next/server";
export type TriggerHandlerOptions = {
path: string;
};
export function makeHandler(
client: TriggerClient,
options: TriggerHandlerOptions
) {
client.path = options.path;
return async function handler(req: NextApiRequest, res: NextApiResponse) {
const normalizedRequest = await convertToStandardRequest(client.url, req);
const response = await client.handleRequest(normalizedRequest);
if (!response) {
res.status(404).json({ error: "Not found" });
return;
}
res.status(response.status).json(response.body);
};
}
export function makeAppHandler(
client: TriggerClient,
options: TriggerHandlerOptions
) {
client.path = options.path;
const POST = async function handler(req: Request) {
const response = await client.handleRequest(req);
if (!response) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return NextResponse.json(response.body, { status: response.status });
};
return {
POST,
dynamic: "force-dynamic",
runtime: "nodejs",
preferredRegion: "auto",
};
}
async function convertToStandardRequest(
url: string,
nextReq: NextApiRequest
): Promise<Request> {
const { headers: nextHeaders, method } = nextReq;
const headers = new Headers();
Object.entries(nextHeaders).forEach(([key, value]) => {
headers.set(key, value as string);
});
// Create a new Request object
const webReq = new Request(url, {
headers,
method,
// @ts-ignore
body: nextReq,
});
return webReq;
}
+16
View File
@@ -0,0 +1,16 @@
{
"extends": "@trigger.dev/tsconfig/node18.json",
"include": ["./src/**/*.ts", "tsup.config.ts"],
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"declaration": false,
"declarationMap": false,
"lib": ["DOM", "DOM.Iterable"],
"paths": {
"@trigger.dev/sdk": ["../trigger-sdk/src/index"],
"@trigger.dev/sdk/*": ["../trigger-sdk/src/*"]
}
},
"exclude": ["node_modules"]
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from "tsup";
export default defineConfig([
{
name: "main",
entry: ["./src/index.ts"],
outDir: "./dist",
platform: "node",
format: ["cjs"],
legacyOutput: true,
sourcemap: true,
clean: true,
bundle: true,
splitting: false,
dts: true,
external: ["http", "https", "util", "events", "tty", "os", "timers"],
esbuildPlugins: [],
},
]);
+1 -1
View File
@@ -158,7 +158,7 @@ export class IO {
},
async (task) => {
return await this._apiClient.updateSource(
this._triggerClient.name,
this._triggerClient.id,
options.key,
options
);
+242 -212
View File
@@ -46,10 +46,10 @@ const registerSourceEvent: EventSpecification<RegisterSourceEvent> = {
};
export type TriggerClientOptions = {
id: string;
url?: string;
apiKey?: string;
apiUrl?: string;
endpoint?: string;
path?: string;
logLevel?: LogLevel;
};
@@ -66,7 +66,7 @@ export class TriggerClient {
string,
(
source: HandleTriggerSource,
request: HttpSourceEvent
request: Request
) => Promise<{
events: Array<SendEvent>;
response?: NormalizedResponse;
@@ -85,21 +85,32 @@ export class TriggerClient {
#client: ApiClient;
#logger: Logger;
name: string;
endpoint: string;
private _url: string;
id: string;
path?: string;
constructor(name: string, options: TriggerClientOptions) {
this.name = name;
this.endpoint = options.endpoint ?? buildEndpointUrl(options.path);
constructor(options: TriggerClientOptions) {
this.id = options.id;
this._url = buildClientUrl(options.url);
this.#options = options;
this.#client = new ApiClient(this.#options);
this.#logger = new Logger("trigger.dev", this.#options.logLevel);
}
async handleRequest(request: NormalizedRequest): Promise<NormalizedResponse> {
this.#logger.debug("handling request", { request });
get url() {
return `${this._url}${
this.path ? `${this.path.startsWith("/") ? "" : "/"}${this.path}` : ""
}`;
}
const apiKey = request.headers["x-trigger-api-key"];
async handleRequest(request: Request): Promise<NormalizedResponse> {
this.#logger.debug("handling request", {
url: request.url,
headers: Object.fromEntries(request.headers.entries()),
method: request.method,
});
const apiKey = request.headers.get("x-trigger-api-key");
if (!this.authorized(apiKey)) {
return {
@@ -110,10 +121,28 @@ export class TriggerClient {
};
}
if (request.method === "GET") {
const action = request.headers["x-trigger-action"];
if (request.method !== "POST") {
return {
status: 405,
body: {
message: "Method not allowed",
},
};
}
if (action === "PING") {
const action = request.headers.get("x-trigger-action");
if (!action) {
return {
status: 400,
body: {
message: "Missing x-trigger-action header",
},
};
}
switch (action) {
case "PING": {
return {
status: 200,
body: {
@@ -121,10 +150,104 @@ export class TriggerClient {
},
};
}
case "GET_ENDPOINT_DATA": {
// if the x-trigger-job-id header is set, we return the job with that id
const jobId = request.headers.get("x-trigger-job-id");
// if the x-trigger-job-id header is set, we return the job with that id
if (request.headers["x-trigger-job-id"]) {
const job = this.#registeredJobs[request.headers["x-trigger-job-id"]];
if (jobId) {
const job = this.#registeredJobs[jobId];
if (!job) {
return {
status: 404,
body: {
message: "Job not found",
},
};
}
return {
status: 200,
body: job.toJSON(),
};
}
const body: GetEndpointDataResponse = {
jobs: Object.values(this.#registeredJobs).map((job) => job.toJSON()),
sources: Object.values(this.#registeredSources),
dynamicTriggers: Object.values(this.#registeredDynamicTriggers).map(
(trigger) => ({
id: trigger.id,
jobs: this.#jobMetadataByDynamicTriggers[trigger.id] ?? [],
})
),
dynamicSchedules: Object.entries(this.#registeredSchedules).map(
([id, jobs]) => ({
id,
jobs,
})
),
};
// if the x-trigger-job-id header is not set, we return all jobs
return {
status: 200,
body,
};
}
case "INITIALIZE": {
await this.listen();
return {
status: 200,
body: {
message: "Initialized",
},
};
}
case "INITIALIZE_TRIGGER": {
const json = await request.json();
const body = InitializeTriggerBodySchema.safeParse(json);
if (!body.success) {
return {
status: 400,
body: {
message: "Invalid trigger body",
},
};
}
const dynamicTrigger = this.#registeredDynamicTriggers[body.data.id];
if (!dynamicTrigger) {
return {
status: 404,
body: {
message: "Dynamic trigger not found",
},
};
}
return {
status: 200,
body: dynamicTrigger.registeredTriggerForParams(body.data.params),
};
}
case "EXECUTE_JOB": {
const json = await request.json();
const execution = RunJobBodySchema.safeParse(json);
if (!execution.success) {
return {
status: 400,
body: {
message: "Invalid execution",
},
};
}
const job = this.#registeredJobs[execution.data.job.id];
if (!job) {
return {
@@ -135,202 +258,108 @@ export class TriggerClient {
};
}
const results = await this.#executeJob(execution.data, job);
if (results.error) {
return {
status: 500,
body: results.error,
};
}
return {
status: 200,
body: job.toJSON(),
body: {
completed: results.completed,
output: results.output,
executionId: execution.data.run.id,
task: results.task,
},
};
}
case "PREPROCESS_RUN": {
const json = await request.json();
const body = PreprocessRunBodySchema.safeParse(json);
const body: GetEndpointDataResponse = {
jobs: Object.values(this.#registeredJobs).map((job) => job.toJSON()),
sources: Object.values(this.#registeredSources),
dynamicTriggers: Object.values(this.#registeredDynamicTriggers).map(
(trigger) => ({
id: trigger.id,
jobs: this.#jobMetadataByDynamicTriggers[trigger.id] ?? [],
})
),
dynamicSchedules: Object.entries(this.#registeredSchedules).map(
([id, jobs]) => ({
id,
jobs,
})
),
};
// if the x-trigger-job-id header is not set, we return all jobs
return {
status: 200,
body,
};
}
if (request.method === "POST") {
// Get the action from the headers
const action = request.headers["x-trigger-action"];
switch (action) {
case "INITIALIZE": {
await this.listen();
if (!body.success) {
return {
status: 200,
status: 400,
body: {
message: "Initialized",
message: "Invalid body",
},
};
}
case "INITIALIZE_TRIGGER": {
const body = InitializeTriggerBodySchema.safeParse(request.body);
if (!body.success) {
return {
status: 400,
body: {
message: "Invalid trigger body",
},
};
}
const dynamicTrigger = this.#registeredDynamicTriggers[body.data.id];
if (!dynamicTrigger) {
return {
status: 404,
body: {
message: "Dynamic trigger not found",
},
};
}
const job = this.#registeredJobs[body.data.job.id];
if (!job) {
return {
status: 200,
body: dynamicTrigger.registeredTriggerForParams(body.data.params),
};
}
case "EXECUTE_JOB": {
const execution = RunJobBodySchema.safeParse(request.body);
if (!execution.success) {
return {
status: 400,
body: {
message: "Invalid execution",
},
};
}
const job = this.#registeredJobs[execution.data.job.id];
if (!job) {
return {
status: 404,
body: {
message: "Job not found",
},
};
}
const results = await this.#executeJob(execution.data, job);
if (results.error) {
return {
status: 500,
body: results.error,
};
}
return {
status: 200,
status: 404,
body: {
completed: results.completed,
output: results.output,
executionId: execution.data.run.id,
task: results.task,
message: "Job not found",
},
};
}
case "PREPROCESS_RUN": {
const body = PreprocessRunBodySchema.safeParse(request.body);
if (!body.success) {
return {
status: 400,
body: {
message: "Invalid body",
},
};
}
const results = await this.#preprocessRun(body.data, job);
const job = this.#registeredJobs[body.data.job.id];
if (!job) {
return {
status: 404,
body: {
message: "Job not found",
},
};
}
const results = await this.#preprocessRun(body.data, job);
return {
status: 200,
body: {
abort: results.abort,
elements: results.elements,
},
};
}
case "DELIVER_HTTP_SOURCE_REQUEST": {
const headers = HttpSourceRequestHeadersSchema.safeParse(
Object.fromEntries(request.headers.entries())
);
if (!headers.success) {
return {
status: 200,
status: 400,
body: {
abort: results.abort,
elements: results.elements,
message: "Invalid headers",
},
};
}
case "DELIVER_HTTP_SOURCE_REQUEST": {
const headers = HttpSourceRequestHeadersSchema.safeParse(
request.headers
);
if (!headers.success) {
return {
status: 400,
body: {
message: "Invalid headers",
},
};
}
const sourceRequest = new Request(headers.data["x-ts-http-url"], {
method: headers.data["x-ts-http-method"],
headers: headers.data["x-ts-http-headers"],
body:
headers.data["x-ts-http-method"] !== "GET"
? request.body
: undefined,
});
const sourceRequest = {
url: headers.data["x-ts-http-url"],
method: headers.data["x-ts-http-method"],
headers: headers.data["x-ts-http-headers"],
rawBody: request.body,
};
const key = headers.data["x-ts-key"];
const dynamicId = headers.data["x-ts-dynamic-id"];
const secret = headers.data["x-ts-secret"];
const params = headers.data["x-ts-params"];
const data = headers.data["x-ts-data"];
const key = headers.data["x-ts-key"];
const dynamicId = headers.data["x-ts-dynamic-id"];
const secret = headers.data["x-ts-secret"];
const params = headers.data["x-ts-params"];
const data = headers.data["x-ts-data"];
const source = {
key,
dynamicId,
secret,
params,
data,
};
const source = {
key,
dynamicId,
secret,
params,
data,
};
const { response, events } = await this.#handleHttpSourceRequest(
source,
sourceRequest
);
const { response, events } = await this.#handleHttpSourceRequest(
source,
sourceRequest
);
return {
status: 200,
body: {
events,
response,
},
};
}
return {
status: 200,
body: {
events,
response,
},
};
}
}
@@ -478,11 +507,11 @@ export class TriggerClient {
}
async registerTrigger(id: string, key: string, options: RegisterTriggerBody) {
return this.#client.registerTrigger(this.name, id, key, options);
return this.#client.registerTrigger(this.id, id, key, options);
}
async getAuth(id: string) {
return this.#client.getAuth(this.name, id);
return this.#client.getAuth(this.id, id);
}
async sendEvent(event: SendEvent, options?: SendEventOptions) {
@@ -490,14 +519,14 @@ export class TriggerClient {
}
async registerSchedule(id: string, key: string, schedule: ScheduleMetadata) {
return this.#client.registerSchedule(this.name, id, key, schedule);
return this.#client.registerSchedule(this.id, id, key, schedule);
}
async unregisterSchedule(id: string, key: string) {
return this.#client.unregisterSchedule(this.name, id, key);
return this.#client.unregisterSchedule(this.id, id, key);
}
authorized(apiKey: string) {
authorized(apiKey?: string | null) {
const localApiKey = this.#options.apiKey ?? process.env.TRIGGER_API_KEY;
if (!localApiKey) {
@@ -514,8 +543,8 @@ export class TriggerClient {
async listen() {
// Register the endpoint
await this.#client.registerEndpoint({
url: this.endpoint,
name: this.name,
url: this.url,
name: this.id,
});
}
@@ -635,7 +664,7 @@ export class TriggerClient {
data: any;
params: any;
},
sourceRequest: HttpSourceRequest
sourceRequest: Request
): Promise<{ response: NormalizedResponse; events: SendEvent[] }> {
this.#logger.debug("Handling HTTP source request", {
source,
@@ -733,30 +762,31 @@ export class TriggerClient {
}
}
function buildEndpointUrl(path?: string): string {
// Try to get the endpoint from the environment
const endpoint = process.env.TRIGGER_ENDPOINT;
function buildClientUrl(url?: string): string {
if (!url) {
// Try and get the host from the environment
const host =
process.env.TRIGGER_CLIENT_HOST ??
process.env.HOST ??
process.env.HOSTNAME ??
process.env.NOW_URL ??
process.env.VERCEL_URL;
// If the endpoint is set, we return it + the path
if (endpoint) {
return endpoint + (path ?? "");
// If the host is set, we return it + the path
if (host) {
return "https://" + host;
}
// If we can't get the host, we throw an error
throw new Error(
"Could not determine the url for this TriggerClient. Please set the TRIGGER_CLIENT_HOST environment variable or pass in the `url` option to the TriggerClient constructor."
);
}
// Try and get the host from the environment
const host =
process.env.TRIGGER_HOST ??
process.env.HOST ??
process.env.HOSTNAME ??
process.env.NOW_URL ??
process.env.VERCEL_URL;
// If the host is set, we return it + the path
if (host) {
return "https://" + host + (path ?? "");
// Check to see if url has the protocol, and if it doesn't, add it
if (!url.startsWith("http")) {
return "https://" + url;
}
// If we can't get the host, we throw an error
throw new Error(
"Could not determine the endpoint for the trigger client. Please set the TRIGGER_ENDPOINT environment variable."
);
return url;
}
@@ -43,7 +43,7 @@ type SqsSourceEvent = {
type ExternalSourceChannelMap = {
HTTP: {
event: HttpSourceEvent;
event: Request;
register: {
url: string;
};
@@ -85,11 +85,16 @@ type RegisterFunction<
ctx: TriggerContext
) => Promise<UpdateTriggerSourceBody | undefined>;
export type HandlerEvent<
TChannel extends ChannelNames,
TParams extends any = any
> = {
rawEvent: ExternalSourceChannelMap[TChannel]["event"];
source: HandleTriggerSource & { params: TParams };
};
type HandlerFunction<TChannel extends ChannelNames, TParams extends any> = (
event: {
rawEvent: ExternalSourceChannelMap[TChannel]["event"];
source: HandleTriggerSource & { params: TParams };
},
event: HandlerEvent<TChannel, TParams>,
logger: Logger
) => Promise<{ events: SendEvent[]; response?: NormalizedResponse } | void>;
+2 -1
View File
@@ -1,5 +1,6 @@
import type {
EventFilter,
Logger,
RuntimeEnvironmentType,
SecureString,
TriggerMetadata,
@@ -8,7 +9,7 @@ import { DisplayElement } from "@trigger.dev/internal";
import { Job } from "./job";
import { TriggerClient } from "./triggerClient";
export type { SecureString };
export type { SecureString, Logger };
export interface TriggerContext {
job: { id: string; version: string };
+2 -2
View File
@@ -5,8 +5,8 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"paths": {
"@trigger.dev/internal/*": ["../common-schemas/src/*"],
"@trigger.dev/internal": ["../common-schemas/src/index"]
"@trigger.dev/internal/*": ["../internal/src/*"],
"@trigger.dev/internal": ["../internal/src/index"]
},
"lib": ["DOM", "DOM.Iterable"],
"declaration": false,
+1257 -497
View File
File diff suppressed because it is too large Load Diff