diff --git a/examples/nextjs-clerk/package.json b/examples/nextjs-clerk/package.json index 61bac5d8e..d1ea5ae94 100644 --- a/examples/nextjs-clerk/package.json +++ b/examples/nextjs-clerk/package.json @@ -20,10 +20,13 @@ "react-dom": "^18.2.0", "tailwindcss": "3.3.2", "typescript": "5.1.6", - "@trigger.dev/sdk": "2.0.0-next.4", - "@trigger.dev/nextjs": "1.0.0-next.5" + "@trigger.dev/sdk": "^2.0.0-next.4", + "@trigger.dev/nextjs": "^1.0.0-next.5" }, "devDependencies": { - "@trigger.dev/init": "workspace:*" + "@trigger.dev/cli": "workspace:*" + }, + "trigger.dev": { + "endpointId": "nextjs-clerk" } } \ No newline at end of file diff --git a/examples/nextjs-clerk/src/app/api/trigger/route.ts b/examples/nextjs-clerk/src/app/api/trigger/route.ts index 6524d4895..f7f24556f 100644 --- a/examples/nextjs-clerk/src/app/api/trigger/route.ts +++ b/examples/nextjs-clerk/src/app/api/trigger/route.ts @@ -1,10 +1,8 @@ - import { Job, TriggerClient, eventTrigger } from "@trigger.dev/sdk"; import { createAppRoute } from "@trigger.dev/nextjs"; const client = new TriggerClient({ id: "my-nextjs-project", - url: process.env.VERCEL_URL, apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, }); @@ -25,7 +23,4 @@ new Job(client, { }, }); -export const { POST, dynamic } = createAppRoute(client, { - path: "/api/trigger", -}); - \ No newline at end of file +export const { POST, dynamic } = createAppRoute(client); diff --git a/examples/nextjs-example/src/app/api/v2/trigger/route.ts b/examples/nextjs-example/src/app/api/v2/trigger/route.ts index 749781edc..f2a3c3ef4 100644 --- a/examples/nextjs-example/src/app/api/v2/trigger/route.ts +++ b/examples/nextjs-example/src/app/api/v2/trigger/route.ts @@ -5,7 +5,6 @@ import { Job, TriggerClient } from "@trigger.dev/sdk"; export const client = new TriggerClient({ id: "nextjs-appdir-example", - url: process.env.VERCEL_URL, apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, logLevel: "debug", @@ -38,6 +37,4 @@ new Job(client, { }, }); -export const { POST, dynamic } = createAppRoute(client, { - path: "/api/v2/trigger", -}); +export const { POST, dynamic } = createAppRoute(client); diff --git a/examples/nextjs-example/src/pages/api/trigger.ts b/examples/nextjs-example/src/pages/api/trigger.ts index 62902ec92..751ec8277 100644 --- a/examples/nextjs-example/src/pages/api/trigger.ts +++ b/examples/nextjs-example/src/pages/api/trigger.ts @@ -4,7 +4,7 @@ import "@/jobs/openai"; import "@/jobs/resend"; import { createPagesRoute } from "@trigger.dev/nextjs"; -const { handler, config } = createPagesRoute(client, { path: "/api/trigger" }); +const { handler, config } = createPagesRoute(client); export { config }; diff --git a/examples/nextjs-example/src/trigger.ts b/examples/nextjs-example/src/trigger.ts index 85ab259c7..9d580545f 100644 --- a/examples/nextjs-example/src/trigger.ts +++ b/examples/nextjs-example/src/trigger.ts @@ -26,7 +26,6 @@ import fetch from "node-fetch"; export const client = new TriggerClient({ id: "nextjs-example", - url: process.env.VERCEL_URL, apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, logLevel: "debug", diff --git a/packages/init-trigger/CHANGELOG.md b/packages/cli/CHANGELOG.md similarity index 100% rename from packages/init-trigger/CHANGELOG.md rename to packages/cli/CHANGELOG.md diff --git a/packages/cli/CONTRIBUTING.md b/packages/cli/CONTRIBUTING.md new file mode 100644 index 000000000..20152362d --- /dev/null +++ b/packages/cli/CONTRIBUTING.md @@ -0,0 +1,19 @@ +## How to run the CLI locally + +First, cd into the cli directory: + +```sh +cd packages/cli +``` + +Then run `pnpm run dev` which will build and re-build on any changes to the CLI package + +Next, head to an example repo (e.g. `examples/nextjs-clerk`) and add the package to `devDependencies` if it's not already: + +```json +"devDependencies": { + "@trigger.dev/cli": "workspace:*" +}, +``` + +After running `pnpm i`, you should be able to cd to the `examples/nextjs-clerk` and then run `pnpm exec cli` to run the `@trigger.dev/cli`. You won't need to keep doing `pnpm i` or anything to pickup changes from `packages/cli`. diff --git a/packages/init-trigger/README.md b/packages/cli/README.md similarity index 100% rename from packages/init-trigger/README.md rename to packages/cli/README.md diff --git a/packages/init-trigger/package.json b/packages/cli/package.json similarity index 88% rename from packages/init-trigger/package.json rename to packages/cli/package.json index c14c5798b..25f14f1bb 100644 --- a/packages/init-trigger/package.json +++ b/packages/cli/package.json @@ -1,14 +1,14 @@ { - "name": "@trigger.dev/init", + "name": "@trigger.dev/cli", "version": "0.2.1-next.8", - "description": "The CLI to easily initialize Trigger.dev in your Next.js project", + "description": "The Trigger.dev CLI", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/triggerdotdev/trigger.dev.git", - "directory": "packages/init-trigger" + "directory": "packages/cli" }, "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ "type": "module", "exports": "./dist/index.js", "bin": { - "init": "./dist/index.js" + "cli": "./dist/index.js" }, "devDependencies": { "@types/fs-extra": "^11.0.1", @@ -61,6 +61,8 @@ "fs-extra": "^11.1.0", "gradient-string": "^2.0.2", "inquirer": "^9.1.4", + "localtunnel": "^2.0.2", + "ngrok": "5.0.0-beta.2", "node-fetch": "^3.3.0", "ora": "^6.1.2", "path-to-regexp": "^6.2.1", diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts new file mode 100644 index 000000000..0710e1388 --- /dev/null +++ b/packages/cli/src/cli/index.ts @@ -0,0 +1,201 @@ +import { Command, Option } from "commander"; +import inquirer from "inquirer"; +import { initCommand } from "../commands/init.js"; +import { COMMAND_NAME, DEFAULT_TRIGGER_URL } from "../consts.js"; +import { getVersion } from "../utils/getVersion.js"; +import pathModule from "node:path"; +import { devCommand } from "../commands/dev.js"; + +export const program = new Command(); + +program.name(COMMAND_NAME).description("The Trigger.dev CLI").version("0.0.1"); + +program + .command("init") + .description("Initialize Trigger.dev in your Next.js project") + .option( + "-p, --project-path ", + "The path to the Next.js project", + "." + ) + .option( + "-k, --api-key ", + "The development API key to use for the project." + ) + .option( + "-e, --endpoint-id ", + "The unique ID for the endpoint to use for this project. (e.g. my-nextjs-project)" + ) + .option( + "-t, --trigger-url ", + "The URL of the Trigger.dev instance to use." + ) + .version(getVersion(), "-v, --version", "Display the version number") + .action(async (options) => { + await initCommand(options); + }); + +program + .command("dev") + .description( + "Tunnel your local Next.js project to Trigger.dev and start running jobs" + ) + .argument("[path]", "The path to the Next.js project", ".") + .option( + "-p, --port ", + "The local port your Next.js project is on", + "3000" + ) + .option( + "-e, --env-file ", + "The name of the env file to load", + ".env.local" + ) + .addOption( + new Option("-t, --tunnel ", "Choose the tunnel provider") + .choices(["ngrok", "localtunnel", "trigger.dev (coming soon)"]) + .default("ngrok") + ) + .version(getVersion(), "-v, --version", "Display the version number") + .action(async (options) => { + await devCommand(options); + }); + +export const promptTriggerUrl = async (): Promise => { + const { instanceType } = await inquirer.prompt<{ + instanceType: "cloud" | "self-hosted"; + }>([ + { + type: "list", + name: "instanceType", + message: "Are you using the Trigger.dev cloud or self-hosted?", + choices: [ + { + name: "Trigger.dev Cloud (https://cloud.trigger.dev)", + value: "cloud", + default: true, + }, + { + name: "Self hosted", + value: "self-hosted", + }, + ], + }, + ]); + + if (instanceType === "cloud") { + return DEFAULT_TRIGGER_URL; + } + + const { triggerUrl } = await inquirer.prompt<{ triggerUrl: string }>({ + type: "input", + name: "triggerUrl", + message: "Enter the URL of your self-hosted Trigger.dev instance", + validate: (input) => { + if (!input) { + return "Please enter the URL of your self-hosted Trigger.dev instance"; + } + + return true; + }, + }); + + return triggerUrl; +}; + +export const promptApiKey = async (instanceUrl: string): Promise => { + // First prompt if they want to enter their API key now, and if they say Yes, then prompt for it and return it + const { apiKey } = await inquirer.prompt<{ apiKey: string }>({ + type: "input", + name: "apiKey", + message: `Enter your development API key (Find yours ➡️ ${instanceUrl})`, + validate: (input) => { + // Make sure they enter something like tr_dev_******** + if (!input) { + return "Please enter your development API key"; + } + + if (!input.startsWith("tr_dev_")) { + return "Please enter a valid development API key or leave blank to skip (should start with tr_dev_)"; + } + + return true; + }, + }); + + return apiKey; +}; + +export const promptEndpointSlug = async (path: string): Promise => { + const { endpointSlug } = await inquirer.prompt<{ + endpointSlug: string; + }>({ + type: "input", + name: "endpointSlug", + default: slugify(pathModule.basename(path)), + message: "Enter a unique ID for your endpoint", + validate: (input) => { + if (!input) { + return "Please enter a unique slug for your endpoint"; + } + + return true; + }, + }); + + return endpointSlug; +}; + +export const promptEndpointUrl = async ( + instanceUrl: string +): Promise => { + const { endpointUrl } = await inquirer.prompt<{ + endpointUrl: string; + }>({ + type: "input", + name: "endpointUrl", + message: "What's the URL of your Next.js project?", + validate: (input) => { + if (!input) { + return "Please enter the URL of your Next.js project"; + } + + // If instanceUrl is a cloud instance, then the URL must be publicly accessible + const url = new URL(input); + const triggerUrl = new URL(instanceUrl); + + if (triggerUrl.hostname !== "localhost" && url.hostname === "localhost") { + return `Your Trigger.dev instance is hosted at ${triggerUrl.hostname}, so your Next.js project must also be publicly accessible. See our docs for more info: https://trigger.dev/docs/documentation/guides/tunneling-localhost`; + } + + // Make sure triggerUrl and url don't use the same port if they are both localhost + if ( + triggerUrl.hostname === "localhost" && + url.hostname === "localhost" && + triggerUrl.port === url.port + ) { + return `Your Trigger.dev instance and your Next.js project are both trying to use port ${triggerUrl.port}. Please use a different port for one of them`; + } + + return true; + }, + }); + + return endpointUrl; +}; + +export const obfuscateApiKey = (apiKey: string) => { + const [prefix, slug, secretPart] = apiKey.split("_") as [ + string, + string, + string + ]; + return `${prefix}_${slug}_${"*".repeat(secretPart.length)}`; +}; + +function slugify(value: string): string { + return value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, ""); +} diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts new file mode 100644 index 000000000..b26a58dd5 --- /dev/null +++ b/packages/cli/src/commands/dev.ts @@ -0,0 +1,8 @@ +export type DevCommandOptions = { + projectPath: string; +}; + +export async function devCommand(options: DevCommandOptions) { + // Read from package.json to get the endpointId + // Read from .env.local to get the TRIGGER_API_KEY and TRIGGER_API_URL +} diff --git a/packages/init-trigger/src/index.ts b/packages/cli/src/commands/init.ts similarity index 67% rename from packages/init-trigger/src/index.ts rename to packages/cli/src/commands/init.ts index c49210eea..b7b04666d 100644 --- a/packages/init-trigger/src/index.ts +++ b/packages/cli/src/commands/init.ts @@ -1,35 +1,45 @@ #!/usr/bin/env node -import { CliResults, parseCliOptions, runCliPrompts } from "./cli/index.js"; -import { addDependencies } from "./utils/addDependencies.js"; -import { logger } from "./utils/logger.js"; -import { resolvePath } from "./utils/parseNameAndPath.js"; -import { renderTitle } from "./utils/renderTitle.js"; import fs from "fs/promises"; +import inquirer from "inquirer"; import pathModule from "path"; -import { simpleGit } from "simple-git"; -import { TriggerApi } from "./utils/triggerApi.js"; -import { DEFAULT_TRIGGER_URL } from "./consts.js"; -import ora from "ora"; -import { renderApiKey } from "./utils/renderApiKey.js"; import { pathToRegexp } from "path-to-regexp"; +import { simpleGit } from "simple-git"; +import { + promptApiKey, + promptEndpointSlug, + promptTriggerUrl, +} from "../cli/index.js"; +import { COMMAND_NAME, DEFAULT_TRIGGER_URL } from "../consts.js"; +import { addDependencies } from "../utils/addDependencies.js"; +import { logger } from "../utils/logger.js"; +import { resolvePath } from "../utils/parseNameAndPath.js"; +import { renderApiKey } from "../utils/renderApiKey.js"; +import { renderTitle } from "../utils/renderTitle.js"; -const main = async () => { +export type InitCommandOptions = { + projectPath: string; + triggerUrl?: string; + endpointSlug?: string; + apiKey?: string; +}; + +type ResolvedOptions = Required; + +export const initCommand = async (options: InitCommandOptions) => { renderTitle(); - const cliOptions = await parseCliOptions(); - - if (cliOptions.flags.triggerUrl === DEFAULT_TRIGGER_URL) { + if (options.triggerUrl === DEFAULT_TRIGGER_URL) { logger.info(`✨ Initializing project in Trigger.dev Cloud`); - } else if (typeof cliOptions.flags.triggerUrl === "string") { + } else if (typeof options.triggerUrl === "string") { logger.info( - `✨ Initializing project using Trigger.dev at ${cliOptions.flags.triggerUrl}` + `✨ Initializing project using Trigger.dev at ${options.triggerUrl}` ); } else { logger.info(`✨ Initializing Trigger.dev in project`); } - const resolvedPath = resolvePath(cliOptions.flags.projectPath); + const resolvedPath = resolvePath(options.projectPath); // Detect if are are in a Next.js project const isNextJsProject = await detectNextJsProject(resolvedPath); @@ -60,9 +70,12 @@ const main = async () => { process.exit(1); } - const cliResults = await runCliPrompts(cliOptions); + const resolvedOptions = await resolveOptionsWithPrompts( + options, + resolvedPath + ); - const apiKey = cliResults.flags.apiKey; + const apiKey = resolvedOptions.apiKey; if (!apiKey) { logger.error("You must provide an API key to continue."); @@ -75,7 +88,7 @@ const main = async () => { ]); // Setup environment variables - await setupEnvironmentVariables(resolvedPath, cliResults); + await setupEnvironmentVariables(resolvedPath, resolvedOptions); const usesSrcDir = await detectUseOfSrcDir(resolvedPath); @@ -88,52 +101,94 @@ const main = async () => { const routeDir = pathModule.join(resolvedPath, usesSrcDir ? "src" : ""); if (nextJsDir === "pages") { - await createTriggerPageRoute(routeDir, cliResults, usesSrcDir); + await createTriggerPageRoute(routeDir, resolvedOptions, usesSrcDir); } else { - await createTriggerAppRoute(routeDir, cliResults, usesSrcDir); + await createTriggerAppRoute(routeDir, resolvedOptions, usesSrcDir); } await detectMiddlewareUsage(resolvedPath, usesSrcDir); - await waitForProjectToBuild(); + await addConfigurationToPackageJson(resolvedPath, resolvedOptions); - const api = new TriggerApi(apiKey, cliResults.flags.triggerUrl); - - const response = await api.createEndpoint({ - id: cliResults.flags.endpointSlug, - url: `${cliResults.flags.endpointUrl}${ - cliResults.flags.endpointUrl.endsWith("/") ? "" : "/" - }api/trigger`, - }); - - if (!response.ok) { - logger.error( - `${response.error}. Please contact eric@trigger.dev for assistance.` - ); - - process.exit(1); - } - - logger.success(`✅ Successfully initialized Trigger.dev!`); - logger.info( - `🔗 Visit your Trigger.dev dashboard at ${cliResults.flags.triggerUrl}` - ); + await printNextSteps(resolvedOptions); process.exit(0); }; -main().catch((err) => { - logger.error("Aborting installation..."); - if (err instanceof Error) { - logger.error(err); - } else { - logger.error( - "An unknown error has occurred. Please open an issue on github with the below:" - ); - console.log(err); +async function printNextSteps(options: ResolvedOptions) { + logger.success(`✅ Successfully initialized Trigger.dev!`); + + logger.info( + `🔗 You can now connect to ${options.triggerUrl} and run jobs locally using the '@trigger.dev/cli dev' command` + ); +} + +async function addConfigurationToPackageJson( + path: string, + options: ResolvedOptions +) { + const pkgJsonPath = pathModule.join(path, "package.json"); + const pkgBuffer = await fs.readFile(pkgJsonPath); + const pkgJson = JSON.parse(pkgBuffer.toString()); + + pkgJson["trigger.dev"] = { + endpointId: options.endpointSlug, + }; + + // Write the updated package.json file + await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2)); + + logger.success(`✅ Wrote trigger.dev config to package.json`); +} + +export const resolveOptionsWithPrompts = async ( + options: InitCommandOptions, + path: string +): Promise => { + const resolvedOptions: InitCommandOptions = { ...options }; + + try { + if (!options.triggerUrl) { + resolvedOptions.triggerUrl = await promptTriggerUrl(); + } + + if (!options.apiKey) { + resolvedOptions.apiKey = await promptApiKey(resolvedOptions.triggerUrl!); + } + + if (!options.endpointSlug) { + resolvedOptions.endpointSlug = await promptEndpointSlug(path); + } + } catch (err) { + // If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true + // If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default trigger project + // Otherwise we have to do some fancy namespace extension logic on the Error type which feels overkill for one line + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (err instanceof Error && (err as any).isTTYError) { + logger.warn( + `'${COMMAND_NAME} init' needs an interactive terminal to provide options` + ); + + const { shouldContinue } = await inquirer.prompt<{ + shouldContinue: boolean; + }>({ + name: "shouldContinue", + type: "confirm", + message: `Continue initializing your trigger.dev project?`, + default: true, + }); + + if (!shouldContinue) { + logger.info("Exiting..."); + process.exit(0); + } + } else { + throw err; + } } - process.exit(1); -}); + + return resolvedOptions as ResolvedOptions; +}; // Detects if the project is a Next.js project at path async function detectNextJsProject(path: string): Promise { @@ -227,10 +282,10 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) { if (!matcher || matcher.length === 0) { logger.warn( - `⚠️ ⚠️ ⚠️ It looks like you're using Next.js middleware at ${pathModule.relative( + `⚠️ ⚠️ ⚠️ It looks like there might be conflicting Next.js middleware in ${pathModule.relative( process.cwd(), middlewarePath - )}. This can cause problems with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware for more info.` + )} which can cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware` ); return; @@ -246,10 +301,10 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) { // Check to see if /api/trigger matches the regex, if it does, then we need to output a warning with a link to the docs to fix it if (matcherRegex.test("/api/trigger")) { logger.warn( - `🚨 It looks like you're using Next.js middleware at ${pathModule.relative( + `🚨 It looks like there might be conflicting Next.js middleware in ${pathModule.relative( process.cwd(), middlewarePath - )} that matches the '/api/trigger' path. This can cause problems with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware for more info.` + )} which will cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware` ); } } else if ( @@ -260,10 +315,10 @@ async function detectMiddlewareUsage(path: string, usesSrcDir = false) { if (matcherRegexes.some((r) => r.test("/api/trigger"))) { logger.warn( - `🚨 It looks like you're using Next.js middleware at ${pathModule.relative( + `🚨 It looks like there might be conflicting Next.js middleware in ${pathModule.relative( process.cwd(), middlewarePath - )} that matches the '/api/trigger' path. This can cause problems with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware for more info.` + )} which will cause issues with Trigger.dev. Please see https://trigger.dev/docs/documentation/guides/platforms/nextjs#middleware` ); } } @@ -305,19 +360,18 @@ async function getMiddlewareConfigMatcher( async function createTriggerPageRoute( path: string, - cliResults: CliResults, + options: ResolvedOptions, usesSrcDir = false ) { const routeContent = ` import { Job, TriggerClient, eventTrigger } from "@trigger.dev/sdk"; import { createPagesRoute } from "@trigger.dev/nextjs"; -const { handler, config } = createPagesRoute(client, { path: "/api/trigger" }); +const { handler, config } = createPagesRoute(client); export { config }; const client = new TriggerClient({ - id: "${cliResults.flags.endpointSlug}", - url: process.env.VERCEL_URL, + id: "${options.endpointSlug}", apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, }); @@ -360,7 +414,7 @@ export default handler; async function createTriggerAppRoute( path: string, - cliResults: CliResults, + options: ResolvedOptions, usesSrcDir = false ) { const routeContent = ` @@ -368,8 +422,7 @@ import { Job, TriggerClient, eventTrigger } from "@trigger.dev/sdk"; import { createAppRoute } from "@trigger.dev/nextjs"; const client = new TriggerClient({ - id: "${cliResults.flags.endpointSlug}", - url: process.env.VERCEL_URL, + id: "${options.endpointSlug}", apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, }); @@ -390,9 +443,7 @@ new Job(client, { }, }); -export const { POST, dynamic } = createAppRoute(client, { - path: "/api/trigger", -}); +export const { POST, dynamic } = createAppRoute(client); `; const directories = pathModule.join(path, "app", "api", "trigger"); @@ -411,37 +462,30 @@ export const { POST, dynamic } = createAppRoute(client, { ); } -async function setupEnvironmentVariables(path: string, cliResults: CliResults) { - if (cliResults.flags.apiKey) { +async function setupEnvironmentVariables( + path: string, + options: ResolvedOptions +) { + if (options.apiKey) { await setupEnvironmentVariable( path, ".env.local", "TRIGGER_API_KEY", - cliResults.flags.apiKey, + options.apiKey, true, renderApiKey ); } - if (cliResults.flags.triggerUrl) { + if (options.triggerUrl) { await setupEnvironmentVariable( path, ".env.local", "TRIGGER_API_URL", - cliResults.flags.triggerUrl, + options.triggerUrl, true ); } - - if (cliResults.flags.endpointUrl) { - await setupEnvironmentVariable( - path, - ".env.local", - "VERCEL_URL", - cliResults.flags.endpointUrl, - false - ); - } } async function setupEnvironmentVariable( @@ -496,13 +540,3 @@ async function pathExists(path: string): Promise { return false; } } - -async function waitForProjectToBuild() { - const spinner = ora("Waiting for project to build...").start(); - - await new Promise((resolve) => { - setTimeout(resolve, 1000); - }); - - spinner.stop(); -} diff --git a/packages/init-trigger/src/consts.ts b/packages/cli/src/consts.ts similarity index 94% rename from packages/init-trigger/src/consts.ts rename to packages/cli/src/consts.ts index 8742ab961..9ac435b6d 100644 --- a/packages/init-trigger/src/consts.ts +++ b/packages/cli/src/consts.ts @@ -16,6 +16,6 @@ export const TITLE_TEXT = ` `; export const DEFAULT_APP_NAME = "my-triggers"; -export const COMMAND_NAME = "@trigger.dev/init"; +export const COMMAND_NAME = "@trigger.dev/cli"; export const TEMPLATE_ORGANIZATION = "triggerdotdev"; export const DEFAULT_TRIGGER_URL = "https://cloud.trigger.dev"; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 000000000..f8856cf41 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +import { program } from "./cli/index.js"; +import { logger } from "./utils/logger.js"; + +const main = async () => { + await program.parseAsync(); +}; + +main().catch((err) => { + logger.error("Aborting installation..."); + if (err instanceof Error) { + logger.error(err); + } else { + logger.error( + "An unknown error has occurred. Please open an issue on github with the below:" + ); + console.log(err); + } + process.exit(1); +}); diff --git a/packages/init-trigger/src/utils/addDependencies.ts b/packages/cli/src/utils/addDependencies.ts similarity index 100% rename from packages/init-trigger/src/utils/addDependencies.ts rename to packages/cli/src/utils/addDependencies.ts diff --git a/packages/init-trigger/src/utils/createDotEnvFile.ts b/packages/cli/src/utils/createDotEnvFile.ts similarity index 100% rename from packages/init-trigger/src/utils/createDotEnvFile.ts rename to packages/cli/src/utils/createDotEnvFile.ts diff --git a/packages/init-trigger/src/utils/createProject.ts b/packages/cli/src/utils/createProject.ts similarity index 100% rename from packages/init-trigger/src/utils/createProject.ts rename to packages/cli/src/utils/createProject.ts diff --git a/packages/init-trigger/src/utils/getUserPkgManager.ts b/packages/cli/src/utils/getUserPkgManager.ts similarity index 100% rename from packages/init-trigger/src/utils/getUserPkgManager.ts rename to packages/cli/src/utils/getUserPkgManager.ts diff --git a/packages/init-trigger/src/utils/getVersion.ts b/packages/cli/src/utils/getVersion.ts similarity index 100% rename from packages/init-trigger/src/utils/getVersion.ts rename to packages/cli/src/utils/getVersion.ts diff --git a/packages/init-trigger/src/utils/git.ts b/packages/cli/src/utils/git.ts similarity index 100% rename from packages/init-trigger/src/utils/git.ts rename to packages/cli/src/utils/git.ts diff --git a/packages/init-trigger/src/utils/logger.ts b/packages/cli/src/utils/logger.ts similarity index 100% rename from packages/init-trigger/src/utils/logger.ts rename to packages/cli/src/utils/logger.ts diff --git a/packages/init-trigger/src/utils/parseNameAndPath.ts b/packages/cli/src/utils/parseNameAndPath.ts similarity index 100% rename from packages/init-trigger/src/utils/parseNameAndPath.ts rename to packages/cli/src/utils/parseNameAndPath.ts diff --git a/packages/init-trigger/src/utils/renderApiKey.ts b/packages/cli/src/utils/renderApiKey.ts similarity index 100% rename from packages/init-trigger/src/utils/renderApiKey.ts rename to packages/cli/src/utils/renderApiKey.ts diff --git a/packages/init-trigger/src/utils/renderTitle.ts b/packages/cli/src/utils/renderTitle.ts similarity index 100% rename from packages/init-trigger/src/utils/renderTitle.ts rename to packages/cli/src/utils/renderTitle.ts diff --git a/packages/init-trigger/src/utils/templateRef.ts b/packages/cli/src/utils/templateRef.ts similarity index 100% rename from packages/init-trigger/src/utils/templateRef.ts rename to packages/cli/src/utils/templateRef.ts diff --git a/packages/init-trigger/src/utils/triggerApi.ts b/packages/cli/src/utils/triggerApi.ts similarity index 100% rename from packages/init-trigger/src/utils/triggerApi.ts rename to packages/cli/src/utils/triggerApi.ts diff --git a/packages/init-trigger/tsconfig.json b/packages/cli/tsconfig.json similarity index 100% rename from packages/init-trigger/tsconfig.json rename to packages/cli/tsconfig.json diff --git a/packages/init-trigger/tsup.config.ts b/packages/cli/tsup.config.ts similarity index 100% rename from packages/init-trigger/tsup.config.ts rename to packages/cli/tsup.config.ts diff --git a/packages/init-trigger/src/cli/index.ts b/packages/init-trigger/src/cli/index.ts deleted file mode 100644 index d38564dbc..000000000 --- a/packages/init-trigger/src/cli/index.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Command } from "commander"; -import inquirer from "inquirer"; -import { COMMAND_NAME, DEFAULT_TRIGGER_URL } from "../consts.js"; -import { getVersion } from "../utils/getVersion.js"; -import { logger } from "../utils/logger.js"; - -export interface CliFlags { - projectPath: string; - triggerUrl: string; - endpointUrl: string; - endpointSlug: string; - apiKey?: string; -} - -export interface CliResults { - flags: CliFlags; -} - -const defaultOptions: CliResults = { - flags: { - projectPath: ".", - triggerUrl: DEFAULT_TRIGGER_URL, - endpointUrl: "http://localhost:3000", - endpointSlug: "my-nextjs-project", - }, -}; - -export const parseCliOptions = async () => { - const cliResults = defaultOptions; - - const program = new Command().name(COMMAND_NAME); - - program - .description("A CLI for initializing Trigger.dev in your Next.js") - .option( - "-p, --project-path ", - "The path to the Next.js project", - "." - ) - .option( - "-k, --api-key ", - "The development API key to use for the project." - ) - .option( - "-e, --endpoint-slug ", - "The unique slug for the endpoint to use for this project. (e.g. my-nextjs-project)", - "my-nextjs-project" - ) - .option( - "-u, --endpoint-url ", - "The URL of your local Next.js project. (e.g. http://localhost:3000). NOTE: Must be a publicly accessible URL if you are using a deployed Trigger.dev instance" - ) - .option( - "-t, --trigger-url ", - "The URL of the Trigger.dev instance to use. (e.g. https://cloud.trigger.dev)" - ) - .version(getVersion(), "-v, --version", "Display the version number") - .parse(process.argv); - - cliResults.flags = program.opts(); - - return cliResults; -}; - -export const runCliPrompts = async (cliResults: CliResults) => { - try { - if (!cliResults.flags.triggerUrl) { - cliResults.flags.triggerUrl = await promptTriggerUrl(); - } - - if (!cliResults.flags.apiKey) { - cliResults.flags.apiKey = await promptApiKey(cliResults.flags.triggerUrl); - } - - if (!cliResults.flags.endpointSlug) { - cliResults.flags.endpointSlug = await promptEndpointSlug(); - } - - if (!cliResults.flags.endpointUrl) { - cliResults.flags.endpointUrl = await promptEndpointUrl( - cliResults.flags.triggerUrl - ); - } - } catch (err) { - // If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true - // If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default trigger project - // Otherwise we have to do some fancy namespace extension logic on the Error type which feels overkill for one line - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (err instanceof Error && (err as any).isTTYError) { - logger.warn( - `${COMMAND_NAME} needs an interactive terminal to provide options` - ); - - const { shouldContinue } = await inquirer.prompt<{ - shouldContinue: boolean; - }>({ - name: "shouldContinue", - type: "confirm", - message: `Continue initializing your trigger.dev project?`, - default: true, - }); - - if (!shouldContinue) { - logger.info("Exiting..."); - process.exit(0); - } - } else { - throw err; - } - } - - return cliResults; -}; - -const promptTriggerUrl = async (): Promise => { - const { instanceType } = await inquirer.prompt<{ - instanceType: "cloud" | "self-hosted"; - }>([ - { - type: "list", - name: "instanceType", - message: "Are you using the Trigger.dev cloud or self-hosted?", - choices: [ - { - name: "Trigger.dev Cloud", - value: "cloud", - default: true, - }, - { - name: "Self hosted", - value: "self-hosted", - }, - ], - }, - ]); - - if (instanceType === "cloud") { - return DEFAULT_TRIGGER_URL; - } - - const { triggerUrl } = await inquirer.prompt<{ triggerUrl: string }>({ - type: "input", - name: "triggerUrl", - message: "Enter the URL of your self-hosted Trigger.dev instance", - validate: (input) => { - if (!input) { - return "Please enter the URL of your self-hosted Trigger.dev instance"; - } - - return true; - }, - }); - - return triggerUrl; -}; - -const promptApiKey = async (instanceUrl: string): Promise => { - // First prompt if they want to enter their API key now, and if they say Yes, then prompt for it and return it - const { apiKey } = await inquirer.prompt<{ apiKey: string }>({ - type: "input", - name: "apiKey", - message: `Enter your development API key (Find yours ➡️ ${instanceUrl})`, - validate: (input) => { - // Make sure they enter something like tr_dev_******** - if (!input) { - return "Please enter your development API key"; - } - - if (!input.startsWith("tr_dev_")) { - return "Please enter a valid development API key or leave blank to skip (should start with tr_dev_)"; - } - - return true; - }, - }); - - return apiKey; -}; - -const promptEndpointSlug = async (): Promise => { - const { endpointSlug } = await inquirer.prompt<{ - endpointSlug: string; - }>({ - type: "input", - name: "endpointSlug", - message: "Enter a unique slug for your endpoint (required)", - validate: (input) => { - if (!input) { - return "Please enter a unique slug for your endpoint"; - } - - return true; - }, - }); - - return endpointSlug; -}; - -const promptEndpointUrl = async (instanceUrl: string): Promise => { - const { endpointUrl } = await inquirer.prompt<{ - endpointUrl: string; - }>({ - type: "input", - name: "endpointUrl", - message: "What's the URL of your Next.js project?", - validate: (input) => { - if (!input) { - return "Please enter the URL of your Next.js project"; - } - - // If instanceUrl is a cloud instance, then the URL must be publicly accessible - const url = new URL(input); - const triggerUrl = new URL(instanceUrl); - - if (triggerUrl.hostname !== "localhost" && url.hostname === "localhost") { - return `Your Trigger.dev instance is hosted at ${triggerUrl.hostname}, so your Next.js project must also be publicly accessible. See our docs for more info: https://trigger.dev/docs/documentation/guides/tunneling-localhost`; - } - - // Make sure triggerUrl and url don't use the same port if they are both localhost - if ( - triggerUrl.hostname === "localhost" && - url.hostname === "localhost" && - triggerUrl.port === url.port - ) { - return `Your Trigger.dev instance and your Next.js project are both trying to use port ${triggerUrl.port}. Please use a different port for one of them`; - } - - return true; - }, - }); - - return endpointUrl; -}; - -export const obfuscateApiKey = (apiKey: string) => { - const [prefix, slug, secretPart] = apiKey.split("_") as [ - string, - string, - string - ]; - return `${prefix}_${slug}_${"*".repeat(secretPart.length)}`; -}; diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index dc2422f61..ea9fb4ad7 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -2,21 +2,12 @@ import type { TriggerClient } from "@trigger.dev/sdk"; import type { NextApiRequest, NextApiResponse } from "next"; import { NextResponse } from "next/server"; -export type TriggerHandlerOptions = { - path: string; -}; - -export function createPagesRoute( - client: TriggerClient, - options: TriggerHandlerOptions -) { - client.path = options.path; - +export function createPagesRoute(client: TriggerClient) { const handler = async function handler( req: NextApiRequest, res: NextApiResponse ) { - const normalizedRequest = await convertToStandardRequest(client.url, req); + const normalizedRequest = await convertToStandardRequest(req); const response = await client.handleRequest(normalizedRequest); @@ -39,12 +30,7 @@ export function createPagesRoute( }; } -export function createAppRoute( - client: TriggerClient, - options: TriggerHandlerOptions -) { - client.path = options.path; - +export function createAppRoute(client: TriggerClient) { const POST = async function handler(req: Request) { const response = await client.handleRequest(req); @@ -64,7 +50,6 @@ export function createAppRoute( } async function convertToStandardRequest( - url: string, nextReq: NextApiRequest ): Promise { const { headers: nextHeaders, method } = nextReq; @@ -75,8 +60,8 @@ async function convertToStandardRequest( headers.set(key, value as string); }); - // Create a new Request object - const webReq = new Request(url, { + // Create a new Request object (hardcode the url because it doesn't really matter what it is) + const webReq = new Request("https://next.js/api/trigger", { headers, method, // @ts-ignore diff --git a/packages/trigger-sdk/src/triggerClient.ts b/packages/trigger-sdk/src/triggerClient.ts index ff6eed319..4dbdebad5 100644 --- a/packages/trigger-sdk/src/triggerClient.ts +++ b/packages/trigger-sdk/src/triggerClient.ts @@ -1,8 +1,8 @@ import { ErrorWithStackSchema, - IndexEndpointResponse, HandleTriggerSource, HttpSourceRequestHeadersSchema, + IndexEndpointResponse, InitializeTriggerBodySchema, LogLevel, Logger, @@ -46,16 +46,11 @@ const registerSourceEvent: EventSpecification = { export type TriggerClientOptions = { id: string; - url?: string; apiKey?: string; apiUrl?: string; logLevel?: LogLevel; }; -export type ListenOptions = { - url: string; -}; - export class TriggerClient { #options: TriggerClientOptions; #registeredJobs: Record>, any>> = @@ -84,24 +79,15 @@ export class TriggerClient { #client: ApiClient; #logger: Logger; - private _url: string; id: string; - path?: string; constructor(options: TriggerClientOptions) { this.id = options.id; - this._url = buildClientUrl(options.url); this.#options = options; this.#client = new ApiClient(this.#options); this.#logger = new Logger("trigger.dev", this.#options.logLevel); } - get url() { - return `${this._url}${ - this.path ? `${this.path.startsWith("/") ? "" : "/"}${this.path}` : "" - }`; - } - async handleRequest(request: Request): Promise { this.#logger.debug("handling request", { url: request.url, @@ -812,32 +798,3 @@ export class TriggerClient { }; } } - -function buildClientUrl(url?: string): string { - if (!url) { - // Try and get the host from the environment - const host = - process.env.TRIGGER_CLIENT_HOST ?? - process.env.HOST ?? - process.env.HOSTNAME ?? - process.env.NOW_URL ?? - process.env.VERCEL_URL; - - // If the host is set, we return it + the path - if (host) { - return "https://" + host; - } - - // If we can't get the host, we throw an error - throw new Error( - "Could not determine the url for this TriggerClient. Please set the TRIGGER_CLIENT_HOST environment variable or pass in the `url` option to the TriggerClient constructor." - ); - } - - // Check to see if url has the protocol, and if it doesn't, add it - if (!url.startsWith("http")) { - return "https://" + url; - } - - return url; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dfa48c3c4..3eb26b42d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -461,9 +461,9 @@ importers: examples/nextjs-clerk: specifiers: '@clerk/nextjs': ^4.21.12 - '@trigger.dev/init': workspace:* - '@trigger.dev/nextjs': 1.0.0-next.5 - '@trigger.dev/sdk': 2.0.0-next.4 + '@trigger.dev/cli': workspace:* + '@trigger.dev/nextjs': ^1.0.0-next.5 + '@trigger.dev/sdk': ^2.0.0-next.4 '@types/node': 20.3.2 '@types/react': ^18.0.21 '@types/react-dom': ^18.0.6 @@ -489,7 +489,7 @@ importers: tailwindcss: 3.3.2 typescript: 5.1.6 devDependencies: - '@trigger.dev/init': link:../../packages/init-trigger + '@trigger.dev/cli': link:../../packages/cli examples/nextjs-example: specifiers: @@ -626,6 +626,61 @@ importers: rimraf: 3.0.2 tsup: 6.6.3 + packages/cli: + specifiers: + '@types/degit': ^2.8.3 + '@types/fs-extra': ^11.0.1 + '@types/gradient-string': ^1.1.2 + '@types/inquirer': ^9.0.3 + '@types/node': '16' + '@types/node-fetch': ^2.6.2 + chalk: ^5.2.0 + commander: ^9.4.1 + degit: ^2.8.4 + execa: ^7.0.0 + fs-extra: ^11.1.0 + gradient-string: ^2.0.2 + inquirer: ^9.1.4 + localtunnel: ^2.0.2 + ngrok: 5.0.0-beta.2 + node-fetch: ^3.3.0 + ora: ^6.1.2 + path-to-regexp: ^6.2.1 + rimraf: ^3.0.2 + simple-git: ^3.19.0 + terminal-link: ^3.0.0 + tsup: ^6.5.0 + type-fest: ^3.6.0 + typescript: ^4.9.5 + zod: ^3.20.2 + dependencies: + '@types/degit': 2.8.3 + chalk: 5.2.0 + commander: 9.5.0 + degit: 2.8.4 + execa: 7.0.0 + fs-extra: 11.1.0 + gradient-string: 2.0.2 + inquirer: 9.1.4 + localtunnel: 2.0.2 + ngrok: 5.0.0-beta.2 + node-fetch: 3.3.0 + ora: 6.1.2 + path-to-regexp: 6.2.1 + simple-git: 3.19.0 + terminal-link: 3.0.0 + zod: 3.21.4 + devDependencies: + '@types/fs-extra': 11.0.1 + '@types/gradient-string': 1.1.2 + '@types/inquirer': 9.0.3 + '@types/node': 16.18.11 + '@types/node-fetch': 2.6.2 + rimraf: 3.0.2 + tsup: 6.6.3_typescript@4.9.5 + type-fest: 3.6.0 + typescript: 4.9.5 + packages/database: specifiers: '@prisma/client': ^4.16.0 @@ -684,57 +739,6 @@ importers: '@types/react': 18.0.26 typescript: 4.9.5 - packages/init-trigger: - specifiers: - '@types/degit': ^2.8.3 - '@types/fs-extra': ^11.0.1 - '@types/gradient-string': ^1.1.2 - '@types/inquirer': ^9.0.3 - '@types/node': '16' - '@types/node-fetch': ^2.6.2 - chalk: ^5.2.0 - commander: ^9.4.1 - degit: ^2.8.4 - execa: ^7.0.0 - fs-extra: ^11.1.0 - gradient-string: ^2.0.2 - inquirer: ^9.1.4 - node-fetch: ^3.3.0 - ora: ^6.1.2 - path-to-regexp: ^6.2.1 - rimraf: ^3.0.2 - simple-git: ^3.19.0 - terminal-link: ^3.0.0 - tsup: ^6.5.0 - type-fest: ^3.6.0 - typescript: ^4.9.5 - zod: ^3.20.2 - dependencies: - '@types/degit': 2.8.3 - chalk: 5.2.0 - commander: 9.5.0 - degit: 2.8.4 - execa: 7.0.0 - fs-extra: 11.1.0 - gradient-string: 2.0.2 - inquirer: 9.1.4 - node-fetch: 3.3.0 - ora: 6.1.2 - path-to-regexp: 6.2.1 - simple-git: 3.19.0 - terminal-link: 3.0.0 - zod: 3.21.4 - devDependencies: - '@types/fs-extra': 11.0.1 - '@types/gradient-string': 1.1.2 - '@types/inquirer': 9.0.3 - '@types/node': 16.18.11 - '@types/node-fetch': 2.6.2 - rimraf: 3.0.2 - tsup: 6.6.3_typescript@4.9.5 - type-fest: 3.6.0 - typescript: 4.9.5 - packages/integration-kit: specifiers: '@trigger.dev/tsconfig': workspace:* @@ -9024,7 +9028,6 @@ packages: /@sindresorhus/is/4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - dev: true /@slack/logger/3.0.0: resolution: {integrity: sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==} @@ -10524,7 +10527,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==} @@ -10824,9 +10826,8 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.3.1 + '@types/node': 20.3.2 '@types/responselike': 1.0.0 - dev: true /@types/chai-subset/1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} @@ -10964,7 +10965,7 @@ packages: resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} dependencies: '@types/jsonfile': 6.1.1 - '@types/node': 20.3.1 + '@types/node': 20.3.2 dev: true /@types/glob/7.2.0: @@ -11004,7 +11005,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==} @@ -11075,7 +11075,7 @@ packages: /@types/jsonfile/6.1.1: resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} dependencies: - '@types/node': 20.3.1 + '@types/node': 20.3.2 dev: true /@types/jsonwebtoken/9.0.1: @@ -11090,8 +11090,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.3.1 - dev: true + '@types/node': 20.3.2 /@types/lodash/4.14.191: resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} @@ -11244,8 +11243,7 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.3.1 - dev: true + '@types/node': 20.3.2 /@types/retry/0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} @@ -11309,7 +11307,7 @@ packages: /@types/through/0.0.30: resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} dependencies: - '@types/node': 20.3.1 + '@types/node': 20.3.2 dev: true /@types/tinycolor2/1.4.3: @@ -11357,8 +11355,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 20.3.1 - dev: true + '@types/node': 20.3.2 optional: true /@typescript-eslint/eslint-plugin/5.59.6_bhjpu5ld5qmewts22wsycix4mi: @@ -12424,10 +12421,18 @@ packages: engines: {node: '>=4'} dev: true + /axios/0.21.4_debug@4.3.2: + resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} + dependencies: + follow-redirects: 1.15.2_debug@4.3.2 + transitivePeerDependencies: + - debug + dev: false + /axios/0.25.0_debug@4.3.4: resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.2 transitivePeerDependencies: - debug dev: true @@ -12435,7 +12440,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 @@ -12443,7 +12448,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 @@ -12452,7 +12457,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: @@ -12881,7 +12886,6 @@ packages: /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - dev: true /buffer-equal-constant-time/1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -13024,7 +13028,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==} @@ -13050,7 +13053,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==} @@ -13429,7 +13431,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==} @@ -14012,6 +14013,17 @@ packages: supports-color: 8.1.1 dev: true + /debug/4.3.2: + resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -14072,7 +14084,6 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 - dev: true /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} @@ -14144,7 +14155,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==} @@ -14495,7 +14505,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /endent/2.1.0: resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} @@ -15995,6 +16004,20 @@ packages: - supports-color 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'} @@ -16117,7 +16140,6 @@ packages: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 - dev: true /fetch-blob/3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} @@ -16273,7 +16295,7 @@ packages: engines: {node: '>=0.4.0'} dev: true - /follow-redirects/1.15.2: + /follow-redirects/1.15.2_debug@4.3.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -16281,6 +16303,8 @@ packages: peerDependenciesMeta: debug: optional: true + dependencies: + debug: 4.3.2 /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -16616,7 +16640,6 @@ packages: engines: {node: '>=8'} dependencies: pump: 3.0.0 - dev: true /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -16908,7 +16931,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==} @@ -17133,6 +17155,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-entities/2.3.3: resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} dev: true @@ -17258,7 +17286,6 @@ packages: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true /https-proxy-agent/4.0.0: resolution: {integrity: sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==} @@ -18636,6 +18663,19 @@ packages: engines: {node: '>=14'} dev: true + /localtunnel/2.0.2: + resolution: {integrity: sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==} + engines: {node: '>=8.3.0'} + hasBin: true + dependencies: + axios: 0.21.4_debug@4.3.2 + debug: 4.3.2 + openurl: 1.1.1 + yargs: 17.1.1 + transitivePeerDependencies: + - supports-color + dev: false + /locate-path/3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -18664,6 +18704,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 @@ -18757,7 +18801,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==} @@ -19430,12 +19473,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==} @@ -19886,6 +19927,23 @@ packages: - babel-plugin-macros dev: false + /ngrok/5.0.0-beta.2: + resolution: {integrity: sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ==} + engines: {node: '>=14.2'} + hasBin: true + requiresBuild: true + dependencies: + extract-zip: 2.0.1 + got: 11.8.6 + lodash.clonedeep: 4.5.0 + uuid: 8.3.2 + yaml: 2.3.1 + optionalDependencies: + hpagent: 0.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /nice-try/1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true @@ -20019,7 +20077,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==} @@ -20286,6 +20343,10 @@ packages: resolution: {integrity: sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==} dev: false + /openurl/1.1.1: + resolution: {integrity: sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==} + dev: false + /optionator/0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -20374,7 +20435,6 @@ packages: /p-cancelable/2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} - dev: true /p-event/4.2.0: resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} @@ -20693,7 +20753,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==} @@ -21430,7 +21489,6 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true /pumpify/1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} @@ -21500,7 +21558,6 @@ packages: /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - dev: true /ramda/0.28.0: resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} @@ -22304,7 +22361,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==} @@ -22370,7 +22426,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==} @@ -25638,6 +25693,11 @@ packages: resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} engines: {node: '>= 14'} + /yaml/2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + engines: {node: '>= 14'} + dev: false + /yargs-parser/18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -25683,6 +25743,19 @@ packages: y18n: 5.0.8 yargs-parser: 20.2.9 + /yargs/17.1.1: + resolution: {integrity: sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==} + engines: {node: '>=12'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: false + /yargs/17.6.2: resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==} engines: {node: '>=12'} @@ -25700,7 +25773,6 @@ packages: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - dev: true /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}