diff --git a/.changeset/mighty-seals-fold.md b/.changeset/mighty-seals-fold.md new file mode 100644 index 000000000..f4ad9ab29 --- /dev/null +++ b/.changeset/mighty-seals-fold.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/cli": patch +--- + +Adding the create-integration command to scaffold out new integration packages (inside or outside of the monorepo) diff --git a/package.json b/package.json index 7a1050dc1..d635cf386 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "turbo": "^1.10.3", "vite": "^4.1.1", "vite-tsconfig-paths": "^4.0.5", - "vitest": "^0.28.4" + "vitest": "^0.28.4", + "@trigger.dev/cli": "workspace:*" }, "packageManager": "pnpm@7.18.1", "dependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index c67a821c2..e7bfc80aa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -35,7 +35,6 @@ "trigger-cli": "./dist/index.js" }, "devDependencies": { - "@types/fs-extra": "^11.0.1", "@types/gradient-string": "^1.1.2", "@types/inquirer": "^9.0.3", "@types/node": "16", @@ -59,7 +58,6 @@ "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", diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index 6ce983dd5..690778095 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -5,6 +5,7 @@ import { COMMAND_NAME, CLOUD_TRIGGER_URL } from "../consts.js"; import { getVersion } from "../utils/getVersion.js"; import pathModule from "node:path"; import { devCommand } from "../commands/dev.js"; +import { createIntegrationCommand } from "../commands/createIntegration.js"; export const program = new Command(); @@ -57,26 +58,26 @@ program await devCommand(path, options); }); -// program -// .command("create-integration") -// .description("Create a new integration package for Trigger.dev") -// .argument( -// "[path]", -// "The path where you would like the package to be created", -// "." -// ) -// .option( -// "-n, --package-name ", -// "The name of the package to create (e.g. @trigger.dev/slack)" -// ) -// .option( -// "-s, --sdk-package ", -// "The name of the SDK package to use (e.g. @slack/web-api)" -// ) -// .version(getVersion(), "-v, --version", "Display the version number") -// .action(async (path, options) => { -// await createIntegrationCommand(path, options); -// }); +program + .command("create-integration") + .description("Create a new integration package for Trigger.dev") + .argument( + "[path]", + "The path where you would like the package to be created", + "." + ) + .option( + "-n, --package-name ", + "The name of the package to create (e.g. @trigger.dev/slack)" + ) + .option( + "-s, --sdk-package ", + "The name of the SDK package to use (e.g. @slack/web-api)" + ) + .version(getVersion(), "-v, --version", "Display the version number") + .action(async (path, options) => { + await createIntegrationCommand(path, options); + }); export const promptTriggerUrl = async (): Promise => { const { instanceType } = await inquirer.prompt<{ diff --git a/packages/cli/src/commands/createIntegration.ts b/packages/cli/src/commands/createIntegration.ts index 1ed04fda9..74a998240 100644 --- a/packages/cli/src/commands/createIntegration.ts +++ b/packages/cli/src/commands/createIntegration.ts @@ -1,13 +1,26 @@ +import inquirer from "inquirer"; +import pathModule from "node:path"; +import ora from "ora"; import { z } from "zod"; +import { COMMAND_NAME } from "../consts.js"; +import { getLatestPackageVersion } from "../utils/addDependencies.js"; +import { + createFile, + pathExists, + readJSONFile, + writeJSONFile, +} from "../utils/fileSystem.js"; +import { generateIntegrationFiles } from "../utils/generateIntegrationFiles.js"; +import { installDependencies } from "../utils/installDependencies.js"; import { logger } from "../utils/logger.js"; import { resolvePath } from "../utils/parseNameAndPath.js"; -import { COMMAND_NAME } from "../consts.js"; -import inquirer from "inquirer"; -// import { OpenAIApi } from "openai"; const CLIOptionsSchema = z.object({ packageName: z.string().optional(), sdkPackage: z.string().optional(), + extraInfo: z.string().optional(), + skipGeneratingCode: z.coerce.boolean().optional(), + authMethod: z.enum(["api-key", "oauth", "both-methods"]).optional(), }); type CLIOptions = z.infer; @@ -26,28 +39,294 @@ export async function createIntegrationCommand(path: string, cliOptions: any) { const resolvedPath = resolvePath(path); + // make sure the resolvedPath doesn't exist + // if it does, print a warning and exit + const resolvedPathExists = await pathExists(resolvedPath); + + if (resolvedPathExists) { + logger.error( + `The path ${resolvedPath} already exists. Please try again with a different path.` + ); + + process.exit(1); + } + const resolvedOptions = await resolveOptionsWithPrompts( options, resolvedPath ); - console.log(resolvedOptions); + const latestVersion = await getLatestPackageVersion( + resolvedOptions.sdkPackage, + "latest" + ); + + if (!latestVersion) { + logger.error( + `Could not find the latest version of ${resolvedOptions.sdkPackage}. Please try again later.` + ); + + process.exit(1); + } + + const triggerMonorepoPath = await detectTriggerMonorepoPath(resolvedPath); + + const sdkVersion = await getInternalOrExternalPackageVersion({ + path: "packages/trigger-sdk", + packageName: "@trigger.dev/sdk", + tag: "next", + monorepoPath: triggerMonorepoPath, + }); + + if (!sdkVersion) { + logger.error( + `Could not find the latest version of @trigger.dev/sdk. Please try again later.` + ); + + process.exit(1); + } + + const integrationKitVersion = await getInternalOrExternalPackageVersion({ + path: "packages/integration-kit", + packageName: "@trigger.dev/integration-kit", + tag: "latest", + monorepoPath: triggerMonorepoPath, + }); + + if (!integrationKitVersion) { + logger.error( + `Could not find the latest version of @trigger.dev/integration-kit. Please try again later.` + ); + + process.exit(1); + } + + // Create the package.json + const packageJson = { + name: resolvedOptions.packageName, + version: "0.0.1", + description: `Trigger.dev integration for ${resolvedOptions.sdkPackage}`, + main: "./dist/index.js", + types: "./dist/index.d.ts", + publishConfig: { + access: "public", + }, + files: ["dist/index.js", "dist/index.d.ts", "dist/index.js.map"], + devDependencies: { + "@types/node": "16.x", + rimraf: "^3.0.2", + tsup: "7.1.x", + typescript: "4.9.4", + }, + scripts: { + clean: "rimraf dist", + build: "npm run clean && npm run build:tsup", + "build:tsup": "tsup", + typecheck: "tsc --noEmit", + }, + dependencies: { + [latestVersion.name]: `^${latestVersion.version}`, + [sdkVersion.name]: sdkVersion.version, + [integrationKitVersion.name]: integrationKitVersion.version, + }, + engines: { + node: ">=16.8.0", + }, + }; + + await createFileInPath( + resolvedPath, + "package.json", + JSON.stringify(packageJson, null, 2) + ); + + // Create the tsconfig.json + const tsconfigJson = { + compilerOptions: { + composite: false, + declaration: false, + declarationMap: false, + esModuleInterop: true, + forceConsistentCasingInFileNames: true, + inlineSources: false, + isolatedModules: true, + moduleResolution: "node16", + noUnusedLocals: false, + noUnusedParameters: false, + preserveWatchOutput: true, + skipLibCheck: true, + strict: true, + experimentalDecorators: true, + emitDecoratorMetadata: true, + sourceMap: true, + resolveJsonModule: true, + lib: ["es2019"], + module: "commonjs", + target: "es2021", + }, + include: ["./src/**/*.ts", "tsup.config.ts"], + exclude: ["node_modules"], + }; + + await createFileInPath( + resolvedPath, + "tsconfig.json", + JSON.stringify(tsconfigJson, null, 2) + ); + + // Create the tsup.config.ts + const tsupConfig = ` +import { defineConfig } from "tsup"; + +export default defineConfig([ + { + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + treeshake: { + preset: "smallest", + }, + esbuildPlugins: [], + external: ["http", "https", "util", "events", "tty", "os", "timers"], + }, +]); + +`; + + await createFileInPath(resolvedPath, "tsup.config.ts", tsupConfig); + + if (resolvedOptions.skipGeneratingCode) { + await createFileInPath(resolvedPath, "src/index.ts", "export {}"); + } else { + await attemptToGenerateIntegrationFiles( + pathModule.join(resolvedPath, "src"), + resolvedOptions + ); + } + + // If inside the monorepo: + if (triggerMonorepoPath) { + // Add the dependency to the nextjs-example package.json + // Add the paths to the nextjs-example tsconfig paths + await updateNextJsExampleProjectWithNewIntegration( + triggerMonorepoPath, + resolvedPath, + resolvedOptions + ); + } + + // Install the dependencies + await installDependencies(resolvedPath); + + logger.success( + `✅ Successfully initialized ${resolvedOptions.packageName} at ${resolvedPath}` + ); + logger.info("Next steps:"); + logger.info(` 1. If you generated code, double check it for errors.`); + logger.info( + ` 2. Read the "Creating an Integration" guide at https://trigger.dev/docs/integrations/create` + ); + + if (triggerMonorepoPath) { + logger.info( + ` 3. Write some test jobs in the examples/nextjs-example project` + ); + } +} + +async function attemptToGenerateIntegrationFiles( + path: string, + options: ResolvedCLIOptions +) { + const spinner = ora("Generating integration code (may take ~30s)").start(); + + function generateExtraInfo( + authMethod: "api-key" | "oauth" | "both-methods", + extraInfo?: string + ): string { + let authExtraInfo = ""; + + switch (authMethod) { + case "api-key": { + authExtraInfo = + "Note that the only auth method that this integration supports is API keys so can only useLocalAuth to true and don't use the clientFactory option"; + break; + } + case "oauth": { + authExtraInfo = + "Note that the only auth method that this integration supports is OAuth so can only useLocalAuth to false and make sure to use the clientFactory option"; + break; + } + case "both-methods": { + authExtraInfo = + "Note that this integration supports both API keys and OAuth so the options passed to the constructor must support both."; + break; + } + } + + return `${authExtraInfo}\n\n${extraInfo ?? ""}`; + } + + const extraInfo = generateExtraInfo(options.authMethod, options.extraInfo); + + const files = await generateIntegrationFiles({ + packageName: options.packageName, + sdkPackage: options.sdkPackage, + extraInfo, + }); + + if (files) { + await Promise.all( + Object.entries(files).map(([file, contents]) => + createFileInPath(path, file, contents) + ) + ); + + spinner.succeed(`Generated integration code in ${path}`); + } else { + spinner.fail("Failed to generate integration code"); + } +} + +async function createFileInPath( + path: string, + fileName: string, + contents: string +) { + await createFile(pathModule.join(path, fileName), contents); } const resolveOptionsWithPrompts = async ( options: CLIOptions, - _path: string + path: string ): Promise => { const resolvedOptions: CLIOptions = { ...options }; try { if (!options.packageName) { - resolvedOptions.packageName = await promptPackageName(); + resolvedOptions.packageName = await promptPackageName(path); } if (!options.sdkPackage) { resolvedOptions.sdkPackage = await promptSdkPackage(); } + + if (!process.env.OPENAI_API_KEY) { + resolvedOptions.skipGeneratingCode = true; + } + + if (!resolvedOptions.skipGeneratingCode) { + resolvedOptions.authMethod = await promptAuthMethod(); + resolvedOptions.extraInfo = await promptExtraInfo(); + } } 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 @@ -79,10 +358,13 @@ const resolveOptionsWithPrompts = async ( return resolvedOptions as ResolvedCLIOptions; }; -export const promptPackageName = async (): Promise => { +export const promptPackageName = async (path: string): Promise => { + const basename = pathModule.basename(path); + const { packageName } = await inquirer.prompt<{ packageName: string }>({ type: "input", name: "packageName", + default: `@trigger.dev/${basename}`, message: "What is the name of your integration package?", validate: (input) => { if (!input) { @@ -100,7 +382,7 @@ export const promptSdkPackage = async (): Promise => { const { sdkPackage } = await inquirer.prompt<{ sdkPackage: string }>({ type: "input", name: "sdkPackage", - message: "What is the name of the SDK package you would like to use?", + message: "What is the name of the npm package of the integration?", validate: (input) => { if (!input) { return "Please enter an SDK package name"; @@ -112,3 +394,181 @@ export const promptSdkPackage = async (): Promise => { return sdkPackage; }; + +export const promptExtraInfo = async (): Promise => { + const { extraInfo } = await inquirer.prompt<{ extraInfo?: string }>({ + type: "input", + name: "extraInfo", + message: + "Please describe in english anything else about using the SDK that might be useful (optional)", + }); + + return extraInfo; +}; + +// Choose between api-key, oauth, or both +export const promptAuthMethod = async (): Promise< + "api-key" | "oauth" | "both-methods" +> => { + const { authMethod } = await inquirer.prompt<{ + authMethod: "api-key" | "oauth" | "both-methods"; + }>({ + type: "list", + name: "authMethod", + message: "What authentication method does this API use?", + choices: [ + { + name: "API Key", + value: "api-key", + }, + { + name: "OAuth", + value: "oauth", + }, + { + name: "Both API Key and OAuth", + value: "both-methods", + }, + ], + }); + + return authMethod; +}; + +export const promptSkipGeneratingCode = async (): Promise => { + const { skipGeneratingCode } = await inquirer.prompt<{ + skipGeneratingCode: boolean; + }>({ + type: "checkbox", + name: "skipGeneratingCode", + default: false, + message: "Would you like to skip generating the initial code?", + }); + + return skipGeneratingCode; +}; + +// Find where the github repo is located and check if it's the trigger.dev monorepo +async function detectTriggerMonorepoPath( + path: string +): Promise { + const gitPath = await findGitPath(path); + + if (!gitPath) { + return; + } + + // Read the package.json file at + const rootPackageJsonPath = pathModule.join(gitPath, "package.json"); + const rootPackageJsonExists = await pathExists(rootPackageJsonPath); + + if (!rootPackageJsonExists) { + return; + } + + const rootPackageJson = await readJSONFile(rootPackageJsonPath); + + if (rootPackageJson.name === "triggerdotdev") { + return gitPath; + } + + return; +} + +async function getInternalOrExternalPackageVersion({ + packageName, + tag, + path, + monorepoPath, +}: { + packageName: string; + tag: string; + path: string; + monorepoPath?: string; +}): Promise<{ name: string; version: string } | undefined> { + if (!monorepoPath) { + return await getLatestPackageVersion(packageName, tag); + } + + // If there is a monorepo path then we will read the version from the package.json at that path + const packageJsonPath = pathModule.join(monorepoPath, path, "package.json"); + const packageJsonExists = await pathExists(packageJsonPath); + + if (!packageJsonExists) { + return await getLatestPackageVersion(packageName, tag); + } + + const packageJson = await readJSONFile(packageJsonPath); + + return { + name: packageJson.name, + version: `workspace:^${packageJson.version}`, + }; +} + +// Recursively search for a .git folder +async function findGitPath(path: string): Promise { + const gitPath = pathModule.join(path, ".git"); + + const gitPathExists = await pathExists(gitPath); + + if (gitPathExists) { + return path; + } + + const parentPath = pathModule.dirname(path); + + if (parentPath === path) { + return undefined; + } + + return findGitPath(parentPath); +} + +async function updateNextJsExampleProjectWithNewIntegration( + monorepoPath: string, + integrationPath: string, + resolvedOptions: ResolvedCLIOptions +) { + const nextjsPath = pathModule.join( + monorepoPath, + "examples", + "nextjs-example" + ); + + const packageJsonPath = pathModule.join(nextjsPath, "package.json"); + const packageJson = await readJSONFile(packageJsonPath); + + const newPackageJson = { + ...packageJson, + dependencies: { + ...packageJson.dependencies, + [resolvedOptions.packageName]: `workspace:*`, + }, + }; + + await writeJSONFile(packageJsonPath, newPackageJson); + + const tsConfigPath = pathModule.join(nextjsPath, "tsconfig.json"); + const tsConfig = await readJSONFile(tsConfigPath); + + const newTsConfig = { + ...tsConfig, + compilerOptions: { + ...tsConfig.compilerOptions, + paths: { + ...tsConfig.compilerOptions.paths, + [resolvedOptions.packageName]: [ + `../../integrations/${pathModule.basename( + integrationPath + )}/src/index`, + ], + [`${resolvedOptions.packageName}/*`]: [ + `../../integrations/${pathModule.basename(integrationPath)}/src/*`, + ], + }, + }, + }; + + await writeJSONFile(tsConfigPath, newTsConfig); +} diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 339f3bcf3..f9092a164 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -19,6 +19,7 @@ import { renderTitle } from "../utils/renderTitle.js"; import { detectNextJsProject } from "../utils/detectNextJsProject.js"; import { TriggerApi, WhoamiResponse } from "../utils/triggerApi.js"; import { readFile } from "tsconfig"; +import { pathExists } from "../utils/fileSystem.js"; export type InitCommandOptions = { projectPath: string; @@ -706,12 +707,3 @@ async function setupEnvironmentVariable( ); } } - -async function pathExists(path: string): Promise { - try { - await fs.access(path); - return true; - } catch (error) { - return false; - } -} diff --git a/packages/cli/src/utils/addDependencies.ts b/packages/cli/src/utils/addDependencies.ts index 07146dc48..6f647f332 100644 --- a/packages/cli/src/utils/addDependencies.ts +++ b/packages/cli/src/utils/addDependencies.ts @@ -74,7 +74,7 @@ const PackageSchema = z.object({ "dist-tags": z.record(z.string()), }); -async function getLatestPackageVersion( +export async function getLatestPackageVersion( packageName: string, tag: string ): Promise { diff --git a/packages/cli/src/utils/createDotEnvFile.ts b/packages/cli/src/utils/createDotEnvFile.ts deleted file mode 100644 index 1e88b77a7..000000000 --- a/packages/cli/src/utils/createDotEnvFile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import path from "path"; -import fs from "fs-extra"; - -export async function createDotEnvFile(projectPath: string, apiKey?: string) { - const envPath = path.join(projectPath, ".env"); - const envExists = await fs.pathExists(envPath); - if (envExists) { - return; - } - const envContents = apiKey - ? `TRIGGER_API_KEY=${apiKey}` - : "TRIGGER_API_KEY="; - await fs.writeFile(envPath, envContents); -} diff --git a/packages/cli/src/utils/fileSystem.ts b/packages/cli/src/utils/fileSystem.ts new file mode 100644 index 000000000..e9efe0702 --- /dev/null +++ b/packages/cli/src/utils/fileSystem.ts @@ -0,0 +1,44 @@ +import fsModule, { writeFile } from "fs/promises"; +import fsSync from "fs"; +import pathModule from "path"; + +// Creates a file at the given path, if the directory doesn't exist it will be created +export async function createFile( + path: string, + contents: string +): Promise { + await fsModule.mkdir(pathModule.dirname(path), { recursive: true }); + await fsModule.writeFile(path, contents); + + return path; +} + +export async function pathExists(path: string): Promise { + try { + await fsModule.access(path); + + return true; + } catch (err) { + return false; + } +} + +export async function removeFile(path: string) { + await fsModule.unlink(path); +} + +export async function readJSONFile(path: string) { + const fileContents = await fsModule.readFile(path, "utf-8"); + + return JSON.parse(fileContents); +} + +export async function writeJSONFile(path: string, json: any) { + await writeFile(path, JSON.stringify(json, null, 2)); +} + +export function readJSONFileSync(path: string) { + const fileContents = fsSync.readFileSync(path, "utf-8"); + + return JSON.parse(fileContents); +} diff --git a/packages/cli/src/utils/generateIntegrationFiles.ts b/packages/cli/src/utils/generateIntegrationFiles.ts new file mode 100644 index 000000000..64edad241 --- /dev/null +++ b/packages/cli/src/utils/generateIntegrationFiles.ts @@ -0,0 +1,370 @@ +import { OpenAIApi, Configuration } from "openai"; +import { z } from "zod"; + +function createExampleResponse(args: { + index: string; + types: string; + tasks: string; + packageName: string; + sdkPackage: string; +}) { + const exampleFiles = { + "index.ts": args.index, + "types.ts": args.types, + "tasks.ts": args.tasks, + }; + + return ` + Here is an example of what the files should look like for the package "${ + args.packageName + }" using the SDK package "${args.sdkPackage}". + + (Note that these files are formatted as a pair of JSON key/values, where the key is the file name and the value is the file contents.) + + ${JSON.stringify(exampleFiles, null, 2)} + `; +} + +function createPrompt( + packageName: string, + sdkPackage: string, + extraInfo?: string +) { + return `'I\'m wanting to know what the minimal starting point index.ts, types.ts, and tasks.ts files should look like for the package "${packageName}" using the SDK package "${sdkPackage}". ${ + extraInfo ?? "" + }`; +} + +function createExampleMessages() { + return [ + { + role: "user", + content: createPrompt( + "@trigger.dev.slack", + "@slack/web-api", + "Note that the only auth method support for slack is OAuth2 so shouldn't allow usesLocalAuth set to true" + ), + }, + { + role: "assistant", + content: createExampleResponse({ + packageName: "@trigger.dev/slack", + sdkPackage: "@slack/web-api", + index: ` +import { WebClient } from "@slack/web-api"; +import type { IntegrationClient, TriggerIntegration } from "@trigger.dev/sdk"; +import type { SlackSDK, SlackIntegrationOptions } from "./types"; +import * as tasks from "./tasks"; + +export * from "./types"; + +type SlackIntegrationClient = IntegrationClient; +type SlackIntegration = TriggerIntegration; + +export class Slack + implements SlackIntegration +{ + client: SlackIntegrationClient; + + constructor(private options: SlackIntegrationOptions) { + this.client = { + tasks, + usesLocalAuth: false, + clientFactory: (auth) => { + return new WebClient(auth.accessToken); + }, + }; + } + + get id() { + return this.options.id; + } + + get metadata() { + return { id: "slack", name: "Slack.com" }; + } +} + `, + tasks: ` +import type { + SlackSDK, + ChatPostMessageParams, + ChatPostMessageResponse, +} from "./types"; + +export const postMessage: AuthenticatedTask< + SlackSDK, + ChatPostMessageParams, + ChatPostMessageResponse +> = { + run: async (params, client, task, io, auth) => { + const response = await client.chat.postMessage(params); + + return response; + }, + init: (params) => { + return { + name: "Post Message", + params, + icon: "slack", + properties: [ + { + label: "Channel ID", + text: params.channel, + }, + ...(params.text ? [{ label: "Message", text: params.text }] : []), + ], + }; + }, +}; + `, + types: ` +import { WebClient } from "@slack/web-api"; +import { Prettify } from "@trigger.dev/integration-kit"; + +export type SlackSDK = WebClient; + +export type SlackIntegrationOptions = { + id: string; +}; + +export type ChatPostMessageParams = { + channel: string; + text?: string; + as_user?: boolean; + attachments?: MessageAttachment[]; + blocks?: (KnownBlock | Block)[]; + icon_emoji?: string; + icon_url?: string; + metadata?: MessageMetadata; + link_names?: boolean; + mrkdwn?: boolean; + parse?: "full" | "none"; + reply_broadcast?: boolean; + thread_ts?: string; + unfurl_links?: boolean; + unfurl_media?: boolean; + username?: string; +}; + +export type ChatPostMessageResponse = Prettify>>; + `, + }), + }, + { + role: "user", + content: createPrompt( + "@trigger.dev/typeform", + "@typeform/api-client", + "Note that the only auth method support for typeform is API Key should only allow usesLocalAuth set to true" + ), + }, + { + role: "assistant", + content: createExampleResponse({ + packageName: "@trigger.dev/typeform", + sdkPackage: "@typeform/api-client", + index: ` +import { createClient } from "@typeform/api-client"; +import { + TypeformIntegrationOptions, + TypeformSDK, +} from "./types"; +import { + TriggerIntegration, + IntegrationClient, +} from "@trigger.dev/sdk"; + +import * as tasks from "./tasks"; + + +export * from "./types"; + +type TypeformIntegration = TriggerIntegration; +type TypeformIntegrationClient = IntegrationClient; + +type TypeformSource = ReturnType; +type TypeformTrigger = ReturnType; + +export class Typeform implements TypeformIntegration { + client: TypeformIntegrationClient; + + constructor(private options: TypeformIntegrationOptions) { + this.client = { + tasks, + usesLocalAuth: true, + client: createClient({ token: options.token }), + auth: { + token: options.token, + apiBaseUrl: options.apiBaseUrl, + }, + }; + } + + get id() { + return this.options.id; + } + + get metadata() { + return { id: "typeform", name: "Typeform" }; + } +} + `, + tasks: ` +import type { AuthenticatedTask } from "@trigger.dev/sdk"; +import type { + GetFormParams, + GetFormResponse, + TypeformSDK, +} from "./types"; + +export const getForm: AuthenticatedTask = + { + run: async (params, client) => { + return client.forms.get(params); + }, + init: (params) => { + return { + name: "Get Form", + params, + icon: "typeform", + properties: [ + { + label: "Form ID", + text: params.uid, + }, + ], + }; + }, + }; + `, + types: ` +import { Prettify } from "@trigger.dev/integration-kit"; +import { Typeform, createClient } from "@typeform/api-client"; + +export type TypeformIntegrationOptions = { + id: string; + token: string; + apiBaseUrl?: string; +}; + +export type TypeformSDK = ReturnType; + +export type GetFormParams = { + uid: string; +}; +export type GetFormResponse = Prettify; + `, + }), + }, + ] as const; +} + +export async function generateIntegrationFiles(payload: { + packageName: string; + sdkPackage: string; + extraInfo?: string; +}) { + if (!process.env.OPENAI_API_KEY) { + return; + } + + const openai = new OpenAIApi( + new Configuration({ + apiKey: process.env.OPENAI_API_KEY, + organization: process.env.OPENAI_ORGANIZATION, + }) + ); + + try { + const response = await openai.createChatCompletion({ + model: "gpt-4", + messages: [ + { + role: "system", + content: + "You will be provided with the task of creating the typescript files necessary for a new Trigger.dev integration package. You will be provided the name of the integration package and the name of the SDK package.", + }, + ...createExampleMessages(), + { + role: "user", + content: createPrompt( + payload.packageName, + payload.sdkPackage, + payload.extraInfo + ), + }, + ], + function_call: { name: "createTypescriptFiles" }, + functions: [ + { + name: "createTypescriptFiles", + description: + "Creates the initial typescript files for a new Trigger.dev package", + parameters: { + type: "object", + properties: { + "index.ts": { + type: "string", + description: "The contents of the index.ts file", + }, + "tasks.ts": { + type: "string", + description: "The contents of the tasks.ts file", + }, + "types.ts": { + type: "string", + description: "The contents of the types.ts file", + }, + }, + }, + }, + ], + }); + + const responseData = response.data; + + const firstChoice = responseData.choices[0]; + + if (!firstChoice) { + return; + } + + const message = firstChoice.message; + + if (!message) { + return; + } + + if (!message.function_call || !message.function_call.arguments) { + return; + } + + const functionCallArgs = safeJsonParse(message.function_call.arguments); + + if (!functionCallArgs) { + return; + } + + const filesSchema = z.record(z.string()); + + const files = filesSchema.safeParse(functionCallArgs); + + if (!files.success) { + return; + } + + return files.data; + } catch (error) { + console.error(error); + return; + } +} + +function safeJsonParse(jsonString: string) { + try { + return JSON.parse(jsonString); + } catch (e) { + return null; + } +} diff --git a/packages/cli/src/utils/getVersion.ts b/packages/cli/src/utils/getVersion.ts index 1a7922d76..46d91f05a 100644 --- a/packages/cli/src/utils/getVersion.ts +++ b/packages/cli/src/utils/getVersion.ts @@ -1,12 +1,12 @@ import { type PackageJson } from "type-fest"; import path from "path"; -import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; +import { readJSONFileSync } from "./fileSystem.js"; -export const getVersion = () => { +export function getVersion() { const packageJsonPath = path.join(PKG_ROOT, "package.json"); - const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson; + const packageJsonContent = readJSONFileSync(packageJsonPath) as PackageJson; return packageJsonContent.version ?? "1.0.0"; -}; +} diff --git a/packages/cli/src/utils/git.ts b/packages/cli/src/utils/git.ts index 31c4ea639..c9b3e5a43 100644 --- a/packages/cli/src/utils/git.ts +++ b/packages/cli/src/utils/git.ts @@ -1,11 +1,11 @@ import chalk from "chalk"; import { execSync } from "child_process"; import { execa } from "execa"; -import fs from "fs-extra"; import inquirer from "inquirer"; import ora from "ora"; import path from "path"; import { logger } from "./logger.js"; +import { pathExists, removeFile } from "./fileSystem.js"; const isGitInstalled = (dir: string): boolean => { try { @@ -17,8 +17,8 @@ const isGitInstalled = (dir: string): boolean => { }; /** @returns Whether or not the provided directory has a `.git` subdirectory in it. */ -const isRootGitRepo = (dir: string): boolean => { - return fs.existsSync(path.join(dir, ".git")); +const isRootGitRepo = (dir: string): Promise => { + return pathExists(path.join(dir, ".git")); }; /** @returns Whether or not this directory or a parent directory has a `.git` directory. */ @@ -64,7 +64,7 @@ export const initializeGit = async (projectDir: string) => { const spinner = ora("Creating a new git repo...\n").start(); - const isRoot = isRootGitRepo(projectDir); + const isRoot = await isRootGitRepo(projectDir); const isInside = await isInsideGitRepo(projectDir); const dirName = path.parse(projectDir).name; // skip full path for logging @@ -86,7 +86,7 @@ export const initializeGit = async (projectDir: string) => { return; } // Deleting the .git folder - fs.removeSync(path.join(projectDir, ".git")); + await removeFile(path.join(projectDir, ".git")); } else if (isInside && !isRoot) { // Dir is inside a git worktree spinner.stop(); diff --git a/packages/cli/src/utils/installDependencies.ts b/packages/cli/src/utils/installDependencies.ts new file mode 100644 index 000000000..f6812ef89 --- /dev/null +++ b/packages/cli/src/utils/installDependencies.ts @@ -0,0 +1,74 @@ +import { getUserPkgManager, type PackageManager } from "./getUserPkgManager.js"; +import { logger } from "./logger.js"; +import ora, { type Ora } from "ora"; +import chalk from "chalk"; +import { execa } from "execa"; + +export async function installDependencies(projectDir: string) { + logger.info("Installing dependencies..."); + + const pkgManager = getUserPkgManager(); + + const installSpinner = await runInstallCommand(pkgManager, projectDir); + + // If the spinner was used to show the progress, use succeed method on it + // If not, use the succeed on a new spinner + (installSpinner || ora()).succeed( + chalk.green("Successfully installed dependencies!\n") + ); +} + +async function runInstallCommand( + pkgManager: PackageManager, + projectDir: string +): Promise { + switch (pkgManager) { + // When using npm, inherit the stderr stream so that the progress bar is shown + case "npm": + await execa(pkgManager, ["install"], { + cwd: projectDir, + stderr: "inherit", + }); + + return null; + // When using yarn or pnpm, use the stdout stream and ora spinner to show the progress + case "pnpm": + const pnpmSpinner = ora("Running pnpm install...").start(); + const pnpmSubprocess = execa(pkgManager, ["install"], { + cwd: projectDir, + stdout: "pipe", + }); + + await new Promise((res, rej) => { + pnpmSubprocess.stdout?.on("data", (data: Buffer) => { + const text = data.toString(); + + if (text.includes("Progress")) { + pnpmSpinner.text = text.includes("|") + ? text.split(" | ")[1] ?? "" + : text; + } + }); + pnpmSubprocess.on("error", (e) => rej(e)); + pnpmSubprocess.on("close", () => res()); + }); + + return pnpmSpinner; + case "yarn": + const yarnSpinner = ora("Running yarn...").start(); + const yarnSubprocess = execa(pkgManager, [], { + cwd: projectDir, + stdout: "pipe", + }); + + await new Promise((res, rej) => { + yarnSubprocess.stdout?.on("data", (data: Buffer) => { + yarnSpinner.text = data.toString(); + }); + yarnSubprocess.on("error", (e) => rej(e)); + yarnSubprocess.on("close", () => res()); + }); + + return yarnSpinner; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 075fea365..2059a9647 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,7 @@ importers: '@manypkg/cli': ^0.19.2 '@tailwindcss/forms': ^0.5.3 '@tailwindcss/typography': ^0.5.8 + '@trigger.dev/cli': workspace:* autoprefixer: ^10.4.12 eslint-config-custom: workspace:* node-fetch: 2.6.x @@ -26,6 +27,7 @@ importers: '@manypkg/cli': 0.19.2 '@tailwindcss/forms': 0.5.3_tailwindcss@3.1.8 '@tailwindcss/typography': 0.5.9_tailwindcss@3.1.8 + '@trigger.dev/cli': link:packages/cli autoprefixer: 10.4.13_postcss@8.4.21 eslint-config-custom: link:config-packages/eslint-config-custom postcss: 8.4.21 @@ -527,11 +529,11 @@ importers: specifiers: '@trigger.dev/integration-kit': workspace:^2.0.0-next.5 '@trigger.dev/sdk': workspace:^2.0.0-next.15 - '@trigger.dev/tsconfig': workspace:* '@typeform/api-client': ^1.8.0 - '@types/node': '18' + '@types/node': 16.x rimraf: ^3.0.2 - tsup: ^6.5.0 + tsup: 7.1.x + typescript: 4.9.4 zod: 3.21.4 dependencies: '@trigger.dev/integration-kit': link:../../packages/integration-kit @@ -539,15 +541,14 @@ importers: '@typeform/api-client': 1.8.0 zod: 3.21.4 devDependencies: - '@trigger.dev/tsconfig': link:../../config-packages/tsconfig - '@types/node': 18.15.13 + '@types/node': 16.18.11 rimraf: 3.0.2 - tsup: 6.6.3 + tsup: 7.1.0_typescript@4.9.4 + typescript: 4.9.4 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' @@ -557,7 +558,6 @@ importers: 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 @@ -581,7 +581,6 @@ importers: 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 @@ -595,7 +594,6 @@ importers: tsconfig: 7.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 @@ -3732,6 +3730,15 @@ packages: dev: true optional: true + /@esbuild/android-arm/0.18.11: + resolution: {integrity: sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64/0.16.17: resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} engines: {node: '>=12'} @@ -3768,6 +3775,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64/0.18.11: + resolution: {integrity: sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64/0.16.17: resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} engines: {node: '>=12'} @@ -3804,6 +3820,15 @@ packages: dev: true optional: true + /@esbuild/android-x64/0.18.11: + resolution: {integrity: sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64/0.16.17: resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} engines: {node: '>=12'} @@ -3840,6 +3865,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64/0.18.11: + resolution: {integrity: sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64/0.16.17: resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} engines: {node: '>=12'} @@ -3876,6 +3910,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64/0.18.11: + resolution: {integrity: sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64/0.16.17: resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} engines: {node: '>=12'} @@ -3912,6 +3955,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64/0.18.11: + resolution: {integrity: sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64/0.16.17: resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} engines: {node: '>=12'} @@ -3948,6 +4000,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64/0.18.11: + resolution: {integrity: sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm/0.16.17: resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} engines: {node: '>=12'} @@ -3984,6 +4045,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm/0.18.11: + resolution: {integrity: sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64/0.16.17: resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} engines: {node: '>=12'} @@ -4020,6 +4090,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64/0.18.11: + resolution: {integrity: sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32/0.16.17: resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} engines: {node: '>=12'} @@ -4056,6 +4135,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32/0.18.11: + resolution: {integrity: sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.15.18: resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} @@ -4101,6 +4189,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64/0.18.11: + resolution: {integrity: sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el/0.16.17: resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} engines: {node: '>=12'} @@ -4137,6 +4234,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el/0.18.11: + resolution: {integrity: sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64/0.16.17: resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} engines: {node: '>=12'} @@ -4173,6 +4279,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64/0.18.11: + resolution: {integrity: sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64/0.16.17: resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} engines: {node: '>=12'} @@ -4209,6 +4324,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64/0.18.11: + resolution: {integrity: sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x/0.16.17: resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} engines: {node: '>=12'} @@ -4245,6 +4369,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x/0.18.11: + resolution: {integrity: sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64/0.16.17: resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} engines: {node: '>=12'} @@ -4281,6 +4414,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64/0.18.11: + resolution: {integrity: sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64/0.16.17: resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} engines: {node: '>=12'} @@ -4317,6 +4459,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64/0.18.11: + resolution: {integrity: sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64/0.16.17: resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} engines: {node: '>=12'} @@ -4353,6 +4504,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64/0.18.11: + resolution: {integrity: sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64/0.16.17: resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} engines: {node: '>=12'} @@ -4389,6 +4549,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64/0.18.11: + resolution: {integrity: sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64/0.16.17: resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} engines: {node: '>=12'} @@ -4425,6 +4594,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64/0.18.11: + resolution: {integrity: sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32/0.16.17: resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} engines: {node: '>=12'} @@ -4461,6 +4639,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32/0.18.11: + resolution: {integrity: sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64/0.16.17: resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} engines: {node: '>=12'} @@ -4497,6 +4684,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64/0.18.11: + resolution: {integrity: sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils/4.4.0_eslint@8.31.0: resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9778,13 +9974,6 @@ packages: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} dev: true - /@types/fs-extra/11.0.1: - resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} - dependencies: - '@types/jsonfile': 6.1.1 - '@types/node': 20.3.3 - dev: true - /@types/generic-pool/3.8.1: resolution: {integrity: sha512-eaMAbZS0EfKvaP5PUZ/Cdf5uJBO2t6T3RdvQTKuMqUwGhNpCnPAsKWEMyV+mCeCQG3UiHrtgdzni8X6DmhxRaQ==} deprecated: This is a stub types definition. generic-pool provides its own type definitions, so you do not need this installed. @@ -9932,12 +10121,6 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/jsonfile/6.1.1: - resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} - dependencies: - '@types/node': 20.3.3 - dev: true - /@types/jsonwebtoken/9.0.1: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: @@ -11736,6 +11919,16 @@ packages: load-tsconfig: 0.2.3 dev: true + /bundle-require/4.0.1_esbuild@0.18.11: + resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.17' + dependencies: + esbuild: 0.18.11 + load-tsconfig: 0.2.3 + dev: true + /busboy/1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -13572,6 +13765,36 @@ packages: '@esbuild/win32-x64': 0.17.6 dev: true + /esbuild/0.18.11: + resolution: {integrity: sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.11 + '@esbuild/android-arm64': 0.18.11 + '@esbuild/android-x64': 0.18.11 + '@esbuild/darwin-arm64': 0.18.11 + '@esbuild/darwin-x64': 0.18.11 + '@esbuild/freebsd-arm64': 0.18.11 + '@esbuild/freebsd-x64': 0.18.11 + '@esbuild/linux-arm': 0.18.11 + '@esbuild/linux-arm64': 0.18.11 + '@esbuild/linux-ia32': 0.18.11 + '@esbuild/linux-loong64': 0.18.11 + '@esbuild/linux-mips64el': 0.18.11 + '@esbuild/linux-ppc64': 0.18.11 + '@esbuild/linux-riscv64': 0.18.11 + '@esbuild/linux-s390x': 0.18.11 + '@esbuild/linux-x64': 0.18.11 + '@esbuild/netbsd-x64': 0.18.11 + '@esbuild/openbsd-x64': 0.18.11 + '@esbuild/sunos-x64': 0.18.11 + '@esbuild/win32-arm64': 0.18.11 + '@esbuild/win32-ia32': 0.18.11 + '@esbuild/win32-x64': 0.18.11 + dev: true + /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -15195,6 +15418,7 @@ packages: graceful-fs: 4.2.10 jsonfile: 6.1.0 universalify: 2.0.0 + dev: true /fs-extra/7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} @@ -16882,6 +17106,7 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.10 + dev: true /jsonpointer/5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} @@ -19329,6 +19554,22 @@ packages: yaml: 1.10.2 dev: true + /postcss-load-config/4.0.1: + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + yaml: 2.3.1 + dev: true + /postcss-load-config/4.0.1_3fojqsmttcn75cbnzsztj3o6qa: resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} @@ -22277,6 +22518,42 @@ packages: - ts-node dev: true + /tsup/7.1.0_typescript@4.9.4: + resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==} + engines: {node: '>=16.14'} + hasBin: true + peerDependencies: + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.1.0' + peerDependenciesMeta: + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 4.0.1_esbuild@0.18.11 + cac: 6.7.14 + chokidar: 3.5.3 + debug: 4.3.4 + esbuild: 0.18.11 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 4.0.1 + resolve-from: 5.0.0 + rollup: 3.10.0 + source-map: 0.8.0-beta.0 + sucrase: 3.32.0 + tree-kill: 1.2.2 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /tsutils/3.21.0: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -22673,6 +22950,7 @@ packages: /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + dev: true /unpipe/1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}