From 18e0f13605593fbbd4390f3ca5eb6e60f1e2fa8e Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Tue, 7 Jun 2022 11:43:32 +0530 Subject: [PATCH] [Feature] App Store CLI - Make it super easy to add an app. (#2917) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- README.md | 2 +- apps/web/pages/api/integrations/[...args].ts | 13 +- apps/web/pages/apps/[slug]/index.tsx | 2 +- package.json | 8 +- packages/app-store-cli/.gitignore | 2 + packages/app-store-cli/package.json | 32 ++ packages/app-store-cli/readme.md | 39 ++ packages/app-store-cli/src/CliApp.tsx | 329 +++++++++++++ packages/app-store-cli/src/app-store.ts | 127 +++++ packages/app-store-cli/src/cli.tsx | 49 ++ packages/app-store-cli/tsconfig.json | 19 + packages/app-store/.gitignore | 1 + packages/app-store/_baseApp/README.mdx | 1 + packages/app-store/_baseApp/_metadata.ts | 18 + packages/app-store/_baseApp/api/add.ts | 49 ++ packages/app-store/_baseApp/api/index.ts | 1 + .../_baseApp/components/InstallAppButton.tsx | 19 + .../app-store/_baseApp/components/icon.tsx | 15 + .../app-store/_baseApp/components/index.ts | 2 + packages/app-store/_baseApp/config.json | 1 + packages/app-store/_baseApp/index.ts | 3 + packages/app-store/_baseApp/package.json | 14 + packages/app-store/_baseApp/static/icon.svg | 1 + .../_pages/setup/_getStaticProps.tsx | 3 + .../app-store/_utils/useAddAppMutation.ts | 2 +- packages/app-store/apiHandlers.tsx | 24 - packages/app-store/components.tsx | 35 +- packages/app-store/giphy/_metadata.ts | 1 + packages/app-store/giphy/api/add.ts | 1 + .../hubspotothercalendar/_metadata.ts | 1 + packages/app-store/jitsivideo/api/add.ts | 2 +- packages/app-store/metadata.ts | 45 -- packages/app-store/metamask/api/add.ts | 2 +- .../app-store/slackmessaging/_metadata.ts | 1 + packages/app-store/spacebooking/README.mdx | 8 +- packages/app-store/utils.ts | 4 +- packages/lib/deriveAppDictKeyFromType.ts | 38 ++ packages/prisma/delete-app.ts | 36 ++ packages/prisma/package.json | 3 +- packages/prisma/schema.prisma | 1 + packages/prisma/seed-app-store.config.json | 1 + packages/prisma/seed-app-store.ts | 10 + turbo.json | 6 +- yarn.lock | 450 +++++++++++++++++- 44 files changed, 1290 insertions(+), 131 deletions(-) create mode 100644 packages/app-store-cli/.gitignore create mode 100644 packages/app-store-cli/package.json create mode 100644 packages/app-store-cli/readme.md create mode 100644 packages/app-store-cli/src/CliApp.tsx create mode 100644 packages/app-store-cli/src/app-store.ts create mode 100644 packages/app-store-cli/src/cli.tsx create mode 100644 packages/app-store-cli/tsconfig.json create mode 100644 packages/app-store/.gitignore create mode 100644 packages/app-store/_baseApp/README.mdx create mode 100644 packages/app-store/_baseApp/_metadata.ts create mode 100644 packages/app-store/_baseApp/api/add.ts create mode 100644 packages/app-store/_baseApp/api/index.ts create mode 100644 packages/app-store/_baseApp/components/InstallAppButton.tsx create mode 100644 packages/app-store/_baseApp/components/icon.tsx create mode 100644 packages/app-store/_baseApp/components/index.ts create mode 100644 packages/app-store/_baseApp/config.json create mode 100644 packages/app-store/_baseApp/index.ts create mode 100644 packages/app-store/_baseApp/package.json create mode 100644 packages/app-store/_baseApp/static/icon.svg delete mode 100644 packages/app-store/apiHandlers.tsx delete mode 100644 packages/app-store/metadata.ts create mode 100644 packages/lib/deriveAppDictKeyFromType.ts create mode 100644 packages/prisma/delete-app.ts create mode 100644 packages/prisma/seed-app-store.config.json diff --git a/README.md b/README.md index d74e4b345b..39c6eb28f0 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ Next make sure you have your app running `yarn dx`. Then in the slack chat type 4. Fill in any information you want in the "App info" tab 5. Go to tab "Auth" 6. Now copy the Client ID and Client Secret to your .env file into the `HUBSPOT_CLIENT_ID` and `HUBSPOT_CLIENT_SECRET` fields. -7. Set the Redirect URL for OAuth `/api/integrations/hubspot othercalendar/callback` replacing Cal.com URL with the URI at which your application runs. +7. Set the Redirect URL for OAuth `/api/integrations/hubspotothercalendar/callback` replacing Cal.com URL with the URI at which your application runs. 8. In the "Scopes" section at the bottom of the page, make sure you select "Read" and "Write" for scope called `crm.objects.contacts` 9. Click the "Save" button at the bottom footer. 10. You're good to go. Now you can see any booking in Cal.com created as a meeting in HubSpot for your contacts. diff --git a/apps/web/pages/api/integrations/[...args].ts b/apps/web/pages/api/integrations/[...args].ts index a03b5a969a..fb05aa8a1e 100644 --- a/apps/web/pages/api/integrations/[...args].ts +++ b/apps/web/pages/api/integrations/[...args].ts @@ -1,5 +1,7 @@ import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; +import { deriveAppDictKeyFromType } from "@calcom/lib/deriveAppDictKeyFromType"; + import { getSession } from "@lib/auth"; import { HttpError } from "@lib/core/http/error"; @@ -13,15 +15,14 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(404).json({ message: `API route not found` }); } - const [_appName, apiEndpoint] = args; - const appName = _appName.split("_").join(""); // Transform `zoom_video` to `zoomvideo`; - + const [appName, apiEndpoint] = args; try { /* Absolute path didn't work */ - const handlerMap = (await import("@calcom/app-store/apiHandlers")).default; - const handlers = await handlerMap[appName as keyof typeof handlerMap]; - const handler = handlers[apiEndpoint as keyof typeof handlers] as NextApiHandler; + const handlerMap = (await import("@calcom/app-store/apps.server.generated")).apiHandlers; + const handlerKey = deriveAppDictKeyFromType(appName, handlerMap); + const handlers = await handlerMap[handlerKey as keyof typeof handlerMap]; + const handler = handlers[apiEndpoint as keyof typeof handlers] as NextApiHandler; if (typeof handler !== "function") throw new HttpError({ statusCode: 404, message: `API handler not found` }); diff --git a/apps/web/pages/apps/[slug]/index.tsx b/apps/web/pages/apps/[slug]/index.tsx index 2ef7603080..ab8f67010d 100644 --- a/apps/web/pages/apps/[slug]/index.tsx +++ b/apps/web/pages/apps/[slug]/index.tsx @@ -114,7 +114,7 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => { let source = ""; try { - /* If the app doesn't have a README we fallback to the packagfe description */ + /* If the app doesn't have a README we fallback to the package description */ source = fs.readFileSync(postFilePath).toString(); } catch (error) { console.log(`No README.mdx provided for: ${appDirname}`); diff --git a/package.json b/package.json index c036cefce7..68d6d5befd 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "docs-dev": "yarn predev && turbo run dev --scope=\"@calcom/docs\"", "docs-build": "turbo run build --scope=\"@calcom/docs\" --include-dependencies", "docs-start": "turbo run start --scope=\"@calcom/docs\"", - "dx": "yarn predev && (git submodule update || true) && turbo run dx", + "dx:web": "yarn predev && (git submodule update || true) && turbo run dx", + "dx": "run-p 'dx:web' 'app-store:watch'", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "heroku-postbuild": "turbo run @calcom/web#build", "lint": "turbo run lint", @@ -42,7 +43,9 @@ "embed-tests": "turbo run embed-tests", "test-e2e": "turbo run test-e2e --concurrency=1", "type-check": "turbo run type-check", - "embed-tests-prepare": "yarn workspace @calcom/prisma db-reset && yarn build" + "embed-tests-prepare": "yarn workspace @calcom/prisma db-reset && yarn build", + "app-store": "yarn workspace @calcom/app-store-cli cli", + "app-store:watch": "yarn workspace @calcom/app-store-cli watch --watch" }, "devDependencies": { "dotenv-checker": "^1.1.5", @@ -51,6 +54,7 @@ "prettier": "^2.5.1" }, "dependencies": { + "run-parallel": "^1.2.0", "turbo": "1.2.9" }, "resolutions": { diff --git a/packages/app-store-cli/.gitignore b/packages/app-store-cli/.gitignore new file mode 100644 index 0000000000..f06235c460 --- /dev/null +++ b/packages/app-store-cli/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/packages/app-store-cli/package.json b/packages/app-store-cli/package.json new file mode 100644 index 0000000000..6c5501e691 --- /dev/null +++ b/packages/app-store-cli/package.json @@ -0,0 +1,32 @@ +{ + "name": "@calcom/app-store-cli", + "version": "0.0.0", + "bin": "dist/cli.js", + "engines": { + "node": ">=10" + }, + "scripts": { + "cli": "ts-node --transpile-only src/cli.tsx", + "watch": "ts-node --transpile-only src/app-store.ts --watch", + "generate": "ts-node --transpile-only src/app-store.ts" + }, + "files": [ + "dist/cli.js" + ], + "dependencies": { + "@calcom/lib": "*", + "ink": "^3.2.0", + "ink-select-input": "^4.2.1", + "ink-text-input": "^4.0.3", + "meow": "^9.0.0", + "react": "^17.0.2" + }, + "devDependencies": { + "chokidar": "^3.5.3", + "@types/react": "^18.0.9", + "eslint-plugin-react": "^7.30.0", + "eslint-plugin-react-hooks": "^4.5.0", + "ts-node": "^10.6.0", + "typescript": "^4.6.4" + } +} diff --git a/packages/app-store-cli/readme.md b/packages/app-store-cli/readme.md new file mode 100644 index 0000000000..5567aa5abd --- /dev/null +++ b/packages/app-store-cli/readme.md @@ -0,0 +1,39 @@ +## Steps to create an app + +- Create a folder in packages/app-store/{APP_NAME} = {APP} +- Fill it with a sample app + - Modify {APP}/_metadata.ts with the data provided + +## Approach + +- appType is derived from App Name(a slugify operation that makes a string that can be used as a director name, a variable name for imports and a URL path). +- appType is then used to create the app directory. It becomes `config.type` of config.json. config.type is the value used to create an entry in App table and retrieve any apps or credentials. It also becomes App.dirName +- dirnames that don't start with _ are considered apps in packages/app-store and based on those apps .generated.ts* files are created. This allows pre-cli apps to keep on working. +- app directory is populated with app-store/_baseApp with newly updated config.json and package.json +- `packages/prisma/seed-app-store.config.json` is updated with new app. + +NOTE: After app-store-cli is live, Credential.appId and Credential.type would be same for new apps. For old apps they would remain different. Credential.type would be used to identify credentials in integrations call and Credential.appId/App.slug would be used to identify apps. +If we rename all existing apps to their slug names, we can remove type and then use just appId to refer to things everywhere. This can be done later on. + +## TODO + +- Beta Release + - Show a warning somewhere that app directory must not be renamed manually, edit command must be used. + - edit command should ask for slug and verify if it exists + +- Improvements + - Prefill fields in edit command + - Merge app-store:watch and app-store commands, introduce app-store --watch + - Allow inputs in non interactive way as well - That would allow easily copy pasting commands. + - App already exists check. Ask user to run edit/regenerate command + - An app created through CLI should be able to completely skip API validation for testing purposes. Credentials should be created with no API specified specific to the app. It would allow us to test any app end to end not worrying about the corresponding API endpoint. + - Require assets path relative to app dir. + +## Roadmap +- Avoid delete and edit on apps created outside of cli +- Someone can add wrong directory name(which doesn't satisfy slug requirements) manually. How to handle it. +- Allow editing and updating app from the cal app itself - including assets uploading when developing locally. +- Improvements in shared code across app + - Use baseApp/api/add.ts for all apps with configuration of credentials creation and redirection URL. +- Delete creation side effects if App creation fails - Might make debugging difficult + - This is so that web app doesn't break because of additional app folders or faulty db-seed \ No newline at end of file diff --git a/packages/app-store-cli/src/CliApp.tsx b/packages/app-store-cli/src/CliApp.tsx new file mode 100644 index 0000000000..fad104aee1 --- /dev/null +++ b/packages/app-store-cli/src/CliApp.tsx @@ -0,0 +1,329 @@ +import child_process from "child_process"; +import fs from "fs"; +import { Box, Text, useApp, useInput, useStdin } from "ink"; +import SelectInput from "ink-select-input"; +import TextInput from "ink-text-input"; +import path from "path"; +import React, { FC, useEffect, useRef, useState } from "react"; + +const slugify = (str: string) => { + // It is to be a valid dir name, a valid JS variable name and a valid URL path + return str.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); +}; + +function getSlugFromAppName(appName: string | null): string | null { + if (!appName) { + return appName; + } + return slugify(appName); +} + +function getAppDirPath(slug: any) { + return path.join(appStoreDir, `${slug}`); +} + +const appStoreDir = path.resolve(__dirname, "..", "..", "app-store"); +const workspaceDir = path.resolve(__dirname, "..", "..", ".."); +const execSync = (...args) => { + const result = child_process.execSync(...args).toString(); + if (process.env.DEBUG === "1") { + console.log(`$: ${args[0]}`); + console.log(result); + } + return args[0]; +}; +function absolutePath(appRelativePath) { + return path.join(appStoreDir, appRelativePath); +} +const updatePackageJson = ({ slug, appDescription, appDirPath }) => { + const packageJsonConfig = JSON.parse(fs.readFileSync(`${appDirPath}/package.json`).toString()); + packageJsonConfig.name = `@calcom/${slug}`; + packageJsonConfig.description = appDescription; + // packageJsonConfig.description = `@calcom/${appName}`; + fs.writeFileSync(`${appDirPath}/package.json`, JSON.stringify(packageJsonConfig, null, 2)); +}; + +const BaseAppFork = { + create: function* ({ + category, + editMode = false, + appDescription, + appName, + slug, + appTitle, + publisherName, + publisherEmail, + }) { + const appDirPath = getAppDirPath(slug); + let message = !editMode ? "Forking base app" : "Updating app"; + yield message; + if (!editMode) { + execSync(`mkdir -p ${appDirPath}`); + execSync(`cp -r ${absolutePath("_baseApp/*")} ${appDirPath}`); + } + updatePackageJson({ slug, appDirPath, appDescription }); + + let config = { + "/*": "Don't modify slug - If required, do it using cli edit command", + name: appName, + title: appTitle, + // Plan to remove it. DB already has it and name of dir is also the same. + slug: slug, + type: `${slug}_${category}`, + imageSrc: `/api/app-store/${slug}/icon.svg`, + logo: `/api/app-store/${slug}/icon.svg`, + url: `https://cal.com/apps/${slug}`, + variant: category, + categories: [category], + publisher: publisherName, + email: publisherEmail, + description: appDescription, + // TODO: Use this to avoid edit and delete on the apps created outside of cli + __createdUsingCli: true, + }; + const currentConfig = JSON.parse(fs.readFileSync(`${appDirPath}/config.json`).toString()); + config = { + ...currentConfig, + ...config, + }; + fs.writeFileSync(`${appDirPath}/config.json`, JSON.stringify(config, null, 2)); + message = !editMode ? "Forked base app" : "Updated app"; + yield message; + }, + delete: function ({ slug }) { + const appDirPath = getAppDirPath(slug); + execSync(`rm -rf ${appDirPath}`); + }, +}; + +const Seed = { + seedConfigPath: absolutePath("../prisma/seed-app-store.config.json"), + update: function ({ slug, category, noDbUpdate }) { + const seedConfig = JSON.parse(fs.readFileSync(this.seedConfigPath).toString()); + if (!seedConfig.find((app) => app.slug === slug)) { + seedConfig.push({ + "/*": "This file is auto-generated and managed by `yarn app-store`. Don't edit manually but it is to be committed", + dirName: slug, + categories: [category], + slug: slug, + type: `${slug}_${category}`, + }); + } + + fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2)); + if (!noDbUpdate) { + execSync(`cd ${workspaceDir} && yarn db-seed`); + } + }, + revert: async function ({ slug, noDbUpdate }) { + let seedConfig = JSON.parse(fs.readFileSync(this.seedConfigPath).toString()); + seedConfig = seedConfig.filter((app) => app.slug !== slug); + fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2)); + if (!noDbUpdate) { + execSync(`yarn workspace @calcom/prisma delete-app ${slug}`); + } + }, +}; + +const generateAppFiles = () => { + execSync(`cd ${__dirname} && yarn ts-node --transpile-only src/app-store.ts`); +}; + +const CreateApp = ({ noDbUpdate, slug = null, editMode = false }) => { + // AppName + // Type of App - Other, Calendar, Video, Payment, Messaging, Web3 + const [appInputData, setAppInputData] = useState({}); + const [inputIndex, setInputIndex] = useState(0); + const fields = [ + { label: "App Name", name: "appName", type: "text" }, + { label: "App Title", name: "appTitle", type: "text" }, + { label: "App Description", name: "appDescription", type: "text" }, + { + label: "Category of App", + name: "appCategory", + type: "select", + options: [ + { label: "calendar", value: "calendar" }, + { label: "video", value: "video" }, + { label: "payment", value: "payment" }, + { label: "messaging", value: "messaging" }, + { label: "web3", value: "web3" }, + { label: "other", value: "other" }, + ], + }, + { label: "Publisher Name", name: "publisherName", type: "text" }, + { label: "Publisher Email", name: "publisherEmail", type: "text" }, + ]; + const field = fields[inputIndex]; + const fieldLabel = field?.label || ""; + const fieldName = field?.name || ""; + const fieldValue = appInputData[fieldName] || ""; + const appName = appInputData["appName"]; + const category = appInputData["appCategory"]; + const appTitle = appInputData["appTitle"]; + const appDescription = appInputData["appDescription"]; + const publisherName = appInputData["publisherName"]; + const publisherEmail = appInputData["publisherEmail"]; + const [result, setResult] = useState("..."); + const allFieldsFilled = inputIndex === fields.length; + + useEffect(() => { + // When all fields have been filled + if (allFieldsFilled) { + const it = BaseAppFork.create({ + category, + appDescription, + appName, + slug, + appTitle, + publisherName, + publisherEmail, + }); + for (const item of it) { + setResult(item); + } + + Seed.update({ slug, category, noDbUpdate }); + + generateAppFiles(); + + // FIXME: Even after CLI showing this message, it is stuck doing work before exiting + // So we ask the user to wait for some time + setResult( + `App has been given slug: ${slug}. Just wait for a few seconds for the process to complete and start editing ${getAppDirPath( + slug + )} to work on your app.` + ); + } + }); + + if (!slug && editMode) { + return --slug is required; + } + + if (!editMode) { + slug = getSlugFromAppName(appName); + } + + if (allFieldsFilled) { + return ( + <> + + {editMode + ? `Editing app with slug ${slug}` + : `Creating app with name '${appName}' of type '${category}'`} + + {result} + + Please note that you should use cli only to rename an app directory as it needs to be updated in DB + as well + + + ); + } + + // Hack: using field.name == "appTitle" to identify that app Name has been submitted and not being edited. + if (!editMode && field.name === "appTitle" && slug && fs.existsSync(getAppDirPath(slug))) { + return ( + <> + App with slug {slug} already exists. If you want to edit it, use edit command + + ); + } + return ( + + {`${fieldLabel}:`} + {field.type == "text" ? ( + { + if (!value) { + return; + } + setInputIndex((index) => { + return index + 1; + }); + }} + onChange={(value) => { + setAppInputData((appInputData) => { + return { + ...appInputData, + [fieldName]: value, + }; + }); + }} + /> + ) : ( + + items={field.options} + onSelect={(item) => { + setAppInputData((appInputData) => { + return { + ...appInputData, + [fieldName]: item.value, + }; + }); + setInputIndex((index) => { + return index + 1; + }); + }}> + )} + + ); +}; + +const DeleteApp = ({ noDbUpdate, slug }) => { + const [confirmedAppSlug, setConfirmedAppSlug] = useState(""); + const [allowDeletion, setAllowDeletion] = useState(false); + const [state, setState] = useState({}); + useEffect(() => { + if (allowDeletion) { + BaseAppFork.delete({ slug }); + Seed.revert({ slug }); + generateAppFiles(); + setState({ description: `App with slug ${slug} has been deleted`, done: true }); + } + }, [allowDeletion, slug]); + return ( + <> + + Confirm the slug of the app that you want to delete. Note, that it would cleanup the app directory, + App table and Credential table + + {!state.done && ( + { + if (value === slug) { + setState({ description: `Deletion started`, done: true }); + setAllowDeletion(true); + } else { + setState({ description: `Slug doesn't match - Should have been ${slug}`, done: true }); + } + }} + onChange={(val) => { + setConfirmedAppSlug(val); + }}> + )} + {state.description} + + ); +}; + +const App: FC<{ noDbUpdate?: boolean; command: "create" | "delete"; slug?: string }> = ({ + command, + noDbUpdate, + slug, +}) => { + if (command === "create") { + return ; + } + if (command === "delete") { + return ; + } + if (command === "edit") { + return ; + } +}; +module.exports = App; +export default App; diff --git a/packages/app-store-cli/src/app-store.ts b/packages/app-store-cli/src/app-store.ts new file mode 100644 index 0000000000..3562e74a44 --- /dev/null +++ b/packages/app-store-cli/src/app-store.ts @@ -0,0 +1,127 @@ +const fs = require("fs"); +const path = require("path"); +let isInWatchMode = false; +if (process.argv[2] === "--watch") { + isInWatchMode = true; +} +const chokidar = require("chokidar"); +const { debounce } = require("lodash"); +const APP_STORE_PATH = path.join(__dirname, "..", "..", "app-store"); +function getAppName(candidatePath) { + function isValidAppName(candidatePath) { + if (!candidatePath.startsWith("_") && !candidatePath.includes("/") && !candidatePath.includes("\\")) { + return candidatePath; + } + } + + if (isValidAppName(candidatePath)) { + // Already a dirname of an app + return candidatePath; + } + // Get dirname of app from full path + const dirName = path.relative(APP_STORE_PATH, candidatePath); + return isValidAppName(dirName) ? dirName : null; +} + +function generateFiles() { + const browserOutput = [`import dynamic from "next/dynamic"`]; + const serverOutput = []; + const appDirs = []; + + fs.readdirSync(`${APP_STORE_PATH}`).forEach(function (dir) { + if (fs.statSync(`${APP_STORE_PATH}/${dir}`).isDirectory()) { + if (!getAppName(dir)) { + return; + } + appDirs.push(dir); + } + }); + + function forEachAppDir(callback) { + for (let i = 0; i < appDirs.length; i++) { + callback(appDirs[i]); + } + } + + function getObjectExporter(objectName, { fileToBeImported, importBuilder, entryBuilder }) { + const output = []; + forEachAppDir((appName) => { + if (fs.existsSync(path.join(APP_STORE_PATH, appName, fileToBeImported))) { + output.push(importBuilder(appName)); + } + }); + + output.push(`export const ${objectName} = {`); + + forEachAppDir((dirName) => { + if (fs.existsSync(path.join(APP_STORE_PATH, dirName, fileToBeImported))) { + output.push(entryBuilder(dirName)); + } + }); + + output.push(`};`); + return output; + } + + serverOutput.push( + ...getObjectExporter("apiHandlers", { + fileToBeImported: "api/index.ts", + importBuilder: (appName) => `const ${appName}_api = import("./${appName}/api");`, + entryBuilder: (appName) => `${appName}:${appName}_api,`, + }) + ); + + browserOutput.push( + ...getObjectExporter("appStoreMetadata", { + fileToBeImported: "_metadata.ts", + importBuilder: (appName) => `import { metadata as ${appName}_meta } from "./${appName}/_metadata";`, + entryBuilder: (appName) => `${appName}:${appName}_meta,`, + }) + ); + + browserOutput.push( + ...getObjectExporter("InstallAppButtonMap", { + fileToBeImported: "components/InstallAppButton.tsx", + importBuilder: (appName) => + `const ${appName}_installAppButton = dynamic(() =>import("./${appName}/components/InstallAppButton"));`, + entryBuilder: (appName) => `${appName}:${appName}_installAppButton,`, + }) + ); + const banner = `/** + This file is autogenerated using the command \`yarn app-store --watch\`. It automatically runs when using \`yarn dx\` + Don't modify this file manually. +**/ +`; + fs.writeFileSync(`${APP_STORE_PATH}/apps.server.generated.ts`, `${banner}${serverOutput.join("\n")}`); + fs.writeFileSync(`${APP_STORE_PATH}/apps.browser.generated.tsx`, `${banner}${browserOutput.join("\n")}`); + console.log("Generated `apps.server.generated.ts` and `apps.browser.generated.tsx`"); +} + +const debouncedGenerateFiles = debounce(generateFiles); + +if (isInWatchMode) { + chokidar + .watch(APP_STORE_PATH) + .on("addDir", (dirPath) => { + const appName = getAppName(dirPath); + if (appName) { + console.log(`Added ${appName}`); + debouncedGenerateFiles(); + } + }) + .on("change", (filePath) => { + if (filePath.endsWith("config.json")) { + console.log("Config file changed"); + debouncedGenerateFiles(); + } + }) + .on("unlinkDir", (dirPath) => { + const appName = getAppName(dirPath); + if (appName) { + console.log(`Removed ${appName}`); + debouncedGenerateFiles(); + } + }); +} else { + generateFiles(); +} diff --git a/packages/app-store-cli/src/cli.tsx b/packages/app-store-cli/src/cli.tsx new file mode 100644 index 0000000000..3f8196d870 --- /dev/null +++ b/packages/app-store-cli/src/cli.tsx @@ -0,0 +1,49 @@ +#!/usr/bin/env node +import { render } from "ink"; +import meow from "meow"; +import React from "react"; + +import App from "./CliApp"; + +const cli = meow( + ` + Usage + $ app-store create/delete/edit - Edit and Delete commands must be used on apps created using cli + + Options + [--slug] Slug. This is the name of app dir for apps created with cli. +`, + { + flags: { + noDbUpdate: { + type: "boolean", + }, + slug: { + type: "string", + }, + }, + allowUnknownFlags: false, + } +); + +if (cli.input.length !== 1) { + cli.showHelp(); +} + +const command = cli.input[0] as "create" | "delete" | "edit"; +const supportedCommands = ["create", "delete", "edit"]; + +if (!supportedCommands.includes(command)) { + cli.showHelp(); +} + +let slug = null; + +if (command === "delete" || command === "edit") { + slug = cli.flags.slug; + if (!slug) { + console.log("--slug is required"); + cli.showHelp(); + } +} +render(); diff --git a/packages/app-store-cli/tsconfig.json b/packages/app-store-cli/tsconfig.json new file mode 100644 index 0000000000..abbeb20d05 --- /dev/null +++ b/packages/app-store-cli/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "jsx": "react", + "esModuleInterop": true, + "outDir": "dist", + "noEmitOnError": false, + "target": "ES2020", + "baseUrl": "." + }, + "include": [ + "next-env.d.ts", + "../../packages/types/*.d.ts", + "../../packages/types/next-auth.d.ts", + "./src/**/*.ts", + "./src/**/*.tsx", + "../lib/**/*.ts" + ] +} diff --git a/packages/app-store/.gitignore b/packages/app-store/.gitignore new file mode 100644 index 0000000000..29e1d4862b --- /dev/null +++ b/packages/app-store/.gitignore @@ -0,0 +1 @@ +*.generated.* diff --git a/packages/app-store/_baseApp/README.mdx b/packages/app-store/_baseApp/README.mdx new file mode 100644 index 0000000000..c10da2e9e0 --- /dev/null +++ b/packages/app-store/_baseApp/README.mdx @@ -0,0 +1 @@ +Edit README.mdx file to update it. \ No newline at end of file diff --git a/packages/app-store/_baseApp/_metadata.ts b/packages/app-store/_baseApp/_metadata.ts new file mode 100644 index 0000000000..819e63f6ad --- /dev/null +++ b/packages/app-store/_baseApp/_metadata.ts @@ -0,0 +1,18 @@ +import type { App } from "@calcom/types/App"; + +import config from "./config.json"; +import _package from "./package.json"; + +export const metadata = { + description: _package.description, + category: "other", + // FIXME: Currently for an app to be shown as installed, it must have this variable set. Either hardcoded or if it depends on some env variable, that should be checked here + installed: true, + rating: 0, + reviews: 0, + trending: true, + verified: true, + ...config, +} as App; + +export default metadata; diff --git a/packages/app-store/_baseApp/api/add.ts b/packages/app-store/_baseApp/api/add.ts new file mode 100644 index 0000000000..062fe2f62f --- /dev/null +++ b/packages/app-store/_baseApp/api/add.ts @@ -0,0 +1,49 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import prisma from "@calcom/prisma"; + +import appConfig from "../config.json"; + +// TODO: There is a lot of code here that would be used by almost all apps +// - Login Validation +// - Looking up credential. +// - Creating credential would be specific to app, so there can be just createCredential method that app can expose +// - Redirection after successful installation can also be configured by app +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (!req.session?.user?.id) { + return res.status(401).json({ message: "You must be logged in to do this" }); + } + // TODO: Define appType once and import everywhere + const slug = appConfig.slug; + const appType = appConfig.type; + try { + const alreadyInstalled = await prisma.credential.findFirst({ + where: { + appId: slug, + userId: req.session.user.id, + }, + }); + if (alreadyInstalled) { + throw new Error("Already installed"); + } + const installation = await prisma.credential.create({ + data: { + type: appType, + key: {}, + userId: req.session.user.id, + appId: slug, + }, + }); + if (!installation) { + throw new Error(`Unable to create user credential for type ${type}`); + } + } catch (error: unknown) { + if (error instanceof Error) { + console.error(error.message); + return res.status(500).json({ message: error.message }); + } + return res.status(500); + } + + return res.status(200).json({ url: "/apps/installed" }); +} diff --git a/packages/app-store/_baseApp/api/index.ts b/packages/app-store/_baseApp/api/index.ts new file mode 100644 index 0000000000..4c0d2ead01 --- /dev/null +++ b/packages/app-store/_baseApp/api/index.ts @@ -0,0 +1 @@ +export { default as add } from "./add"; diff --git a/packages/app-store/_baseApp/components/InstallAppButton.tsx b/packages/app-store/_baseApp/components/InstallAppButton.tsx new file mode 100644 index 0000000000..e5c3fd105c --- /dev/null +++ b/packages/app-store/_baseApp/components/InstallAppButton.tsx @@ -0,0 +1,19 @@ +import type { InstallAppButtonProps } from "@calcom/app-store/types"; + +import useAddAppMutation from "../../_utils/useAddAppMutation"; +import appConfig from "../config.json"; + +export default function InstallAppButton(props: InstallAppButtonProps) { + const mutation = useAddAppMutation(appConfig.type); + + return ( + <> + {props.render({ + onClick() { + mutation.mutate(""); + }, + loading: mutation.isLoading, + })} + + ); +} diff --git a/packages/app-store/_baseApp/components/icon.tsx b/packages/app-store/_baseApp/components/icon.tsx new file mode 100644 index 0000000000..b4c2401ca1 --- /dev/null +++ b/packages/app-store/_baseApp/components/icon.tsx @@ -0,0 +1,15 @@ +export default function Icon() { + return ( + + + + ); +} diff --git a/packages/app-store/_baseApp/components/index.ts b/packages/app-store/_baseApp/components/index.ts new file mode 100644 index 0000000000..e191e97eec --- /dev/null +++ b/packages/app-store/_baseApp/components/index.ts @@ -0,0 +1,2 @@ +export { default as InstallAppButton } from "./InstallAppButton"; +export { default as Icon } from "./icon"; diff --git a/packages/app-store/_baseApp/config.json b/packages/app-store/_baseApp/config.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/app-store/_baseApp/config.json @@ -0,0 +1 @@ +{} diff --git a/packages/app-store/_baseApp/index.ts b/packages/app-store/_baseApp/index.ts new file mode 100644 index 0000000000..a698d1f5c5 --- /dev/null +++ b/packages/app-store/_baseApp/index.ts @@ -0,0 +1,3 @@ +export * as api from "./api"; +export * as components from "./components"; +export { metadata } from "./_metadata"; diff --git a/packages/app-store/_baseApp/package.json b/packages/app-store/_baseApp/package.json new file mode 100644 index 0000000000..ff2856f018 --- /dev/null +++ b/packages/app-store/_baseApp/package.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "private": true, + "name": "@calcom/cli_base__app_name", + "version": "0.0.0", + "main": "./index.ts", + "description": "Your app description goes here.", + "dependencies": { + "@calcom/lib": "*" + }, + "devDependencies": { + "@calcom/types": "*" + } +} diff --git a/packages/app-store/_baseApp/static/icon.svg b/packages/app-store/_baseApp/static/icon.svg new file mode 100644 index 0000000000..059dfae262 --- /dev/null +++ b/packages/app-store/_baseApp/static/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/app-store/_pages/setup/_getStaticProps.tsx b/packages/app-store/_pages/setup/_getStaticProps.tsx index 0e8b631199..f7f497246f 100644 --- a/packages/app-store/_pages/setup/_getStaticProps.tsx +++ b/packages/app-store/_pages/setup/_getStaticProps.tsx @@ -2,6 +2,9 @@ import { GetStaticPropsContext } from "next"; export const AppSetupPageMap = { zapier: import("../../zapier/pages/setup/_getStaticProps"), + "apple-calendar": { + getStaticProps: null, + }, }; export const getStaticProps = async (ctx: GetStaticPropsContext) => { diff --git a/packages/app-store/_utils/useAddAppMutation.ts b/packages/app-store/_utils/useAddAppMutation.ts index 4848348a15..feb2473ee8 100644 --- a/packages/app-store/_utils/useAddAppMutation.ts +++ b/packages/app-store/_utils/useAddAppMutation.ts @@ -5,7 +5,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants"; import { App } from "@calcom/types/App"; function useAddAppMutation(type: App["type"], options?: Parameters[2]) { - const appName = type.replace(/_/g, ""); + const appName = type; const mutation = useMutation(async () => { const state: IntegrationOAuthCallbackState = { returnTo: WEBAPP_URL + "/apps/installed" + location.search, diff --git a/packages/app-store/apiHandlers.tsx b/packages/app-store/apiHandlers.tsx deleted file mode 100644 index c29bcc1314..0000000000 --- a/packages/app-store/apiHandlers.tsx +++ /dev/null @@ -1,24 +0,0 @@ -export const apiHandlers = { - // examplevideo: import("./_example/api"), - applecalendar: import("./applecalendar/api"), - caldavcalendar: import("./caldavcalendar/api"), - googlecalendar: import("./googlecalendar/api"), - hubspotothercalendar: import("./hubspotothercalendar/api"), - office365calendar: import("./office365calendar/api"), - slackmessaging: import("./slackmessaging/api"), - stripepayment: import("./stripepayment/api"), - tandemvideo: import("./tandemvideo/api"), - vital: import("./vital/api"), - zoomvideo: import("@calcom/zoomvideo/api"), - office365video: import("@calcom/office365video/api"), - wipemycalother: import("./wipemycalother/api"), - jitsivideo: import("./jitsivideo/api"), - huddle01video: import("./huddle01video/api"), - metamask: import("./metamask/api"), - giphy: import("./giphy/api"), - spacebookingother: import("./spacebooking/api"), - // @todo Until we use DB slugs everywhere - zapierother: import("./zapier/api"), -}; - -export default apiHandlers; diff --git a/packages/app-store/components.tsx b/packages/app-store/components.tsx index 849b71cae5..2bcfc4445d 100644 --- a/packages/app-store/components.tsx +++ b/packages/app-store/components.tsx @@ -1,35 +1,14 @@ import { useSession } from "next-auth/react"; -import dynamic from "next/dynamic"; import { WEBAPP_URL } from "@calcom/lib/constants"; +import { deriveAppDictKeyFromType } from "@calcom/lib/deriveAppDictKeyFromType"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { App } from "@calcom/types/App"; import Button from "@calcom/ui/Button"; +import { InstallAppButtonMap } from "./apps.browser.generated"; import { InstallAppButtonProps } from "./types"; -export const InstallAppButtonMap = { - // examplevideo: dynamic(() => import("./_example/components/InstallAppButton")), - applecalendar: dynamic(() => import("./applecalendar/components/InstallAppButton")), - caldavcalendar: dynamic(() => import("./caldavcalendar/components/InstallAppButton")), - googlecalendar: dynamic(() => import("./googlecalendar/components/InstallAppButton")), - hubspotothercalendar: dynamic(() => import("./hubspotothercalendar/components/InstallAppButton")), - office365calendar: dynamic(() => import("./office365calendar/components/InstallAppButton")), - slackmessaging: dynamic(() => import("./slackmessaging/components/InstallAppButton")), - stripepayment: dynamic(() => import("./stripepayment/components/InstallAppButton")), - tandemvideo: dynamic(() => import("./tandemvideo/components/InstallAppButton")), - zoomvideo: dynamic(() => import("./zoomvideo/components/InstallAppButton")), - office365video: dynamic(() => import("./office365video/components/InstallAppButton")), - wipemycalother: dynamic(() => import("./wipemycalother/components/InstallAppButton")), - zapier: dynamic(() => import("./zapier/components/InstallAppButton")), - jitsivideo: dynamic(() => import("./jitsivideo/components/InstallAppButton")), - huddle01video: dynamic(() => import("./huddle01video/components/InstallAppButton")), - metamask: dynamic(() => import("./metamask/components/InstallAppButton")), - giphy: dynamic(() => import("./giphy/components/InstallAppButton")), - spacebookingother: dynamic(() => import("./spacebooking/components/InstallAppButton")), - vital: dynamic(() => import("./vital/components/InstallAppButton")), -}; - export const InstallAppButton = ( props: { type: App["type"]; @@ -37,14 +16,8 @@ export const InstallAppButton = ( ) => { const { status } = useSession(); const { t } = useLocale(); - let appName = props.type.replace(/_/g, ""); - let InstallAppButtonComponent = InstallAppButtonMap[appName as keyof typeof InstallAppButtonMap]; - /** So we can either call it by simple name (ex. `slack`, `giphy`) instead of - * `slackmessaging`, `giphyother` while maintaining retro-compatibility. */ - if (!InstallAppButtonComponent) { - [appName] = props.type.split("_"); - InstallAppButtonComponent = InstallAppButtonMap[appName as keyof typeof InstallAppButtonMap]; - } + const key = deriveAppDictKeyFromType(props.type, InstallAppButtonMap); + const InstallAppButtonComponent = InstallAppButtonMap[key as keyof typeof InstallAppButtonMap]; if (!InstallAppButtonComponent) return null; if (status === "unauthenticated") return ( diff --git a/packages/app-store/giphy/_metadata.ts b/packages/app-store/giphy/_metadata.ts index 15c6500fe7..1d0ef909c0 100644 --- a/packages/app-store/giphy/_metadata.ts +++ b/packages/app-store/giphy/_metadata.ts @@ -5,6 +5,7 @@ import _package from "./package.json"; export const metadata = { name: "Giphy", description: _package.description, + installed: true, category: "other", // If using static next public folder, can then be referenced from the base URL (/). imageSrc: "/api/app-store/giphy/icon.svg", diff --git a/packages/app-store/giphy/api/add.ts b/packages/app-store/giphy/api/add.ts index c49218c041..98592d0c7f 100644 --- a/packages/app-store/giphy/api/add.ts +++ b/packages/app-store/giphy/api/add.ts @@ -27,6 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) type: appType, key: {}, userId: req.session.user.id, + appId: "giphy", }, }); if (!installation) { diff --git a/packages/app-store/hubspotothercalendar/_metadata.ts b/packages/app-store/hubspotothercalendar/_metadata.ts index 5212102700..2f380a3f03 100644 --- a/packages/app-store/hubspotothercalendar/_metadata.ts +++ b/packages/app-store/hubspotothercalendar/_metadata.ts @@ -4,6 +4,7 @@ import _package from "./package.json"; export const metadata = { name: "HubSpot CRM", + installed: !!process.env.HUBSPOT_CLIENT_ID, description: _package.description, type: "hubspot_other_calendar", imageSrc: "/api/app-store/hubspotothercalendar/icon.svg", diff --git a/packages/app-store/jitsivideo/api/add.ts b/packages/app-store/jitsivideo/api/add.ts index a33e09d77b..e98916482b 100644 --- a/packages/app-store/jitsivideo/api/add.ts +++ b/packages/app-store/jitsivideo/api/add.ts @@ -39,5 +39,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } return res.status(500); } - return res.redirect("/apps/installed"); + return res.status(200).json({ url: "/apps/installed" }); } diff --git a/packages/app-store/metadata.ts b/packages/app-store/metadata.ts deleted file mode 100644 index c14c136f92..0000000000 --- a/packages/app-store/metadata.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { metadata as applecalendar } from "./applecalendar/_metadata"; -import { metadata as caldavcalendar } from "./caldavcalendar/_metadata"; -import { metadata as dailyvideo } from "./dailyvideo/_metadata"; -import { metadata as giphy } from "./giphy/_metadata"; -import { metadata as googlecalendar } from "./googlecalendar/_metadata"; -import { metadata as googlevideo } from "./googlevideo/_metadata"; -import { metadata as hubspotothercalendar } from "./hubspotothercalendar/_metadata"; -import { metadata as huddle01video } from "./huddle01video/_metadata"; -import { metadata as jitsivideo } from "./jitsivideo/_metadata"; -import { metadata as metamask } from "./metamask/_metadata"; -import { metadata as office365calendar } from "./office365calendar/_metadata"; -import { metadata as office365video } from "./office365video/_metadata"; -import { metadata as slackmessaging } from "./slackmessaging/_metadata"; -import { metadata as spacebooking } from "./spacebooking/_metadata"; -import { metadata as stripepayment } from "./stripepayment/_metadata"; -import { metadata as tandemvideo } from "./tandemvideo/_metadata"; -import { metadata as vital } from "./vital/_metadata"; -import { metadata as wipemycalother } from "./wipemycalother/_metadata"; -import { metadata as zapier } from "./zapier/_metadata"; -import { metadata as zoomvideo } from "./zoomvideo/_metadata"; - -export const appStoreMetadata = { - applecalendar, - caldavcalendar, - dailyvideo, - googlecalendar, - googlevideo, - hubspotothercalendar, - huddle01video, - jitsivideo, - office365calendar, - office365video, - slackmessaging, - stripepayment, - spacebooking, - tandemvideo, - vital, - zoomvideo, - wipemycalother, - metamask, - giphy, - zapier, -}; - -export default appStoreMetadata; diff --git a/packages/app-store/metamask/api/add.ts b/packages/app-store/metamask/api/add.ts index 2ecde7d2ae..edd806e426 100644 --- a/packages/app-store/metamask/api/add.ts +++ b/packages/app-store/metamask/api/add.ts @@ -35,5 +35,5 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } return res.status(500); } - return res.redirect("/apps/installed"); + return res.status(200).json({ url: "/apps/installed" }); } diff --git a/packages/app-store/slackmessaging/_metadata.ts b/packages/app-store/slackmessaging/_metadata.ts index e4fc5307e6..ad4868e363 100644 --- a/packages/app-store/slackmessaging/_metadata.ts +++ b/packages/app-store/slackmessaging/_metadata.ts @@ -14,6 +14,7 @@ export const metadata = { slug: "slack", title: "Slack App", trending: true, + // DB has type slack_app. It is an inconsistency type: "slack_messaging", url: "https://slack.com/", variant: "conferencing", diff --git a/packages/app-store/spacebooking/README.mdx b/packages/app-store/spacebooking/README.mdx index d829970254..af4c367c35 100644 --- a/packages/app-store/spacebooking/README.mdx +++ b/packages/app-store/spacebooking/README.mdx @@ -2,9 +2,9 @@ width="640" height="360" src="https://www.loom.com/embed/f8d2cd9b2ac74f0c916f20c4441bd1da" - frameborder="0" - webkitallowfullscreen - mozallowfullscreen - allowfullscreen> + frameBorder="0" + webkitallowfullscreen="true" + mozallowfullscreen="true" + allowFullScreen> Looking to honor May 4th? Search no further. Download this app to make your booking success page resemble a long time ago in a galaxy far far away. diff --git a/packages/app-store/utils.ts b/packages/app-store/utils.ts index 8171623257..a191e8ae0c 100644 --- a/packages/app-store/utils.ts +++ b/packages/app-store/utils.ts @@ -3,10 +3,10 @@ import { TFunction } from "next-i18next"; import type { App } from "@calcom/types/App"; -import { LocationType } from "./locations"; // If you import this file on any app it should produce circular dependency // import appStore from "./index"; -import { appStoreMetadata } from "./metadata"; +import { appStoreMetadata } from "./apps.browser.generated"; +import { LocationType } from "./locations"; const ALL_APPS_MAP = Object.keys(appStoreMetadata).reduce((store, key) => { store[key] = appStoreMetadata[key as keyof typeof appStoreMetadata]; diff --git a/packages/lib/deriveAppDictKeyFromType.ts b/packages/lib/deriveAppDictKeyFromType.ts new file mode 100644 index 0000000000..63a7f05a6e --- /dev/null +++ b/packages/lib/deriveAppDictKeyFromType.ts @@ -0,0 +1,38 @@ +export function deriveAppDictKeyFromType(appType: string, dict: Record) { + let handlers = dict[appType]; + + if (handlers) { + return appType; + } + + // Transforms zoom_video to zoom + // Apps creates through cli would also meet this condition as their type would always be in the format {slug}_{category} and app dir name would be {slug} + const appTypeVariant1 = appType.substring(0, appType.lastIndexOf("_")); + handlers = dict[appTypeVariant1]; + if (handlers) { + return appTypeVariant1; + } + + // Transform `zoom_video` to `zoomvideo`; + const appTypeVariant2 = + appType.substring(0, appType.lastIndexOf("_")) + appType.substring(appType.lastIndexOf("_") + 1); + handlers = dict[appTypeVariant2]; + if (handlers) { + return appTypeVariant2; + } + + return appType; + + // const categories = ["video", "other", "calendar", "web3", "payment", "messaging"]; + + // // Instead of doing a blind split at _ and using the first part, apply this hack only on strings that match legacy type. + // // Transform zoomvideo to zoom + // categories.some((type) => { + // const matcher = new RegExp(`(.+)${type}$`); + // if (appType.match(matcher)) { + // appType = appType.replace(matcher, "$1"); + // return true; + // } + // return appType; + // }); +} diff --git a/packages/prisma/delete-app.ts b/packages/prisma/delete-app.ts new file mode 100644 index 0000000000..b7861e8ac5 --- /dev/null +++ b/packages/prisma/delete-app.ts @@ -0,0 +1,36 @@ +import prisma from "."; + +require("dotenv").config({ path: "../../.env" }); +// TODO: Put some restrictions here to run it on local DB only. +// Production DB currently doesn't support app deletion +async function main() { + const appId = process.argv[2]; + try { + await prisma.app.delete({ + where: { + slug: appId, + }, + }); + await prisma.credential.deleteMany({ + where: { + appId: appId, + }, + }); + console.log(`Deleted app from DB: '${appId}'`); + } catch (e) { + if (e.code === "P2025") { + console.log(`App '${appId}' already deleted from DB`); + return; + } + throw e; + } +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/packages/prisma/package.json b/packages/prisma/package.json index bf367c327d..9ed3342dea 100644 --- a/packages/prisma/package.json +++ b/packages/prisma/package.json @@ -16,7 +16,8 @@ "dx": "yarn db-setup", "generate-schemas": "prisma generate && prisma format", "post-install": "yarn generate-schemas", - "seed-app-store": "ts-node --transpile-only ./seed-app-store.ts" + "seed-app-store": "ts-node --transpile-only ./seed-app-store.ts", + "delete-app": "ts-node --transpile-only ./delete-app.ts" }, "devDependencies": { "npm-run-all": "^4.1.5", diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index d082e4e938..2723607d1a 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -86,6 +86,7 @@ model Credential { user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int? app App? @relation(fields: [appId], references: [slug], onDelete: Cascade) + // How to make it a required column? appId String? } diff --git a/packages/prisma/seed-app-store.config.json b/packages/prisma/seed-app-store.config.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/packages/prisma/seed-app-store.config.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/prisma/seed-app-store.ts b/packages/prisma/seed-app-store.ts index 87e4b3cc10..d2dc16ed3e 100644 --- a/packages/prisma/seed-app-store.ts +++ b/packages/prisma/seed-app-store.ts @@ -1,4 +1,6 @@ import { Prisma } from "@prisma/client"; +import fs from "fs"; +import path from "path"; import prisma from "."; @@ -131,6 +133,14 @@ async function main() { webhook_secret: process.env.STRIPE_WEBHOOK_SECRET, }); } + + const generatedApps = JSON.parse( + fs.readFileSync(path.join(__dirname, "seed-app-store.config.json"), "utf8") + ); + for (let i = 0; i < generatedApps.length; i++) { + const generatedApp = generatedApps[i]; + await createApp(generatedApp.slug, generatedApp.dirName, generatedApp.categories, generatedApp.type); + } } main() diff --git a/turbo.json b/turbo.json index 48ffaff483..f93470731a 100644 --- a/turbo.json +++ b/turbo.json @@ -20,8 +20,12 @@ "cache": false, "dependsOn": ["@calcom/prisma#db-deploy"] }, + "@calcom/app-store-cli#generate": { + "cache": false + }, "@calcom/web#build": { "dependsOn": [ + "@calcom/app-store-cli#generate", "^build", "@calcom/prisma#build", "$CALENDSO_ENCRYPTION_KEY", @@ -156,7 +160,7 @@ "dependsOn": ["@calcom/prisma#db-reset", "@calcom/web#test", "@calcom/web#build"] }, "type-check": { - "dependsOn": ["@calcom/embed-react#build", "@calcom/embed-core#build", "@calcom/embed-snippet#build"], + "dependsOn": ["@calcom/app-store-cli#generate", "@calcom/embed-react#build", "@calcom/embed-core#build", "@calcom/embed-snippet#build"], "outputs": [] } } diff --git a/yarn.lock b/yarn.lock index 0165a50208..354358e858 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3550,6 +3550,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/minimist@^1.2.0": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + "@types/module-alias@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.1.tgz#e5893236ce922152d57c5f3f978f764f4deeb45f" @@ -3673,7 +3678,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.0.9", "@types/react@>=16": +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.0.9", "@types/react@>=16", "@types/react@^18.0.9": version "18.0.9" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== @@ -3794,6 +3799,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/yoga-layout@1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" + integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== + "@types/zen-observable@0.8.3": version "0.8.3" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" @@ -4228,6 +4238,11 @@ arr-flatten@^1.1.0: resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== +arr-rotate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/arr-rotate/-/arr-rotate-1.0.0.tgz#c11877d06a0a42beb39ab8956a06779d9b71d248" + integrity sha512-yOzOZcR9Tn7enTF66bqKorGGH0F36vcPaSWg8fO0c0UYb3LX3VMXj5ZxEqQLNOecAhlRJ7wYZja5i4jTlnbIfQ== + arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" @@ -4249,6 +4264,17 @@ array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -4277,6 +4303,21 @@ array.prototype.flatmap@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + arrify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -4364,6 +4405,11 @@ auth0@^2.35.1: rest-facade "^1.16.3" retry "^0.13.1" +auto-bind@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" + integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== + autolinker@^3.11.0: version "3.15.0" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.15.0.tgz#03956088648f236642a5783612f9ca16adbbed38" @@ -5108,6 +5154,15 @@ camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -5356,6 +5411,11 @@ cleye@1.2.0: terminal-columns "^1.4.1" type-flag "^2.1.0" +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -5468,6 +5528,13 @@ code-block-writer@^11.0.0: dependencies: tslib "2.3.1" +code-excerpt@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-3.0.0.tgz#fcfb6748c03dba8431c19f5474747fad3f250f10" + integrity sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw== + dependencies: + convert-to-spaces "^1.0.1" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -5613,6 +5680,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, dependencies: safe-buffer "~5.1.1" +convert-to-spaces@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715" + integrity sha512-cj09EBuObp9gZNQCzc7hByQyrs6jVGE+o9kSJmeUoj+GiPiJvi5LYqEH/Hmme4+MTLHM+Ejtq+FChpjjEnsPdQ== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -5907,7 +5979,15 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -decamelize@^1.2.0: +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -6033,6 +6113,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -6437,6 +6525,42 @@ es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -6761,6 +6885,26 @@ eslint-plugin-react@^7.29.4: semver "^6.3.0" string.prototype.matchall "^4.0.6" +eslint-plugin-react@^7.30.0: + version "7.30.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz#8e7b1b2934b8426ac067a0febade1b13bd7064e3" + integrity sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A== + dependencies: + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.1" + object.values "^1.1.5" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.7" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -7447,7 +7591,7 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -figures@^3.0.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -7747,11 +7891,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -8190,6 +8349,11 @@ har-validator@^5.0.0, har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -8202,6 +8366,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -8217,6 +8386,13 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" @@ -8404,6 +8580,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -8666,6 +8849,52 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ink-select-input@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ink-select-input/-/ink-select-input-4.2.1.tgz#121108ccbcb42aa619f9f0baedb796c24c971a2a" + integrity sha512-WvlrYdwmdnD6/nE/9mNhaaanTQOKmwy/hT/vuAqbDec3PUQBQ8Pkwszii/8eGvDTx5bGiUHu18P9D5IoB/ERaw== + dependencies: + arr-rotate "^1.0.0" + figures "^3.2.0" + lodash.isequal "^4.5.0" + +ink-text-input@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ink-text-input/-/ink-text-input-4.0.3.tgz#6348fef942e74b06a465f98851706516a1e2be8d" + integrity sha512-eQD01ik9ltmNoHmkeQ2t8LszYkv2XwuPSUz3ie/85qer6Ll/j0QSlSaLNl6ENHZakBHdCBVZY04iOXcLLXA0PQ== + dependencies: + chalk "^4.1.0" + type-fest "^0.15.1" + +ink@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ink/-/ink-3.2.0.tgz#434793630dc57d611c8fe8fffa1db6b56f1a16bb" + integrity sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg== + dependencies: + ansi-escapes "^4.2.1" + auto-bind "4.0.0" + chalk "^4.1.0" + cli-boxes "^2.2.0" + cli-cursor "^3.1.0" + cli-truncate "^2.1.0" + code-excerpt "^3.0.0" + indent-string "^4.0.0" + is-ci "^2.0.0" + lodash "^4.17.20" + patch-console "^1.0.0" + react-devtools-core "^4.19.1" + react-reconciler "^0.26.2" + scheduler "^0.20.2" + signal-exit "^3.0.2" + slice-ansi "^3.0.0" + stack-utils "^2.0.2" + string-width "^4.2.2" + type-fest "^0.12.0" + widest-line "^3.1.0" + wrap-ansi "^6.2.0" + ws "^7.5.5" + yoga-layout-prebuilt "^1.9.6" + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" @@ -8856,6 +9085,13 @@ is-core-module@^2.2.0, is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -9114,7 +9350,7 @@ is-retry-allowed@^2.2.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== -is-shared-array-buffer@^1.0.1: +is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== @@ -10432,7 +10668,7 @@ kind-of@^5.0.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -10841,6 +11077,16 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" @@ -11122,6 +11368,24 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + merge-class-names@^1.1.1: version "1.4.2" resolved "https://registry.yarnpkg.com/merge-class-names/-/merge-class-names-1.4.2.tgz#78d6d95ab259e7e647252a7988fd25a27d5a8835" @@ -11578,6 +11842,11 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + mini-svg-data-uri@^1.2.3: version "1.4.4" resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939" @@ -11600,6 +11869,15 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -12102,6 +12380,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -12282,6 +12570,14 @@ object.hasown@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.19.5" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -12698,6 +12994,11 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +patch-console@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/patch-console/-/patch-console-1.0.0.tgz#19b9f028713feb8a3c023702a8cc8cb9f7466f9d" + integrity sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA== + path-browserify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -13344,6 +13645,11 @@ queue@^6.0.2: dependencies: inherits "~2.0.3" +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -13481,6 +13787,14 @@ react-debounce-input@=3.2.4: lodash.debounce "^4" prop-types "^15.7.2" +react-devtools-core@^4.19.1: + version "4.24.6" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.6.tgz#3262114f483465179c97a49b7ada845048f4f97e" + integrity sha512-+6y6JAtAo1NUUxaCwCYTb13ViBpc7RjNTlj1HZRlDJmi7UYToj5+BNn8Duzz2YizzAzmRUWZkRM7OtqxnN6TnA== + dependencies: + shell-quote "^1.6.1" + ws "^7" + react-digit-input@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-digit-input/-/react-digit-input-2.1.0.tgz#8b0be6d3ea247fd361855483f21d0aafba341196" @@ -13624,6 +13938,15 @@ react-query@^3.39.0: broadcast-channel "^3.4.1" match-sorter "^6.0.2" +react-reconciler@^0.26.2: + version "0.26.2" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91" + integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + react-redux@^7.2.4: version "7.2.8" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de" @@ -13764,6 +14087,14 @@ react@18.1.0, react@^18.1.0: dependencies: loose-envify "^1.1.0" +react@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -13821,6 +14152,14 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + redis-errors@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" @@ -13890,6 +14229,15 @@ regexp.prototype.flags@^1.4.1: call-bind "^1.0.2" define-properties "^1.1.3" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -14199,7 +14547,7 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -run-parallel@^1.1.9: +run-parallel@^1.1.9, run-parallel@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== @@ -14276,6 +14624,14 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" @@ -14329,7 +14685,7 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.5, semver@^7.3.7: +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -14835,7 +15191,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14853,7 +15209,7 @@ string-width@^5.0.0: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.6: +string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== @@ -14884,6 +15240,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -14892,6 +15257,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -14991,6 +15365,13 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -15548,6 +15929,11 @@ traverse@~0.6.6: resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" @@ -15808,6 +16194,21 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" + integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== + +type-fest@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.15.1.tgz#d2c4e73d3e4a53cf1a906396dd460a1c5178ca00" + integrity sha512-n+UXrN8i5ioo7kqT/nF8xsEzLaqFra7k32SEsSPwvXVGyAcRgV/FUQN/sgfptJTR1oRmmq7z4IXMFSM7im7C9A== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -15910,6 +16311,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unbzip2-stream@^1.0.9: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" @@ -16795,6 +17206,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -16855,6 +17273,11 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" +ws@^7, ws@^7.5.5: + version "7.5.8" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" + integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== + ws@^7.3.1, ws@^7.4.6: version "7.5.7" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" @@ -17007,7 +17430,7 @@ yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@20.x, yargs-parser@^20.2.2: +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -17094,6 +17517,13 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yoga-layout-prebuilt@^1.9.6: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz#2936fbaf4b3628ee0b3e3b1df44936d6c146faa6" + integrity sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g== + dependencies: + "@types/yoga-layout" "1.9.2" + yup@^0.32.9: version "0.32.11" resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"