Implement the disable job feature backend when indexing endpoints (#382)

This commit is contained in:
Eric Allam
2023-08-23 10:30:06 +01:00
committed by GitHub
parent ee79ab30a9
commit 3ce5397072
25 changed files with 511 additions and 409 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---
Added the send-event command
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
Support disabling jobs using the `enabled` flag
@@ -7,10 +7,12 @@ import { logger } from "../logger.server";
import { RegisterSourceService } from "../sources/registerSource.server";
import { RegisterDynamicScheduleService } from "../triggers/registerDynamicSchedule.server";
import { RegisterDynamicTriggerService } from "../triggers/registerDynamicTrigger.server";
import { DisableJobService } from "../jobs/disableJob.server";
export class IndexEndpointService {
#prismaClient: PrismaClient;
#registerJobService = new RegisterJobService();
#disableJobService = new DisableJobService();
#registerSourceService = new RegisterSourceService();
#registerDynamicTriggerService = new RegisterDynamicTriggerService();
#registerDynamicScheduleService = new RegisterDynamicScheduleService();
@@ -57,23 +59,95 @@ export class IndexEndpointService {
sources: 0,
dynamicTriggers: 0,
dynamicSchedules: 0,
disabledJobs: 0,
};
const existingJobs = await this.#prismaClient.job.findMany({
where: {
projectId: endpoint.projectId,
},
include: {
aliases: {
where: {
name: "latest",
environmentId: endpoint.environmentId,
},
include: {
version: true,
},
take: 1,
},
},
});
for (const job of jobs) {
if (!job.enabled) {
continue;
const disabledJob = await this.#disableJobService
.call(endpoint, { slug: job.id, version: job.version })
.catch((error) => {
logger.error("Failed to disable job", {
endpointId: endpoint.id,
job,
error,
});
return;
});
if (disabledJob) {
indexStats.disabledJobs++;
}
} else {
try {
await this.#registerJobService.call(endpoint, job);
indexStats.jobs++;
} catch (error) {
logger.error("Failed to register job", {
endpointId: endpoint.id,
job,
error,
});
}
}
}
try {
await this.#registerJobService.call(endpoint, job);
// TODO: we need to do this for sources, dynamic triggers, and dynamic schedules
const missingJobs = existingJobs.filter((job) => {
return !jobs.find((j) => j.id === job.slug);
});
indexStats.jobs++;
} catch (error) {
logger.error("Failed to register job", {
endpointId: endpoint.id,
job,
error,
});
if (missingJobs.length > 0) {
logger.debug("Disabling missing jobs", {
endpointId: endpoint.id,
missingJobIds: missingJobs.map((job) => job.slug),
});
for (const job of missingJobs) {
const latestVersion = job.aliases[0]?.version;
if (!latestVersion) {
continue;
}
const disabledJob = await this.#disableJobService
.call(endpoint, {
slug: job.slug,
version: latestVersion.version,
})
.catch((error) => {
logger.error("Failed to disable job", {
endpointId: endpoint.id,
job,
error,
});
return;
});
if (disabledJob) {
indexStats.disabledJobs++;
}
}
}
@@ -0,0 +1,103 @@
import type { JobVersion } from "@trigger.dev/database";
import type { PrismaClient } from "~/db.server";
import { prisma } from "~/db.server";
import { ExtendedEndpoint, findEndpoint } from "~/models/endpoint.server";
import type { AuthenticatedEnvironment } from "../apiAuth.server";
import { DisableScheduleSourceService } from "../schedules/disableScheduleSource.server";
export type DisableJobServiceOptions = {
slug: string;
version: string;
};
export class DisableJobService {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call(
endpointIdOrEndpoint: string | ExtendedEndpoint,
options: DisableJobServiceOptions
) {
const endpoint =
typeof endpointIdOrEndpoint === "string"
? await findEndpoint(endpointIdOrEndpoint)
: endpointIdOrEndpoint;
return this.#disableJob(endpoint.environment, options);
}
async #disableJob(
environment: AuthenticatedEnvironment,
options: DisableJobServiceOptions
): Promise<JobVersion | undefined> {
// Find the job
const job = await this.#prismaClient.job.findUnique({
where: {
projectId_slug: {
projectId: environment.projectId,
slug: options.slug,
},
},
});
if (!job) {
return;
}
const jobVersion = await this.#prismaClient.jobVersion.findUnique({
where: {
jobId_version_environmentId: {
jobId: job.id,
version: options.version,
environmentId: environment.id,
},
},
});
if (!jobVersion) {
return;
}
if (jobVersion.status === "DISABLED") {
return;
}
// Upsert the JobVersion
const updatedJobVersion = await this.#prismaClient.jobVersion.update({
where: {
id: jobVersion.id,
},
data: {
status: "DISABLED",
},
});
await this.#disableEventDispatcher(updatedJobVersion);
return updatedJobVersion;
}
async #disableEventDispatcher(jobVersion: JobVersion) {
const eventDispatcher = await this.#prismaClient.eventDispatcher.update({
where: {
dispatchableId_environmentId: {
dispatchableId: jobVersion.jobId,
environmentId: jobVersion.environmentId,
},
},
data: {
enabled: false,
},
});
const service = new DisableScheduleSourceService();
await service.call({
key: jobVersion.jobId,
dispatcher: eventDispatcher,
});
}
}
@@ -236,8 +236,10 @@ export class RegisterJobService {
eventSpecification,
preprocessRuns: metadata.preprocessRuns,
startPosition: "LATEST",
status: "ACTIVE",
},
update: {
status: "ACTIVE",
startPosition: "LATEST",
eventSpecification,
preprocessRuns: metadata.preprocessRuns,
@@ -401,6 +403,7 @@ export class RegisterJobService {
type: "JOB_VERSION",
id: jobVersion.id,
},
enabled: true,
},
});
@@ -444,6 +447,7 @@ export class RegisterJobService {
type: "JOB_VERSION",
id: jobVersion.id,
},
enabled: true,
},
});
@@ -0,0 +1,44 @@
import type { EventDispatcher } from "@trigger.dev/database";
import { $transaction, PrismaClientOrTransaction, prisma } from "~/db.server";
import { workerQueue } from "../worker.server";
export class DisableScheduleSourceService {
#prismaClient: PrismaClientOrTransaction;
constructor(prismaClient: PrismaClientOrTransaction = prisma) {
this.#prismaClient = prismaClient;
}
public async call({ key, dispatcher }: { key: string; dispatcher: EventDispatcher }) {
const scheduleSourceExists = await this.#prismaClient.scheduleSource.findUnique({
where: {
key_environmentId: {
key,
environmentId: dispatcher.environmentId,
},
},
});
if (!scheduleSourceExists) {
return;
}
return await $transaction(this.#prismaClient, async (tx) => {
const scheduleSource = await this.#prismaClient.scheduleSource.update({
where: {
key_environmentId: {
key,
environmentId: dispatcher.environmentId,
},
},
data: {
active: false,
},
});
await workerQueue.dequeue(`scheduled:${scheduleSource.id}`, { tx });
return scheduleSource;
});
}
}
@@ -56,7 +56,6 @@ export class NextScheduledEventService {
},
{
runAt: scheduleTime,
queueName: `scheduler:${scheduleSource.environmentId}`,
tx,
jobKey: `scheduled:${scheduleSource.id}`,
}
@@ -70,6 +70,7 @@ export class RegisterScheduleSourceService {
},
metadata: schedule.metadata ?? {},
externalAccountId: externalAccount ? externalAccount.id : undefined,
active: environment.autoEnableInternalSources,
},
});
@@ -1,8 +1,5 @@
import { RegisterScheduleBody } from "@trigger.dev/core";
import { $transaction, PrismaClient } from "~/db.server";
import { prisma } from "~/db.server";
import { PrismaClient, prisma } from "~/db.server";
import { AuthenticatedEnvironment } from "../apiAuth.server";
import { RegisterScheduleSourceService } from "./registerScheduleSource.server";
export class UnregisterScheduleService {
#prismaClient: PrismaClient;
+20
View File
@@ -149,3 +149,23 @@ yarn dlx @trigger.dev/cli@latest whoami
```
</CodeGroup>
## send-event Command
The `send-event` command will send an event to your Trigger.dev project. This is useful for testing your Trigger.dev project locally.
<CodeGroup>
```bash npm
npx @trigger.dev/cli@latest send-event -n "event.name" -p "{ \"key\": \"value\" }"
```
```bash pnpm
pnpm dlx @trigger.dev/cli@latest send-event -n "event.name" -p "{ \"key\": \"value\" }"
```
```bash yarn
yarn dlx @trigger.dev/cli@latest send-event -n "event.name" -p "{ \"key\": \"value\" }"
```
</CodeGroup>
+1
View File
@@ -10,6 +10,7 @@
"supabase": "nodemon --watch src/supabase.ts -r tsconfig-paths/register -r dotenv/config src/supabase.ts",
"supabase:types": "npx supabase gen types typescript --project-id $SUPABASE_PROJECT_ID --schema public --schema auth --schema storage > src/supabase-types.ts",
"events": "nodemon --watch src/events.ts -r tsconfig-paths/register -r dotenv/config src/events.ts",
"schedules": "nodemon --watch src/schedules.ts -r tsconfig-paths/register -r dotenv/config src/schedules.ts",
"stressTest": "nodemon --watch src/stressTest.ts -r tsconfig-paths/register -r dotenv/config src/stressTest.ts",
"delays": "nodemon --watch src/delays.ts -r tsconfig-paths/register -r dotenv/config src/delays.ts",
"dev:trigger": "trigger-cli dev --port 8080"
+1
View File
@@ -13,6 +13,7 @@ client.defineJob({
id: "event-example-1",
name: "Event Example 1",
version: "1.0.0",
enabled: true,
trigger: eventTrigger({
name: "event.example",
}),
+33
View File
@@ -0,0 +1,33 @@
import { createExpressServer } from "@trigger.dev/express";
import { TriggerClient, intervalTrigger } from "@trigger.dev/sdk";
export const client = new TriggerClient({
id: "job-catalog",
apiKey: process.env["TRIGGER_API_KEY"],
apiUrl: process.env["TRIGGER_API_URL"],
verbose: false,
ioLogLocalEnabled: true,
});
client.defineJob({
id: "schedule-example-1",
name: "Schedule Example 1",
version: "1.0.0",
enabled: true,
trigger: intervalTrigger({
seconds: 60 * 3, // 3 minutes
}),
run: async (payload, io, ctx) => {
await io.runTask("task-example-1", { name: "Task 1" }, async () => {
return {
message: "Hello World",
};
});
await io.wait("wait-1", 1);
await io.logger.info("Hello World", { ctx });
},
});
createExpressServer(client);
+5
View File
@@ -129,10 +129,13 @@ client.defineJob({
},
});
// Use the stripe CLI to test this job:
// stripe trigger price.created
client.defineJob({
id: "stripe-on-price",
name: "Stripe On Price",
version: "0.1.0",
enabled: true,
trigger: stripe.onPrice({ events: ["price.created", "price.updated"] }),
run: async (payload, io, ctx) => {
if (ctx.event.name === "price.created") {
@@ -143,6 +146,8 @@ client.defineJob({
},
});
// Use the stripe CLI to test this job:
// stripe trigger product.created
client.defineJob({
id: "stripe-on-product",
name: "Stripe On Product",
+18
View File
@@ -9,6 +9,7 @@ import { CLOUD_TRIGGER_URL, COMMAND_NAME } from "../consts";
import { telemetryClient } from "../telemetry/telemetry";
import { getVersion } from "../utils/getVersion";
import { updateCommand } from "../commands/update";
import { sendEventCommand } from "../commands/sendEvent";
export const program = new Command();
@@ -104,6 +105,23 @@ program
}
});
program
.command("send-event")
.description("Sends an event to the Trigger.dev API")
.argument("[path]", "The path to the project", ".")
.option("-e, --env-file <name>", "The name of the env file to load", ".env.local")
.requiredOption("-n, --name <name>", "The name of the event to send")
.requiredOption("-p, --payload <payload>", "The JSON payload to send with the event")
.option("-i, --id <id>", "The ID of the event to send")
.version(getVersion(), "-v, --version", "Display the version number")
.action(async (path, options) => {
try {
await sendEventCommand(path, options);
} catch (e) {
throw e;
}
});
export const promptTriggerUrl = async (): Promise<string> => {
const { instanceType } = await inquirer.prompt<{
instanceType: "cloud" | "self-hosted";
+1 -61
View File
@@ -1,6 +1,5 @@
import childProcess from "child_process";
import chokidar from "chokidar";
import dotenv from "dotenv";
import fs from "fs/promises";
import ngrok from "ngrok";
import fetch from "node-fetch";
@@ -8,9 +7,8 @@ import ora, { Ora } from "ora";
import pathModule from "path";
import util from "util";
import { z } from "zod";
import { CLOUD_API_URL } from "../consts";
import { telemetryClient } from "../telemetry/telemetry";
import { pathExists, readFile } from "../utils/fileSystem";
import { getTriggerApiDetails } from "../utils/getTriggerApiDetails";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { TriggerApi } from "../utils/triggerApi";
@@ -222,64 +220,6 @@ export async function getEndpointIdFromPackageJson(path: string, options: DevCom
return value as string;
}
export async function readEnvFilesWithBackups(
path: string,
envFile: string,
backups: string[]
): Promise<{ content: string; fileName: string } | undefined> {
const envFilePath = pathModule.join(path, envFile);
const envFileExists = await pathExists(envFilePath);
if (envFileExists) {
const content = await readFile(envFilePath);
return { content, fileName: envFile };
}
for (const backup of backups) {
const backupPath = pathModule.join(path, backup);
const backupExists = await pathExists(backupPath);
if (backupExists) {
const content = await readFile(backupPath);
return { content, fileName: backup };
}
}
return;
}
export async function getTriggerApiDetails(path: string, envFile: string) {
const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile, [
".env",
".env.local",
".env.development.local",
]);
if (!resolvedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const parsedEnvFile = dotenv.parse(resolvedEnvFile.content);
if (!parsedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const apiKey = parsedEnvFile.TRIGGER_API_KEY;
const apiUrl = parsedEnvFile.TRIGGER_API_URL;
if (!apiKey) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
return { apiKey, apiUrl: apiUrl ?? CLOUD_API_URL, envFile: resolvedEnvFile.fileName };
}
async function resolveEndpointUrl(apiUrl: string, port: number, hostname: string) {
const apiURL = new URL(apiUrl);
+72
View File
@@ -0,0 +1,72 @@
import { randomUUID } from "crypto";
import ora from "ora";
import { z } from "zod";
import { getTriggerApiDetails } from "../utils/getTriggerApiDetails";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { TriggerApi } from "../utils/triggerApi";
const SendEventCommandOptionsSchema = z.object({
envFile: z.string(),
name: z.string(),
payload: z.string(),
id: z.string().optional(),
});
export type SendEventCommandOptions = z.infer<typeof SendEventCommandOptionsSchema>;
export async function sendEventCommand(path: string, anyOptions: any) {
const result = SendEventCommandOptionsSchema.safeParse(anyOptions);
if (!result.success) {
logger.error(result.error.message);
return;
}
console.log("Sending event", { options: result.data });
const options = result.data;
const resolvedPath = resolvePath(path);
// Read from .env.local or .env to get the TRIGGER_API_KEY and TRIGGER_API_URL
const apiDetails = await getTriggerApiDetails(resolvedPath, options.envFile);
if (!apiDetails) {
return;
}
const { apiUrl, envFile, apiKey } = apiDetails;
const parsedPayload = safeJSONParse(options.payload);
if (typeof parsedPayload !== "object") {
logger.error(
`The payload must be a valid JSON object. You can also pipe the payload in via stdin.`
);
return;
}
const id = options.id ?? randomUUID();
const name = options.name;
const spinner = ora(`[trigger.dev] Sending event ${name} with id ${id}`).start();
const triggerApi = new TriggerApi(apiKey, apiUrl);
const ok = await triggerApi.sendEvent(id, name, parsedPayload);
if (ok) {
spinner.succeed(`[trigger.dev] Event ${name} with id ${id} sent`);
} else {
spinner.fail(`[trigger.dev] Event ${name} with id ${id} failed to send`);
}
}
function safeJSONParse(payload: string): any {
try {
return JSON.parse(payload);
} catch (e) {
return payload;
}
}
+2 -1
View File
@@ -2,8 +2,9 @@ import { z } from "zod";
import { logger } from "../utils/logger";
import { resolvePath } from "../utils/parseNameAndPath";
import { TriggerApi } from "../utils/triggerApi";
import { DevCommandOptions, getEndpointIdFromPackageJson, getTriggerApiDetails } from "./dev";
import { DevCommandOptions, getEndpointIdFromPackageJson } from "./dev";
import ora from "ora";
import { getTriggerApiDetails } from "../utils/getTriggerApiDetails";
export const WhoAmICommandOptionsSchema = z.object({
envFile: z.string(),
@@ -0,0 +1,63 @@
import pathModule from "path";
import { pathExists, readFile } from "./fileSystem";
import { logger } from "./logger";
import dotenv from "dotenv";
import { CLOUD_API_URL } from "../consts";
export async function readEnvFilesWithBackups(
path: string,
envFile: string,
backups: string[]
): Promise<{ content: string; fileName: string } | undefined> {
const envFilePath = pathModule.join(path, envFile);
const envFileExists = await pathExists(envFilePath);
if (envFileExists) {
const content = await readFile(envFilePath);
return { content, fileName: envFile };
}
for (const backup of backups) {
const backupPath = pathModule.join(path, backup);
const backupExists = await pathExists(backupPath);
if (backupExists) {
const content = await readFile(backupPath);
return { content, fileName: backup };
}
}
return;
}
export async function getTriggerApiDetails(path: string, envFile: string) {
const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile, [
".env",
".env.local",
".env.development.local",
]);
if (!resolvedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const parsedEnvFile = dotenv.parse(resolvedEnvFile.content);
if (!parsedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const apiKey = parsedEnvFile.TRIGGER_API_KEY;
const apiUrl = parsedEnvFile.TRIGGER_API_URL;
if (!apiKey) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
return { apiKey, apiUrl: apiUrl ?? CLOUD_API_URL, envFile: resolvedEnvFile.fileName };
}
+19
View File
@@ -80,6 +80,25 @@ export class TriggerApi {
return;
}
async sendEvent(id: string, name: string, payload: any) {
const response = await fetch(`${this.baseUrl}/api/v1/events`, {
method: "POST",
headers: {
Accept: "application/json",
Authorization: `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
event: {
id,
name,
payload,
},
}),
});
return response.ok;
}
async registerEndpoint(options: CreateEndpointOptions): Promise<EndpointResponse> {
const response = await fetch(`${this.baseUrl}/api/v1/endpoints`, {
method: "POST",
@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "JobVersionStatus" AS ENUM ('ACTIVE', 'DISABLED');
-- AlterTable
ALTER TABLE "JobVersion" ADD COLUMN "status" "JobVersionStatus" NOT NULL DEFAULT 'ACTIVE';
+10 -3
View File
@@ -461,9 +461,16 @@ model JobVersion {
dynamicTriggers DynamicTrigger[]
triggerSources TriggerSource[]
status JobVersionStatus @default(ACTIVE)
@@unique([jobId, version, environmentId])
}
enum JobVersionStatus {
ACTIVE
DISABLED
}
model EventExample {
id String @id @default(cuid())
@@ -640,9 +647,9 @@ model EventRecord {
deliverAt DateTime @default(now())
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cancelledAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cancelledAt DateTime?
isTest Boolean @default(false)
runs JobRun[]
+1 -1
View File
@@ -143,7 +143,7 @@ export class Job<
trigger: this.trigger.toJSON(),
integrations: this.integrations,
startPosition: "latest", // this is deprecated, leaving this for now to make sure newer clients work with older servers
enabled: typeof this.options.enabled === "boolean" ? this.options.enabled : true,
enabled: this.enabled,
preprocessRuns: this.trigger.preprocessRuns,
internal,
};
+9 -4
View File
@@ -409,10 +409,6 @@ export class TriggerClient {
}
attach(job: Job<Trigger<any>, any>): void {
if (!job.enabled) {
return;
}
this.#registeredJobs[job.id] = job;
job.trigger.attachToJob(this, job);
@@ -609,6 +605,15 @@ export class TriggerClient {
}
async #executeJob(body: RunJobBody, job: Job<Trigger<any>, any>): Promise<RunJobResponse> {
if (!job.enabled) {
return {
status: "ERROR",
error: {
message: "Job is disabled",
},
};
}
this.#internalLogger.debug("executing job", {
execution: body,
job: job.toJSON(),
+4 -324
View File
@@ -607,41 +607,6 @@ importers:
'@types/react-dom': 18.2.7
concurrently: 8.2.0
examples/test-cli:
specifiers:
'@trigger.dev/cli': workspace:*
'@trigger.dev/nextjs': ^2.0.10
'@trigger.dev/react': ^2.0.10
'@trigger.dev/resend': ^2.0.10
'@trigger.dev/sdk': ^2.0.10
'@types/node': 20.5.2
'@types/react': 18.2.17
'@types/react-dom': 18.2.7
autoprefixer: 10.4.15
next: 13.4.19
postcss: 8.4.28
react: 18.2.0
react-dom: 18.2.0
tailwindcss: 3.3.3
typescript: 5.1.6
dependencies:
'@trigger.dev/nextjs': 2.0.10_e6fmr53v4gucxeda46gdvhgi74
'@trigger.dev/react': 2.0.10_biqbaboplfbrettd7655fr4n2y
'@trigger.dev/resend': 2.0.10
'@trigger.dev/sdk': 2.0.10
'@types/node': 20.5.2
'@types/react': 18.2.17
'@types/react-dom': 18.2.7
autoprefixer: 10.4.15_postcss@8.4.28
next: 13.4.19_biqbaboplfbrettd7655fr4n2y
postcss: 8.4.28
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
tailwindcss: 3.3.3
typescript: 5.1.6
devDependencies:
'@trigger.dev/cli': link:../../packages/cli
integrations/github:
specifiers:
'@octokit/request': ^6.2.5
@@ -5918,10 +5883,6 @@ packages:
resolution: {integrity: sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==}
dev: false
/@next/env/13.4.19:
resolution: {integrity: sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==}
dev: false
/@next/eslint-plugin-next/12.3.4:
resolution: {integrity: sha512-BFwj8ykJY+zc1/jWANsDprDIu2MgwPOIKxNVnrKvPs+f5TPegrVnem8uScND+1veT4B7F6VeqgaNLFW1Hzl9Og==}
dependencies:
@@ -5999,15 +5960,6 @@ packages:
dev: false
optional: true
/@next/swc-darwin-arm64/13.4.19:
resolution: {integrity: sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64/12.3.4:
resolution: {integrity: sha512-PPF7tbWD4k0dJ2EcUSnOsaOJ5rhT3rlEt/3LhZUGiYNL8KvoqczFrETlUx0cUYaXe11dRA3F80Hpt727QIwByQ==}
engines: {node: '>= 10'}
@@ -6043,15 +5995,6 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64/13.4.19:
resolution: {integrity: sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-freebsd-x64/12.3.4:
resolution: {integrity: sha512-KM9JXRXi/U2PUM928z7l4tnfQ9u8bTco/jb939pdFUHqc28V43Ohd31MmZD1QzEK4aFlMRaIBQOWQZh4D/E5lQ==}
engines: {node: '>= 10'}
@@ -6105,15 +6048,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu/13.4.19:
resolution: {integrity: sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl/12.3.4:
resolution: {integrity: sha512-EETZPa1juczrKLWk5okoW2hv7D7WvonU+Cf2CgsSoxgsYbUCZ1voOpL4JZTOb6IbKMDo6ja+SbY0vzXZBUMvkQ==}
engines: {node: '>= 10'}
@@ -6149,15 +6083,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl/13.4.19:
resolution: {integrity: sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu/12.3.4:
resolution: {integrity: sha512-4csPbRbfZbuWOk3ATyWcvVFdD9/Rsdq5YHKvRuEni68OCLkfy4f+4I9OBpyK1SKJ00Cih16NJbHE+k+ljPPpag==}
engines: {node: '>= 10'}
@@ -6193,15 +6118,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu/13.4.19:
resolution: {integrity: sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl/12.3.4:
resolution: {integrity: sha512-YeBmI+63Ro75SUiL/QXEVXQ19T++58aI/IINOyhpsRL1LKdyfK/35iilraZEFz9bLQrwy1LYAR5lK200A9Gjbg==}
engines: {node: '>= 10'}
@@ -6237,15 +6153,6 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl/13.4.19:
resolution: {integrity: sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc/12.3.4:
resolution: {integrity: sha512-Sd0qFUJv8Tj0PukAYbCCDbmXcMkbIuhnTeHm9m4ZGjCf6kt7E/RMs55Pd3R5ePjOkN7dJEuxYBehawTR/aPDSQ==}
engines: {node: '>= 10'}
@@ -6281,15 +6188,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc/13.4.19:
resolution: {integrity: sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc/12.3.4:
resolution: {integrity: sha512-rt/vv/vg/ZGGkrkKcuJ0LyliRdbskQU+91bje+PgoYmxTZf/tYs6IfbmgudBJk6gH3QnjHWbkphDdRQrseRefQ==}
engines: {node: '>= 10'}
@@ -6325,15 +6223,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc/13.4.19:
resolution: {integrity: sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc/12.3.4:
resolution: {integrity: sha512-DQ20JEfTBZAgF8QCjYfJhv2/279M6onxFjdG/+5B0Cyj00/EdBxiWb2eGGFgQhrBbNv/lsvzFbbi0Ptf8Vw/bg==}
engines: {node: '>= 10'}
@@ -6369,15 +6258,6 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc/13.4.19:
resolution: {integrity: sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@nicolo-ribaudo/eslint-scope-5-internals/5.1.1-v1:
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
dependencies:
@@ -10823,24 +10703,6 @@ packages:
resolution: {integrity: sha512-VGq/H3PuRoj0shOcg1S5Flv3YD2qNz2ttk8w5xe5AHQE1I8NO9EHSBUxezIpk4dD6M7bQDtwHBMqqU2EwMwyUw==}
dev: false
/@tanstack/react-query/5.0.0-beta.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-JdK1HRw20tuwg3GfT3QZTkuS7s2KDa9FeozuJ7jZULlwPczZagouqYmM6+PL0ad6jfCnw8NzmLFtZdlBx6cTmA==}
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@tanstack/query-core': 5.0.0-beta.0
client-only: 0.0.1
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
/@tanstack/react-query/5.0.0-beta.2_react@18.2.0:
resolution: {integrity: sha512-JdK1HRw20tuwg3GfT3QZTkuS7s2KDa9FeozuJ7jZULlwPczZagouqYmM6+PL0ad6jfCnw8NzmLFtZdlBx6cTmA==}
peerDependencies:
@@ -10927,15 +10789,6 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
/@trigger.dev/core/2.0.10:
resolution: {integrity: sha512-6RtAsDmNBfMMpbk1454fqhqEWd730obdC3EqmsibHOSYuYbqzCqgeHekHFz2Ouff5PngQq67PSEbJL/XR/rGDQ==}
engines: {node: '>=16.8.0'}
dependencies:
ulid: 2.3.0
zod: 3.21.4
zod-error: 1.5.0
dev: false
/@trigger.dev/core/2.0.7:
resolution: {integrity: sha512-z86G0sbqu0ePP0eg3jA/v65TOq9ZLCHUpgis/cqaLopsybnMScWt9S9MV1Dq9FVqHHqKvyuLBugZTJwsj+FXGg==}
engines: {node: '>=16.8.0'}
@@ -10945,16 +10798,6 @@ packages:
zod-error: 1.5.0
dev: false
/@trigger.dev/integration-kit/2.0.10:
resolution: {integrity: sha512-/OTsgPPzCK3QQ2qCKTR/9Q1ixNcrSkYI9RnEogriQcBYYG2gOLCP60ntwuGBiV7r2qA1LjyCYURa2LFtIsZqZg==}
engines: {node: '>=16.8.0'}
dependencies:
node-fetch: 2.6.12
uuid: 9.0.0
transitivePeerDependencies:
- encoding
dev: false
/@trigger.dev/nextjs/1.0.0_fy3ffmrhk4zoekjz4cwhclpq64:
resolution: {integrity: sha512-Dt32BaAKYNJqYbPcpdzUOfkgpDRIftyd4oj2lBFilIHZUCJpfG1MdYbr4YWFhNE5Z04Pj0I+uN8kaxTkTZp+Ig==}
engines: {node: '>=16.8.0'}
@@ -10969,78 +10812,6 @@ packages:
- supports-color
dev: false
/@trigger.dev/nextjs/2.0.10_e6fmr53v4gucxeda46gdvhgi74:
resolution: {integrity: sha512-DG3yNO3C/doz4qyqG8bEmXEQaJ2yAngwGoq0RPfZL/uWYDrW+A6Ga4MPfzjht88pZ3P1bVUx8dFxbxvChWK4DQ==}
engines: {node: '>=16.8.0'}
peerDependencies:
'@trigger.dev/sdk': ^2.0.10
next: '>=12.0.0 <14.0.0'
dependencies:
'@trigger.dev/sdk': 2.0.10
debug: 4.3.4
next: 13.4.19_biqbaboplfbrettd7655fr4n2y
transitivePeerDependencies:
- supports-color
dev: false
/@trigger.dev/react/2.0.10_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-l+04aXNvTVRslUAWea6SGrXUIcBeJ+TtRrSpHwPSK6L+PfJF9kdHQz95I2WJWRJeWJaBcLQEWITDdQgYg6ieZA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18
dependencies:
'@tanstack/react-query': 5.0.0-beta.2_biqbaboplfbrettd7655fr4n2y
'@trigger.dev/core': 2.0.10
debug: 4.3.4
react: 18.2.0
zod: 3.21.4
transitivePeerDependencies:
- react-dom
- react-native
- supports-color
dev: false
/@trigger.dev/resend/2.0.10:
resolution: {integrity: sha512-JOdeL3buI6riVh1BeUFwevFBsSp5wzWIgkdHHUaya1A1RguY95CxUZSpZWiorRnzt2ukTc7OztVDjgWpQCESTw==}
engines: {node: '>=16.8.0'}
dependencies:
'@trigger.dev/integration-kit': 2.0.10
'@trigger.dev/sdk': 2.0.10
resend: 0.9.1
transitivePeerDependencies:
- bufferutil
- debug
- encoding
- supports-color
- utf-8-validate
dev: false
/@trigger.dev/sdk/2.0.10:
resolution: {integrity: sha512-uJqvDlAg11/upYwY5qqppAxfhsyLRlBAbCZqF1WK2uoT9Gg6eEp57Axr4I2GqE16Wact/TqWCPnHow4xXhMD2A==}
engines: {node: '>=16.8.0'}
dependencies:
'@trigger.dev/core': 2.0.10
chalk: 5.3.0
cronstrue: 2.21.0
debug: 4.3.4
evt: 2.4.13
get-caller-file: 2.0.5
git-remote-origin-url: 4.0.0
git-repo-info: 2.1.1
node-fetch: 2.6.12
slug: 6.1.0
terminal-link: 3.0.0
ulid: 2.3.0
uuid: 9.0.0
ws: 8.12.0
zod: 3.21.4
zod-error: 1.5.0
transitivePeerDependencies:
- bufferutil
- encoding
- supports-color
- utf-8-validate
dev: false
/@trigger.dev/sdk/2.0.7:
resolution: {integrity: sha512-40wpHx38opv2roJDz7CP4ZJYp6nMYJWLgehVjCqcvQWagcE00D6mTu3TZyFwBU8C/fy0QmdnnLehhruZMnpGpA==}
engines: {node: '>=16.8.0'}
@@ -12912,22 +12683,6 @@ packages:
postcss-value-parser: 4.2.0
dev: false
/autoprefixer/10.4.15_postcss@8.4.28:
resolution: {integrity: sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
peerDependencies:
postcss: ^8.1.0
dependencies:
browserslist: 4.21.10
caniuse-lite: 1.0.30001522
fraction.js: 4.2.0
normalize-range: 0.1.2
picocolors: 1.0.0
postcss: 8.4.28
postcss-value-parser: 4.2.0
dev: false
/available-typed-arrays/1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
@@ -12955,7 +12710,7 @@ packages:
/axios/0.26.1:
resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==}
dependencies:
follow-redirects: 1.15.2
follow-redirects: 1.15.2_debug@4.3.2
transitivePeerDependencies:
- debug
dev: false
@@ -12963,7 +12718,7 @@ packages:
/axios/0.27.2:
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
dependencies:
follow-redirects: 1.15.2
follow-redirects: 1.15.2_debug@4.3.2
form-data: 4.0.0
transitivePeerDependencies:
- debug
@@ -12971,7 +12726,7 @@ packages:
/axios/1.4.0:
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
dependencies:
follow-redirects: 1.15.2
follow-redirects: 1.15.2_debug@4.3.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -13397,17 +13152,6 @@ packages:
pako: 0.2.9
dev: true
/browserslist/4.21.10:
resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001522
electron-to-chromium: 1.4.498
node-releases: 2.0.13
update-browserslist-db: 1.0.11_browserslist@4.21.10
dev: false
/browserslist/4.21.4:
resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -13740,10 +13484,6 @@ packages:
/caniuse-lite/1.0.30001504:
resolution: {integrity: sha512-5uo7eoOp2mKbWyfMXnGO9rJWOGU8duvzEiYITW+wivukL7yHH4gX9yuRaobu6El4jPxo6jKZfG+N6fB621GD/Q==}
/caniuse-lite/1.0.30001522:
resolution: {integrity: sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==}
dev: false
/cartesian/1.0.1:
resolution: {integrity: sha512-tR3qKRYpRJ6FXEGuoBwpuCYcwydrk1N2rduy7eWg1Msepi3i5fCxheryw4VBlCqjCbk3Vhjh3eg+IGHtl5H74A==}
dependencies:
@@ -14568,7 +14308,6 @@ packages:
optional: true
dependencies:
ms: 2.1.2
dev: false
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -15046,10 +14785,6 @@ packages:
/electron-to-chromium/1.4.433:
resolution: {integrity: sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==}
/electron-to-chromium/1.4.498:
resolution: {integrity: sha512-4LODxAzKGVy7CJyhhN5mebwe7U2L29P+0G+HUriHnabm0d7LSff8Yn7t+Wq+2/9ze2Fu1dhX7mww090xfv7qXQ==}
dev: false
/emittery/0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
engines: {node: '>=12'}
@@ -17463,6 +17198,7 @@ packages:
peerDependenciesMeta:
debug:
optional: true
dev: false
/follow-redirects/1.15.2_debug@4.3.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
@@ -17474,7 +17210,6 @@ packages:
optional: true
dependencies:
debug: 4.3.2
dev: false
/for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -21671,46 +21406,6 @@ packages:
- babel-plugin-macros
dev: false
/next/13.4.19_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==}
engines: {node: '>=16.8.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 13.4.19
'@swc/helpers': 0.5.1
busboy: 1.6.0
caniuse-lite: 1.0.30001504
postcss: 8.4.14
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
styled-jsx: 5.1.1_react@18.2.0
watchpack: 2.4.0
zod: 3.21.4
optionalDependencies:
'@next/swc-darwin-arm64': 13.4.19
'@next/swc-darwin-x64': 13.4.19
'@next/swc-linux-arm64-gnu': 13.4.19
'@next/swc-linux-arm64-musl': 13.4.19
'@next/swc-linux-x64-gnu': 13.4.19
'@next/swc-linux-x64-musl': 13.4.19
'@next/swc-win32-arm64-msvc': 13.4.19
'@next/swc-win32-ia32-msvc': 13.4.19
'@next/swc-win32-x64-msvc': 13.4.19
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/ngrok/5.0.0-beta.2:
resolution: {integrity: sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ==}
engines: {node: '>=14.2'}
@@ -21850,10 +21545,6 @@ packages:
/node-releases/2.0.12:
resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==}
/node-releases/2.0.13:
resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
dev: false
/nodemon/3.0.1:
resolution: {integrity: sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==}
engines: {node: '>=10'}
@@ -26979,17 +26670,6 @@ packages:
setimmediate: 1.0.5
dev: false
/update-browserslist-db/1.0.11_browserslist@4.21.10:
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.21.10
escalade: 3.1.1
picocolors: 1.0.0
dev: false
/update-browserslist-db/1.0.11_browserslist@4.21.4:
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
hasBin: true