Merge branch 'main' of github.com:triggerdotdev/trigger.dev

This commit is contained in:
Eric Allam
2023-10-16 16:04:09 +01:00
40 changed files with 2349 additions and 150 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---
Detects JSRuntime (Node/Deno at the moment). Adds basic Deno support
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---
Improve create-integration output. Use templates and shared configs.
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/airtable": patch
---
Export Base and Table
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"astro-build.astro-vscode",
"denoland.vscode-deno"
],
"unwantedRecommendations": [
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"deno.enablePaths": ["references/deno-reference"]
}
@@ -3,6 +3,8 @@
"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/*"],
+9
View File
@@ -0,0 +1,9 @@
{
"name": "@trigger.dev/tsup",
"version": "0.0.0",
"private": true,
"license": "MIT",
"devDependencies": {
"tsup": "7.1.x"
}
}
+3
View File
@@ -0,0 +1,3 @@
export { defineConfig } from "tsup";
export { deepMergeOptions } from "./utils";
export { options as integrationOptions } from "./integration";
+22
View File
@@ -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);
+32
View File
@@ -0,0 +1,32 @@
import { Options } from "tsup";
export const deepMergeOptions = deepMergeRecords<Options>;
function deepMergeRecords<TRecord extends Record<any, any>>(...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;
}
+1
View File
@@ -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 */
+1
View File
@@ -68,6 +68,7 @@
"execa": "^7.0.0",
"gradient-string": "^2.0.2",
"inquirer": "^9.1.4",
"liquidjs": "^10.9.2",
"localtunnel": "^2.0.2",
"mock-fs": "^5.2.0",
"nanoid": "^4.0.2",
+4 -1
View File
@@ -14,7 +14,10 @@ import { checkApiKeyIsDevServer } from "../utils/getApiKeyType";
export const program = new Command();
program.name(COMMAND_NAME).description("The Trigger.dev CLI").version("0.0.1");
program
.name(COMMAND_NAME)
.description("The Trigger.dev CLI")
.version(getVersion(), "-v, --version", "Display the version number");
program
.command("init")
+123 -97
View File
@@ -9,7 +9,8 @@ import { generateIntegrationFiles } from "../utils/generateIntegrationFiles";
import { getPackageName } from "../utils/getPackagName";
import { installDependencies } from "../utils/installDependencies";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { relativePath, resolvePath } from "../utils/parseNameAndPath";
import { createIntegrationFileFromTemplate } from "../utils/createIntegrationFileFromTemplate";
const CLIOptionsSchema = z.object({
packageName: z.string().optional(),
@@ -91,108 +92,107 @@ export async function createIntegrationCommand(path: string, cliOptions: any) {
process.exit(1);
}
// Create the package.json
const packageJson = {
name: resolvedOptions.packageName,
version: "0.0.1",
description: `Trigger.dev integration for ${resolvedOptions.sdkPackage}`,
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: {
"@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: {
[latestVersion.name]: `^${latestVersion.version}`,
[sdkVersion.name]: sdkVersion.version,
[integrationKitVersion.name]: integrationKitVersion.version,
},
engines: {
node: ">=16.8.0",
},
const integrationVersion = await getInternalOrExternalPackageVersion({
path: "integrations/github",
packageName: "@trigger.dev/github",
tag: "latest",
monorepoPath: triggerMonorepoPath,
prependWorkspace: false,
});
if (!integrationVersion) {
logger.error(
`Could not find the latest version of @trigger.dev/github. Please try again later.`
);
process.exit(1);
}
const baseVariables = {
packageName: resolvedOptions.packageName,
sdkPackage: resolvedOptions.sdkPackage,
integrationVersion: integrationVersion,
latestVersion: latestVersion,
sdkVersion: sdkVersion,
integrationKitVersion: integrationKitVersion,
triggerMonorepoPath,
};
await createFileInPath(resolvedPath, "package.json", JSON.stringify(packageJson, null, 2));
const getOutputPath = (relativePath: string) => pathModule.join(resolvedPath, relativePath);
// Create the tsconfig.json
const tsconfigJson = {
compilerOptions: {
composite: false,
declaration: false,
declarationMap: false,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
inlineSources: false,
isolatedModules: true,
moduleResolution: "node16",
noUnusedLocals: false,
noUnusedParameters: false,
preserveWatchOutput: true,
skipLibCheck: true,
strict: true,
experimentalDecorators: true,
emitDecoratorMetadata: true,
sourceMap: true,
resolveJsonModule: true,
lib: ["es2019"],
module: "commonjs",
target: "es2021",
const miscIntegrationFiles = [
{
relativeTemplatePath: "package.json.j2",
outputPath: getOutputPath("package.json"),
},
include: ["./src/**/*.ts", "tsup.config.ts"],
exclude: ["node_modules"],
};
await createFileInPath(resolvedPath, "tsconfig.json", JSON.stringify(tsconfigJson, null, 2));
const readme = `
# ${resolvedOptions.packageName}
`;
await createFileInPath(resolvedPath, "README.md", readme);
// Create the tsup.config.ts
const tsupConfig = `
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,
treeshake: {
preset: "smallest",
{
// use `tsc --showConfig` to update external tsconfig
relativeTemplatePath: `tsconfig-${triggerMonorepoPath ? "internal" : "external"}.json.j2`,
outputPath: getOutputPath("tsconfig.json"),
},
esbuildPlugins: [],
external: ["http", "https", "util", "events", "tty", "os", "timers"],
},
]);
{
relativeTemplatePath: `tsup.config-${triggerMonorepoPath ? "internal" : "external"}.js.j2`,
outputPath: getOutputPath("tsup.config.ts"),
},
{
relativeTemplatePath: "README.md.j2",
outputPath: getOutputPath("README.md"),
},
];
`;
await createFileInPath(resolvedPath, "tsup.config.ts", tsupConfig);
await createIntegrationFiles(miscIntegrationFiles, baseVariables);
// create src/*
if (resolvedOptions.skipGeneratingCode) {
await createFileInPath(resolvedPath, "src/index.ts", "export {}");
const getSrcOutputPath = (relativePath: string) =>
getOutputPath(pathModule.join("src", relativePath));
const srcIntegrationFiles = [
{
relativeTemplatePath: pathModule.join("payload-examples", "index.js.j2"),
outputPath: getSrcOutputPath(pathModule.join("payload-examples", "index.ts")),
},
{
relativeTemplatePath: "events.js.j2",
outputPath: getSrcOutputPath("events.ts"),
},
{
relativeTemplatePath: "index.js.j2",
outputPath: getSrcOutputPath("index.ts"),
},
{
relativeTemplatePath: "models.js.j2",
outputPath: getSrcOutputPath("models.ts"),
},
{
relativeTemplatePath: "schemas.js.j2",
outputPath: getSrcOutputPath("schemas.ts"),
},
{
relativeTemplatePath: "types.js.j2",
outputPath: getSrcOutputPath("types.ts"),
},
{
relativeTemplatePath: "utils.js.j2",
outputPath: getSrcOutputPath("utils.ts"),
},
{
relativeTemplatePath: "webhooks.js.j2",
outputPath: getSrcOutputPath("webhooks.ts"),
},
];
const validIdentifier = pathModule
.basename(path)
.replace(/[^a-zA-Z0-9]+/g, "")
.replace(/^[0-9]+/g, "");
await createIntegrationFiles(srcIntegrationFiles, {
...baseVariables,
apiKeyPropertyName: "apiKey", // TODO: prompt for this
authMethod: resolvedOptions.authMethod,
identifier: validIdentifier.length ? validIdentifier : "packageName",
});
} else {
await attemptToGenerateIntegrationFiles(pathModule.join(resolvedPath, "src"), resolvedOptions);
}
@@ -295,8 +295,9 @@ const resolveOptionsWithPrompts = async (
resolvedOptions.skipGeneratingCode = true;
}
resolvedOptions.authMethod = await promptAuthMethod();
if (!resolvedOptions.skipGeneratingCode) {
resolvedOptions.authMethod = await promptAuthMethod();
resolvedOptions.extraInfo = await promptExtraInfo();
}
} catch (err) {
@@ -448,11 +449,13 @@ async function getInternalOrExternalPackageVersion({
tag,
path,
monorepoPath,
prependWorkspace = true,
}: {
packageName: string;
tag: string;
path: string;
monorepoPath?: string;
prependWorkspace?: boolean;
}): Promise<{ name: string; version: string } | undefined> {
if (!monorepoPath) {
return await getLatestPackageVersion(packageName, tag);
@@ -470,7 +473,7 @@ async function getInternalOrExternalPackageVersion({
return {
name: packageJson.name,
version: `workspace:^${packageJson.version}`,
version: `${prependWorkspace ? "workspace:^" : ""}${packageJson.version}`,
};
}
@@ -550,3 +553,26 @@ async function updateJobCatalogWithNewIntegration(
};
await writeJSONFile(tsConfigPath, newTsConfig);
}
const createIntegrationFiles = async (
files: {
relativeTemplatePath: string;
outputPath: string;
}[],
variables?: Record<string, any>
) => {
for (const file of files) {
const result = await createIntegrationFileFromTemplate({ ...file, variables });
handleCreateResult(file.outputPath, result);
}
};
const handleCreateResult = (
outputPath: string,
result: Awaited<ReturnType<typeof createIntegrationFileFromTemplate>>
) => {
if (!result.success) {
throw new Error(`Failed to create ${pathModule.basename(outputPath)}: ${result.error}`);
}
logger.success(`✔ Created ${pathModule.basename(outputPath)} at ${relativePath(outputPath)}`);
};
+14 -50
View File
@@ -1,22 +1,18 @@
import boxen from "boxen";
import chalk from "chalk";
import childProcess from "child_process";
import chokidar from "chokidar";
import fs from "fs/promises";
import ngrok from "ngrok";
import { run as ncuRun } from "npm-check-updates";
import ora, { Ora } from "ora";
import pRetry, { AbortError } from "p-retry";
import pathModule from "path";
import util from "util";
import { z } from "zod";
import { Framework, getFramework } from "../frameworks";
import { Framework } from "../frameworks";
import { standardWatchFilePaths, standardWatchIgnoreRegex } from "../frameworks/watchConfig";
import { telemetryClient } from "../telemetry/telemetry";
import { getEnvFilename } from "../utils/env";
import fetch from "../utils/fetchUseProxy";
import { getTriggerApiDetails } from "../utils/getTriggerApiDetails";
import { getUserPackageManager } from "../utils/getUserPkgManager";
import { JsRuntime, getJsRuntime } from "../utils/jsRuntime";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { RequireKeys } from "../utils/requiredKeys";
@@ -45,6 +41,8 @@ const formattedDate = new Intl.DateTimeFormat("en", {
second: "numeric",
});
let runtime: JsRuntime;
export async function devCommand(path: string, anyOptions: any) {
telemetryClient.dev.started(path, anyOptions);
@@ -57,12 +55,12 @@ export async function devCommand(path: string, anyOptions: any) {
const options = result.data;
const resolvedPath = resolvePath(path);
runtime = await getJsRuntime(resolvedPath, logger);
//check for outdated packages, don't await this
checkForOutdatedPackages(resolvedPath);
runtime.checkForOutdatedPackages();
// Read from package.json to get the endpointId
const endpointId = await getEndpointIdFromPackageJson(resolvedPath, options);
const endpointId = await getEndpointId(runtime, options.clientId);
if (!endpointId) {
logger.error(
"You must run the `init` command first to setup the project you are missing \n'trigger.dev': { 'endpointId': 'your-client-id' } from your package.json file, or pass in the --client-id option to this command"
@@ -73,8 +71,8 @@ export async function devCommand(path: string, anyOptions: any) {
logger.success(`✔️ [trigger.dev] Detected TriggerClient id: ${endpointId}`);
//resolve the options using the detected framework (use default if there isn't a matching framework)
const packageManager = await getUserPackageManager(resolvedPath);
const framework = await getFramework(resolvedPath, packageManager);
const packageManager = await runtime.getUserPackageManager();
const framework = await runtime.getFramework();
const resolvedOptions = await resolveOptions(framework, resolvedPath, options);
// Read from .env.local or .env to get the TRIGGER_API_KEY and TRIGGER_API_URL
@@ -250,8 +248,7 @@ async function startIndexing({
apiClient,
}: RefreshOptions & { apiClient: TriggerApi }) {
spinner.start();
const refreshedEndpointId = await getEndpointIdFromPackageJson(path, resolvedOptions);
const refreshedEndpointId = await getEndpointId(runtime, resolvedOptions.clientId);
const authorizedKey = await apiClient.whoami();
if (!authorizedKey) {
@@ -396,43 +393,10 @@ async function verifyEndpoint(
return;
}
export async function checkForOutdatedPackages(path: string) {
const updates = (await ncuRun({
packageFile: `${path}/package.json`,
filter: "/trigger.dev/.+$/",
upgrade: false,
})) as {
[key: string]: string;
};
if (typeof updates === "undefined" || Object.keys(updates).length === 0) {
return;
}
const packageFile = await fs.readFile(`${path}/package.json`);
const data = JSON.parse(Buffer.from(packageFile).toString("utf8"));
const dependencies = data.dependencies;
console.log(chalk.bgYellow("Updates available for trigger.dev packages"));
console.log(chalk.bgBlue("Run npx @trigger.dev/cli@latest update"));
for (let dep in updates) {
console.log(`${dep} ${dependencies[dep]}${updates[dep]}`);
}
}
export async function getEndpointIdFromPackageJson(path: string, options: DevCommandOptions) {
if (options.clientId) {
return options.clientId;
}
const pkgJsonPath = pathModule.join(path, "package.json");
const pkgBuffer = await fs.readFile(pkgJsonPath);
const pkgJson = JSON.parse(pkgBuffer.toString());
const value = pkgJson["trigger.dev"]?.endpointId;
if (!value || typeof value !== "string") return;
return value as string;
export function getEndpointId(runtime: JsRuntime, clientId?: string) {
if (clientId) {
return clientId;
} else return runtime.getEndpointId();
}
async function resolveEndpointUrl(apiUrl: string, port: number, hostname: string) {
+14
View File
@@ -22,6 +22,7 @@ import { resolvePath } from "../utils/parseNameAndPath";
import { readPackageJson } from "../utils/readPackageJson";
import { renderTitle } from "../utils/renderTitle";
import { TriggerApi, WhoamiResponse } from "../utils/triggerApi";
import { getJsRuntime } from "../utils/jsRuntime";
export type InitCommandOptions = {
projectPath: string;
@@ -38,6 +39,19 @@ export const initCommand = async (options: InitCommandOptions) => {
const resolvedPath = resolvePath(options.projectPath);
// assuming nodejs by default
let runtimeId: string = "nodejs";
try {
runtimeId = (await getJsRuntime(resolvedPath, logger)).id;
} catch {}
if (runtimeId !== "nodejs") {
logger.error(
`We currently only support automatic setup for NodeJS projects. This is a ${runtimeId} project. View our manual installation guides here: https://trigger.dev/docs/documentation/quickstarts/introduction`
);
telemetryClient.init.failed("not_supported_runtime", options);
return;
}
await renderTitle(resolvedPath);
if (options.triggerUrl === CLOUD_TRIGGER_URL) {
+4 -2
View File
@@ -2,9 +2,10 @@ import { z } from "zod";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { TriggerApi } from "../utils/triggerApi";
import { DevCommandOptions, getEndpointIdFromPackageJson } from "./dev";
import { DevCommandOptions, getEndpointId } from "./dev";
import ora from "ora";
import { getTriggerApiDetails } from "../utils/getTriggerApiDetails";
import { getJsRuntime } from "../utils/jsRuntime";
export const WhoAmICommandOptionsSchema = z.object({
envFile: z.string(),
@@ -26,7 +27,8 @@ export async function whoamiCommand(path: string, anyOptions: any) {
const resolvedPath = resolvePath(path);
// Read from package.json to get the endpointId
const endpointId = await getEndpointIdFromPackageJson(resolvedPath, options as DevCommandOptions);
const runtime = await getJsRuntime(resolvedPath, logger);
const endpointId = await getEndpointId(runtime);
if (!endpointId) {
logger.error(
"You must run the `init` command first to setup the project you are missing \n'trigger.dev': { 'endpointId': 'your-client-id' } from your package.json file, or pass in the --client-id option to this command"
@@ -0,0 +1 @@
# {{ packageName }}
@@ -0,0 +1,118 @@
import { EventSpecification } from "@trigger.dev/sdk";
import { CommentEvent, IssueEvent } from "./schemas";
import { Get{{ identifier | capitalize }}Payload } from "./types";
import {
commentCreated,
commentRemoved,
commentUpdated,
issueCreated,
issueRemoved,
issueUpdated,
} from "./payload-examples";
import { onCommentProperties, onIssueProperties, updatedFromProperties } from "./utils";
export const onComment: EventSpecification<Get{{ identifier | capitalize }}Payload<CommentEvent>> = {
name: "Comment",
title: "On Comment",
source: "linear.app",
icon: "linear",
examples: [commentCreated, commentRemoved, commentUpdated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<CommentEvent>,
runProperties: (payload) => [
{ label: "Event action", text: payload.action },
...onCommentProperties(payload),
...updatedFromProperties(payload),
],
};
export const onCommentCreated: EventSpecification<Get{{ identifier | capitalize }}Payload<CommentEvent, "create">> = {
name: "Comment",
title: "On Comment Created",
source: "linear.app",
icon: "linear",
filter: {
action: ["create"],
},
examples: [commentCreated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<CommentEvent, "create">,
runProperties: (payload) => onCommentProperties(payload),
};
export const onCommentRemoved: EventSpecification<Get{{ identifier | capitalize }}Payload<CommentEvent, "remove">> = {
name: "Comment",
title: "On Comment Removed",
source: "linear.app",
icon: "linear",
filter: {
action: ["remove"],
},
examples: [commentRemoved],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<CommentEvent, "remove">,
runProperties: (payload) => onCommentProperties(payload),
};
export const onCommentUpdated: EventSpecification<Get{{ identifier | capitalize }}Payload<CommentEvent, "update">> = {
name: "Comment",
title: "On Comment Updated",
source: "linear.app",
icon: "linear",
filter: {
action: ["update"],
},
examples: [commentUpdated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<CommentEvent, "update">,
runProperties: (payload) => [...onCommentProperties(payload), ...updatedFromProperties(payload)],
};
export const onIssue: EventSpecification<Get{{ identifier | capitalize }}Payload<IssueEvent>> = {
name: "Issue",
title: "On Issue",
source: "linear.app",
icon: "linear",
examples: [issueCreated, issueRemoved, issueUpdated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<IssueEvent>,
runProperties: (payload) => [
{ label: "Event action", text: payload.action },
...onIssueProperties(payload),
...updatedFromProperties(payload),
],
};
export const onIssueCreated: EventSpecification<Get{{ identifier | capitalize }}Payload<IssueEvent, "create">> = {
name: "Issue",
title: "On Issue Created",
source: "linear.app",
icon: "linear",
filter: {
action: ["create"],
},
examples: [issueCreated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<IssueEvent, "create">,
runProperties: (payload) => onIssueProperties(payload),
};
export const onIssueRemoved: EventSpecification<Get{{ identifier | capitalize }}Payload<IssueEvent, "remove">> = {
name: "Issue",
title: "On Issue Removed",
source: "linear.app",
icon: "linear",
filter: {
action: ["remove"],
},
examples: [issueRemoved],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<IssueEvent, "remove">,
runProperties: (payload) => onIssueProperties(payload),
};
export const onIssueUpdated: EventSpecification<Get{{ identifier | capitalize }}Payload<IssueEvent, "update">> = {
name: "Issue",
title: "On Issue Updated",
source: "linear.app",
icon: "linear",
filter: {
action: ["update"],
},
examples: [issueUpdated],
parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload<IssueEvent, "update">,
runProperties: (payload) => [...onIssueProperties(payload), ...updatedFromProperties(payload)],
};
@@ -0,0 +1,244 @@
import {
TriggerIntegration,
RunTaskOptions,
IO,
IOTask,
IntegrationTaskKey,
RunTaskErrorCallback,
Json,
retry,
ConnectionAuth,
Prettify,
} from "@trigger.dev/sdk";
import {{ identifier | capitalize }}Client from "{{ sdkPackage }}";
import * as events from "./events";
import { {{ identifier | capitalize }}ReturnType, Serialized{{ identifier | capitalize }}Output } from "./types";
import { TriggerParams, Webhooks, createTrigger, createWebhookEventSource } from "./webhooks";
import { Models } from "./models";
export type {{ identifier | capitalize }}IntegrationOptions = {
id: string;
{{ apiKeyPropertyName }}: string;
};
export type {{ identifier | capitalize }}RunTask = InstanceType<typeof {{ identifier | capitalize }}>["runTask"];
export class {{ identifier | capitalize }}{{ " " }} implements TriggerIntegration {
private _options: {{ identifier | capitalize }}IntegrationOptions;
private _client?: any;
private _io?: IO;
private _connectionKey?: string;
constructor(private options: {{ identifier | capitalize }}IntegrationOptions) {
if (Object.keys(options).includes("{{ apiKeyPropertyName }}") && !options.{{ apiKeyPropertyName }}) {
throw `Can't create {{ identifier | capitalize }} integration (${options.id}) as {{ apiKeyPropertyName }} was undefined`;
}
this._options = options;
}
get authSource() {
{% case authMethod %}
{% when "api-key" %}
return "LOCAL" as const;
{% when "oauth" %}
return "HOSTED" as const;
{% when "both-methods" %}
return this._options.{{ apiKeyPropertyName }} ? "LOCAL" : "HOSTED";
{% endcase %}
}
get id() {
return this.options.id;
}
get metadata() {
return { id: "{{ identifier }}", name: "{{ identifier | capitalize }}" };
}
get source() {
return createWebhookEventSource(this);
}
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
const {{ identifier }} = new {{ identifier | capitalize }}(this._options);
{{ identifier }}._io = io;
{{ identifier }}._connectionKey = connectionKey;
{{ identifier }}._client = this.createClient(auth);
return {{ identifier }};
}
createClient(auth?: ConnectionAuth) {
// oauth
if (auth) {
return new {{ identifier | capitalize }}Client({
auth: auth.accessToken,
});
}
// apiKey auth
if (this._options.{{ apiKeyPropertyName }}) {
return new {{ identifier | capitalize }}Client({
apiKey: this._options.{{ apiKeyPropertyName }},
});
}
throw new Error("No auth");
}
runTask<T, TResult extends Json<T> | void>(
key: IntegrationTaskKey,
callback: (client: {{ identifier | capitalize }}Client, task: IOTask, io: IO) => Promise<TResult>,
options?: RunTaskOptions,
errorCallback?: RunTaskErrorCallback
): Promise<TResult> {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
return this._io.runTask<TResult>(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
return callback(this._client, task, io);
},
{
icon: "{{ identifier }}",
retry: retry.standardBackoff,
...(options ?? {}),
connectionKey: this._connectionKey,
},
errorCallback ?? onError
);
}
// top-level task
request<T = any>(
key: IntegrationTaskKey,
params: {
route: string | URL;
options: Parameters<{{ identifier | capitalize }}Client["request"]>[1];
}
): {{ identifier | capitalize }}ReturnType<T> {
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 },
}
);
}
// nested tasks
get models() {
return new Models(this.runTask.bind(this));
}
// events
onComment(params: TriggerParams = {}) {
return createTrigger(this.source, events.onComment, params);
}
onCommentCreated(params: TriggerParams = {}) {
return createTrigger(this.source, events.onCommentCreated, params);
}
onCommentRemoved(params: TriggerParams = {}) {
return createTrigger(this.source, events.onCommentRemoved, params);
}
onCommentUpdated(params: TriggerParams = {}) {
return createTrigger(this.source, events.onCommentUpdated, params);
}
// triggers (webhooks)
// private, just here to keep webhook logic in a separate file
get #webhooks() {
return new Webhooks(this.runTask.bind(this));
}
webhook = this.#webhooks.webhook;
webhooks = this.#webhooks.webhooks;
createWebhook = this.#webhooks.createWebhook;
deleteWebhook = this.#webhooks.deleteWebhook;
updateWebhook = this.#webhooks.updateWebhook;
}
class {{ identifier | capitalize }}ApiError extends Error {
constructor(
message: string,
readonly request: Request,
readonly response: Response
) {
super(message);
this.name = "{{ identifier | capitalize }}ApiError";
}
}
function is{{ identifier | capitalize }}ApiError(error: unknown): error is {{ identifier | capitalize }}ApiError {
if (typeof error !== "object" || error === null) {
return false;
}
const apiError = error as {{ identifier | capitalize }}ApiError;
return (
apiError.name === "{{ identifier | capitalize }}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<RunTaskErrorCallback> {
if (!is{{ identifier | capitalize }}ApiError(error)) {
return;
}
if (!shouldRetry(error.request.method, error.response.status)) {
return {
skipRetrying: true,
};
}
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,
};
}
}
}
export const serialize{{ identifier | capitalize }}Output = <T>(obj: T): Prettify<Serialized{{ identifier | capitalize }}Output<T>> => {
return JSON.parse(JSON.stringify(obj), (key, value) => {
if (typeof value === "function" || key.startsWith("_")) {
return undefined;
}
return value;
});
};
@@ -0,0 +1,79 @@
import { IntegrationTaskKey } from "@trigger.dev/sdk";
import { Model, ModelVersion } from "{{ sdkPackage }}";
import { {{ capitalizedIdentifier }}RunTask } from "./index";
import { modelProperties } from "./utils";
import { {{ capitalizedIdentifier }}ReturnType } from "./types";
export class Models {
constructor(private runTask: {{ capitalizedIdentifier }}RunTask) {}
get(
key: IntegrationTaskKey,
params: {
model_owner: string;
model_name: string;
}
): {{ capitalizedIdentifier }}ReturnType<Model> {
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: {{ capitalizedIdentifier }}RunTask) {}
get(
key: IntegrationTaskKey,
params: {
model_owner: string;
model_name: string;
version_id: string;
}
): {{ capitalizedIdentifier }}ReturnType<ModelVersion> {
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(
key: IntegrationTaskKey,
params: {
model_owner: string;
model_name: string;
}
): {{ capitalizedIdentifier }}ReturnType<ModelVersion[]> {
return this.runTask(
key,
(client) => {
return client.models.versions.list(params.model_owner, params.model_name);
},
{
name: "List Models",
params,
properties: modelProperties(params),
}
);
}
}
@@ -0,0 +1,40 @@
{
"name": "{{ packageName }}",
"version": "{{ integrationVersion.version }}",
"description": "Trigger.dev integration for {{ sdkPackage }}",
"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": {
{% if triggerMonorepoPath %}
"@trigger.dev/tsconfig": "workspace:*",
"@trigger.dev/tsup": "workspace:*",
{% endif %}
"@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": {
"{{ latestVersion.name }}": "^{{ latestVersion.version }}",
"{{ sdkVersion.name }}": "{{ sdkVersion.version }}",
"{{ integrationKitVersion.name }}": "{{ integrationKitVersion.version }}",
"zod": "3.21.4"
},
"engines": {
"node": ">=16.8.0"
}
}
@@ -0,0 +1,40 @@
import { EventSpecificationExample } from "@trigger.dev/sdk";
import CommentCreated from "./CommentCreated.json"
import CommentRemoved from "./CommentRemoved.json"
import CommentUpdated from "./CommentUpdated.json"
import IssueCreated from "./IssueCreated.json"
import IssueRemoved from "./IssueRemoved.json"
import IssueUpdated from "./IssueUpdated.json"
export const commentCreated: EventSpecificationExample = {
id: "CommentCreated",
name: "Comment created",
payload: CommentCreated,
};
export const commentRemoved: EventSpecificationExample = {
id: "CommentRemoved",
name: "Comment removed",
payload: CommentRemoved,
};
export const commentUpdated: EventSpecificationExample = {
id: "CommentUpdated",
name: "Comment updated",
payload: CommentUpdated,
};
export const issueCreated: EventSpecificationExample = {
id: "IssueCreated",
name: "Issue created",
payload: IssueCreated,
};
export const issueRemoved: EventSpecificationExample = {
id: "IssueRemoved",
name: "Issue removed",
payload: IssueRemoved,
};
export const issueUpdated: EventSpecificationExample = {
id: "IssueUpdated",
name: "Issue updated",
payload: IssueUpdated,
};
@@ -0,0 +1,120 @@
import { z } from "zod";
export const WebhookResourceTypeSchema = z.union([
z.literal("Comment"),
z.literal("Issue"),
]);
export type WebhookResourceType = z.infer<typeof WebhookResourceTypeSchema>;
export const WebhookActionTypeSchema = z.union([
z.literal("create"),
z.literal("remove"),
z.literal("update"),
]);
export type WebhookActionType = z.infer<typeof WebhookActionTypeSchema>;
const IssueLabelDataSchema = z.object({
archivedAt: z.coerce.date().optional().nullable(),
color: z.string(),
createdAt: z.coerce.date(),
creatorId: z.string().optional().nullable(),
description: z.string().optional().nullable(),
id: z.string(),
name: z.string(),
organizationId: z.string(),
parentId: z.string().optional().nullable(),
teamId: z.string().optional().nullable(),
updatedAt: z.coerce.date(),
});
const IssueDataSchema = z.object({
archivedAt: z.coerce.date().optional().nullable(),
assignee: z.object({ id: z.string(), name: z.string() }).optional().nullable(),
assigneeId: z.string().optional().nullable(),
id: z.string(),
labelIds: z.array(z.string()),
labels: z.array(IssueLabelDataSchema.pick({ id: true, color: true, name: true })),
number: z.number(),
parentId: z.string().optional().nullable(),
previousIdentifiers: z.array(z.string()),
priority: z.number(),
priorityLabel: z.string(),
projectId: z.string().optional().nullable(),
sortOrder: z.number(),
team: z.object({ id: z.string(), key: z.string(), name: z.string() }),
teamId: z.string(),
title: z.string(),
trashed: z.boolean().optional().nullable(),
triagedAt: z.coerce.date().optional().nullable(),
updatedAt: z.coerce.date(),
});
const CommentDataSchema = z.object({
archivedAt: z.coerce.date().optional().nullable(),
body: z.string(),
botActorId: z.string().optional().nullable(),
createdAt: z.coerce.date(),
editedAt: z.string().optional().nullable(),
id: z.string(),
issue: IssueDataSchema.pick({ id: true, title: true }),
issueId: z.string(),
parentId: z.string().optional().nullable(),
reactionData: z.array(z.object({}).passthrough()),
updatedAt: z.coerce.date(),
userId: z.string().optional().nullable(),
});
export const WebhookPayloadBaseSchema = z.object({
createdAt: z.coerce.date(),
organizationId: z.string().optional().nullable(),
url: z.string().url().optional().nullable(),
webhookId: z.string(),
webhookTimestamp: z.coerce.date(),
});
const CREATE = z.literal("create");
const REMOVE = z.literal("remove");
const UPDATE = z.literal("update");
export const CommentEventBaseSchema = WebhookPayloadBaseSchema.extend({
type: z.literal("Comment"),
data: CommentDataSchema,
});
export const CommentEventSchema = z.discriminatedUnion("action", [
CommentEventBaseSchema.extend({
action: CREATE,
}),
CommentEventBaseSchema.extend({
action: REMOVE,
}),
CommentEventBaseSchema.extend({
action: UPDATE,
updatedFrom: CommentDataSchema.partial(),
}),
]);
export type CommentEvent = z.infer<typeof CommentEventSchema>;
export const IssueEventBaseSchema = WebhookPayloadBaseSchema.extend({
type: z.literal("Issue"),
data: IssueDataSchema,
});
export const IssueEventSchema = z.discriminatedUnion("action", [
IssueEventBaseSchema.extend({
action: CREATE,
}),
IssueEventBaseSchema.extend({
action: REMOVE,
}),
IssueEventBaseSchema.extend({
action: UPDATE,
updatedFrom: IssueDataSchema.partial(),
}),
]);
export type IssueEvent = z.infer<typeof IssueEventSchema>;
export const WebhookPayloadSchema = z.union([
CommentEventSchema,
IssueEventSchema,
]);
export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"composite": false,
"declaration": false,
"declarationMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceMap": true,
"resolveJsonModule": true,
"lib": ["dom", "dom.iterable", "es2019"],
"module": "commonjs",
"target": "es2021",
"stripInternal": true
},
"include": ["./src/**/*.ts", "tsup.config.ts"],
"exclude": ["node_modules"]
}
@@ -0,0 +1,4 @@
{
"extends": "@trigger.dev/tsconfig/integration.json",
"include": ["./src/**/*.ts", "tsup.config.ts"]
}
@@ -0,0 +1,20 @@
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,
treeshake: {
preset: "smallest",
},
esbuildPlugins: [],
external: ["http", "https", "util", "events", "tty", "os", "timers"],
});
@@ -0,0 +1,7 @@
import { defineConfig, deepMergeOptions, integrationOptions } from "@trigger.dev/tsup";
const options = deepMergeOptions(integrationOptions, {
// extend base config here
});
export default defineConfig(options);
@@ -0,0 +1,28 @@
import { Request } from "{{ sdkPackage }}";
import { WebhookActionType, WebhookPayload } from "./schemas";
export type Get{{ identifier | capitalize }}Payload<
TPayload extends WebhookPayload,
TAction extends any = any,
> = TAction extends WebhookActionType ? Extract<TPayload, { action: TAction }> : TPayload;
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
export type Serialized{{ identifier | capitalize }}Output<T> = T extends object
? T extends Array<infer U>
? Array<Serialized{{ identifier | capitalize }}Output<U>>
: { [K in keyof T as Exclude<K, FunctionKeys<T> | `_${string}`>]: Serialized{{ identifier | capitalize }}Output<T[K]> }
: T;
export type {{ identifier | capitalize }}ReturnType<
TPayload extends Omit<Request, "_request">,
K extends unknown = unknown,
> = Promise<
Awaited<Serialized{{ identifier | capitalize }}Output<Awaited<K extends keyof TPayload ? TPayload[K] : TPayload>>>
>;
export type AwaitNested<T extends object, K extends keyof T> = Omit<T, K> & {
[key in K]: Awaited<T[K]>;
};
@@ -0,0 +1,77 @@
import { CommentEvent, IssueEvent, WebhookPayload } from "./schemas";
import { Get{{ identifier | capitalize }}Payload } from "./types";
export type QueryVariables = {
after: string;
before: string;
first: number;
includeArchived: boolean;
last: number;
};
export type Nullable<T> = Partial<{
[K in keyof T]: T[K] | null;
}>;
export const onCommentProperties = (payload: Get{{ identifier | capitalize }}Payload<CommentEvent>) => {
return [
{ label: "Comment ID", text: payload.data.id },
{ label: "Issue ID", text: payload.data.issueId },
{ label: "Issue Title", text: payload.data.issue.title, url: payload.url ?? undefined },
];
};
export const onIssueProperties = (payload: Get{{ identifier | capitalize }}Payload<IssueEvent>) => {
return [
{ label: "Issue ID", text: payload.data.id },
{
label: "Issue",
text: `[${payload.data.team.key}-${payload.data.number}] ${payload.data.title}`,
url: payload.url ?? undefined,
},
];
};
export const queryProperties = (query: Nullable<QueryVariables>) => {
return [
...(query.after ? [{ label: "After", text: query.after }] : []),
...(query.before ? [{ label: "Before", text: query.before }] : []),
...(query.first ? [{ label: "First", text: String(query.first) }] : []),
...(query.last ? [{ label: "Last", text: String(query.last) }] : []),
...(query.includeArchived
? [{ label: "Include archived", text: String(query.includeArchived) }]
: []),
];
};
export const updatedFromProperties = (payload: WebhookPayload) => {
if (payload.action !== "update") return [];
return [
{
label: "Updated Keys",
text: Object.keys(payload.updatedFrom)
.filter((key) => !["editedAt", "updatedAt"].includes(key))
.join(", "),
},
];
};
export const modelProperties = (
params: Partial<{
model_owner: string;
model_name: string;
version_id: string;
destination: string;
}>
) => {
return [
...(params.model_owner ? [{ label: "Model Owner", text: params.model_owner }] : []),
...(params.model_name ? [{ label: "Model Name", text: params.model_name }] : []),
...(params.version_id ? [{ label: "Model Version", text: params.version_id }] : []),
...(params.destination ? [{ label: "Destination Model", text: params.destination }] : []),
];
};
export const streamingProperty = (params: { stream?: boolean }) => {
return [{ label: "Streaming Enabled", text: String(!!params.stream) }];
};
@@ -0,0 +1,297 @@
import {
EventFilter,
ExternalSource,
ExternalSourceTrigger,
HandlerEvent,
IntegrationTaskKey,
Logger,
} from "@trigger.dev/sdk";
import {
Document,
{{ identifier | capitalize }}Webhooks,
WebhookPayload,
DeletePayload,
Webhook,
} from "{{ sdkPackage }}";
import { z } from "zod";
import * as events from "./events";
import { {{ identifier | capitalize }}, {{ identifier | capitalize }}RunTask, serialize{{ identifier | capitalize }}Output } from "./index";
import { WebhookPayloadSchema } from "./schemas";
import { {{ identifier | capitalize }}ReturnType } from "./types";
import { queryProperties } from "./utils";
export class Webhooks {
runTask: {{ identifier | capitalize }}RunTask;
constructor(runTask: {{ identifier | capitalize }}RunTask) {
this.runTask = runTask;
}
webhook(key: IntegrationTaskKey, params: { id: string }): {{ identifier | capitalize }}ReturnType<Webhook> {
return this.runTask(
key,
async (client, task, io) => {
return serialize{{ identifier | capitalize }}Output(await client.webhook(params.id));
},
{
name: "Get Webhook",
params,
properties: [{ label: "Webhook ID", text: params.id }],
}
);
}
webhooks(key: IntegrationTaskKey, params?: Document.WebhooksQueryVariables): {{ identifier | capitalize }}ReturnType<Webhook[]> {
return this.runTask(
key,
async (client, task, io) => {
let connections = await client.webhooks(params);
const hooks = connections.nodes;
while (connections.pageInfo.hasNextPage) {
connections = await connections.fetchNext();
hooks.push(...connections.nodes);
}
return serialize{{ identifier | capitalize }}Output(hooks);
},
{
name: "List Webhooks",
params,
properties: queryProperties(params ?? {}),
}
);
}
createWebhook(
key: IntegrationTaskKey,
params: Document.WebhookCreateInput
): {{ identifier | capitalize }}ReturnType<Omit<WebhookPayload, "webhook"> & { webhook: Webhook | undefined }> {
return this.runTask(
key,
async (client, task, io) => {
const payload = await client.createWebhook({ ...params, allPublicTeams: !params.teamId });
return serialize{{ identifier | capitalize }}Output({
...payload,
webhook: await payload.webhook,
});
},
{
name: "Create Webhook",
params,
properties: [
{ label: "Webhook URL", text: params.url },
{ label: "Resource Types", text: params.resourceTypes.join(", ") },
],
}
);
}
deleteWebhook(key: IntegrationTaskKey, params: { id: string }): {{ identifier | capitalize }}ReturnType<DeletePayload> {
return this.runTask(
key,
async (client, task, io) => {
return serialize{{ identifier | capitalize }}Output(await client.deleteWebhook(params.id));
},
{
name: "Delete Webhook",
params,
properties: [{ label: "Webhook ID", text: params.id }],
}
);
}
updateWebhook(
key: IntegrationTaskKey,
params: { id: string; input: Document.WebhookUpdateInput }
): {{ identifier | capitalize }}ReturnType<Omit<WebhookPayload, "webhook"> & { webhook: Webhook | undefined }> {
return this.runTask(
key,
async (client, task) => {
const payload = await client.updateWebhook(params.id, params.input);
return serialize{{ identifier | capitalize }}Output({
...payload,
webhook: await payload.webhook,
});
},
{
name: "Update Webhook",
params,
properties: [
{ label: "Webhook ID", text: params.id },
...(params.input.url ? [{ label: "Webhook URL", text: params.input.url }] : []),
...(params.input.resourceTypes
? [{ label: "Resource Types", text: params.input.resourceTypes.join(", ") }]
: []),
],
}
);
}
}
type {{ identifier | capitalize }}Events = (typeof events)[keyof typeof events];
export type TriggerParams = {
teamId?: string;
filter?: EventFilter;
};
type CreateTriggersResult<TEventSpecification extends {{ identifier | capitalize }}Events> = ExternalSourceTrigger<
TEventSpecification,
ReturnType<typeof createWebhookEventSource>
>;
export function createTrigger<TEventSpecification extends {{ identifier | capitalize }}Events>(
source: ReturnType<typeof createWebhookEventSource>,
event: TEventSpecification,
params: TriggerParams
): CreateTriggersResult<TEventSpecification> {
return new ExternalSourceTrigger({
event,
params,
source,
options: {},
});
}
const WebhookRegistrationDataSchema = z.object({
success: z.literal(true),
webhook: z.object({
id: z.string(),
enabled: z.boolean(),
}),
});
export function createWebhookEventSource(
integration: {{ identifier | capitalize }}
): ExternalSource<{{ identifier | capitalize }}, TriggerParams, "HTTP", {}> {
return new ExternalSource("HTTP", {
id: "{{ identifier }}.webhook",
schema: z.object({
teamId: z.string().optional(),
}),
version: "0.1.0",
integration,
key: (params) => `${params.teamId ? params.teamId : "all"}`,
handler: webhookHandler,
register: async (event, io, ctx) => {
const { params, source: httpSource, options } = event;
// (key-specific) stored data, undefined if not registered yet
const webhookData = WebhookRegistrationDataSchema.safeParse(httpSource.data);
// set of events to register
const allEvents = Array.from(new Set([...options.event.desired, ...options.event.missing]));
const registeredOptions = {
event: allEvents,
};
// easily identify webhooks on {{ identifier }}
const label = `trigger.${params.teamId ? params.teamId : "all"}`;
if (httpSource.active && webhookData.success) {
const hasMissingOptions = Object.values(options).some(
(option) => option.missing.length > 0
);
if (!hasMissingOptions) return;
const updatedWebhook = await io.integration.updateWebhook("update-webhook", {
id: webhookData.data.webhook.id,
input: {
label,
resourceTypes: allEvents,
secret: httpSource.secret,
url: httpSource.url,
},
});
return {
data: WebhookRegistrationDataSchema.parse(updatedWebhook),
options: registeredOptions,
};
}
// check for existing hooks that match url
const listResponse = await io.integration.webhooks("list-webhooks");
const existingWebhook = listResponse.find((w) => w.url === httpSource.url);
if (existingWebhook) {
const updatedWebhook = await io.integration.updateWebhook("update-webhook", {
id: existingWebhook.id,
input: {
label,
resourceTypes: allEvents,
secret: httpSource.secret,
url: httpSource.url,
},
});
return {
data: WebhookRegistrationDataSchema.parse(updatedWebhook),
options: registeredOptions,
};
}
const createPayload = await io.integration.createWebhook("create-webhook", {
label,
resourceTypes: allEvents,
secret: httpSource.secret,
teamId: params.teamId,
url: httpSource.url,
});
return {
data: WebhookRegistrationDataSchema.parse(createPayload),
secret: (await createPayload.webhook)?.secret,
options: registeredOptions,
};
},
});
}
async function webhookHandler(event: HandlerEvent<"HTTP">, logger: Logger, integration: {{ identifier | capitalize }}) {
logger.debug("[@trigger.dev/{{ identifier }}] Handling webhook payload");
const { rawEvent: request, source } = event;
const payloadUuid = request.headers.get("{{ identifier | capitalize }}-Delivery");
const payloadEvent = request.headers.get("{{ identifier | capitalize }}-Event");
if (!payloadUuid || !payloadEvent) {
logger.debug("[@trigger.dev/{{ identifier }}] Missing required {{ identifier | capitalize }} headers");
return { events: [] };
}
if (!request.body) {
logger.debug("[@trigger.dev/{{ identifier }}] No body found");
return { events: [] };
}
const signature = request.headers.get("WEBHOOK_SIGNATURE_HEADER");
if (!signature) {
logger.error("[@trigger.dev/{{ identifier }}] Error validating webhook signature, no signature found");
throw Error("[@trigger.dev/{{ identifier }}] No signature found");
}
const rawBody = await request.text();
const body = JSON.parse(rawBody);
const webhookHelper = new {{ identifier | capitalize }}Webhooks(source.secret);
if (!webhookHelper.verify(Buffer.from(rawBody), signature)) {
logger.error("[@trigger.dev/{{ identifier }}] Error validating webhook signature, they don't match");
throw Error("[@trigger.dev/{{ identifier }}] Invalid signature");
}
const webhookPayload = WebhookPayloadSchema.parse(body);
return {
events: [
{
id: payloadUuid,
name: payloadEvent,
source: "{{ identifier }}.app",
payload: webhookPayload,
context: {},
},
],
};
}
@@ -0,0 +1,61 @@
import fs from "fs/promises";
import { Liquid } from "liquidjs";
import path from "path";
import { pathExists } from "./fileSystem";
import { templatesPath } from "../paths";
type Result =
| {
success: true;
alreadyExisted: boolean;
}
| {
success: false;
error: string;
};
const templatesDir = path.join(templatesPath(), "integration");
const liquid = new Liquid({
root: templatesDir,
trimTagRight: true,
trimOutputRight: true,
});
export async function createIntegrationFileFromTemplate(params: {
relativeTemplatePath: string;
variables?: Record<string, any>;
outputPath: string;
}): Promise<Result> {
if (await pathExists(params.outputPath)) {
return {
success: true,
alreadyExisted: true,
};
}
try {
const output = await liquid.renderFile(params.relativeTemplatePath, params.variables);
const directoryName = path.dirname(params.outputPath);
await fs.mkdir(directoryName, { recursive: true });
await fs.writeFile(params.outputPath, output);
return {
success: true,
alreadyExisted: false,
};
} catch (e) {
if (e instanceof Error) {
return {
success: false,
error: e.message,
};
}
return {
success: false,
error: JSON.stringify(e),
};
}
}
+137
View File
@@ -0,0 +1,137 @@
import { Framework, getFramework } from "../frameworks";
import { PackageManager, getUserPackageManager } from "./getUserPkgManager";
import { Logger } from "./logger";
import { run as ncuRun } from "npm-check-updates";
import chalk from "chalk";
import fs from "fs/promises";
import pathModule from "path";
export abstract class JsRuntime {
logger: Logger;
projectRootPath: string;
constructor(projectRootPath: string, logger: Logger) {
this.logger = logger;
this.projectRootPath = projectRootPath;
}
abstract get id(): string;
abstract checkForOutdatedPackages(): Promise<void>;
abstract getUserPackageManager(): Promise<PackageManager | undefined>;
abstract getFramework(): Promise<Framework | undefined>;
abstract getEndpointId(): Promise<string | undefined>;
}
export async function getJsRuntime(projectRootPath: string, logger: Logger): Promise<JsRuntime> {
if (await NodeJsRuntime.isNodeJsRuntime(projectRootPath)) {
return new NodeJsRuntime(projectRootPath, logger);
} else if (await DenoRuntime.isDenoJsRuntime(projectRootPath)) {
return new DenoRuntime(projectRootPath, logger);
}
throw new Error("Unsupported runtime");
}
class NodeJsRuntime extends JsRuntime {
static async isNodeJsRuntime(projectRootPath: string): Promise<boolean> {
try {
await fs.stat(pathModule.join(projectRootPath, "package.json"));
return true;
} catch {
return false;
}
}
get id() {
return "nodejs";
}
get packageJsonPath(): string {
return pathModule.join(this.projectRootPath, "package.json");
}
async checkForOutdatedPackages(): Promise<void> {
const updates = (await ncuRun({
packageFile: `${this.packageJsonPath}`,
filter: "/trigger.dev/.+$/",
upgrade: false,
})) as {
[key: string]: string;
};
if (typeof updates === "undefined" || Object.keys(updates).length === 0) {
return;
}
const packageFile = await fs.readFile(this.packageJsonPath);
const data = JSON.parse(Buffer.from(packageFile).toString("utf8"));
const dependencies = data.dependencies;
console.log(chalk.bgYellow("Updates available for trigger.dev packages"));
console.log(chalk.bgBlue("Run npx @trigger.dev/cli@latest update"));
for (let dep in updates) {
console.log(`${dep} ${dependencies[dep]}${updates[dep]}`);
}
}
async getUserPackageManager() {
return getUserPackageManager(this.projectRootPath);
}
async getFramework() {
const userPackageManager = await this.getUserPackageManager();
return getFramework(this.projectRootPath, userPackageManager);
}
async getEndpointId() {
const pkgJsonPath = pathModule.join(this.projectRootPath, "package.json");
const pkgBuffer = await fs.readFile(pkgJsonPath);
const pkgJson = JSON.parse(pkgBuffer.toString());
const value = pkgJson["trigger.dev"]?.endpointId;
if (!value || typeof value !== "string") return undefined;
return value;
}
}
class DenoRuntime extends JsRuntime {
getDenoJsonPath(): Promise<string> {
try {
return fs
.stat(pathModule.join(this.projectRootPath, "deno.json"))
.then(() => pathModule.join(this.projectRootPath, "deno.json"));
} catch {
return fs
.stat(pathModule.join(this.projectRootPath, "deno.jsonc"))
.then(() => pathModule.join(this.projectRootPath, "deno.jsonc"));
}
}
get id() {
return "deno";
}
static async isDenoJsRuntime(projectRootPath: string): Promise<boolean> {
try {
try {
await fs.stat(pathModule.join(projectRootPath, "deno.json"));
} catch (e) {
await fs.stat(pathModule.join(projectRootPath, "deno.jsonc"));
}
return true;
} catch {
return false;
}
}
async checkForOutdatedPackages() {
// not implemented currently
}
async getUserPackageManager() {
return undefined;
}
async getFramework() {
// not implemented currently
return undefined;
}
async getEndpointId() {
const pkgJsonPath = await this.getDenoJsonPath();
const pkgBuffer = await fs.readFile(pkgJsonPath);
const pkgJson = JSON.parse(pkgBuffer.toString());
return pkgJson["trigger.dev"]?.endpointId;
}
}
+1
View File
@@ -1,5 +1,6 @@
import chalk from "chalk";
export type Logger = typeof logger;
export const logger = {
error(...args: unknown[]) {
console.log(chalk.red(...args));
@@ -4,3 +4,8 @@ import pathModule from "path";
export const resolvePath = (input: string) => {
return pathModule.resolve(process.cwd(), input);
};
// Takes an absolute path and derives the relative path from the current working directory
export const relativePath = (input: string) => {
return pathModule.relative(process.cwd(), input);
};
+51
View File
@@ -367,6 +367,12 @@ importers:
config-packages/tsconfig:
specifiers: {}
config-packages/tsup:
specifiers:
tsup: 7.1.x
devDependencies:
tsup: 7.1.0
docs:
specifiers: {}
@@ -679,6 +685,7 @@ importers:
execa: ^7.0.0
gradient-string: ^2.0.2
inquirer: ^9.1.4
liquidjs: ^10.9.2
localtunnel: ^2.0.2
mock-fs: ^5.2.0
nanoid: ^4.0.2
@@ -714,6 +721,7 @@ importers:
execa: 7.0.0
gradient-string: 2.0.2
inquirer: 9.1.4
liquidjs: 10.9.3
localtunnel: 2.0.2
mock-fs: 5.2.0
nanoid: 4.0.2
@@ -23374,6 +23382,14 @@ packages:
/lines-and-columns/1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
/liquidjs/10.9.3:
resolution: {integrity: sha512-vG8fLPSBtf0C6FJg0DwzxwWF0sPE+wIRrrGsNoe0DP3Pg4HOPqycGDT14V4UfyskUXsum7DkhUuZQ9tBRjbX+g==}
engines: {node: '>=14'}
hasBin: true
dependencies:
commander: 10.0.1
dev: false
/listenercount/1.0.1:
resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
dev: false
@@ -30635,6 +30651,41 @@ packages:
- ts-node
dev: true
/tsup/7.1.0:
resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==}
engines: {node: '>=16.14'}
hasBin: true
peerDependencies:
'@swc/core': ^1
postcss: ^8.4.12
typescript: '>=4.1.0'
peerDependenciesMeta:
'@swc/core':
optional: true
postcss:
optional: true
typescript:
optional: true
dependencies:
bundle-require: 4.0.1_esbuild@0.18.11
cac: 6.7.14
chokidar: 3.5.3
debug: 4.3.4
esbuild: 0.18.11
execa: 5.1.1
globby: 11.1.0
joycon: 3.1.1
postcss-load-config: 4.0.1
resolve-from: 5.0.0
rollup: 3.29.1
source-map: 0.8.0-beta.0
sucrase: 3.32.0
tree-kill: 1.2.2
transitivePeerDependencies:
- supports-color
- ts-node
dev: true
/tsup/7.1.0_typescript@4.9.4:
resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==}
engines: {node: '>=16.14'}
+3
View File
@@ -0,0 +1,3 @@
{
"deno.enable": true
}
+8
View File
@@ -0,0 +1,8 @@
{
"trigger.dev": {
"endpointId": "borderless"
},
"tasks": {
"dev": "deno run --watch main.ts"
}
}
+684
View File
@@ -0,0 +1,684 @@
{
"version": "3",
"packages": {
"specifiers": {
"npm:@trigger.dev/express": "npm:@trigger.dev/express@2.1.7_@trigger.dev+sdk@2.1.7",
"npm:@trigger.dev/sdk": "npm:@trigger.dev/sdk@2.1.7"
},
"npm": {
"@remix-run/web-blob@3.1.0": {
"integrity": "sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g==",
"dependencies": {
"@remix-run/web-stream": "@remix-run/web-stream@1.1.0",
"web-encoding": "web-encoding@1.1.5"
}
},
"@remix-run/web-fetch@4.4.1": {
"integrity": "sha512-xMceEGn2kvfeWS91nHSOhEQHPGgjFnmDVpWFZrbWPVdiTByMZIn421/tdSF6Kd1RsNsY+5Iwt3JFEKZHAcMQHw==",
"dependencies": {
"@remix-run/web-blob": "@remix-run/web-blob@3.1.0",
"@remix-run/web-file": "@remix-run/web-file@3.1.0",
"@remix-run/web-form-data": "@remix-run/web-form-data@3.1.0",
"@remix-run/web-stream": "@remix-run/web-stream@1.1.0",
"@web3-storage/multipart-parser": "@web3-storage/multipart-parser@1.0.0",
"abort-controller": "abort-controller@3.0.0",
"data-uri-to-buffer": "data-uri-to-buffer@3.0.1",
"mrmime": "mrmime@1.0.1"
}
},
"@remix-run/web-file@3.1.0": {
"integrity": "sha512-dW2MNGwoiEYhlspOAXFBasmLeYshyAyhIdrlXBi06Duex5tDr3ut2LFKVj7tyHLmn8nnNwFf1BjNbkQpygC2aQ==",
"dependencies": {
"@remix-run/web-blob": "@remix-run/web-blob@3.1.0"
}
},
"@remix-run/web-form-data@3.1.0": {
"integrity": "sha512-NdeohLMdrb+pHxMQ/Geuzdp0eqPbea+Ieo8M8Jx2lGC6TBHsgHzYcBvr0LyPdPVycNRDEpWpiDdCOdCryo3f9A==",
"dependencies": {
"web-encoding": "web-encoding@1.1.5"
}
},
"@remix-run/web-stream@1.1.0": {
"integrity": "sha512-KRJtwrjRV5Bb+pM7zxcTJkhIqWWSy+MYsIxHK+0m5atcznsf15YwUBWHWulZerV2+vvHH1Lp1DD7pw6qKW8SgA==",
"dependencies": {
"web-streams-polyfill": "web-streams-polyfill@3.2.1"
}
},
"@trigger.dev/core@2.1.7": {
"integrity": "sha512-Ts0xMFiWi4ph4da6BIbuegz2mMSVYJxWEh5S2mhM5vc+S6cl+MEbTdh+neUVa/0N4qq3Io07e9yDTXnTfiifzg==",
"dependencies": {
"ulid": "ulid@2.3.0",
"zod": "zod@3.21.4",
"zod-error": "zod-error@1.5.0"
}
},
"@trigger.dev/express@2.1.7_@trigger.dev+sdk@2.1.7": {
"integrity": "sha512-uMoSDpOZJdYs+UXwEkhWYCYm8T6e+cu33LXmXhoF2tpm7m8PritFgR7LPX4H9UdIddRATdsmWgOqPMOQneEtDg==",
"dependencies": {
"@remix-run/web-fetch": "@remix-run/web-fetch@4.4.1",
"@trigger.dev/sdk": "@trigger.dev/sdk@2.1.7",
"debug": "debug@4.3.4",
"express": "express@4.18.2"
}
},
"@trigger.dev/sdk@2.1.7": {
"integrity": "sha512-t3pbXj6+I38a5LNGJg9Ed09NCt0AWnw7hJtUCZyuEfBQulRnVSekNLsxCyGlgiTPiyRvbyefzPrX1GhXh0MY1w==",
"dependencies": {
"@trigger.dev/core": "@trigger.dev/core@2.1.7",
"chalk": "chalk@5.3.0",
"cronstrue": "cronstrue@2.32.0",
"debug": "debug@4.3.4",
"evt": "evt@2.5.3",
"get-caller-file": "get-caller-file@2.0.5",
"git-remote-origin-url": "git-remote-origin-url@4.0.0",
"git-repo-info": "git-repo-info@2.1.1",
"node-fetch": "node-fetch@2.6.13",
"slug": "slug@6.1.0",
"terminal-link": "terminal-link@3.0.0",
"ulid": "ulid@2.3.0",
"uuid": "uuid@9.0.1",
"ws": "ws@8.13.0",
"zod": "zod@3.21.4"
}
},
"@web3-storage/multipart-parser@1.0.0": {
"integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==",
"dependencies": {}
},
"@zxing/text-encoding@0.9.0": {
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
"dependencies": {}
},
"abort-controller@3.0.0": {
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "event-target-shim@5.0.1"
}
},
"accepts@1.3.8": {
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "mime-types@2.1.35",
"negotiator": "negotiator@0.6.3"
}
},
"ansi-escapes@5.0.0": {
"integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==",
"dependencies": {
"type-fest": "type-fest@1.4.0"
}
},
"array-flatten@1.1.1": {
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"dependencies": {}
},
"available-typed-arrays@1.0.5": {
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dependencies": {}
},
"body-parser@1.20.1": {
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "bytes@3.1.2",
"content-type": "content-type@1.0.5",
"debug": "debug@2.6.9",
"depd": "depd@2.0.0",
"destroy": "destroy@1.2.0",
"http-errors": "http-errors@2.0.0",
"iconv-lite": "iconv-lite@0.4.24",
"on-finished": "on-finished@2.4.1",
"qs": "qs@6.11.0",
"raw-body": "raw-body@2.5.1",
"type-is": "type-is@1.6.18",
"unpipe": "unpipe@1.0.0"
}
},
"bytes@3.1.2": {
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dependencies": {}
},
"call-bind@1.0.2": {
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dependencies": {
"function-bind": "function-bind@1.1.1",
"get-intrinsic": "get-intrinsic@1.2.1"
}
},
"chalk@5.3.0": {
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"dependencies": {}
},
"content-disposition@0.5.4": {
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "safe-buffer@5.2.1"
}
},
"content-type@1.0.5": {
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dependencies": {}
},
"cookie-signature@1.0.6": {
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"dependencies": {}
},
"cookie@0.5.0": {
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"dependencies": {}
},
"cronstrue@2.32.0": {
"integrity": "sha512-dmNflOCNJL6lZEj0dp2YhGIPY83VTjFue6d9feFhnNtrER6mAjBrUvSgK95j3IB/xNGpLjaZDIDG6ACKTZr9Yw==",
"dependencies": {}
},
"data-uri-to-buffer@3.0.1": {
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
"dependencies": {}
},
"debug@2.6.9": {
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "ms@2.0.0"
}
},
"debug@4.3.4": {
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "ms@2.1.2"
}
},
"depd@2.0.0": {
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dependencies": {}
},
"destroy@1.2.0": {
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"dependencies": {}
},
"ee-first@1.1.1": {
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"dependencies": {}
},
"encodeurl@1.0.2": {
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"dependencies": {}
},
"escape-html@1.0.3": {
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dependencies": {}
},
"etag@1.8.1": {
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dependencies": {}
},
"event-target-shim@5.0.1": {
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"dependencies": {}
},
"evt@2.5.3": {
"integrity": "sha512-wZKx0JgXaTOVOXI2saNVxINU6VToOHDowMwb3NRcU6l+C59eW3w9dZgNxjokiM8rvMgc7/11yFG0cSDxn4qxgA==",
"dependencies": {
"minimal-polyfills": "minimal-polyfills@2.2.3",
"run-exclusive": "run-exclusive@2.2.19",
"tsafe": "tsafe@1.6.5"
}
},
"express@4.18.2": {
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": {
"accepts": "accepts@1.3.8",
"array-flatten": "array-flatten@1.1.1",
"body-parser": "body-parser@1.20.1",
"content-disposition": "content-disposition@0.5.4",
"content-type": "content-type@1.0.5",
"cookie": "cookie@0.5.0",
"cookie-signature": "cookie-signature@1.0.6",
"debug": "debug@2.6.9",
"depd": "depd@2.0.0",
"encodeurl": "encodeurl@1.0.2",
"escape-html": "escape-html@1.0.3",
"etag": "etag@1.8.1",
"finalhandler": "finalhandler@1.2.0",
"fresh": "fresh@0.5.2",
"http-errors": "http-errors@2.0.0",
"merge-descriptors": "merge-descriptors@1.0.1",
"methods": "methods@1.1.2",
"on-finished": "on-finished@2.4.1",
"parseurl": "parseurl@1.3.3",
"path-to-regexp": "path-to-regexp@0.1.7",
"proxy-addr": "proxy-addr@2.0.7",
"qs": "qs@6.11.0",
"range-parser": "range-parser@1.2.1",
"safe-buffer": "safe-buffer@5.2.1",
"send": "send@0.18.0",
"serve-static": "serve-static@1.15.0",
"setprototypeof": "setprototypeof@1.2.0",
"statuses": "statuses@2.0.1",
"type-is": "type-is@1.6.18",
"utils-merge": "utils-merge@1.0.1",
"vary": "vary@1.1.2"
}
},
"finalhandler@1.2.0": {
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "debug@2.6.9",
"encodeurl": "encodeurl@1.0.2",
"escape-html": "escape-html@1.0.3",
"on-finished": "on-finished@2.4.1",
"parseurl": "parseurl@1.3.3",
"statuses": "statuses@2.0.1",
"unpipe": "unpipe@1.0.0"
}
},
"for-each@0.3.3": {
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dependencies": {
"is-callable": "is-callable@1.2.7"
}
},
"forwarded@0.2.0": {
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dependencies": {}
},
"fresh@0.5.2": {
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"dependencies": {}
},
"function-bind@1.1.1": {
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dependencies": {}
},
"get-caller-file@2.0.5": {
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dependencies": {}
},
"get-intrinsic@1.2.1": {
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dependencies": {
"function-bind": "function-bind@1.1.1",
"has": "has@1.0.3",
"has-proto": "has-proto@1.0.1",
"has-symbols": "has-symbols@1.0.3"
}
},
"git-remote-origin-url@4.0.0": {
"integrity": "sha512-EAxDksNdjuWgmVW9pVvA9jQDi/dmTaiDONktIy7qiRRhBZUI4FQK1YvBvteuTSX24aNKg9lfgxNYJEeeSXe6DA==",
"dependencies": {
"gitconfiglocal": "gitconfiglocal@2.1.0"
}
},
"git-repo-info@2.1.1": {
"integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==",
"dependencies": {}
},
"gitconfiglocal@2.1.0": {
"integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==",
"dependencies": {
"ini": "ini@1.3.8"
}
},
"gopd@1.0.1": {
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "get-intrinsic@1.2.1"
}
},
"has-flag@4.0.0": {
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dependencies": {}
},
"has-proto@1.0.1": {
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dependencies": {}
},
"has-symbols@1.0.3": {
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dependencies": {}
},
"has-tostringtag@1.0.0": {
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"dependencies": {
"has-symbols": "has-symbols@1.0.3"
}
},
"has@1.0.3": {
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
"function-bind": "function-bind@1.1.1"
}
},
"http-errors@2.0.0": {
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "depd@2.0.0",
"inherits": "inherits@2.0.4",
"setprototypeof": "setprototypeof@1.2.0",
"statuses": "statuses@2.0.1",
"toidentifier": "toidentifier@1.0.1"
}
},
"iconv-lite@0.4.24": {
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": "safer-buffer@2.1.2"
}
},
"inherits@2.0.4": {
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dependencies": {}
},
"ini@1.3.8": {
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dependencies": {}
},
"ipaddr.js@1.9.1": {
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dependencies": {}
},
"is-arguments@1.1.1": {
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dependencies": {
"call-bind": "call-bind@1.0.2",
"has-tostringtag": "has-tostringtag@1.0.0"
}
},
"is-callable@1.2.7": {
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dependencies": {}
},
"is-generator-function@1.0.10": {
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
"dependencies": {
"has-tostringtag": "has-tostringtag@1.0.0"
}
},
"is-typed-array@1.1.12": {
"integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
"dependencies": {
"which-typed-array": "which-typed-array@1.1.11"
}
},
"media-typer@0.3.0": {
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dependencies": {}
},
"merge-descriptors@1.0.1": {
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
"dependencies": {}
},
"methods@1.1.2": {
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"dependencies": {}
},
"mime-db@1.52.0": {
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dependencies": {}
},
"mime-types@2.1.35": {
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "mime-db@1.52.0"
}
},
"mime@1.6.0": {
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dependencies": {}
},
"minimal-polyfills@2.2.3": {
"integrity": "sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw==",
"dependencies": {}
},
"mrmime@1.0.1": {
"integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==",
"dependencies": {}
},
"ms@2.0.0": {
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dependencies": {}
},
"ms@2.1.2": {
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dependencies": {}
},
"ms@2.1.3": {
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dependencies": {}
},
"negotiator@0.6.3": {
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"dependencies": {}
},
"node-fetch@2.6.13": {
"integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
"dependencies": {
"whatwg-url": "whatwg-url@5.0.0"
}
},
"object-inspect@1.12.3": {
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"dependencies": {}
},
"on-finished@2.4.1": {
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "ee-first@1.1.1"
}
},
"parseurl@1.3.3": {
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dependencies": {}
},
"path-to-regexp@0.1.7": {
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"dependencies": {}
},
"proxy-addr@2.0.7": {
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": {
"forwarded": "forwarded@0.2.0",
"ipaddr.js": "ipaddr.js@1.9.1"
}
},
"qs@6.11.0": {
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "side-channel@1.0.4"
}
},
"range-parser@1.2.1": {
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dependencies": {}
},
"raw-body@2.5.1": {
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "bytes@3.1.2",
"http-errors": "http-errors@2.0.0",
"iconv-lite": "iconv-lite@0.4.24",
"unpipe": "unpipe@1.0.0"
}
},
"run-exclusive@2.2.19": {
"integrity": "sha512-K3mdoAi7tjJ/qT7Flj90L7QyPozwUaAG+CVhkdDje4HLKXUYC3N/Jzkau3flHVDLQVhiHBtcimVodMjN9egYbA==",
"dependencies": {
"minimal-polyfills": "minimal-polyfills@2.2.3"
}
},
"safe-buffer@5.2.1": {
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dependencies": {}
},
"safer-buffer@2.1.2": {
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dependencies": {}
},
"send@0.18.0": {
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "debug@2.6.9",
"depd": "depd@2.0.0",
"destroy": "destroy@1.2.0",
"encodeurl": "encodeurl@1.0.2",
"escape-html": "escape-html@1.0.3",
"etag": "etag@1.8.1",
"fresh": "fresh@0.5.2",
"http-errors": "http-errors@2.0.0",
"mime": "mime@1.6.0",
"ms": "ms@2.1.3",
"on-finished": "on-finished@2.4.1",
"range-parser": "range-parser@1.2.1",
"statuses": "statuses@2.0.1"
}
},
"serve-static@1.15.0": {
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "encodeurl@1.0.2",
"escape-html": "escape-html@1.0.3",
"parseurl": "parseurl@1.3.3",
"send": "send@0.18.0"
}
},
"setprototypeof@1.2.0": {
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dependencies": {}
},
"side-channel@1.0.4": {
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
"call-bind": "call-bind@1.0.2",
"get-intrinsic": "get-intrinsic@1.2.1",
"object-inspect": "object-inspect@1.12.3"
}
},
"slug@6.1.0": {
"integrity": "sha512-x6vLHCMasg4DR2LPiyFGI0gJJhywY6DTiGhCrOMzb3SOk/0JVLIaL4UhyFSHu04SD3uAavrKY/K3zZ3i6iRcgA==",
"dependencies": {}
},
"statuses@2.0.1": {
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dependencies": {}
},
"supports-color@7.2.0": {
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "has-flag@4.0.0"
}
},
"supports-hyperlinks@2.3.0": {
"integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
"dependencies": {
"has-flag": "has-flag@4.0.0",
"supports-color": "supports-color@7.2.0"
}
},
"terminal-link@3.0.0": {
"integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==",
"dependencies": {
"ansi-escapes": "ansi-escapes@5.0.0",
"supports-hyperlinks": "supports-hyperlinks@2.3.0"
}
},
"toidentifier@1.0.1": {
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dependencies": {}
},
"tr46@0.0.3": {
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dependencies": {}
},
"tsafe@1.6.5": {
"integrity": "sha512-895zss8xqqHKTc28sHGIfZKnt3C5jrstB1DyPr/h3/flK0zojsZUMQL1/W4ytdDW6KI4Oth62nb9rrxmA3s3Iw==",
"dependencies": {}
},
"type-fest@1.4.0": {
"integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
"dependencies": {}
},
"type-is@1.6.18": {
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "media-typer@0.3.0",
"mime-types": "mime-types@2.1.35"
}
},
"ulid@2.3.0": {
"integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==",
"dependencies": {}
},
"unpipe@1.0.0": {
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"dependencies": {}
},
"util@0.12.5": {
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
"dependencies": {
"inherits": "inherits@2.0.4",
"is-arguments": "is-arguments@1.1.1",
"is-generator-function": "is-generator-function@1.0.10",
"is-typed-array": "is-typed-array@1.1.12",
"which-typed-array": "which-typed-array@1.1.11"
}
},
"utils-merge@1.0.1": {
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dependencies": {}
},
"uuid@9.0.1": {
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"dependencies": {}
},
"vary@1.1.2": {
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"dependencies": {}
},
"web-encoding@1.1.5": {
"integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==",
"dependencies": {
"@zxing/text-encoding": "@zxing/text-encoding@0.9.0",
"util": "util@0.12.5"
}
},
"web-streams-polyfill@3.2.1": {
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"dependencies": {}
},
"webidl-conversions@3.0.1": {
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dependencies": {}
},
"whatwg-url@5.0.0": {
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "tr46@0.0.3",
"webidl-conversions": "webidl-conversions@3.0.1"
}
},
"which-typed-array@1.1.11": {
"integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
"dependencies": {
"available-typed-arrays": "available-typed-arrays@1.0.5",
"call-bind": "call-bind@1.0.2",
"for-each": "for-each@0.3.3",
"gopd": "gopd@1.0.1",
"has-tostringtag": "has-tostringtag@1.0.0"
}
},
"ws@8.13.0": {
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"dependencies": {}
},
"zod-error@1.5.0": {
"integrity": "sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ==",
"dependencies": {
"zod": "zod@3.21.4"
}
},
"zod@3.21.4": {
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
"dependencies": {}
}
}
},
"remote": {}
}
+41
View File
@@ -0,0 +1,41 @@
import { TriggerClient } from "npm:@trigger.dev/sdk";
import { eventTrigger } from "npm:@trigger.dev/sdk";
export const triggerClient = new TriggerClient({
id: "borderless",
apiKey: "...",
});
// your first job
triggerClient.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!",
};
},
});
Deno.serve(async (req) => {
const response = await triggerClient.handleRequest(req);
if (!response) {
return Response.json(
{ error: "Not found" },
{
status: 404,
}
);
}
return Response.json(response.body, {
status: response.status,
headers: response.headers,
});
});