diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index eeca8bc62..a4dfa7e96 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -83,3 +83,7 @@ curl --request POST \ } }' ``` + +## Dependency & Package graph + +![Dependency Graph](assets/dependencyGraph.png) diff --git a/README.md b/README.md index ca8d37531..a4ece6f94 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,3 @@ -# trigger.dev +# Trigger.dev -## Development - -> **Warning** -> All the following commands should be launched from the **monorepo root directory** - -1. Install the dependencies. - ```bash - pnpm install - ``` -2. Environment variables. You will need to create copies of the `.env.example` files in `app/webapp` - ```sh - cp ./apps/webapp/.env.example ./apps/webapp/.env - ``` -3. Start postgresql, pulsar, and the pizzly server - - ```bash - pnpm run docker:db - ``` - - > **Note:** The npm script will complete while Docker sets up the container in the background. Ensure that Docker has finished and your container is running before proceeding. - -4. Generate prisma schema - ```bash - pnpm run generate - ``` -5. Run the Prisma migration to the database - - ```bash - pnpm run db:migrate:deploy - ``` - -6. Run the first build (with dependencies via the `...` option) - ```bash - pnpm run build --filter=webapp... - ``` - **Running simply `pnpm run build` will build everything, including the NextJS app.** -7. Run the Remix dev server - -```bash -pnpm run dev --filter=webapp -``` - -## Tests, Typechecks, Lint, Install packages... - -Check the `turbo.json` file to see the available pipelines. - -- Run the Cypress tests and Dev - ```bash - pnpm run test:e2e:dev --filter=webapp - ``` -- Lint everything - ```bash - pnpm run lint - ``` -- Typecheck the whole monorepo - ```bash - pnpm run typecheck - ``` -- Test the whole monorepo - ```bash - pnpm run test - or - pnpm run test:dev - ``` -- How to install an npm package in the Remix app ? - ```bash - pnpm add dayjs --filter webapp - ``` -- Tweak the tsconfigs, eslint configs in the `config-package` folder. Any package or app will then extend from these configs. +The developer-first open-source Zapier Alternative diff --git a/apps/coordinator/src/server.ts b/apps/coordinator/src/server.ts index da20fe635..324d359fd 100644 --- a/apps/coordinator/src/server.ts +++ b/apps/coordinator/src/server.ts @@ -16,7 +16,7 @@ import { } from "internal-platform"; import { v4 } from "uuid"; import { WebSocket } from "ws"; -import { z } from "zod"; +import { z, ZodError } from "zod"; import { TriggerServerConnection } from "./connection"; import { env } from "./env"; import { pulsarClient } from "./pulsarClient"; @@ -282,13 +282,13 @@ export class TriggerServer { try { // TODO: do this in a better/safer way - const parsedTrigger = TriggerMetadataSchema.parse(data.trigger); + const trigger = TriggerMetadataSchema.parse(data.trigger); // register the workflow with the platform const response = await this.#apiClient.registerWorkflow({ id: data.workflowId, name: data.workflowName, - trigger: parsedTrigger, + trigger, package: { name: data.packageName, version: data.packageVersion, @@ -396,7 +396,19 @@ export class TriggerServer { return true; } catch (error) { - this.#logger.error("Failed to initialize workflow", error); + if (error instanceof ZodError) { + this.#logger.error( + `Failed to initialize workflow because the trigger is invalid: ${JSON.stringify( + data.trigger + )}`, + error.issues + ); + } else { + this.#logger.error( + "Failed to initialize workflow for some unknown reason", + error + ); + } return false; } diff --git a/apps/webapp/app/entry.server.tsx b/apps/webapp/app/entry.server.tsx index ae4035bea..7f21d6660 100644 --- a/apps/webapp/app/entry.server.tsx +++ b/apps/webapp/app/entry.server.tsx @@ -3,6 +3,7 @@ import { RemixServer } from "@remix-run/react"; import { renderToString } from "react-dom/server"; import * as Sentry from "@sentry/remix"; import * as MessageBroker from "~/services/messageBroker.server"; +import * as WebhookProxy from "~/services/webhookProxy.server"; import { prisma } from "./db.server"; export default function handleRequest( @@ -36,3 +37,4 @@ if (process.env.NODE_ENV === "production" && process.env.SENTRY_DSN) { } MessageBroker.init(); +WebhookProxy.init(); diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index dda3ceda7..a1556ea63 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -30,6 +30,8 @@ const EnvironmentSchema = z.object({ .string() .default("0") .transform((v) => v === "1"), + NGROK_AUTH_TOKEN: z.string().optional(), + NGROK_SUBDOMAIN: z.string().optional(), }); export type Environment = z.infer; diff --git a/apps/webapp/app/models/registeredWebhook.server.ts b/apps/webapp/app/models/registeredWebhook.server.ts new file mode 100644 index 000000000..801376a0f --- /dev/null +++ b/apps/webapp/app/models/registeredWebhook.server.ts @@ -0,0 +1,22 @@ +import { prisma } from "~/db.server"; + +export type RegisteredWebhookWithRelationships = NonNullable< + Awaited> +>; + +export async function findRegisteredWebhookById(id: string) { + return prisma.registeredWebhook.findFirst({ + where: { + id, + }, + include: { + connectionSlot: { + include: { + connection: true, + }, + }, + trigger: true, + workflow: true, + }, + }); +} diff --git a/apps/webapp/app/models/workflowConnectionSlot.server.ts b/apps/webapp/app/models/workflowConnectionSlot.server.ts new file mode 100644 index 000000000..9171f6e8c --- /dev/null +++ b/apps/webapp/app/models/workflowConnectionSlot.server.ts @@ -0,0 +1,15 @@ +import { prisma } from "~/db.server"; + +export async function findWorkflowConnectionSlotById(id: string) { + return prisma.workflowConnectionSlot.findFirst({ + where: { + id, + }, + include: { + connection: true, + workflow: true, + trigger: true, + registeredWebhook: true, + }, + }); +} diff --git a/apps/webapp/app/routes/api/v1/internal/webhooks/$serviceIdentifier.$id.ts b/apps/webapp/app/routes/api/v1/internal/webhooks/$serviceIdentifier.$id.ts new file mode 100644 index 000000000..6842e7b34 --- /dev/null +++ b/apps/webapp/app/routes/api/v1/internal/webhooks/$serviceIdentifier.$id.ts @@ -0,0 +1,36 @@ +import type { ActionArgs } from "@remix-run/server-runtime"; +import { z } from "zod"; +import { findRegisteredWebhookById } from "~/models/registeredWebhook.server"; +import { HandleWebhook } from "~/services/webhooks/handleWebhook.server"; + +export async function action({ request, params }: ActionArgs) { + const { id, serviceIdentifier } = z + .object({ id: z.string(), serviceIdentifier: z.string() }) + .parse(params); + + const webhook = await findRegisteredWebhookById(id); + + if (!webhook) { + return { + status: 404, + body: `Could not find webhook with id ${id} and serviceIdentifier ${serviceIdentifier}`, + }; + } + + if (webhook.connectionSlot.connection?.apiIdentifier !== serviceIdentifier) { + return { status: 500, body: "Service identifier does not match" }; + } + + try { + const handleWebhookService = new HandleWebhook(); + + await handleWebhookService.call(webhook, serviceIdentifier, request); + + return { status: 200 }; + } catch (error) { + return { + status: 500, + body: error instanceof Error ? error.message : `Unknown error: ${error}`, + }; + } +} diff --git a/apps/webapp/app/services/messageBroker.server.ts b/apps/webapp/app/services/messageBroker.server.ts index d784d6455..5093300a2 100644 --- a/apps/webapp/app/services/messageBroker.server.ts +++ b/apps/webapp/app/services/messageBroker.server.ts @@ -12,6 +12,7 @@ import Pulsar from "pulsar-client"; import { z } from "zod"; import { prisma } from "~/db.server"; import { env } from "~/env.server"; +import { findRegisteredWebhookById } from "~/models/registeredWebhook.server"; import { completeWorkflowRun, failWorkflowRun, @@ -20,6 +21,7 @@ import { startWorkflowRun, triggerEventInRun, } from "~/models/workflowRun.server"; +import { RegisterWebhook } from "./webhooks/registerWebhook.server"; let pulsarClient: PulsarClient; let triggerPublisher: ZodPublisher; @@ -178,6 +180,10 @@ const InternalCatalog = { data: CustomEventCreatedEventSchema, properties: CustomEventCreatedPropertiesSchema, }, + REGISTERED_WEBHOOK_CREATED: { + data: z.object({ id: z.string() }), + properties: z.object({}), + }, }; async function createInternalPubSub() { @@ -193,6 +199,19 @@ async function createInternalPubSub() { }, schema: InternalCatalog, handlers: { + REGISTERED_WEBHOOK_CREATED: async (id, data, properties) => { + const webhook = await findRegisteredWebhookById(data.id); + + if (!webhook) { + return true; + } + + const registerWebhookService = new RegisterWebhook(); + + const isRegistered = await registerWebhookService.call(webhook); + + return isRegistered; // Returning true will mean we don't retry + }, CUSTOM_EVENT_CREATED: async (id, data, properties) => { console.log("CUSTOM_EVENT_CREATED", id, data, properties); diff --git a/apps/webapp/app/services/pizzly.server.ts b/apps/webapp/app/services/pizzly.server.ts new file mode 100644 index 000000000..ea275b395 --- /dev/null +++ b/apps/webapp/app/services/pizzly.server.ts @@ -0,0 +1,4 @@ +import { Pizzly } from "@nangohq/pizzly-node"; +import { env } from "~/env.server"; + +export const pizzly = new Pizzly(env.PIZZLY_HOST); diff --git a/apps/webapp/app/services/webhookProxy.server.ts b/apps/webapp/app/services/webhookProxy.server.ts new file mode 100644 index 000000000..4325e3ceb --- /dev/null +++ b/apps/webapp/app/services/webhookProxy.server.ts @@ -0,0 +1,33 @@ +import ngrok from "ngrok"; +import { env } from "~/env.server"; + +let originOrProxyUrl: string; + +declare global { + var __origin_or_proxy_url__: string; +} + +export async function init() { + if (originOrProxyUrl) { + return; + } + + if (env.NODE_ENV === "production" || !env.NGROK_AUTH_TOKEN) { + originOrProxyUrl = env.APP_ORIGIN; + } else { + if (!global.__origin_or_proxy_url__) { + const proxyUrl = await ngrok.connect({ + addr: process.env.REMIX_APP_PORT || 3000, + authtoken: env.NGROK_AUTH_TOKEN, + subdomain: env.NGROK_SUBDOMAIN, + }); + + console.log(`🚧 Initiated Proxy URL: ${proxyUrl} 🚧`); + + global.__origin_or_proxy_url__ = proxyUrl; + } + originOrProxyUrl = global.__origin_or_proxy_url__; + } +} + +export { originOrProxyUrl }; diff --git a/apps/webapp/app/services/webhooks/handleWebhook.server.ts b/apps/webapp/app/services/webhooks/handleWebhook.server.ts new file mode 100644 index 000000000..3452f384b --- /dev/null +++ b/apps/webapp/app/services/webhooks/handleWebhook.server.ts @@ -0,0 +1,64 @@ +import type { PrismaClient } from "~/db.server"; +import { prisma } from "~/db.server"; +import type { RegisteredWebhookWithRelationships } from "~/models/registeredWebhook.server"; +import { github } from "internal-integrations"; + +export class HandleWebhook { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call( + webhook: RegisteredWebhookWithRelationships, + serviceIdentifier: string, + request: Request + ) { + const requestUrl = new URL(request.url); + const rawSearchParams = requestUrl.searchParams; + const rawBody = await request.json(); + const rawHeaders = Object.fromEntries(request.headers.entries()); + + const webhookEvent = await this.#handleWebhook( + webhook, + serviceIdentifier, + rawBody, + rawHeaders, + rawSearchParams + ); + + console.log( + `Received webhook event: ${JSON.stringify(webhookEvent, null, 2)}` + ); + + return true; + } + + async #handleWebhook( + webhook: RegisteredWebhookWithRelationships, + serviceIdentifier: string, + rawBody: any, + rawHeaders: Record, + rawSearchParams: URLSearchParams + ) { + switch (serviceIdentifier) { + case "github": { + return github.webhooks.handleWebhookRequest({ + request: { + body: rawBody, + headers: rawHeaders, + searchParams: rawSearchParams, + }, + secret: webhook.secret, + params: webhook.connectionSlot.auth, + }); + } + default: { + throw new Error( + `Could not handle webhook with unsupported service identifier: ${serviceIdentifier}` + ); + } + } + } +} diff --git a/apps/webapp/app/services/webhooks/registerWebhook.server.ts b/apps/webapp/app/services/webhooks/registerWebhook.server.ts new file mode 100644 index 000000000..52b594996 --- /dev/null +++ b/apps/webapp/app/services/webhooks/registerWebhook.server.ts @@ -0,0 +1,77 @@ +import type { PrismaClient } from "~/db.server"; +import { prisma } from "~/db.server"; +import type { RegisteredWebhookWithRelationships } from "~/models/registeredWebhook.server"; +import { github } from "internal-integrations"; +import { pizzly } from "../pizzly.server"; +import { originOrProxyUrl } from "../webhookProxy.server"; + +export class RegisterWebhook { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call(webhook: RegisteredWebhookWithRelationships) { + if (webhook.status === "CONNECTED") { + return true; + } + + if (!webhook.connectionSlot || !webhook.connectionSlot.connection) { + return true; // Somehow the connection slot was deleted, so by returning true we're saying we're done with this webhook + } + + const accessToken = await pizzly.accessToken( + webhook.connectionSlot.connection.apiIdentifier, + webhook.connectionSlot.connection.id + ); + + const webhookUrl = `${originOrProxyUrl}/api/v1/internal/webhooks/${webhook.connectionSlot.connection.apiIdentifier}/${webhook.id}`; + + const serviceWebhook = await this.#registerWebhookWithConnection( + webhook.connectionSlot.connection.apiIdentifier, + accessToken, + webhookUrl, + webhook.secret, + webhook.connectionSlot.auth + ); + + await this.#prismaClient.registeredWebhook.update({ + where: { + id: webhook.id, + }, + data: { + status: "CONNECTED", + webhookConfig: serviceWebhook, + }, + }); + + return true; + } + + async #registerWebhookWithConnection( + serviceIdentifier: string, + accessToken: string, + callbackUrl: string, + secret: string, + data: unknown + ) { + switch (serviceIdentifier) { + case "github": { + return github.webhooks.registerWebhook( + { + callbackUrl, + secret: secret, + accessToken, + }, + data + ); + } + default: { + throw new Error( + `Could not register webhook with unsupported service identifier: ${serviceIdentifier}` + ); + } + } + } +} diff --git a/apps/webapp/app/services/workflows/register.server.ts b/apps/webapp/app/services/workflows/register.server.ts index f0bdd234f..94f6753ae 100644 --- a/apps/webapp/app/services/workflows/register.server.ts +++ b/apps/webapp/app/services/workflows/register.server.ts @@ -3,6 +3,8 @@ import type { PrismaClient } from "~/db.server"; import { prisma } from "~/db.server"; import type { Organization } from "~/models/organization.server"; import type { RuntimeEnvironment } from "~/models/runtimeEnvironment.server"; +import { internalPubSub } from "~/services/messageBroker.server"; +import crypto from "node:crypto"; export class RegisterWorkflow { #prismaClient: PrismaClient; @@ -66,6 +68,56 @@ export class RegisterWorkflow { }, }); + if (validation.data.trigger.type === "WEBHOOK") { + const serviceIdentifier = this.#parseServiceIdentifier( + validation.data.trigger.config.id + ); + + const existingConnection = + await this.#findLatestExistingConnectionInOrg( + serviceIdentifier, + organization + ); + + // Create the connectionSlot and then fire the connectionSlot created event + const connectionSlot = + await this.#prismaClient.workflowConnectionSlot.create({ + data: { + workflowId: workflow.id, + triggerId: trigger.id, + serviceIdentifier, + connectionId: existingConnection?.id, + slotName: "trigger", + auth: validation.data.trigger.config.webhook, + }, + }); + + const webhook = await this.#prismaClient.registeredWebhook.create({ + data: { + connectionSlot: { + connect: { + id: connectionSlot.id, + }, + }, + workflow: { + connect: { + id: trigger.workflowId, + }, + }, + trigger: { + connect: { + id: trigger.id, + }, + }, + secret: crypto.randomBytes(32).toString("hex"), + }, + }); + + await internalPubSub.publish("REGISTERED_WEBHOOK_CREATED", { + id: webhook.id, + }); + } + return { status: "success" as const, data: { @@ -83,6 +135,79 @@ export class RegisterWorkflow { }, }); + if (validation.data.trigger.type === "WEBHOOK") { + const serviceIdentifier = this.#parseServiceIdentifier( + validation.data.trigger.config.id + ); + + const existingConnection = + await this.#findLatestExistingConnectionInOrg( + serviceIdentifier, + organization + ); + + const existingConnectionSlot = + await this.#prismaClient.workflowConnectionSlot.findFirst({ + where: { + workflowId: workflow.id, + triggerId: existingTrigger.id, + serviceIdentifier, + }, + }); + + if (existingConnectionSlot) { + await this.#prismaClient.workflowConnectionSlot.update({ + where: { + id: existingConnectionSlot.id, + }, + data: { + connectionId: existingConnectionSlot.connectionId + ? existingConnectionSlot.connectionId + : existingConnection?.id, + auth: validation.data.trigger.config.webhook, + }, + }); + } else { + // Create the connectionSlot and then fire the connectionSlot created event + const connectionSlot = + await this.#prismaClient.workflowConnectionSlot.create({ + data: { + workflowId: workflow.id, + triggerId: existingTrigger.id, + serviceIdentifier, + connectionId: existingConnection?.id, + slotName: "trigger", + auth: validation.data.trigger.config.webhook, + }, + }); + + const webhook = await this.#prismaClient.registeredWebhook.create({ + data: { + connectionSlot: { + connect: { + id: connectionSlot.id, + }, + }, + workflow: { + connect: { + id: existingTrigger.workflowId, + }, + }, + trigger: { + connect: { + id: existingTrigger.id, + }, + }, + secret: crypto.randomBytes(32).toString("hex"), + }, + }); + + await internalPubSub.publish("REGISTERED_WEBHOOK_CREATED", { + id: webhook.id, + }); + } + } + return { status: "success" as const, data: { @@ -107,4 +232,28 @@ export class RegisterWorkflow { //todo Workflow has one trigger (can also have a slot with connection) //todo WorkflowRuns belong to a workflow + environment } + + #parseServiceIdentifier(id: string): string { + const [serviceIdentifier] = id.split("."); + + return serviceIdentifier; + } + + async #findLatestExistingConnectionInOrg( + serviceIdentifier: string, + organization: Organization + ) { + const connection = await this.#prismaClient.aPIConnection.findFirst({ + where: { + organizationId: organization.id, + apiIdentifier: serviceIdentifier, + status: "CONNECTED", + }, + orderBy: { + createdAt: "desc", + }, + }); + + return connection; + } } diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 0a1661fbb..3621db078 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -81,6 +81,7 @@ "express": "^4.18.1", "humanize-duration": "^3.27.3", "internal-platform": "workspace:*", + "internal-integrations": "workspace:*", "javascript-time-ago": "^2.5.7", "json-query": "^2.2.2", "jsonata": "^1.8.6", @@ -92,6 +93,7 @@ "mergent": "^1.4.0", "morgan": "^1.10.0", "nanoid": "^3.3.4", + "ngrok": "^4.3.3", "openapi-types": "^12.0.0", "postcss-import": "^14.1.0", "posthog-js": "^1.31.0", diff --git a/apps/webapp/prisma/migrations/20221220205030_add_connection_slot_model/migration.sql b/apps/webapp/prisma/migrations/20221220205030_add_connection_slot_model/migration.sql new file mode 100644 index 000000000..bbdaffbd4 --- /dev/null +++ b/apps/webapp/prisma/migrations/20221220205030_add_connection_slot_model/migration.sql @@ -0,0 +1,27 @@ +-- CreateTable +CREATE TABLE "WorkflowConnectionSlot" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "triggerId" TEXT, + "connectionId" TEXT, + "slotName" TEXT NOT NULL, + "serviceIdentifier" TEXT NOT NULL, + "auth" JSONB, + + CONSTRAINT "WorkflowConnectionSlot_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "WorkflowConnectionSlot_triggerId_key" ON "WorkflowConnectionSlot"("triggerId"); + +-- CreateIndex +CREATE UNIQUE INDEX "WorkflowConnectionSlot_workflowId_slotName_key" ON "WorkflowConnectionSlot"("workflowId", "slotName"); + +-- AddForeignKey +ALTER TABLE "WorkflowConnectionSlot" ADD CONSTRAINT "WorkflowConnectionSlot_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "Workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowConnectionSlot" ADD CONSTRAINT "WorkflowConnectionSlot_triggerId_fkey" FOREIGN KEY ("triggerId") REFERENCES "WorkflowTrigger"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowConnectionSlot" ADD CONSTRAINT "WorkflowConnectionSlot_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "APIConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/webapp/prisma/migrations/20221221113722_add_registered_webhook_model/migration.sql b/apps/webapp/prisma/migrations/20221221113722_add_registered_webhook_model/migration.sql new file mode 100644 index 000000000..7ab816597 --- /dev/null +++ b/apps/webapp/prisma/migrations/20221221113722_add_registered_webhook_model/migration.sql @@ -0,0 +1,29 @@ +-- CreateTable +CREATE TABLE "RegisteredWebhook" ( + "id" TEXT NOT NULL, + "workflowId" TEXT NOT NULL, + "triggerId" TEXT NOT NULL, + "connectionSlotId" TEXT NOT NULL, + "webhookConfig" JSONB NOT NULL, + "isEnabled" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "RegisteredWebhook_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "RegisteredWebhook_triggerId_key" ON "RegisteredWebhook"("triggerId"); + +-- CreateIndex +CREATE UNIQUE INDEX "RegisteredWebhook_connectionSlotId_key" ON "RegisteredWebhook"("connectionSlotId"); + +-- CreateIndex +CREATE UNIQUE INDEX "RegisteredWebhook_workflowId_triggerId_key" ON "RegisteredWebhook"("workflowId", "triggerId"); + +-- AddForeignKey +ALTER TABLE "RegisteredWebhook" ADD CONSTRAINT "RegisteredWebhook_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "Workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RegisteredWebhook" ADD CONSTRAINT "RegisteredWebhook_triggerId_fkey" FOREIGN KEY ("triggerId") REFERENCES "WorkflowTrigger"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RegisteredWebhook" ADD CONSTRAINT "RegisteredWebhook_connectionSlotId_fkey" FOREIGN KEY ("connectionSlotId") REFERENCES "WorkflowConnectionSlot"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/webapp/prisma/migrations/20221221114754_update_registered_webhook_model/migration.sql b/apps/webapp/prisma/migrations/20221221114754_update_registered_webhook_model/migration.sql new file mode 100644 index 000000000..b5f737fc1 --- /dev/null +++ b/apps/webapp/prisma/migrations/20221221114754_update_registered_webhook_model/migration.sql @@ -0,0 +1,20 @@ +/* + Warnings: + + - You are about to drop the column `isEnabled` on the `RegisteredWebhook` table. All the data in the column will be lost. + - Added the required column `updatedAt` to the `RegisteredWebhook` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "RegisteredWebhookStatus" AS ENUM ('CREATED', 'CONNECTED'); + +-- DropIndex +DROP INDEX "RegisteredWebhook_workflowId_triggerId_key"; + +-- AlterTable +ALTER TABLE "RegisteredWebhook" DROP COLUMN "isEnabled", +ADD COLUMN "connectedAt" TIMESTAMP(3), +ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "status" "RegisteredWebhookStatus" NOT NULL DEFAULT 'CREATED', +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL, +ALTER COLUMN "webhookConfig" DROP NOT NULL; diff --git a/apps/webapp/prisma/migrations/20221221124220_add_secret_to_registered_webhook/migration.sql b/apps/webapp/prisma/migrations/20221221124220_add_secret_to_registered_webhook/migration.sql new file mode 100644 index 000000000..9458dc18c --- /dev/null +++ b/apps/webapp/prisma/migrations/20221221124220_add_secret_to_registered_webhook/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `secret` to the `RegisteredWebhook` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "RegisteredWebhook" ADD COLUMN "secret" TEXT NOT NULL; diff --git a/apps/webapp/prisma/schema.prisma b/apps/webapp/prisma/schema.prisma index 4f44daa4e..b5951489c 100644 --- a/apps/webapp/prisma/schema.prisma +++ b/apps/webapp/prisma/schema.prisma @@ -62,8 +62,9 @@ model APIConnection { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + organizationId String + connectionSlots WorkflowConnectionSlot[] } enum APIConnectionType { @@ -107,8 +108,10 @@ model Workflow { organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String - triggers WorkflowTrigger[] - runs WorkflowRun[] + triggers WorkflowTrigger[] + runs WorkflowRun[] + connectionSlots WorkflowConnectionSlot[] + registeredWebhooks RegisteredWebhook[] // Can have multiple because there are multiple environments @@unique([organizationId, slug]) } @@ -126,8 +129,10 @@ model WorkflowTrigger { config Json status WorkflowTriggerStatus @default(CREATED) - isDefault Boolean @default(false) - runs WorkflowRun[] + isDefault Boolean @default(false) + runs WorkflowRun[] + connectionSlot WorkflowConnectionSlot? + registeredWebhook RegisteredWebhook? @@unique([workflowId, environmentId]) } @@ -144,6 +149,53 @@ enum WorkflowTriggerStatus { CONNECTED } +model WorkflowConnectionSlot { + id String @id @default(cuid()) + + workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade, onUpdate: Cascade) + workflowId String + + trigger WorkflowTrigger? @relation(fields: [triggerId], references: [id], onDelete: Cascade, onUpdate: Cascade) + triggerId String? @unique + + connection APIConnection? @relation(fields: [connectionId], references: [id], onDelete: Cascade, onUpdate: Cascade) + connectionId String? + + slotName String + serviceIdentifier String + auth Json? + + registeredWebhook RegisteredWebhook? + + @@unique([workflowId, slotName]) +} + +model RegisteredWebhook { + id String @id @default(cuid()) + + workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade, onUpdate: Cascade) + workflowId String + + trigger WorkflowTrigger @relation(fields: [triggerId], references: [id], onDelete: Cascade, onUpdate: Cascade) + triggerId String @unique + + connectionSlot WorkflowConnectionSlot @relation(fields: [connectionSlotId], references: [id], onDelete: Cascade, onUpdate: Cascade) + connectionSlotId String @unique + + secret String + webhookConfig Json? + status RegisteredWebhookStatus @default(CREATED) + connectedAt DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +enum RegisteredWebhookStatus { + CREATED + CONNECTED +} + model CustomEvent { id String @id @default(cuid()) name String diff --git a/apps/webapp/remix.config.js b/apps/webapp/remix.config.js index c922c05d0..496b3a0d0 100644 --- a/apps/webapp/remix.config.js +++ b/apps/webapp/remix.config.js @@ -11,12 +11,14 @@ module.exports = { "@nangohq/pizzly-node", "axios", "internal-platform", + "internal-integrations", "@trigger.dev/common-schemas", ], watchPaths: async () => { return [ "../../packages/internal-platform/src/**/*", "../../packages/common-schemas/src/**/*", + "../../packages/internal-integrations/src/**/*", ]; }, }; diff --git a/apps/webapp/tsconfig.json b/apps/webapp/tsconfig.json index 95adbe7de..5e1f2b1c0 100644 --- a/apps/webapp/tsconfig.json +++ b/apps/webapp/tsconfig.json @@ -20,6 +20,10 @@ "~/*": ["./app/*"], "internal-platform": ["../../packages/internal-platform/src/index"], "internal-platform/*": ["../../packages/internal-platform/src/*"], + "internal-integrations": [ + "../../packages/internal-integrations/src/index" + ], + "internal-integrations/*": ["../../packages/internal-integrations/src/*"], "@trigger.dev/common-schemas": [ "../../packages/common-schemas/src/index" ], diff --git a/assets/dependencyGraph.png b/assets/dependencyGraph.png new file mode 100644 index 000000000..fa3cfe6b7 Binary files /dev/null and b/assets/dependencyGraph.png differ diff --git a/examples/github-webhook/package.json b/examples/github-webhook/package.json new file mode 100644 index 000000000..f0e04907d --- /dev/null +++ b/examples/github-webhook/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "name": "@examples/github-webhook", + "version": "0.0.1", + "description": "Example trigger.dev workflow that uses the Github Webhook integration", + "dependencies": { + "@trigger.dev/integrations": "workspace:*", + "@trigger.dev/sdk": "workspace:*", + "zod": "^3.20.2" + }, + "devDependencies": { + "@trigger.dev/tsconfig": "workspace:*", + "@types/node": "^18.11.9", + "tsx": "^3.12.0" + }, + "scripts": { + "dev": "tsx src/index.ts" + } +} \ No newline at end of file diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts new file mode 100644 index 000000000..5acfdde23 --- /dev/null +++ b/examples/github-webhook/src/index.ts @@ -0,0 +1,23 @@ +import { Trigger } from "@trigger.dev/sdk"; +import { github } from "@trigger.dev/integrations"; + +const trigger = new Trigger({ + id: "github-webhook-5", + name: "GitHub Issue changes for jsonhero-web", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.issueEvent({ repo: "apihero-run/jsonhero-web" }), + run: async (event, ctx) => { + await ctx.logger.info( + "Inside the github-webhook workflow, received event", + { + event, + } + ); + + return event; + }, +}); + +trigger.listen(); diff --git a/examples/github-webhook/tsconfig.json b/examples/github-webhook/tsconfig.json new file mode 100644 index 000000000..c3d429f86 --- /dev/null +++ b/examples/github-webhook/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@trigger.dev/tsconfig/node18.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "lib": ["esnext", "dom"], + "outDir": "lib", + "moduleResolution": "node" + }, + "exclude": ["node_modules", "**/*.test.*"] +} diff --git a/packages/internal-integrations/package.json b/packages/internal-integrations/package.json new file mode 100644 index 000000000..bcc6366c9 --- /dev/null +++ b/packages/internal-integrations/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "name": "internal-integrations", + "version": "0.0.1", + "description": "Common code used by integrations and the trigger.dev platform", + "main": "./src/index.ts", + "types": "./src/index.ts", + "devDependencies": { + "@trigger.dev/tsconfig": "workspace:*", + "@types/node": "^18.11.9", + "typescript": "^4.9.4" + }, + "scripts": {}, + "dependencies": { + "zod": "^3.20.2" + } +} \ No newline at end of file diff --git a/packages/internal-integrations/src/github/index.ts b/packages/internal-integrations/src/github/index.ts new file mode 100644 index 000000000..964b113db --- /dev/null +++ b/packages/internal-integrations/src/github/index.ts @@ -0,0 +1,70 @@ +import { + HandleWebhookOptions, + WebhookConfig, + WebhookIntegration, +} from "../types"; + +import { WebhookSchema, IssueEventSchema } from "./schemas"; + +export class GitHubWebhookIntegration implements WebhookIntegration { + registerWebhook(config: WebhookConfig, params: unknown) { + const parsedParams = parseWebhookData(params); + + return registerWebhook(config, { + repo: parsedParams.params.repo, + events: parsedParams.events, + }); + } + + handleWebhookRequest(options: HandleWebhookOptions) { + return options.request.body; + } +} + +export const webhooks = new GitHubWebhookIntegration(); +export const schemas = { + IssueEventSchema, + WebhookSchema, +}; + +async function registerWebhook( + config: WebhookConfig, + options: { repo: string; events: string[] } +) { + // Create the webhook in github + const response = await fetch( + `https://api.github.com/repos/${options.repo}/hooks`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/vnd.github+json", + Authorization: `Bearer ${config.accessToken}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + body: JSON.stringify({ + name: "web", + active: true, + events: options.events, + config: { + url: config.callbackUrl, + content_type: "json", + secret: config.secret, + insecure_ssl: "0", + }, + }), + } + ); + + if (!response.ok) { + throw new Error(`Failed to register webhook: ${response.statusText}`); + } + + const webhook = await response.json(); + + return webhook; +} + +function parseWebhookData(data: unknown) { + return WebhookSchema.parse(data); +} diff --git a/packages/internal-integrations/src/github/schemas.ts b/packages/internal-integrations/src/github/schemas.ts new file mode 100644 index 000000000..7b8081f1c --- /dev/null +++ b/packages/internal-integrations/src/github/schemas.ts @@ -0,0 +1,19 @@ +import { z } from "zod"; + +export const WebhookSchema = z.object({ + events: z.array(z.string()), + params: z.object({ + repo: z.string(), + }), + scopes: z.array(z.string()).optional(), +}); + +export const IssueEventSchema = z.object({ + action: z.literal("opened"), + issue: z.object({ + id: z.string(), + title: z.string(), + body: z.string(), + url: z.string(), + }), +}); diff --git a/packages/internal-integrations/src/index.ts b/packages/internal-integrations/src/index.ts new file mode 100644 index 000000000..109d0d1c5 --- /dev/null +++ b/packages/internal-integrations/src/index.ts @@ -0,0 +1,3 @@ +export * as github from "./github"; + +export * from "./webhooks"; diff --git a/packages/internal-integrations/src/types.ts b/packages/internal-integrations/src/types.ts new file mode 100644 index 000000000..a3b5c415d --- /dev/null +++ b/packages/internal-integrations/src/types.ts @@ -0,0 +1,22 @@ +export interface WebhookConfig { + accessToken: string; + callbackUrl: string; + secret: string; +} + +export interface NormalizedWebhookRequest { + body: any; + headers: Record; + searchParams: URLSearchParams; +} + +export interface HandleWebhookOptions { + request: NormalizedWebhookRequest; + secret?: string; + params: unknown; +} + +export interface WebhookIntegration { + registerWebhook: (config: WebhookConfig, params: unknown) => Promise; + handleWebhookRequest: (options: HandleWebhookOptions) => any; +} diff --git a/packages/internal-integrations/src/webhooks.ts b/packages/internal-integrations/src/webhooks.ts new file mode 100644 index 000000000..ef732b917 --- /dev/null +++ b/packages/internal-integrations/src/webhooks.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export function createWebhookConfig( + schema: TSchema, + id: string, + webhook: any +): { id: string; webhook: z.infer } { + return { + id, + webhook: schema.parse(webhook), + }; +} diff --git a/packages/internal-integrations/tsconfig.json b/packages/internal-integrations/tsconfig.json new file mode 100644 index 000000000..6e9e7dcd1 --- /dev/null +++ b/packages/internal-integrations/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@trigger.dev/tsconfig/node18.json", + "include": ["./src/**/*.ts"], + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "paths": {}, + "lib": ["DOM"] + }, + "exclude": ["node_modules"] +} diff --git a/packages/internal-platform/src/messages/zodPublisher.ts b/packages/internal-platform/src/messages/zodPublisher.ts index aa6533770..99042a162 100644 --- a/packages/internal-platform/src/messages/zodPublisher.ts +++ b/packages/internal-platform/src/messages/zodPublisher.ts @@ -91,8 +91,15 @@ export class ZodPublisher { const id = ulid(); + this.#logger.debug( + "Parsing message data and properties", + type, + data, + properties + ); + const parsedData = messageSchema.data.parse(data); - const parsedProperties = messageSchema.properties.parse(properties); + const parsedProperties = messageSchema.properties.parse(properties ?? {}); const message = JSON.stringify({ id, diff --git a/packages/internal-platform/src/schemas/workflows.ts b/packages/internal-platform/src/schemas/workflows.ts index 30ba4d66f..bb514a3bd 100644 --- a/packages/internal-platform/src/schemas/workflows.ts +++ b/packages/internal-platform/src/schemas/workflows.ts @@ -11,7 +11,7 @@ export const WebhookEventTriggerSchema = z.object({ type: z.literal("WEBHOOK"), config: z.object({ id: z.string(), - params: z.record(z.string()), + webhook: z.any(), }), }); diff --git a/packages/trigger-integrations/DEVELOPMENT.md b/packages/trigger-integrations/DEVELOPMENT.md new file mode 100644 index 000000000..9343e5a36 --- /dev/null +++ b/packages/trigger-integrations/DEVELOPMENT.md @@ -0,0 +1,11 @@ +## Trigger.dev Integrations Development Guide + +### JSON Schema to Zod + +If you need to convert a JSON Schema to Zod, use this handy [transform.tools utility](https://transform.tools/json-schema-to-zod) + +### Add a new provider configuration to pizzly + +```sh +PIZZLY_HOSTPORT=http://localhost:3004 npx pizzly config:create github github "scopes,here" +``` diff --git a/packages/trigger-integrations/package.json b/packages/trigger-integrations/package.json index 1a98e3f40..51d856471 100644 --- a/packages/trigger-integrations/package.json +++ b/packages/trigger-integrations/package.json @@ -9,6 +9,7 @@ ], "devDependencies": { "@trigger.dev/tsconfig": "workspace:*", + "internal-integrations": "workspace:*", "@types/node": "^18.11.9", "rimraf": "^3.0.2", "tsup": "^6.5.0" @@ -20,6 +21,7 @@ "dev": "tsup --watch" }, "dependencies": { - "zod": "^3.20.2" + "zod": "^3.20.2", + "@trigger.dev/sdk": "workspace:*" } } \ No newline at end of file diff --git a/packages/trigger-integrations/src/index.ts b/packages/trigger-integrations/src/index.ts index 08c633673..7df043435 100644 --- a/packages/trigger-integrations/src/index.ts +++ b/packages/trigger-integrations/src/index.ts @@ -1,2 +1,3 @@ -export * from "./integrations/github"; -export * from "./integrations/slack"; +import * as github from "./integrations/github"; + +export { github }; diff --git a/packages/trigger-integrations/src/integrations/github.ts b/packages/trigger-integrations/src/integrations/github.ts deleted file mode 100644 index 4438677a1..000000000 --- a/packages/trigger-integrations/src/integrations/github.ts +++ /dev/null @@ -1,34 +0,0 @@ -export type GitHubIssue = { - id: string; - title: string; - body: string; - url: string; -}; - -export type GitHubIntegration = { - id: "github"; - getIssue(repo: string, id: string): Promise; -}; - -// export const github = createIntegration({ -// id: "github", -// methods: { -// getIssue: { -// request: (repo: string, id: string) => ({ -// url: ` -// https://api.github.com/repos/${repo}/issues/${id} -// `, -// method: "GET", -// headers: { -// Accept: "application/vnd.github.v3+json", -// }, -// }), -// response: (data: any) => ({ -// id: data.id, -// title: data.title, -// body: data.body, -// url: data.html_url, -// }), -// }, -// }, -// }); diff --git a/packages/trigger-integrations/src/integrations/github/index.ts b/packages/trigger-integrations/src/integrations/github/index.ts new file mode 100644 index 000000000..b8610552d --- /dev/null +++ b/packages/trigger-integrations/src/integrations/github/index.ts @@ -0,0 +1 @@ +export * from "./webhooks"; diff --git a/packages/trigger-integrations/src/integrations/github/webhooks.ts b/packages/trigger-integrations/src/integrations/github/webhooks.ts new file mode 100644 index 000000000..1b6fa6cf1 --- /dev/null +++ b/packages/trigger-integrations/src/integrations/github/webhooks.ts @@ -0,0 +1,38 @@ +import { TriggerEvent } from "@trigger.dev/sdk"; +import { createWebhookConfig, github } from "internal-integrations"; + +type IssueEventParams = { + repo: string; +}; + +export function issueEvent( + params: IssueEventParams +): TriggerEvent { + return { + type: "WEBHOOK", + config: createWebhookConfig(github.schemas.WebhookSchema, "github.issue", { + events: ["issues"], + params, + scopes: ["repo"], + }), + schema: github.schemas.IssueEventSchema, + }; +} + +export function issueCommentEvent( + params: IssueEventParams +): TriggerEvent { + return { + type: "WEBHOOK", + config: createWebhookConfig( + github.schemas.WebhookSchema, + "github.issueComment", + { + events: ["issue_comment"], + params, + scopes: ["repo"], + } + ), + schema: github.schemas.IssueEventSchema, + }; +} diff --git a/packages/trigger-integrations/src/integrations/slack.ts b/packages/trigger-integrations/src/integrations/slack.ts deleted file mode 100644 index ca6b27b77..000000000 --- a/packages/trigger-integrations/src/integrations/slack.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface SlackIntegration { - sendMessage(message: string): Promise; -} - -export const slack = { id: "slack" } as unknown as SlackIntegration; diff --git a/packages/trigger-sdk/src/client.ts b/packages/trigger-sdk/src/client.ts index 5368d818a..bcdd52e2c 100644 --- a/packages/trigger-sdk/src/client.ts +++ b/packages/trigger-sdk/src/client.ts @@ -13,9 +13,9 @@ import { Trigger, TriggerOptions } from "./trigger"; import { TriggerContext } from "./types"; import { ContextLogger } from "./logger"; -export class TriggerClient { - #trigger: Trigger; - #options: TriggerOptions; +export class TriggerClient { + #trigger: Trigger; + #options: TriggerOptions; #connection?: HostConnection; #serverRPC?: ZodRPC; @@ -27,10 +27,7 @@ export class TriggerClient { #retryIntervalMs: number = 3000; #logger: Logger; - constructor( - trigger: Trigger, - options: TriggerOptions - ) { + constructor(trigger: Trigger, options: TriggerOptions) { this.#trigger = trigger; this.#options = options; @@ -120,9 +117,11 @@ export class TriggerClient { }, }; + const eventData = this.#options.on.schema.parse(data.trigger.input); + // TODO: handle this better this.#trigger.options - .run(data.trigger.input as TEventData, ctx) + .run(eventData, ctx) .then((output) => { return serverRPC.send("COMPLETE_WORKFLOW_RUN", { id: data.id, diff --git a/packages/trigger-sdk/src/events.ts b/packages/trigger-sdk/src/events.ts index e3b043a56..ef2636558 100644 --- a/packages/trigger-sdk/src/events.ts +++ b/packages/trigger-sdk/src/events.ts @@ -1,8 +1,9 @@ import { z } from "zod"; -export type TriggerEvent = { +export type TriggerEvent = { type: "CUSTOM_EVENT" | "HTTP_ENDPOINT" | "SCHEDULE" | "WEBHOOK"; config: any; + schema: TSchema; }; export type TriggerCustomEventOptions = { @@ -12,9 +13,10 @@ export type TriggerCustomEventOptions = { export function customEvent( options: TriggerCustomEventOptions -): TriggerEvent> { +): TriggerEvent { return { type: "CUSTOM_EVENT", - config: options, + config: { name: options.name }, + schema: options.schema, }; } diff --git a/packages/trigger-sdk/src/trigger/index.ts b/packages/trigger-sdk/src/trigger/index.ts index 7cc19b48b..78b653062 100644 --- a/packages/trigger-sdk/src/trigger/index.ts +++ b/packages/trigger-sdk/src/trigger/index.ts @@ -3,22 +3,23 @@ import { LogLevel } from "internal-bridge"; import { TriggerEvent } from "../events"; import type { TriggerContext } from "../types"; +import { z } from "zod"; -export type TriggerOptions = { +export type TriggerOptions = { id: string; name: string; - on: TriggerEvent; + on: TriggerEvent; apiKey?: string; endpoint?: string; logLevel?: LogLevel; - run: (event: TEventData, ctx: TriggerContext) => Promise; + run: (event: z.infer, ctx: TriggerContext) => Promise; }; -export class Trigger { - options: TriggerOptions; - #client: TriggerClient | undefined; +export class Trigger { + options: TriggerOptions; + #client: TriggerClient | undefined; - constructor(options: TriggerOptions) { + constructor(options: TriggerOptions) { this.options = options; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49ecd703d..386e92263 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,6 +153,7 @@ importers: glob: ^8.0.3 happy-dom: ^6.0.4 humanize-duration: ^3.27.3 + internal-integrations: workspace:* internal-platform: workspace:* javascript-time-ago: ^2.5.7 json-query: ^2.2.2 @@ -166,6 +167,7 @@ importers: morgan: ^1.10.0 msw: ^0.47.0 nanoid: ^3.3.4 + ngrok: ^4.3.3 node-fetch: 2.x nodemon: ^2.0.19 npm-run-all: ^4.1.5 @@ -239,7 +241,7 @@ importers: '@tailwindcss/forms': 0.5.3_tailwindcss@3.1.8 '@tanstack/react-table': 8.7.0_biqbaboplfbrettd7655fr4n2y '@trigger.dev/common-schemas': link:../../packages/common-schemas - '@uiw/react-codemirror': 4.17.1_4ysryuwswq2dahvwdlnde5nvpq + '@uiw/react-codemirror': 4.17.1_xtrn3tyimkzpilanhyvm2ls4eu bcryptjs: 2.4.3 classnames: 2.3.2 clsx: 1.2.1 @@ -250,6 +252,7 @@ importers: date-fns: 2.29.3 express: 4.18.2 humanize-duration: 3.27.3 + internal-integrations: link:../../packages/internal-integrations internal-platform: link:../../packages/internal-platform javascript-time-ago: 2.5.9 json-query: 2.2.2 @@ -262,6 +265,7 @@ importers: mergent: 1.4.0 morgan: 1.10.0 nanoid: 3.3.4 + ngrok: 4.3.3 openapi-types: 12.0.2 postcss-import: 14.1.0_postcss@8.4.19 posthog-js: 1.36.1 @@ -400,6 +404,23 @@ importers: config-packages/tsconfig: specifiers: {} + examples/github-webhook: + specifiers: + '@trigger.dev/integrations': workspace:* + '@trigger.dev/sdk': workspace:* + '@trigger.dev/tsconfig': workspace:* + '@types/node': ^18.11.9 + tsx: ^3.12.0 + zod: ^3.20.2 + dependencies: + '@trigger.dev/integrations': link:../../packages/trigger-integrations + '@trigger.dev/sdk': link:../../packages/trigger-sdk + zod: 3.20.2 + devDependencies: + '@trigger.dev/tsconfig': link:../../config-packages/tsconfig + '@types/node': 18.11.15 + tsx: 3.12.1 + examples/smoke-test: specifiers: '@trigger.dev/integrations': workspace:* @@ -459,6 +480,19 @@ importers: '@types/ws': 8.5.3 tsx: 3.12.1 + packages/internal-integrations: + specifiers: + '@trigger.dev/tsconfig': workspace:* + '@types/node': ^18.11.9 + typescript: ^4.9.4 + zod: ^3.20.2 + dependencies: + zod: 3.20.2 + devDependencies: + '@trigger.dev/tsconfig': link:../../config-packages/tsconfig + '@types/node': 18.11.15 + typescript: 4.9.4 + packages/internal-platform: specifiers: '@trigger.dev/common-schemas': workspace:* @@ -482,16 +516,20 @@ importers: packages/trigger-integrations: specifiers: + '@trigger.dev/sdk': workspace:* '@trigger.dev/tsconfig': workspace:* '@types/node': ^18.11.9 + internal-integrations: workspace:* rimraf: ^3.0.2 tsup: ^6.5.0 zod: ^3.20.2 dependencies: + '@trigger.dev/sdk': link:../trigger-sdk zod: 3.20.2 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 18.11.11 + internal-integrations: link:../internal-integrations rimraf: 3.0.2 tsup: 6.5.0 @@ -2885,13 +2923,12 @@ packages: '@lezer/common': 0.16.1 dev: false - /@codemirror/autocomplete/6.3.4_jvia4rcxqiacrvood3734bhyuy: + /@codemirror/autocomplete/6.3.4_4npvozs3agsv66jx2b7pfvr53q: resolution: {integrity: sha512-irxKsTSjS0OkfMWWt9YxtNK97++/E+XIHfKnRpSVfZyHzda/amYF0BR+T8mMkrGQWidx2zApxHx08GT13egyQA==} peerDependencies: '@codemirror/language': ^6.0.0 '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 - '@lezer/common': ^1.0.0 dependencies: '@codemirror/language': 6.3.1 '@codemirror/state': 6.1.4 @@ -2941,7 +2978,7 @@ packages: /@codemirror/lang-javascript/6.1.1: resolution: {integrity: sha512-F4+kiuC5d5dUSJmff96tJQwpEXs/tX/4bapMRnZWW6bHKK1Fx6MunTzopkCUWRa9bF87GPmb9m7Qtg7Yv8f3uQ==} dependencies: - '@codemirror/autocomplete': 6.3.4_jvia4rcxqiacrvood3734bhyuy + '@codemirror/autocomplete': 6.3.4_4npvozs3agsv66jx2b7pfvr53q '@codemirror/language': 6.3.1 '@codemirror/lint': 6.1.0 '@codemirror/state': 6.1.4 @@ -3104,6 +3141,7 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 + dev: true /@cush/relative/1.0.0: resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==} @@ -3335,6 +3373,7 @@ packages: /@jridgewell/resolve-uri/3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} + dev: true /@jridgewell/set-array/1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} @@ -3343,6 +3382,7 @@ packages: /@jridgewell/sourcemap-codec/1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true /@jridgewell/trace-mapping/0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} @@ -3356,6 +3396,7 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + dev: true /@jsonhero/codemirror-lang-inline-tokens/0.1.0: resolution: {integrity: sha512-nbvaQBSJbLjckdA2HbkiiXXTpAMrjyzDzxymFfAmgSNHNo+GC6e0RjQDWKyDKFEQZCd3s02SQ17ipFA5FVnkfg==} @@ -3746,7 +3787,7 @@ packages: eslint: 8.29.0 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 3.5.2_lt3hqehuojhfcbzgzqfngbtmrq - eslint-plugin-import: 2.26.0_i656iqvetrvx3ajhg4t6psfrl4 + eslint-plugin-import: 2.26.0_qfsg7upu5e4dqco5ntekgyqxwu eslint-plugin-jest: 26.9.0_gtacs36c3cng3fu32eiajkw5qm eslint-plugin-jest-dom: 4.0.3_eslint@8.29.0 eslint-plugin-jsx-a11y: 6.6.1_eslint@8.29.0 @@ -4089,7 +4130,6 @@ packages: /@sindresorhus/is/4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - dev: true /@swc/core-darwin-arm64/1.3.21: resolution: {integrity: sha512-5dBrJyrCzdHOQ9evS9NBJm2geKcXffIuAvSrnwbMHkfTpl+pOM7crry2tolydFXdOE/Jbx8yyahAIXPne1fTHw==} @@ -4097,6 +4137,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@swc/core-darwin-x64/1.3.21: @@ -4105,6 +4146,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm-gnueabihf/1.3.21: @@ -4113,6 +4155,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm64-gnu/1.3.21: @@ -4121,6 +4164,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-arm64-musl/1.3.21: @@ -4129,6 +4173,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-x64-gnu/1.3.21: @@ -4137,6 +4182,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-linux-x64-musl/1.3.21: @@ -4145,6 +4191,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@swc/core-win32-arm64-msvc/1.3.21: @@ -4153,6 +4200,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core-win32-ia32-msvc/1.3.21: @@ -4161,6 +4209,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core-win32-x64-msvc/1.3.21: @@ -4169,6 +4218,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@swc/core/1.3.21: @@ -4187,6 +4237,7 @@ packages: '@swc/core-win32-arm64-msvc': 1.3.21 '@swc/core-win32-ia32-msvc': 1.3.21 '@swc/core-win32-x64-msvc': 1.3.21 + dev: true /@swc/helpers/0.4.14: resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} @@ -4206,7 +4257,6 @@ packages: engines: {node: '>=10'} dependencies: defer-to-connect: 2.0.1 - dev: true /@tailwindcss/forms/0.5.3_tailwindcss@3.1.8: resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==} @@ -4214,7 +4264,7 @@ packages: tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.1.8_v776zzvn44o7tpgzieipaairwm + tailwindcss: 3.1.8_postcss@8.4.19 /@tailwindcss/typography/0.5.8_tailwindcss@3.1.8: resolution: {integrity: sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw==} @@ -4225,7 +4275,7 @@ packages: lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.1.8_v776zzvn44o7tpgzieipaairwm + tailwindcss: 3.1.8_postcss@8.4.19 dev: true /@tanstack/react-table/8.7.0_biqbaboplfbrettd7655fr4n2y: @@ -4315,15 +4365,19 @@ packages: /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true /@tsconfig/node12/1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true /@tsconfig/node14/1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true /@tsconfig/node16/1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true /@types/acorn/4.0.6: resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} @@ -4353,7 +4407,6 @@ packages: '@types/keyv': 3.1.4 '@types/node': 18.11.15 '@types/responselike': 1.0.0 - dev: true /@types/chai-subset/1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} @@ -4453,7 +4506,6 @@ packages: /@types/http-cache-semantics/4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} - dev: true /@types/humanize-duration/3.27.1: resolution: {integrity: sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w==} @@ -4502,7 +4554,6 @@ packages: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: '@types/node': 18.11.15 - dev: true /@types/lodash/4.14.191: resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} @@ -4575,7 +4626,6 @@ packages: /@types/node/8.10.66: resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} - dev: true /@types/prismjs/1.26.0: resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} @@ -4618,7 +4668,6 @@ packages: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: '@types/node': 18.11.15 - dev: true /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} @@ -4696,7 +4745,6 @@ packages: requiresBuild: true dependencies: '@types/node': 18.11.15 - dev: true optional: true /@typescript-eslint/eslint-plugin/5.45.1_tdm6ms4ntwhlpozn7kjqrhum74: @@ -4828,13 +4876,12 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@uiw/codemirror-extensions-basic-setup/4.17.1_ahlr5jha6gxy5y2f2whv3jw24q: + /@uiw/codemirror-extensions-basic-setup/4.17.1_wq4lmc3co73jmz4wylu22jt6hu: resolution: {integrity: sha512-lFH3gFPcpKDckaioYL2KonTYeeoP7gGtaDtDai7DV5UVEyuVPlkGukKCmHz6u0ol/Krs/RTbF4ylt8cDlBT1uA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' '@codemirror/search': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' @@ -4848,14 +4895,11 @@ packages: '@codemirror/view': 0.20.7 dev: false - /@uiw/react-codemirror/4.17.1_4ysryuwswq2dahvwdlnde5nvpq: + /@uiw/react-codemirror/4.17.1_xtrn3tyimkzpilanhyvm2ls4eu: resolution: {integrity: sha512-ah7wFhvVW/uKbQR5D12AqDK51XGZaZI1WYO9/sraZzq32TGphL5BU3vcQd1P0YEZR6YLc23+KWNi2DCQ+EEAbA==} peerDependencies: - '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: @@ -4864,14 +4908,13 @@ packages: '@codemirror/state': 0.20.1 '@codemirror/theme-one-dark': 6.1.0 '@codemirror/view': 0.20.7 - '@uiw/codemirror-extensions-basic-setup': 4.17.1_ahlr5jha6gxy5y2f2whv3jw24q - codemirror: 6.0.1_@lezer+common@1.0.2 + '@uiw/codemirror-extensions-basic-setup': 4.17.1_wq4lmc3co73jmz4wylu22jt6hu + codemirror: 6.0.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 transitivePeerDependencies: - '@codemirror/autocomplete' - '@codemirror/language' - - '@codemirror/lint' - '@codemirror/search' dev: false @@ -4965,6 +5008,7 @@ packages: /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} + dev: true /acorn/7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} @@ -4975,6 +5019,7 @@ packages: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /agent-base/4.2.1: resolution: {integrity: sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==} @@ -5103,6 +5148,7 @@ packages: /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true /arg/5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -5561,7 +5607,6 @@ packages: /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - dev: true /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -5671,7 +5716,6 @@ packages: /cacheable-lookup/5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} - dev: true /cacheable-request/6.1.0: resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} @@ -5697,7 +5741,6 @@ packages: lowercase-keys: 2.0.0 normalize-url: 6.1.0 responselike: 2.0.1 - dev: true /cachedir/2.3.0: resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==} @@ -5970,7 +6013,6 @@ packages: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 - dev: true /clone/1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -5997,18 +6039,16 @@ packages: engines: {node: '>=0.10.0'} dev: false - /codemirror/6.0.1_@lezer+common@1.0.2: + /codemirror/6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: - '@codemirror/autocomplete': 6.3.4_jvia4rcxqiacrvood3734bhyuy + '@codemirror/autocomplete': 6.3.4_4npvozs3agsv66jx2b7pfvr53q '@codemirror/commands': 6.1.2 '@codemirror/language': 6.3.1 '@codemirror/lint': 6.1.0 '@codemirror/search': 6.2.3 '@codemirror/state': 6.1.4 '@codemirror/view': 6.6.0 - transitivePeerDependencies: - - '@lezer/common' dev: false /collection-visit/1.0.0: @@ -6174,6 +6214,7 @@ packages: /create-require/1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true /crelt/1.0.5: resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==} @@ -6435,7 +6476,6 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 - dev: true /deep-eql/4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} @@ -6490,7 +6530,6 @@ packages: /defer-to-connect/2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} - dev: true /define-lazy-prop/2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} @@ -6608,6 +6647,7 @@ packages: /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + dev: true /diff/5.1.0: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} @@ -6706,7 +6746,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /enhanced-resolve/5.12.0: resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==} @@ -7310,7 +7349,7 @@ packages: debug: 4.3.4 enhanced-resolve: 5.12.0 eslint: 8.29.0 - eslint-plugin-import: 2.26.0_i656iqvetrvx3ajhg4t6psfrl4 + eslint-plugin-import: 2.26.0_qfsg7upu5e4dqco5ntekgyqxwu get-tsconfig: 4.2.0 globby: 13.1.2 is-core-module: 2.11.0 @@ -7350,7 +7389,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_jnakocfte2jywffz4vixv5kpsq: + /eslint-module-utils/2.7.4_wbv6cezew2qbikiravago3ef2u: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -7375,6 +7414,7 @@ packages: debug: 3.2.7 eslint: 8.29.0 eslint-import-resolver-node: 0.3.6 + eslint-import-resolver-typescript: 3.5.2_lt3hqehuojhfcbzgzqfngbtmrq transitivePeerDependencies: - supports-color dev: true @@ -7430,7 +7470,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.26.0_i656iqvetrvx3ajhg4t6psfrl4: + /eslint-plugin-import/2.26.0_qfsg7upu5e4dqco5ntekgyqxwu: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -7447,7 +7487,7 @@ packages: doctrine: 2.1.0 eslint: 8.29.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_jnakocfte2jywffz4vixv5kpsq + eslint-module-utils: 2.7.4_wbv6cezew2qbikiravago3ef2u has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -7991,6 +8031,20 @@ packages: engines: {node: '>=8'} dev: true + /extract-zip/2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + dependencies: + debug: 4.3.4 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.0 + transitivePeerDependencies: + - supports-color + dev: false + /extract-zip/2.0.1_supports-color@8.1.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -8064,7 +8118,6 @@ packages: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 - dev: true /fflate/0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} @@ -8418,7 +8471,6 @@ packages: engines: {node: '>=8'} dependencies: pump: 3.0.0 - dev: true /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -8635,7 +8687,6 @@ packages: lowercase-keys: 2.0.0 p-cancelable: 2.1.1 responselike: 2.0.1 - dev: true /got/9.6.0: resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} @@ -8820,6 +8871,12 @@ packages: resolution: {integrity: sha512-2zuLt85Ta+gIyvs4N88pCYskNrxf1TFv3LR9t5mdAZIX8BcgQQ48F2opUptvHa6m8zsy5v/a0i9mWzTrlNWU0Q==} dev: false + /hpagent/0.1.2: + resolution: {integrity: sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==} + requiresBuild: true + dev: false + optional: true + /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -8889,7 +8946,6 @@ packages: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true /https-proxy-agent/3.0.1: resolution: {integrity: sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==} @@ -9912,6 +9968,10 @@ packages: resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} dev: true + /lodash.clonedeep/4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + /lodash.debounce/4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true @@ -9984,7 +10044,6 @@ packages: /lowercase-keys/2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} - dev: true /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -10062,6 +10121,7 @@ packages: /make-error/1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true /map-cache/0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} @@ -10565,12 +10625,10 @@ packages: /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} - dev: true /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - dev: true /min-indent/1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -10802,6 +10860,24 @@ packages: engines: {node: '>= 0.4.0'} dev: true + /ngrok/4.3.3: + resolution: {integrity: sha512-a2KApnkiG5urRxBPdDf76nNBQTnNNWXU0nXw0SsqsPI+Kmt2lGf9TdVYpYrHMnC+T9KhcNSWjCpWqBgC6QcFvw==} + engines: {node: '>=10.19.0 <14 || >=14.2'} + hasBin: true + requiresBuild: true + dependencies: + '@types/node': 8.10.66 + extract-zip: 2.0.1 + got: 11.8.5 + lodash.clonedeep: 4.5.0 + uuid: 8.3.2 + yaml: 1.10.2 + optionalDependencies: + hpagent: 0.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /nice-try/1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true @@ -10887,7 +10963,6 @@ packages: /normalize-url/6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - dev: true /npm-run-all/4.1.5: resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} @@ -11138,7 +11213,6 @@ packages: /p-cancelable/2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} - dev: true /p-limit/2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} @@ -11382,7 +11456,6 @@ packages: /pend/1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - dev: true /performance-now/2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -11490,7 +11563,6 @@ packages: lilconfig: 2.0.6 postcss: 8.4.19 yaml: 1.10.2 - dev: true /postcss-load-config/3.1.4_v776zzvn44o7tpgzieipaairwm: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} @@ -11508,6 +11580,7 @@ packages: postcss: 8.4.19 ts-node: 10.9.1_fww2c4adio7pltl52sxaeea2ii yaml: 1.10.2 + dev: true /postcss-nested/5.0.6_postcss@8.4.19: resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} @@ -11771,7 +11844,6 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true /pumpify/1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} @@ -12300,7 +12372,6 @@ packages: /resolve-alpn/1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: true /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -12344,7 +12415,6 @@ packages: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: lowercase-keys: 2.0.0 - dev: true /restore-cursor/3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} @@ -13169,7 +13239,6 @@ packages: resolve: 1.22.1 transitivePeerDependencies: - ts-node - dev: true /tailwindcss/3.1.8_v776zzvn44o7tpgzieipaairwm: resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==} @@ -13202,6 +13271,7 @@ packages: resolve: 1.22.1 transitivePeerDependencies: - ts-node + dev: true /tapable/2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -13469,6 +13539,7 @@ packages: typescript: 4.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true /ts-toolbelt/9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} @@ -13965,6 +14036,7 @@ packages: /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true /v8-to-istanbul/9.0.1: resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} @@ -14413,11 +14485,11 @@ packages: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - dev: true /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} + dev: true /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}