From dabf26c2c92b7f5aaa05dbbb9d8bdd04621ba8ab Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 09:37:30 +0000 Subject: [PATCH 01/59] Added a message to Test page to make it clearer how to run tests --- .../workflows/$workflowSlug/test.tsx | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx index edaac0339..43bcec41d 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx @@ -9,9 +9,10 @@ import { PanelInfo } from "~/components/layout/PanelInfo"; import { PanelWarning } from "~/components/layout/PanelWarning"; import { PrimaryButton, TertiaryLink } from "~/components/primitives/Buttons"; import { Select } from "~/components/primitives/Select"; +import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; import { useCurrentOrganization } from "~/hooks/useOrganizations"; -import { useCurrentWorkflow } from "~/hooks/useWorkflows"; +import { CurrentWorkflow, useCurrentWorkflow } from "~/hooks/useWorkflows"; import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server"; import { WorkflowTestPresenter } from "~/presenters/testPresenter.server"; import { requireUserId } from "~/services/session.server"; @@ -64,14 +65,17 @@ export default function Page() { ) : ( - - - + <> + {workflowType(workflow)} + + + + )} ); @@ -135,3 +139,16 @@ function Tester({ ); } + +function workflowType(workflow: CurrentWorkflow) { + switch (workflow?.type) { + case "WEBHOOK": + return "This test will simulate receiving this JSON payload for this webhook."; + case "SCHEDULE": + return "This test will simulate receiving a scheduled trigger from this datetime string."; + case "CUSTOM_EVENT": + return "This test will simulate receiving this JSON payload for this custom event."; + default: + return "This workflow hasn't been connected."; + } +} From f739f492095ef4037a37b31e2c1e640cb7127efc Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 09:49:43 +0000 Subject: [PATCH 02/59] First workflow onboarding step is now Choose a Template --- .../__org/workflows/new/index.tsx | 134 ++---------------- 1 file changed, 15 insertions(+), 119 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx index f502b059e..855fe9fc4 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx @@ -1,125 +1,21 @@ -import CheckIcon from "@heroicons/react/20/solid/CheckIcon"; -import { - CloudIcon, - HomeIcon, - RocketLaunchIcon, -} from "@heroicons/react/24/outline"; -import XCircleIcon from "@heroicons/react/24/solid/XCircleIcon"; -import { Link, useFetcher } from "@remix-run/react"; -import classNames from "classnames"; -import { Fragment, useEffect, useState } from "react"; -import { Panel } from "~/components/layout/Panel"; -import { onboarding } from "~/components/onboarding/classNames"; -import { StepNumber } from "~/components/onboarding/StepNumber"; -import { PrimaryButton } from "~/components/primitives/Buttons"; -import { StyledDialog } from "~/components/primitives/Dialog"; -import { Body } from "~/components/primitives/text/Body"; -import { Header3 } from "~/components/primitives/text/Headers"; +import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { SubTitle } from "~/components/primitives/text/SubTitle"; -import { useUser } from "~/hooks/useUser"; +import { TemplatesGrid } from "~/components/templates/TemplatesGrid"; +import { TemplateListPresenter } from "~/presenters/templateListPresenter.server"; + +export const loader = async () => { + const presenter = new TemplateListPresenter(); + return typedjson(await presenter.data()); +}; export default function NewWorkflowStep1Page() { - return ; -} - -function Step1() { - const user = useUser(); - const fetcher = useFetcher(); - let [isOpen, setIsOpen] = useState(false); - - useEffect(() => { - if (fetcher.state === "submitting") { - setIsOpen(false); - } - }, [fetcher.state, setIsOpen]); - + const { templates } = useTypedLoaderData(); return ( - <> - setIsOpen(false)} - appear - show={isOpen} - as={Fragment} - > -
-
- -
-
- - - - - Cloud hosting coming soon… - -
-
- - We're preparing to launch a cloud hosting service for your - Trigger.dev workflows that will make it as easy to deploy - your workflows as a git push. - -
- - {user.isOnCloudWaitlist ? ( - - - Already on the waitlist - - ) : ( - - Notify me when it's ready - - )} - -
-
-
- -
-
-
-
-
- - - Where do you want your workflow hosted? - - -
- - - I'll host the workflow myself - - I will deploy the code to my own servers. - - - -
-
-
- +
+ + Install one of these Templates directly into your codebase + + +
); } From 590dd688a9e85b151afdb325cd4bc0c3f9d42638 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 09:50:19 +0000 Subject: [PATCH 03/59] Fixed max width --- .../__app/orgs/$organizationSlug/__org/workflows/new/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx index 855fe9fc4..93fa73c53 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx @@ -11,7 +11,7 @@ export const loader = async () => { export default function NewWorkflowStep1Page() { const { templates } = useTypedLoaderData(); return ( -
+
Install one of these Templates directly into your codebase From 80373c68747cbe53608517dce37ec4a36c4b396b Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 09:54:22 +0000 Subject: [PATCH 04/59] Removed file for old steps 1, 2, 3 --- .../__org/workflows/new/newRepo.tsx | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/newRepo.tsx diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/newRepo.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/newRepo.tsx deleted file mode 100644 index 63aa7c052..000000000 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/newRepo.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import classNames from "classnames"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { BackToStep1, BackToStep2 } from "~/components/onboarding/BackToSteps"; -import { onboarding } from "~/components/onboarding/classNames"; -import { StepNumber } from "~/components/onboarding/StepNumber"; -import { SubTitle } from "~/components/primitives/text/SubTitle"; -import { TemplatesGrid } from "~/components/templates/TemplatesGrid"; -import { TemplateListPresenter } from "~/presenters/templateListPresenter.server"; - -export const loader = async () => { - const presenter = new TemplateListPresenter(); - return typedjson(await presenter.data()); -}; - -export default function Step3NewRepo1() { - const { templates } = useTypedLoaderData(); - - return ( -
-
- -
-
- -
-
- - - Which template would you like to use? - - -
-
- ); -} From 1218320492b19e6974ab185a3217860dac5ba81c Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 11:30:41 +0000 Subject: [PATCH 05/59] Added a new copy panel --- apps/webapp/app/components/CopyTextButton.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/components/CopyTextButton.tsx b/apps/webapp/app/components/CopyTextButton.tsx index fce6808f4..2c473d6bf 100644 --- a/apps/webapp/app/components/CopyTextButton.tsx +++ b/apps/webapp/app/components/CopyTextButton.tsx @@ -1,4 +1,4 @@ -import { ClipboardIcon } from "@heroicons/react/24/outline"; +import { CheckIcon, ClipboardIcon } from "@heroicons/react/24/outline"; import classNames from "classnames"; import { useCallback, useState } from "react"; import { CopyText } from "./CopyText"; @@ -57,3 +57,31 @@ export function CopyTextButton({ ); } + +export function CopyTextPanel({ value, className }: CopyTextButtonProps) { + const [copied, setCopied] = useState(false); + const onCopied = useCallback(() => { + setCopied(true); + setTimeout(() => { + setCopied(false); + }, 1500); + }, [setCopied]); + return ( + + {copied ? ( +
+ {value} + +
+ ) : ( +
+ {value} + +
+ )} +
+ ); +} + +const copyTextPanelStyles = + "truncate bg-indigo-700/50 pl-3.5 pr-2 py-3 rounded border border-indigo-600 truncate flex items-center justify-between gap-2 hover:cursor-pointer hover:bg-indigo-600/50 hover:border-indigo-600 transition"; From 15ab35c8163ba669d516a0b6d0b6c98763efd86e Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 11:31:51 +0000 Subject: [PATCH 06/59] Added a new copy npm install values to the template cards --- .../components/templates/TemplatesGrid.tsx | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/apps/webapp/app/components/templates/TemplatesGrid.tsx b/apps/webapp/app/components/templates/TemplatesGrid.tsx index fa2558fd8..0f7d106e5 100644 --- a/apps/webapp/app/components/templates/TemplatesGrid.tsx +++ b/apps/webapp/app/components/templates/TemplatesGrid.tsx @@ -2,7 +2,7 @@ import { XCircleIcon } from "@heroicons/react/24/solid"; import { Link } from "@remix-run/react"; import { Fragment, useState } from "react"; import type { TemplateListItem } from "~/presenters/templateListPresenter.server"; -import { ApiLogoIcon } from "../code/ApiLogoIcon"; +import { CopyTextPanel } from "../CopyTextButton"; import { StyledDialog } from "../primitives/Dialog"; import { Body } from "../primitives/text/Body"; import { Header1 } from "../primitives/text/Headers"; @@ -47,15 +47,15 @@ export function TemplatesGrid({ openInNewPage={openInNewPage} onClick={() => setOpenedTemplate(template)} > -
+
-
-
+
+
{template.title} @@ -63,17 +63,10 @@ export function TemplatesGrid({ {template.description}
-
- {template.services.map((service) => ( -
- -
- ))} -
+
); From 1c5d8a4c0be66f70d8b30984ff3063a0e62a7e20 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 11:53:48 +0000 Subject: [PATCH 07/59] Updated the template page to include the npm copy panel, link to repo and docs --- .../components/templates/TemplateOverview.tsx | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/apps/webapp/app/components/templates/TemplateOverview.tsx b/apps/webapp/app/components/templates/TemplateOverview.tsx index 63a702924..cb02269c0 100644 --- a/apps/webapp/app/components/templates/TemplateOverview.tsx +++ b/apps/webapp/app/components/templates/TemplateOverview.tsx @@ -1,10 +1,9 @@ -import { ArrowRightIcon } from "@heroicons/react/20/solid"; import classNames from "classnames"; import { Fragment } from "react"; import type { TemplateListItem } from "~/presenters/templateListPresenter.server"; import { ApiLogoIcon } from "../code/ApiLogoIcon"; -import { OctoKitty } from "../GitHubLoginButton"; -import { TertiaryA, ToxicLink } from "../primitives/Buttons"; +import { CopyTextPanel } from "../CopyTextButton"; +import { SecondaryA } from "../primitives/Buttons"; import { Body } from "../primitives/text/Body"; import { Header1 } from "../primitives/text/Headers"; @@ -92,26 +91,38 @@ function TemplateDetails({
- Repo + Help and guides
- - - - {repositoryUrl.replace("https://github.com/triggerdotdev", "")} +
+ + View Repo + + + View Docs + +
+
+ + Run this command to get started - - - Use this template - - +
+
+
); } From b69058bfcb6ed54c308ed32b275f165b94debb84 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 12:17:00 +0000 Subject: [PATCH 08/59] Style tweaks --- apps/webapp/app/components/templates/TemplateOverview.tsx | 6 +++--- apps/webapp/app/components/templates/TemplatesGrid.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/components/templates/TemplateOverview.tsx b/apps/webapp/app/components/templates/TemplateOverview.tsx index cb02269c0..afceb6017 100644 --- a/apps/webapp/app/components/templates/TemplateOverview.tsx +++ b/apps/webapp/app/components/templates/TemplateOverview.tsx @@ -73,7 +73,7 @@ function TemplateDetails({
-
+
{template.services.map((service) => (
-
+
- Run this command to get started + Get started
diff --git a/apps/webapp/app/components/templates/TemplatesGrid.tsx b/apps/webapp/app/components/templates/TemplatesGrid.tsx index 0f7d106e5..60084452a 100644 --- a/apps/webapp/app/components/templates/TemplatesGrid.tsx +++ b/apps/webapp/app/components/templates/TemplatesGrid.tsx @@ -88,7 +88,7 @@ function TemplateButtonOrLink({ children: React.ReactNode; }) { const classNames = - "group flex w-full flex-col self-stretch overflow-hidden rounded-md border border-slate-700 bg-slate-800 text-left text-slate-200 shadow-md transition hover:cursor-pointer hover:border-slate-500 hover:bg-slate-700/30 disabled:opacity-50"; + "group flex w-full flex-col self-stretch overflow-hidden rounded-md border border-slate-700 bg-slate-800 text-left text-slate-200 shadow-md transition hover:cursor-pointer hover:border-slate-600 hover:bg-slate-700/50 disabled:opacity-50"; if (openInNewPage) { return ( From 74a6a21c35670a2c6cb3dfe9861ba05d4a12709a Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 13:13:41 +0000 Subject: [PATCH 09/59] Adding the create-trigger CLI package --- .../app/routes/api/v1/internal/telemetry.ts | 41 +++ .../app/routes/api/v1/internal/whoami.ts | 1 + packages/create-trigger/README.md | 77 +++++ packages/create-trigger/package.json | 68 ++++ packages/create-trigger/src/cli/index.ts | 295 ++++++++++++++++++ packages/create-trigger/src/consts.ts | 22 ++ packages/create-trigger/src/index.ts | 71 +++++ .../src/utils/createDotEnvFile.ts | 14 + .../create-trigger/src/utils/createProject.ts | 74 +++++ .../src/utils/createTelemetryEvent.ts | 21 ++ .../src/utils/getUserPkgManager.ts | 19 ++ .../create-trigger/src/utils/getVersion.ts | 12 + packages/create-trigger/src/utils/git.ts | 137 ++++++++ .../src/utils/installDependencies.ts | 74 +++++ .../create-trigger/src/utils/logNextSteps.ts | 46 +++ packages/create-trigger/src/utils/logger.ts | 16 + .../src/utils/parseNameAndPath.ts | 38 +++ .../create-trigger/src/utils/renderTitle.ts | 24 ++ .../create-trigger/src/utils/templateRef.ts | 5 + .../create-trigger/src/utils/triggerApi.ts | 80 +++++ packages/create-trigger/tsconfig.json | 49 +++ packages/create-trigger/tsup.config.ts | 16 + 22 files changed, 1200 insertions(+) create mode 100644 apps/webapp/app/routes/api/v1/internal/telemetry.ts create mode 100644 packages/create-trigger/README.md create mode 100644 packages/create-trigger/package.json create mode 100644 packages/create-trigger/src/cli/index.ts create mode 100644 packages/create-trigger/src/consts.ts create mode 100644 packages/create-trigger/src/index.ts create mode 100644 packages/create-trigger/src/utils/createDotEnvFile.ts create mode 100644 packages/create-trigger/src/utils/createProject.ts create mode 100644 packages/create-trigger/src/utils/createTelemetryEvent.ts create mode 100644 packages/create-trigger/src/utils/getUserPkgManager.ts create mode 100644 packages/create-trigger/src/utils/getVersion.ts create mode 100644 packages/create-trigger/src/utils/git.ts create mode 100644 packages/create-trigger/src/utils/installDependencies.ts create mode 100644 packages/create-trigger/src/utils/logNextSteps.ts create mode 100644 packages/create-trigger/src/utils/logger.ts create mode 100644 packages/create-trigger/src/utils/parseNameAndPath.ts create mode 100644 packages/create-trigger/src/utils/renderTitle.ts create mode 100644 packages/create-trigger/src/utils/templateRef.ts create mode 100644 packages/create-trigger/src/utils/triggerApi.ts create mode 100644 packages/create-trigger/tsconfig.json create mode 100644 packages/create-trigger/tsup.config.ts diff --git a/apps/webapp/app/routes/api/v1/internal/telemetry.ts b/apps/webapp/app/routes/api/v1/internal/telemetry.ts new file mode 100644 index 000000000..332e2f7dc --- /dev/null +++ b/apps/webapp/app/routes/api/v1/internal/telemetry.ts @@ -0,0 +1,41 @@ +import type { ActionArgs } from "@remix-run/server-runtime"; +import { json } from "@remix-run/server-runtime"; +import { z } from "zod"; +import { analytics } from "~/services/analytics.server"; +import { authenticateApiRequest } from "~/services/apiAuth.server"; + +const BodySchema = z.object({ + id: z.string(), + event: z.string(), + properties: z.record(z.union([z.string(), z.number()]), z.any()), +}); + +export async function action({ request }: ActionArgs) { + // first make sure this is a POST request + if (request.method.toUpperCase() !== "POST") { + return json({ error: "Method not allowed" }, { status: 405 }); + } + + const rawBody = await request.json(); + const body = BodySchema.parse(rawBody); + + // Next authenticate the request + const authenticatedEnv = await authenticateApiRequest(request); + + const event = { + userId: body.id, + event: body.event, + properties: { + ...body.properties, + environmentType: authenticatedEnv?.slug, + }, + organizationId: authenticatedEnv?.organizationId, + environmentId: authenticatedEnv?.id, + }; + + console.log("Capturing event", event); + + analytics.telemetry.capture(event); + + return json({ status: "OK" }); +} diff --git a/apps/webapp/app/routes/api/v1/internal/whoami.ts b/apps/webapp/app/routes/api/v1/internal/whoami.ts index d9bd8b3e9..103cd6b5f 100644 --- a/apps/webapp/app/routes/api/v1/internal/whoami.ts +++ b/apps/webapp/app/routes/api/v1/internal/whoami.ts @@ -14,5 +14,6 @@ export async function loader({ request }: LoaderArgs) { return json({ organizationId: authenticatedEnv.organizationId, env: authenticatedEnv.slug, + organizationSlug: authenticatedEnv.organization.slug, }); } diff --git a/packages/create-trigger/README.md b/packages/create-trigger/README.md new file mode 100644 index 000000000..6f6082262 --- /dev/null +++ b/packages/create-trigger/README.md @@ -0,0 +1,77 @@ +## ✨ Create Trigger - Get started writing Trigger.dev code quickly + +Trigger.dev is an open source platform that makes it easy to create event-driven background tasks directly your codebase. + +You can run these tasks (or "workflows" as we like to cal them) in your existing Node.js repo, but if you don't have one of those (πŸ‘‹ Next.js devs) or you just want to try us out without the setup, this `create-trigger` CLI will scaffold out a project for you in just a few seconds, either starting from scratch or using one of our many [templates](https://app.trigger.com/templates). + +## πŸ’» Usage + +To scaffold out a new project using `create-trigger`, run any of the following three commands and answer the prompts: + +### npm + +```sh +npm create trigger@latest +``` + +### yarn + +```sh +yarn create trigger +``` + +### pnpm + +```sh +pnpm create trigger@latest +``` + +You can also specify the [template](https://app.trigger.com/templates) you want to use by passing an argument to the command, like so: + +```sh +npm create trigger@latest github-stars-to-slack +``` + +## Advanced Usage + +| Option/Flag | Description | +| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `[template]` | The name of the template to use, e.g. basic-starter | +| `-p, --projectName` | The name of the project, as well as the name of the directory to create. Can be a path to a directory, e.g. ~/projects/my-project | +| `-k, --apiKey` | The development API key to use for the project. Visit https://app.trigger.dev to get yours | +| `--noGit` | Explicitly tell the CLI to not initialize a new git repo in the project | +| `--noInstall` | Explicitly tell the CLI to not run the package manager's install command | + +## Folder structure + +``` +β”œβ”€β”€ LICENSE +β”œβ”€β”€ README.md +β”œβ”€β”€ package.json +β”œβ”€β”€ render.yaml +β”œβ”€β”€ .env +β”œβ”€β”€ .env.example +β”œβ”€β”€ src +β”‚Β Β  └── index.ts +└── tsconfig.json +``` + +### `src/index.ts` + +All your Trigger.dev workflow code will be in here, and this is the part you can start customizing. + +### `.env` + +If provided, we'll save your development API Key here so running the project can connect to our servers. + +### `render.yaml` + +A [Render.com](https://render.com) Blueprint file that makes it easy to deploy your repo as a Background Worker. + +### `README.md` + +Contains useful instructions for getting started with the repo, including how to customize it, running it locally, testing it, and deploying it. + +## Next steps + +After you successfully scaffold out your project, take a look at the README. If you have any issues, please feel free to email us at hello@trigger.dev, or you can ask a question in our [Discord server](https://discord.gg/nkqV9xBYWy) diff --git a/packages/create-trigger/package.json b/packages/create-trigger/package.json new file mode 100644 index 000000000..8ca3151a5 --- /dev/null +++ b/packages/create-trigger/package.json @@ -0,0 +1,68 @@ +{ + "name": "create-trigger", + "version": "0.1.0", + "description": "The Trigger.dev CLI to easily create and manage a Trigger.dev project", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/triggerdotdev/trigger.dev.git", + "directory": "packages/create-trigger" + }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "typescript", + "trigger.dev", + "workflows", + "orchestration", + "events", + "webhooks", + "integrations", + "apis" + ], + "files": [ + "dist" + ], + "type": "module", + "exports": "./dist/index.js", + "bin": { + "create-trigger": "./dist/index.js" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.1", + "@types/gradient-string": "^1.1.2", + "@types/inquirer": "^9.0.3", + "@types/node": "16", + "@types/node-fetch": "^2.6.2", + "rimraf": "^3.0.2", + "tsup": "^6.5.0", + "type-fest": "^3.6.0", + "typescript": "^4.9.5" + }, + "scripts": { + "typecheck": "tsc", + "build": "tsup", + "dev": "tsup --watch", + "clean": "rimraf dist", + "start": "node dist/index.js" + }, + "dependencies": { + "@types/degit": "^2.8.3", + "chalk": "^5.2.0", + "commander": "^9.4.1", + "degit": "^2.8.4", + "execa": "^7.0.0", + "fs-extra": "^11.1.0", + "gradient-string": "^2.0.2", + "inquirer": "^9.1.4", + "node-fetch": "^3.3.0", + "ora": "^6.1.2", + "terminal-link": "^3.0.0" + }, + "engines": { + "node": ">=16" + } +} \ No newline at end of file diff --git a/packages/create-trigger/src/cli/index.ts b/packages/create-trigger/src/cli/index.ts new file mode 100644 index 000000000..8773470d8 --- /dev/null +++ b/packages/create-trigger/src/cli/index.ts @@ -0,0 +1,295 @@ +import chalk from "chalk"; +import { Command } from "commander"; +import inquirer from "inquirer"; +import terminalLink from "terminal-link"; +import { + CREATE_TRIGGER, + DEFAULT_APP_NAME as DEFAULT_PROJECT_NAME, +} from "../consts.js"; +import { getUserPkgManager } from "../utils/getUserPkgManager.js"; +import { getVersion } from "../utils/getVersion.js"; +import { logger } from "../utils/logger.js"; +import { getTemplates } from "../utils/triggerApi.js"; + +export interface CliFlags { + noGit: boolean; + noInstall: boolean; + noTelemetry: boolean; + projectName: string; + apiKey?: string; +} + +export interface CliResults { + templateName: string; + flags: CliFlags; +} + +const defaultOptions: CliResults = { + templateName: "blank-starter", + flags: { + noGit: false, + noInstall: false, + noTelemetry: false, + projectName: DEFAULT_PROJECT_NAME, + }, +}; + +export const runCli = async () => { + const cliResults = defaultOptions; + + const program = new Command().name(CREATE_TRIGGER); + + program + .description("A CLI for creating Trigger.dev projects") + .argument( + "[template-name]", + "The name of the template to use, e.g. basic-starter", + "blank-starter" + ) + .option( + "-p, --projectName ", + "The name of the project, as well as the name of the directory to create. Can be a path to a directory, e.g. ~/projects/my-project", + false + ) + .option( + "-k, --apiKey ", + "The development API key to use for the project. Visit https://app.trigger.dev to get yours", + false + ) + .option( + "--noGit", + "Explicitly tell the CLI to not initialize a new git repo in the project", + false + ) + .option( + "--noInstall", + "Explicitly tell the CLI to not run the package manager's install command", + false + ) + .option( + "--noTelemetry", + "Explicitly tell the CLI to not send usage data to Trigger.dev", + false + ) + .version(getVersion(), "-v, --version", "Display the version number") + .addHelpText( + "afterAll", + `\n The create-trigger CLI was inspired by ${chalk + .hex("#E8DCFF") + .bold("create-t3-stack")} \n` + ) + .parse(process.argv); + + const templateName = program.args[0]; + + if (templateName) { + cliResults.templateName = templateName; + } + + cliResults.flags = program.opts(); + + try { + if ( + process.env.SHELL?.toLowerCase().includes("git") && + process.env.SHELL?.includes("bash") + ) { + logger.warn(` WARNING: It looks like you are using Git Bash which is non-interactive. Please run create-t3-app with another + terminal such as Windows Terminal or PowerShell if you want to use the interactive CLI.`); + + const error = new Error("Non-interactive environment"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (error as any).isTTYError = true; + throw error; + } + + if (!templateName) { + cliResults.templateName = await promptTemplateName( + cliResults.templateName + ); + } + + if (!cliResults.flags.projectName) { + cliResults.flags.projectName = await promptProjectName(); + } + + if (!cliResults.flags.apiKey) { + cliResults.flags.apiKey = await promptApiKey(); + } + + if (!cliResults.flags.noGit) { + cliResults.flags.noGit = !(await promptGit()); + } + + if (!cliResults.flags.noInstall) { + cliResults.flags.noInstall = !(await promptInstall()); + } + } catch (err) { + // If the user is not calling create-trigger from an interactive terminal, inquirer will throw an error with isTTYError = true + // If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default trigger project + // Otherwise we have to do some fancy namespace extension logic on the Error type which feels overkill for one line + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (err instanceof Error && (err as any).isTTYError) { + logger.warn( + `${CREATE_TRIGGER} needs an interactive terminal to provide options` + ); + + const { shouldContinue } = await inquirer.prompt<{ + shouldContinue: boolean; + }>({ + name: "shouldContinue", + type: "confirm", + message: `Continue creating a trigger.dev project?`, + default: true, + }); + + if (!shouldContinue) { + logger.info("Exiting..."); + process.exit(0); + } + + logger.info( + `Bootstrapping the default Trigger.dev template in ./${cliResults.templateName}` + ); + } else { + throw err; + } + } + + return cliResults; +}; + +const promptTemplateName = async ( + defaultTemplateName: string +): Promise => { + const templates = await getTemplates(); + + if (templates.length === 0) { + return defaultTemplateName; + } + + const defaultTemplate = templates.find( + (template) => template.slug === defaultTemplateName + ); + + const templateChoicesWithoutDefault = templates + .filter((template) => template.slug !== defaultTemplateName) + .map((template) => ({ + name: `${template.shortTitle} - ${template.description} [${terminalLink( + "View more", + template.repositoryUrl + )}]`, + value: template.slug, + })); + + const separator = new inquirer.Separator(); + + const choices = defaultTemplate + ? [ + { + name: `${defaultTemplate.shortTitle} - ${ + defaultTemplate.description + } [${terminalLink("View more", defaultTemplate.repositoryUrl)}]`, + value: defaultTemplate.slug, + }, + separator, + ...templateChoicesWithoutDefault, + ] + : templateChoicesWithoutDefault; + + const { templateName } = await inquirer.prompt<{ templateName: string }>({ + name: "templateName", + type: "list", + message: "What template would you like to use?", + choices, + default: defaultTemplateName, + }); + + logger.success(`Great! We're using the ${templateName} template`); + + return templateName; +}; + +const promptProjectName = async (): Promise => { + const { projectName } = await inquirer.prompt<{ projectName: string }>({ + name: "projectName", + type: "input", + message: "What would you like to name your project?", + default: DEFAULT_PROJECT_NAME, + }); + + logger.success(`Great! We're creating your project at ${projectName}`); + + return projectName; +}; + +const promptApiKey = async (): Promise => { + // First prompt if they want to enter their API key now, and if they say Yes, then prompt for it and return it + const { apiKey } = await inquirer.prompt<{ apiKey: string | undefined }>({ + type: "input", + name: "apiKey", + message: "Enter your development API key (optional)", + default: undefined, + validate: (input) => { + // Make sure they enter something like trigger_development_******** + if (input && !input.startsWith("trigger_development_")) { + return "Please enter a valid API key (e.g. trigger_development_********) or leave blank to skip"; + } + + return true; + }, + }); + + if (apiKey) { + logger.success( + `Fantastic! We'll save the API key (trigger_development_********) in the .env file.` + ); + } + + return apiKey; +}; + +const promptGit = async (): Promise => { + const { git } = await inquirer.prompt<{ git: boolean }>({ + name: "git", + type: "confirm", + message: "Initialize a new git repository?", + default: true, + }); + + if (git) { + logger.success("Nice one! Initializing repository!"); + } else { + logger.info("Sounds good! You can come back and run git init later."); + } + + return git; +}; + +const promptInstall = async (): Promise => { + const pkgManager = getUserPkgManager(); + + const { install } = await inquirer.prompt<{ install: boolean }>({ + name: "install", + type: "confirm", + message: + `Would you like us to run '${pkgManager}` + + (pkgManager === "yarn" ? `'?` : ` install'?`), + default: true, + }); + + if (install) { + logger.success("Alright. We'll install the dependencies for you!"); + } else { + if (pkgManager === "yarn") { + logger.info( + `No worries. You can run '${pkgManager}' later to install the dependencies.` + ); + } else { + logger.info( + `No worries. You can run '${pkgManager} install' later to install the dependencies.` + ); + } + } + + return install; +}; diff --git a/packages/create-trigger/src/consts.ts b/packages/create-trigger/src/consts.ts new file mode 100644 index 000000000..6a941e4a1 --- /dev/null +++ b/packages/create-trigger/src/consts.ts @@ -0,0 +1,22 @@ +import path from "path"; +import { fileURLToPath } from "url"; + +// With the move to TSUP as a build tool, this keeps path routes in other files (installers, loaders, etc) in check more easily. +// Path is in relation to a single index.js file inside ./dist +const __filename = fileURLToPath(import.meta.url); +const distPath = path.dirname(__filename); +export const PKG_ROOT = path.join(distPath, "../"); + +export const TITLE_TEXT = ` + _____ _ _ +|_ _| ___ |_| ___ ___ ___ ___ _| | ___ _ _ + | | | _|| || . || . || -_|| _| _ | . || -_|| | | + |_| |_| |_||_ ||_ ||___||_| |_||___||___| \\_/ + |___||___| +`; + +export const DEFAULT_APP_NAME = "my-triggers"; +export const CREATE_TRIGGER = "create-trigger"; +export const TEMPLATE_ORGANIZATION = "triggerdotdev"; +export const TRIGGER_BASE_URL = + process.env.TRIGGER_BASE_URL ?? "https://app.trigger.dev"; diff --git a/packages/create-trigger/src/index.ts b/packages/create-trigger/src/index.ts new file mode 100644 index 000000000..e06a2e4d5 --- /dev/null +++ b/packages/create-trigger/src/index.ts @@ -0,0 +1,71 @@ +#!/usr/bin/env node + +import { runCli } from "./cli/index.js"; +import { createProject } from "./utils/createProject.js"; +import { logger } from "./utils/logger.js"; +import { renderTitle } from "./utils/renderTitle.js"; +import { createTemplateRef } from "./utils/templateRef.js"; +import { installDependencies } from "./utils/installDependencies.js"; +import { initializeGit } from "./utils/git.js"; +import { parseNameAndPath } from "./utils/parseNameAndPath.js"; +import { logNextSteps } from "./utils/logNextSteps.js"; +import { createDotEnvFile } from "./utils/createDotEnvFile.js"; +import { sendTelemetry } from "./utils/triggerApi.js"; +import { createTelemetryEvent } from "./utils/createTelemetryEvent.js"; + +const main = async () => { + renderTitle(); + + const cli = await runCli(); + + const repositoryRef = createTemplateRef(cli.templateName); + + const [scopedProjectName, projectDir] = parseNameAndPath( + cli.flags.projectName + ); + + const projectPath = await createProject( + repositoryRef, + projectDir, + scopedProjectName ?? cli.templateName + ); + + if (!projectPath) { + process.exit(1); + } + + if (!cli.flags.noInstall) { + await installDependencies(projectPath); + } + + if (!cli.flags.noGit) { + await initializeGit(projectPath); + } + + await createDotEnvFile(projectPath, cli.flags.apiKey); + + await logNextSteps({ + projectName: projectDir, + noInstall: cli.flags.noInstall, + apiKey: cli.flags.apiKey, + }); + + if (!cli.flags.noTelemetry) { + await sendTelemetry(createTelemetryEvent(cli), cli.flags.apiKey); + } + + process.exit(0); +}; + +main().catch((err) => { + logger.error("Aborting installation..."); + if (err instanceof Error) { + logger.error(err); + } else { + logger.error( + "An unknown error has occurred. Please open an issue on github with the below:" + ); + console.log(err); + } + process.exit(1); +}); diff --git a/packages/create-trigger/src/utils/createDotEnvFile.ts b/packages/create-trigger/src/utils/createDotEnvFile.ts new file mode 100644 index 000000000..1e88b77a7 --- /dev/null +++ b/packages/create-trigger/src/utils/createDotEnvFile.ts @@ -0,0 +1,14 @@ +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/create-trigger/src/utils/createProject.ts b/packages/create-trigger/src/utils/createProject.ts new file mode 100644 index 000000000..2a258bc47 --- /dev/null +++ b/packages/create-trigger/src/utils/createProject.ts @@ -0,0 +1,74 @@ +import path from "node:path"; +import degit from "degit"; +import ora from "ora"; +import chalk from "chalk"; +import fs from "fs-extra"; +import { logger } from "./logger.js"; + +export async function createProject( + repositoryRef: string, + projectDir: string, + projectName: string +) { + const emitter = degit(repositoryRef); + + emitter.on("info", (info) => { + console.log(info.message); + }); + + emitter.on("warn", (warning) => { + console.warn(warning.message); + }); + + const projectPath = path.resolve(process.cwd(), projectDir); + + // If the project directory already exists, log an error and exit + if (fs.existsSync(projectPath)) { + logger.error(`A directory already exists at: ${projectPath}`); + return; + } + + const spinner = ora( + `Copying ${repositoryRef} to: ${projectDir}...\n` + ).start(); + + spinner.start(); + + await emitter.clone(projectPath); + + // Rewrite the package.json file to use the new project name + updatePackageJson(projectName, projectPath); + // Rewrite the README.md file to use the new project name + updateReadme(projectName, projectPath); + // Remove package-lock.json + fs.removeSync(path.resolve(projectPath, "package-lock.json")); + // Remove .env.example + fs.removeSync(path.resolve(projectPath, ".env.example")); + + spinner.succeed( + `${chalk.cyan.bold(projectName)} ${chalk.green("copied successfully!")}\n` + ); + + return projectDir; +} + +function updatePackageJson(projectName: string, projectDir: string) { + const existingPackageJson = fs.readJSONSync( + path.resolve(projectDir, "package.json") + ); + + const newPackageJson = { + ...existingPackageJson, + name: projectName, + }; + + fs.writeJSONSync(path.resolve(projectDir, "package.json"), newPackageJson, { + spaces: 2, + }); +} + +function updateReadme(projectName: string, projectDir: string) { + const existingReadme = fs.readFileSync(path.resolve(projectDir, "README.md")); + + fs.writeFileSync(path.resolve(projectDir, "README.md"), existingReadme); +} diff --git a/packages/create-trigger/src/utils/createTelemetryEvent.ts b/packages/create-trigger/src/utils/createTelemetryEvent.ts new file mode 100644 index 000000000..d8d0f3ab1 --- /dev/null +++ b/packages/create-trigger/src/utils/createTelemetryEvent.ts @@ -0,0 +1,21 @@ +import { CliResults } from "../cli/index.js"; +import { getVersion } from "./getVersion.js"; +import { TelemetryEvent } from "./triggerApi.js"; +import { randomUUID } from "crypto"; + +export function createTelemetryEvent(cli: CliResults): TelemetryEvent { + return { + id: `anon:${randomUUID()}`, + event: "scaffolded template", + properties: { + projectName: cli.flags.projectName, + templateName: cli.templateName, + noInstall: cli.flags.noInstall, + noGit: cli.flags.noGit, + arch: process.arch, + platform: process.platform, + nodeVersion: process.version, + packageVersion: getVersion(), + }, + }; +} diff --git a/packages/create-trigger/src/utils/getUserPkgManager.ts b/packages/create-trigger/src/utils/getUserPkgManager.ts new file mode 100644 index 000000000..47884b235 --- /dev/null +++ b/packages/create-trigger/src/utils/getUserPkgManager.ts @@ -0,0 +1,19 @@ +export type PackageManager = "npm" | "pnpm" | "yarn"; + +export const getUserPkgManager: () => PackageManager = () => { + // This environment variable is set by npm and yarn but pnpm seems less consistent + const userAgent = process.env.npm_config_user_agent; + + if (userAgent) { + if (userAgent.startsWith("yarn")) { + return "yarn"; + } else if (userAgent.startsWith("pnpm")) { + return "pnpm"; + } else { + return "npm"; + } + } else { + // If no user agent is set, assume npm + return "npm"; + } +}; diff --git a/packages/create-trigger/src/utils/getVersion.ts b/packages/create-trigger/src/utils/getVersion.ts new file mode 100644 index 000000000..1a7922d76 --- /dev/null +++ b/packages/create-trigger/src/utils/getVersion.ts @@ -0,0 +1,12 @@ +import { type PackageJson } from "type-fest"; +import path from "path"; +import fs from "fs-extra"; +import { PKG_ROOT } from "../consts.js"; + +export const getVersion = () => { + const packageJsonPath = path.join(PKG_ROOT, "package.json"); + + const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson; + + return packageJsonContent.version ?? "1.0.0"; +}; diff --git a/packages/create-trigger/src/utils/git.ts b/packages/create-trigger/src/utils/git.ts new file mode 100644 index 000000000..31c4ea639 --- /dev/null +++ b/packages/create-trigger/src/utils/git.ts @@ -0,0 +1,137 @@ +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"; + +const isGitInstalled = (dir: string): boolean => { + try { + execSync("git --version", { cwd: dir }); + return true; + } catch (_e) { + return false; + } +}; + +/** @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")); +}; + +/** @returns Whether or not this directory or a parent directory has a `.git` directory. */ +const isInsideGitRepo = async (dir: string): Promise => { + try { + // If this command succeeds, we're inside a git repo + await execa("git", ["rev-parse", "--is-inside-work-tree"], { + cwd: dir, + stdout: "ignore", + }); + return true; + } catch (_e) { + // Else, it will throw a git-error and we return false + return false; + } +}; + +const getGitVersion = () => { + const stdout = execSync("git --version").toString().trim(); + const gitVersionTag = stdout.split(" ")[2]; + const major = gitVersionTag?.split(".")[0]; + const minor = gitVersionTag?.split(".")[1]; + return { major: Number(major), minor: Number(minor) }; +}; + +/** @returns The git config value of "init.defaultBranch". If it is not set, returns "main". */ +const getDefaultBranch = () => { + const stdout = execSync("git config --global init.defaultBranch || echo main") + .toString() + .trim(); + + return stdout; +}; + +// This initializes the Git-repository for the project +export const initializeGit = async (projectDir: string) => { + logger.info("Initializing Git..."); + + if (!isGitInstalled(projectDir)) { + logger.warn("Git is not installed. Skipping Git initialization."); + return; + } + + const spinner = ora("Creating a new git repo...\n").start(); + + const isRoot = isRootGitRepo(projectDir); + const isInside = await isInsideGitRepo(projectDir); + const dirName = path.parse(projectDir).name; // skip full path for logging + + if (isInside && isRoot) { + // Dir is a root git repo + spinner.stop(); + const { overwriteGit } = await inquirer.prompt<{ + overwriteGit: boolean; + }>({ + name: "overwriteGit", + type: "confirm", + message: `${chalk.redBright.bold( + "Warning:" + )} Git is already initialized in "${dirName}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`, + default: false, + }); + if (!overwriteGit) { + spinner.info("Skipping Git initialization."); + return; + } + // Deleting the .git folder + fs.removeSync(path.join(projectDir, ".git")); + } else if (isInside && !isRoot) { + // Dir is inside a git worktree + spinner.stop(); + const { initializeChildGitRepo } = await inquirer.prompt<{ + initializeChildGitRepo: boolean; + }>({ + name: "initializeChildGitRepo", + type: "confirm", + message: `${chalk.redBright.bold( + "Warning:" + )} "${dirName}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`, + default: false, + }); + if (!initializeChildGitRepo) { + spinner.info("Skipping Git initialization."); + return; + } + } + + // We're good to go, initializing the git repo + try { + const branchName = getDefaultBranch(); + + // --initial-branch flag was added in git v2.28.0 + const { major, minor } = getGitVersion(); + if (major < 2 || minor < 28) { + await execa("git", ["init"], { cwd: projectDir }); + await execa("git", ["branch", "-m", branchName], { cwd: projectDir }); + } else { + await execa("git", ["init", `--initial-branch=${branchName}`], { + cwd: projectDir, + }); + } + await execa("git", ["add", "."], { cwd: projectDir }); + spinner.succeed( + `${chalk.green("Successfully initialized and staged")} ${chalk.green.bold( + "git" + )}\n` + ); + } catch (error) { + // Safeguard, should be unreachable + spinner.fail( + `${chalk.bold.red( + "Failed:" + )} could not initialize git. Update git to the latest version!\n` + ); + } +}; diff --git a/packages/create-trigger/src/utils/installDependencies.ts b/packages/create-trigger/src/utils/installDependencies.ts new file mode 100644 index 000000000..f6812ef89 --- /dev/null +++ b/packages/create-trigger/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/packages/create-trigger/src/utils/logNextSteps.ts b/packages/create-trigger/src/utils/logNextSteps.ts new file mode 100644 index 000000000..6e7b941d7 --- /dev/null +++ b/packages/create-trigger/src/utils/logNextSteps.ts @@ -0,0 +1,46 @@ +import { DEFAULT_APP_NAME, TRIGGER_BASE_URL } from "../consts.js"; +import { getUserPkgManager } from "./getUserPkgManager.js"; +import { logger } from "./logger.js"; +import { whoami } from "./triggerApi.js"; + +// This logs the next steps that the user should take in order to advance the project +export async function logNextSteps({ + projectName = DEFAULT_APP_NAME, + noInstall, + apiKey, +}: { + projectName: string; + noInstall: boolean; + apiKey?: string; +}) { + const pkgManager = getUserPkgManager(); + + logger.info("Next steps:"); + projectName !== "." && logger.info(` cd ${projectName}`); + if (noInstall) { + // To reflect yarn's default behavior of installing packages when no additional args provided + if (pkgManager === "yarn") { + logger.info(` ${pkgManager}`); + } else { + logger.info(` ${pkgManager} install`); + } + } + + if (!apiKey) { + logger.info( + ` visit ${TRIGGER_BASE_URL} to get your development API key and update your .env file` + ); + } + + logger.info(` ${pkgManager === "npm" ? "npm run" : pkgManager} dev`); + + if (apiKey) { + const org = await whoami(apiKey); + + if (org) { + logger.info( + ` visit ${TRIGGER_BASE_URL}/orgs/${org.organizationSlug} to see your triggers` + ); + } + } +} diff --git a/packages/create-trigger/src/utils/logger.ts b/packages/create-trigger/src/utils/logger.ts new file mode 100644 index 000000000..e971b8b95 --- /dev/null +++ b/packages/create-trigger/src/utils/logger.ts @@ -0,0 +1,16 @@ +import chalk from "chalk"; + +export const logger = { + error(...args: unknown[]) { + console.log(chalk.red(...args)); + }, + warn(...args: unknown[]) { + console.log(chalk.yellow(...args)); + }, + info(...args: unknown[]) { + console.log(chalk.cyan(...args)); + }, + success(...args: unknown[]) { + console.log(chalk.green(...args)); + }, +}; diff --git a/packages/create-trigger/src/utils/parseNameAndPath.ts b/packages/create-trigger/src/utils/parseNameAndPath.ts new file mode 100644 index 000000000..3b1fb5e5e --- /dev/null +++ b/packages/create-trigger/src/utils/parseNameAndPath.ts @@ -0,0 +1,38 @@ +import pathModule from "path"; + +/** + * Parses the projectName and its path from the user input. + * + * Returns a tuple of of `[projectName, path]`, where `projectName` is the name put in the "package.json" + * file and `path` is the path to the directory where the project will be created. + * + * If `projectName` is ".", the name of the directory will be used instead. Handles the case where the + * input includes a scoped package name in which case that is being parsed as the name, but not + * included as the path. + * + * For example: + * + * - dir/@mono/app => ["@mono/app", "dir/app"] + * - dir/app => ["app", "dir/app"] + */ +export const parseNameAndPath = (input: string) => { + const paths = input.split("/"); + + let projectName = paths[paths.length - 1]; + + // If the user ran `npx create-t3-app .` or similar, the projectName should be the current directory + if (projectName === ".") { + const parsedCwd = pathModule.resolve(process.cwd()); + projectName = pathModule.basename(parsedCwd); + } + + // If the first part is a @, it's a scoped package + const indexOfDelimiter = paths.findIndex((p) => p.startsWith("@")); + if (paths.findIndex((p) => p.startsWith("@")) !== -1) { + projectName = paths.slice(indexOfDelimiter).join("/"); + } + + const path = paths.filter((p) => !p.startsWith("@")).join("/"); + + return [projectName, path] as const; +}; diff --git a/packages/create-trigger/src/utils/renderTitle.ts b/packages/create-trigger/src/utils/renderTitle.ts new file mode 100644 index 000000000..8fde7f985 --- /dev/null +++ b/packages/create-trigger/src/utils/renderTitle.ts @@ -0,0 +1,24 @@ +import gradient from "gradient-string"; +import { TITLE_TEXT } from "../consts.js"; +import { getUserPkgManager } from "./getUserPkgManager.js"; + +// colors brought in from vscode poimandres theme +const poimandresTheme = { + blue: "#add7ff", + cyan: "#89ddff", + green: "#5de4c7", + magenta: "#fae4fc", + red: "#d0679d", + yellow: "#fffac2", +}; + +export const renderTitle = () => { + const triggerGradient = gradient(Object.values(poimandresTheme)); + + // resolves weird behavior where the ascii is offset + const pkgManager = getUserPkgManager(); + if (pkgManager === "yarn" || pkgManager === "pnpm") { + console.log(""); + } + console.log(triggerGradient.multiline(TITLE_TEXT)); +}; diff --git a/packages/create-trigger/src/utils/templateRef.ts b/packages/create-trigger/src/utils/templateRef.ts new file mode 100644 index 000000000..23f812d67 --- /dev/null +++ b/packages/create-trigger/src/utils/templateRef.ts @@ -0,0 +1,5 @@ +import { TEMPLATE_ORGANIZATION } from "../consts.js"; + +export function createTemplateRef(templateName: string): string { + return `github:${TEMPLATE_ORGANIZATION}/${templateName}`; +} diff --git a/packages/create-trigger/src/utils/triggerApi.ts b/packages/create-trigger/src/utils/triggerApi.ts new file mode 100644 index 000000000..5a9e942fc --- /dev/null +++ b/packages/create-trigger/src/utils/triggerApi.ts @@ -0,0 +1,80 @@ +import fetch from "node-fetch"; +import { TRIGGER_BASE_URL } from "../consts.js"; + +export type WhoamiResponse = { + organizationId: number; + env: string; + organizationSlug: string; +}; + +export async function whoami( + apiKey: string +): Promise { + const response = await fetch(`${TRIGGER_BASE_URL}/api/v1/internal/whoami`, { + method: "GET", + headers: { + Accept: "application/json", + Authorization: `Bearer ${apiKey}`, + }, + }); + + if (response.ok) { + return response.json() as Promise; + } + + return; +} + +export type TriggerTemplate = { + id: string; + slug: string; + title: string; + shortTitle: string; + description: string; + imageUrl: string; + repositoryUrl: string; + markdownDocs: string; + runLocalDocs: string; + priority: number; + services: string[]; + workflowIds: string[]; + createdAt: string; + updatedAt: string; +}; + +export async function getTemplates(): Promise> { + const response = await fetch(`${TRIGGER_BASE_URL}/api/v1/templates`, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + if (response.ok) { + return response.json() as Promise>; + } + + return []; +} + +export type TelemetryEvent = { + id: string; + event: string; + properties: Record; +}; + +export async function sendTelemetry(event: TelemetryEvent, apiKey?: string) { + const headers: Record = { + Accept: "application/json", + }; + + if (apiKey) { + headers.Authorization = `Bearer ${apiKey}`; + } + + await fetch(`${TRIGGER_BASE_URL}/api/v1/internal/telemetry`, { + method: "POST", + headers, + body: JSON.stringify(event), + }); +} diff --git a/packages/create-trigger/tsconfig.json b/packages/create-trigger/tsconfig.json new file mode 100644 index 000000000..88dfdb381 --- /dev/null +++ b/packages/create-trigger/tsconfig.json @@ -0,0 +1,49 @@ +{ + "include": ["src", "tsup.config.ts"], + "compilerOptions": { + /* LANGUAGE COMPILATION OPTIONS */ + "target": "ES2020", + "lib": ["DOM", "DOM.Iterable", "ES2020"], + "module": "Node16", + "moduleResolution": "nodenext", + "resolveJsonModule": true, + "allowJs": true, + "checkJs": true, + + /* EMIT RULES */ + "outDir": "./dist", + "noEmit": true, // TSUP takes care of emitting js for us, in a MUCH faster way + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "removeComments": true, + + /* TYPE CHECKING RULES */ + "strict": true, + // "noImplicitAny": true, // Included in "Strict" + // "noImplicitThis": true, // Included in "Strict" + // "strictBindCallApply": true, // Included in "Strict" + // "strictFunctionTypes": true, // Included in "Strict" + // "strictNullChecks": true, // Included in "Strict" + // "strictPropertyInitialization": true, // Included in "Strict" + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "useUnknownInCatchVariables": true, + "noUncheckedIndexedAccess": true, // TLDR - Checking an indexed value (array[0]) now forces type as there is no confirmation that index exists + // THE BELOW ARE EXTRA STRICT OPTIONS THAT SHOULD ONLY BY CONSIDERED IN VERY SAFE PROJECTS + // "exactOptionalPropertyTypes": true, // TLDR - Setting to undefined is not the same as a property not being defined at all + // "noPropertyAccessFromIndexSignature": true, // TLDR - Use dot notation for objects if youre sure it exists, use ['index'] notaion if unsure + + /* OTHER OPTIONS */ + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + // "emitDecoratorMetadata": true, + // "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "useDefineForClassFields": true + } +} diff --git a/packages/create-trigger/tsup.config.ts b/packages/create-trigger/tsup.config.ts new file mode 100644 index 000000000..94937b364 --- /dev/null +++ b/packages/create-trigger/tsup.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "tsup"; + +const isDev = process.env.npm_lifecycle_event === "dev"; + +export default defineConfig({ + clean: true, + dts: true, + entry: ["src/index.ts"], + format: ["esm"], + minify: !isDev, + metafile: !isDev, + sourcemap: true, + target: "esnext", + outDir: "dist", + onSuccess: isDev ? "node dist/index.js" : undefined, +}); From 51f9bc9d72be3abbe3081dce2bf3573d6b637183 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 13:15:05 +0000 Subject: [PATCH 10/59] Added handy links to the SDK when logging information about the workflows --- .changeset/ten-dancers-hang.md | 5 + .../api/v1/internal/workflows/$workflowP.ts | 6 +- apps/webapp/app/routes/api/v1/templates.ts | 10 + .../api/v2/internal/workflows/$workflowP.ts | 66 +++ apps/webapp/app/services/analytics.server.ts | 24 + .../workflows/registerWorkflow.server.ts | 12 +- apps/wss/src/server.ts | 23 +- packages/internal-bridge/src/logger.ts | 6 + .../internal-bridge/src/schemas/server.ts | 38 ++ packages/internal-platform/src/api/client.ts | 24 +- packages/trigger-sdk/package.json | 1 + packages/trigger-sdk/src/client.ts | 54 ++- pnpm-lock.yaml | 418 +++++++++++++++--- 13 files changed, 618 insertions(+), 69 deletions(-) create mode 100644 .changeset/ten-dancers-hang.md create mode 100644 apps/webapp/app/routes/api/v1/templates.ts create mode 100644 apps/webapp/app/routes/api/v2/internal/workflows/$workflowP.ts diff --git a/.changeset/ten-dancers-hang.md b/.changeset/ten-dancers-hang.md new file mode 100644 index 000000000..4673a42dd --- /dev/null +++ b/.changeset/ten-dancers-hang.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": minor +--- + +Added handly links to the dashboard in log feedback diff --git a/apps/webapp/app/routes/api/v1/internal/workflows/$workflowP.ts b/apps/webapp/app/routes/api/v1/internal/workflows/$workflowP.ts index 90d3d62b2..b531ade8b 100644 --- a/apps/webapp/app/routes/api/v1/internal/workflows/$workflowP.ts +++ b/apps/webapp/app/routes/api/v1/internal/workflows/$workflowP.ts @@ -37,8 +37,10 @@ export async function action({ request, params }: ActionArgs) { case "validationError": { return json({ error: result.errors }, { status: 400 }); } - + case "isArchived": { + return json({ id: result.data.id }); + } case "success": - return json(result.data); + return json({ id: result.data.workflow.id }); } } diff --git a/apps/webapp/app/routes/api/v1/templates.ts b/apps/webapp/app/routes/api/v1/templates.ts new file mode 100644 index 000000000..daf95e1b5 --- /dev/null +++ b/apps/webapp/app/routes/api/v1/templates.ts @@ -0,0 +1,10 @@ +import { json } from "@remix-run/server-runtime"; +import { prisma } from "~/db.server"; + +export async function loader() { + const templates = await prisma.template.findMany({ + orderBy: { priority: "asc" }, + }); + + return json(templates); +} diff --git a/apps/webapp/app/routes/api/v2/internal/workflows/$workflowP.ts b/apps/webapp/app/routes/api/v2/internal/workflows/$workflowP.ts new file mode 100644 index 000000000..b0ff58824 --- /dev/null +++ b/apps/webapp/app/routes/api/v2/internal/workflows/$workflowP.ts @@ -0,0 +1,66 @@ +import type { ActionArgs } from "@remix-run/server-runtime"; +import { json } from "@remix-run/server-runtime"; +import { z } from "zod"; +import { env } from "~/env.server"; +import { authenticateApiRequest } from "~/services/apiAuth.server"; +import { RegisterWorkflow } from "~/services/workflows/registerWorkflow.server"; + +// PUT /api/v2/internal/workflows/:workflowP +export async function action({ request, params }: ActionArgs) { + // first make sure this is a PUT request + if (request.method.toUpperCase() !== "PUT") { + return json({ error: "Method not allowed" }, { status: 405 }); + } + + // Next authenticate the request + const authenticatedEnv = await authenticateApiRequest(request); + + if (!authenticatedEnv) { + return json({ error: "Invalid or Missing API key" }, { status: 401 }); + } + + // Now parse the request body + const body = await request.json(); + + // And the params + const { workflowP } = z.object({ workflowP: z.string() }).parse(params); + + const registerWorkflow = new RegisterWorkflow(); + + const result = await registerWorkflow.call( + workflowP, + body, + authenticatedEnv.organization, + authenticatedEnv + ); + + switch (result.status) { + case "validationError": { + return json({ error: result.errors }, { status: 400 }); + } + case "isArchived": { + return json({ error: "Workflow is archived" }, { status: 400 }); + } + case "success": { + const { workflow, environment, organization, isNew } = result.data; + + const data = { + workflow: { + id: workflow.id, + slug: workflow.slug, + }, + environment: { + id: environment.id, + slug: environment.slug, + }, + organization: { + id: organization.id, + slug: organization.slug, + }, + url: `${env.APP_ORIGIN}/orgs/${organization.slug}/workflows/${workflow.slug}`, + }; + + return json(data, { status: isNew ? 201 : 200 }); + } + } +} diff --git a/apps/webapp/app/services/analytics.server.ts b/apps/webapp/app/services/analytics.server.ts index fea63c923..e584f1628 100644 --- a/apps/webapp/app/services/analytics.server.ts +++ b/apps/webapp/app/services/analytics.server.ts @@ -223,6 +223,30 @@ class BehaviouralAnalytics { }, }; + telemetry = { + capture: ({ + userId, + event, + properties, + organizationId, + environmentId, + }: { + userId: string; + event: string; + properties: Record; + organizationId?: string; + environmentId?: string; + }) => { + this.#capture({ + userId, + event, + eventProperties: properties, + organizationId, + environmentId, + }); + }, + }; + #capture(event: CaptureEvent) { if (this.client === undefined) return; let groups: Record = {}; diff --git a/apps/webapp/app/services/workflows/registerWorkflow.server.ts b/apps/webapp/app/services/workflows/registerWorkflow.server.ts index 34dd891e9..8abcffe4b 100644 --- a/apps/webapp/app/services/workflows/registerWorkflow.server.ts +++ b/apps/webapp/app/services/workflows/registerWorkflow.server.ts @@ -7,7 +7,7 @@ import { prisma } from "~/db.server"; import type { Organization } from "~/models/organization.server"; import type { RuntimeEnvironment } from "~/models/runtimeEnvironment.server"; import type { Workflow } from "~/models/workflow.server"; -import { appEventPublisher, taskQueue } from "../messageBroker.server"; +import { taskQueue } from "../messageBroker.server"; export class RegisterWorkflow { #prismaClient: PrismaClient; @@ -31,7 +31,7 @@ export class RegisterWorkflow { }; } - const workflow = await this.#upsertWorkflow( + const { workflow, isNew } = await this.#upsertWorkflow( slug, validation.data, organization @@ -39,7 +39,7 @@ export class RegisterWorkflow { if (workflow.isArchived) { return { - status: "success" as const, + status: "isArchived" as const, data: { id: workflow.id }, }; } @@ -73,7 +73,7 @@ export class RegisterWorkflow { return { status: "success" as const, - data: { id: workflow.id }, + data: { workflow, environment, organization, isNew }, }; } @@ -172,9 +172,11 @@ export class RegisterWorkflow { await taskQueue.publish("WORKFLOW_CREATED", { id: workflow.id, }); + + return { workflow, isNew: true }; } - return workflow; + return { workflow, isNew: false }; } async upsertSource( diff --git a/apps/wss/src/server.ts b/apps/wss/src/server.ts index 1ea28275b..8ee80de9b 100644 --- a/apps/wss/src/server.ts +++ b/apps/wss/src/server.ts @@ -283,9 +283,9 @@ export class TriggerServer { }, INITIALIZE_HOST: async (data) => { // Initialize workflow - const success = await this.#initializeWorkflow(data); + const response = await this.#initializeWorkflow(data); - if (success) { + if (response) { return { type: "success" as const }; } else { return { @@ -294,6 +294,19 @@ export class TriggerServer { }; } }, + INITIALIZE_HOST_V2: async (data) => { + // Initialize workflow + const response = await this.#initializeWorkflow(data); + + if (response) { + return { type: "success" as const, data: response }; + } else { + return { + type: "error" as const, + message: "Failed to connect to the Pulsar cluster", + }; + } + }, }, }); @@ -342,7 +355,7 @@ export class TriggerServer { } async #initializeWorkflow( - data: z.infer<(typeof ServerRPCSchema)["INITIALIZE_HOST"]["request"]> + data: z.infer<(typeof ServerRPCSchema)["INITIALIZE_HOST_V2"]["request"]> ) { if (this.#isInitialized) { throw new Error( @@ -373,7 +386,7 @@ export class TriggerServer { triggerTTL: data.triggerTTL, }); - this.#workflowId = response.id; + this.#workflowId = response.workflow.id; this.#logger.debug("Initializing trigger subscriber..."); @@ -487,7 +500,7 @@ export class TriggerServer { this.#isInitialized = true; - return true; + return response; } catch (error) { if (error instanceof ZodError) { this.#logger.error( diff --git a/packages/internal-bridge/src/logger.ts b/packages/internal-bridge/src/logger.ts index 313faa2ca..cc02e534a 100644 --- a/packages/internal-bridge/src/logger.ts +++ b/packages/internal-bridge/src/logger.ts @@ -38,6 +38,12 @@ export class Logger { console.log(`${this.#formatName()} `, ...[...args, ...this.#formatTags()]); } + logClean(...args: any[]) { + if (this.#level < 1) return; + + console.log(`${this.#formatName()} `, ...args); + } + error(...args: any[]) { if (this.#level < 2) return; diff --git a/packages/internal-bridge/src/schemas/server.ts b/packages/internal-bridge/src/schemas/server.ts index 1579cf084..8698786a4 100644 --- a/packages/internal-bridge/src/schemas/server.ts +++ b/packages/internal-bridge/src/schemas/server.ts @@ -86,6 +86,44 @@ export const ServerRPCSchema = { ]) .nullable(), }, + INITIALIZE_HOST_V2: { + request: z.object({ + apiKey: z.string(), + workflowId: z.string(), + workflowName: z.string(), + trigger: TriggerMetadataSchema, + packageVersion: z.string(), + packageName: z.string(), + triggerTTL: z.number().optional(), + }), + response: z + .discriminatedUnion("type", [ + z.object({ + type: z.literal("success"), + data: z.object({ + workflow: z.object({ + id: z.string(), + slug: z.string(), + }), + environment: z.object({ + id: z.string(), + slug: z.string(), + }), + organization: z.object({ + id: z.string(), + slug: z.string(), + }), + isNew: z.boolean(), + url: z.string(), + }), + }), + z.object({ + type: z.literal("error"), + message: z.string(), + }), + ]) + .nullable(), + }, START_WORKFLOW_RUN: { request: z.object({ runId: z.string(), diff --git a/packages/internal-platform/src/api/client.ts b/packages/internal-platform/src/api/client.ts index 4b1c1e724..7e09cdd53 100644 --- a/packages/internal-platform/src/api/client.ts +++ b/packages/internal-platform/src/api/client.ts @@ -6,11 +6,13 @@ import { Logger } from "../logger"; export class InternalApiClient { #apiKey: string; #baseUrl: string; + #v2BaseUrl: string; #logger: Logger; constructor(apiKey: string, baseUrl: string) { this.#apiKey = apiKey; this.#baseUrl = `${baseUrl}/api/v1/internal`; + this.#v2BaseUrl = `${baseUrl}/api/v2/internal`; this.#logger = new Logger("trigger.dev [internal-api]"); } @@ -18,6 +20,7 @@ export class InternalApiClient { const ResponseSchema = z.object({ organizationId: z.string(), env: z.string(), + organizationSlug: z.string(), }); const Response401Schema = z.object({ @@ -61,14 +64,26 @@ export class InternalApiClient { async registerWorkflow(workflow: WorkflowMetadata) { const responseSchema = z.object({ - id: z.string(), + workflow: z.object({ + id: z.string(), + slug: z.string(), + }), + environment: z.object({ + id: z.string(), + slug: z.string(), + }), + organization: z.object({ + id: z.string(), + slug: z.string(), + }), + url: z.string(), }); const validationResponseSchema = z.object({ error: z.string(), }); - const response = await fetch(this.#apiUrl(`/workflows/${workflow.id}`), { + const response = await fetch(this.#v2ApiUrl(`/workflows/${workflow.id}`), { method: "PUT", headers: this.#headers({ "Content-Type": "application/json" }), body: JSON.stringify(workflow), @@ -77,7 +92,9 @@ export class InternalApiClient { if (response.ok) { const rawBody = await response.json(); - return responseSchema.parse(rawBody); + const body = responseSchema.parse(rawBody); + + return { ...body, isNew: response.status === 201 }; } if (response.status === 400) { @@ -123,6 +140,7 @@ export class InternalApiClient { } #apiUrl = (path: string) => `${this.#baseUrl}${path}`; + #v2ApiUrl = (path: string) => `${this.#v2BaseUrl}${path}`; #headers = (additionalHeaders?: Record) => ({ Accept: "application/json", Authorization: `Bearer ${this.#apiKey}`, diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index d3d570169..8d7465762 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -41,6 +41,7 @@ "evt": "^2.4.13", "node-fetch": "2.6.x", "slug": "^6.0.0", + "terminal-link": "^3.0.0", "ulid": "^2.3.0", "uuid": "^9.0.0", "ws": "^8.11.0", diff --git a/packages/trigger-sdk/src/client.ts b/packages/trigger-sdk/src/client.ts index 0425b2143..a52da176d 100644 --- a/packages/trigger-sdk/src/client.ts +++ b/packages/trigger-sdk/src/client.ts @@ -15,6 +15,7 @@ import { ContextLogger } from "./logger"; import { Trigger, TriggerOptions } from "./trigger"; import { TriggerContext, TriggerFetch } from "./types"; import { generateErrorMessage, ErrorMessageOptions } from "zod-error"; +import terminalLink from "terminal-link"; const zodErrorMessageOptions: ErrorMessageOptions = { delimiter: { @@ -39,6 +40,23 @@ export class TriggerClient { #logger: Logger; #closedByUser = false; + #registerResponse?: { + workflow: { + id: string; + slug: string; + }; + environment: { + id: string; + slug: string; + }; + organization: { + id: string; + slug: string; + }; + isNew: boolean; + url: string; + }; + #responseCompleteCallbacks = new Map< string, { @@ -100,7 +118,25 @@ export class TriggerClient { this.#initializeRPC(); await this.#initializeHost(); - this.#logger.log(`✨ Connected and listening for events`); + if (this.#registerResponse?.isNew) { + this.#logger.logClean( + `πŸŽ‰ Successfully registered "${ + this.#trigger.name + }" to trigger.dev πŸ‘‰ ${terminalLink( + "View on dashboard", + this.#registerResponse.url, + { fallback: (text, url) => `${text}: (${url})` } + )}. Listening for events...` + ); + } else { + this.#logger.log( + `✨ Connected and listening for events πŸ‘‰ ${terminalLink( + "View on dashboard", + this.#registerResponse!.url, + { fallback: (text, url) => `${text}: (${url})` } + )}` + ); + } } catch (error) { this.#logger.log(`🚩 Could not connect to trigger.dev`); @@ -599,7 +635,13 @@ export class TriggerClient { return this.#trigger.options .run(eventData, ctx) .then((output) => { - this.#logger.log(`Run ${data.id} complete πŸƒ`); + this.#logger.log( + `Run ${data.id} complete πŸ‘‰ ${terminalLink( + "View on dashboard", + `${this.#registerResponse!.url}/runs/${data.id}`, + { fallback: (text, url) => `${text}: (${url})` } + )}` + ); return serverRPC.send("COMPLETE_WORKFLOW_RUN", { runId: data.id, @@ -676,7 +718,7 @@ export class TriggerClient { throw new Error("Cannot initialize host without an RPC connection"); } - const response = await this.#send("INITIALIZE_HOST", { + const response = await this.#send("INITIALIZE_HOST_V2", { apiKey: this.#apiKey, workflowId: this.#trigger.id, workflowName: this.#trigger.name, @@ -686,10 +728,16 @@ export class TriggerClient { triggerTTL: this.#options.triggerTTL, }); + if (!response) { + throw new Error("Could not initialize workflow with server"); + } + if (response?.type === "error") { throw new Error(response.message); } + this.#registerResponse = response.data; + this.#logger.debug("Successfully initialized workflow with server"); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce0db98bd..7274d0137 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,13 +92,13 @@ importers: '@types/json-pointer': 1.0.31 '@types/morgan': 1.9.4 '@types/node': 18.14.0 - '@typescript-eslint/eslint-plugin': 5.53.0_bzepuo66bcyj4mepwnxofjvdli + '@typescript-eslint/eslint-plugin': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm concurrently: 7.6.0 dotenv: 16.0.3 eslint: 8.34.0 eslint-config-prettier: 8.6.0_eslint@8.34.0 eslint-config-standard-with-typescript: 34.0.0_e4cqfx33t3lusso5bte4gguj3u - eslint-plugin-import: 2.27.5_zycheyzypw6s5ouujsf5akzhsy + eslint-plugin-import: 2.27.5_eslint@8.34.0 eslint-plugin-n: 15.6.1_eslint@8.34.0 eslint-plugin-promise: 6.1.1_eslint@8.34.0 nock: 13.3.0 @@ -287,7 +287,7 @@ importers: '@aws-sdk/client-s3': 3.245.0 '@aws-sdk/s3-request-presigner': 3.245.0 '@cfworker/json-schema': 1.12.5 - '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde + '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-json': 6.0.1 @@ -325,7 +325,7 @@ importers: '@trigger.dev/slack': link:../../integrations/slack '@trigger.dev/whatsapp': link:../../integrations/whatsapp '@typeform/embed-react': 2.14.1_react@18.2.0 - '@uiw/react-codemirror': 4.19.5_aguurb4bmecpxzejz52amioxne + '@uiw/react-codemirror': 4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle bcryptjs: 2.4.3 classnames: 2.3.2 clsx: 1.2.1 @@ -844,7 +844,7 @@ importers: tsup: ^6.5.0 zod: ^3.20.2 dependencies: - '@react-email/render': 0.0.3_react@18.2.0 + '@react-email/render': 0.0.3 debug: 4.3.4 zod: 3.20.2 devDependencies: @@ -955,6 +955,51 @@ importers: '@types/node': 16.18.11 typescript: 4.9.4 + packages/create-trigger: + specifiers: + '@types/degit': ^2.8.3 + '@types/fs-extra': ^11.0.1 + '@types/gradient-string': ^1.1.2 + '@types/inquirer': ^9.0.3 + '@types/node': '16' + '@types/node-fetch': ^2.6.2 + chalk: ^5.2.0 + commander: ^9.4.1 + degit: ^2.8.4 + execa: ^7.0.0 + fs-extra: ^11.1.0 + gradient-string: ^2.0.2 + inquirer: ^9.1.4 + node-fetch: ^3.3.0 + ora: ^6.1.2 + rimraf: ^3.0.2 + terminal-link: ^3.0.0 + tsup: ^6.5.0 + type-fest: ^3.6.0 + typescript: ^4.9.5 + dependencies: + '@types/degit': 2.8.3 + chalk: 5.2.0 + commander: 9.5.0 + degit: 2.8.4 + execa: 7.0.0 + fs-extra: 11.1.0 + gradient-string: 2.0.2 + inquirer: 9.1.4 + node-fetch: 3.3.0 + ora: 6.1.2 + terminal-link: 3.0.0 + devDependencies: + '@types/fs-extra': 11.0.1 + '@types/gradient-string': 1.1.2 + '@types/inquirer': 9.0.3 + '@types/node': 16.18.11 + '@types/node-fetch': 2.6.2 + rimraf: 3.0.2 + tsup: 6.6.3_typescript@4.9.5 + type-fest: 3.6.0 + typescript: 4.9.5 + packages/emails: specifiers: '@react-email/button': ^0.0.4 @@ -1148,6 +1193,7 @@ importers: node-fetch: 2.6.x rimraf: ^3.0.2 slug: ^6.0.0 + terminal-link: ^3.0.0 tsup: ^6.5.0 tsx: ^3.12.1 ulid: ^2.3.0 @@ -1161,6 +1207,7 @@ importers: evt: 2.4.13 node-fetch: 2.6.7 slug: 6.1.0 + terminal-link: 3.0.0 ulid: 2.3.0 uuid: 9.0.0 ws: 8.12.0 @@ -3784,13 +3831,12 @@ packages: prettier: 2.8.2 dev: false - /@codemirror/autocomplete/6.4.0_eo6pz6bvsllvatnnwfprpuflde: + /@codemirror/autocomplete/6.4.0_czcfkg2f66rxeiodoti7r2gulu: resolution: {integrity: sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==} peerDependencies: '@codemirror/language': ^6.0.0 '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 - '@lezer/common': ^1.0.0 dependencies: '@codemirror/language': 6.3.2 '@codemirror/state': 6.2.0 @@ -3810,7 +3856,7 @@ packages: /@codemirror/lang-javascript/6.1.2: resolution: {integrity: sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==} dependencies: - '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde + '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 '@codemirror/state': 6.2.0 @@ -5523,6 +5569,16 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /@react-email/render/0.0.3: + resolution: {integrity: sha512-+4eOrLGdTCJjoJU3PunekErjo3PFnhSDFjVINBHrfJT+1wlVdhWfDo7hFdzXJx/JOOYqmnZTilU/umGLdRumKQ==} + engines: {node: '>=18.0.0'} + dependencies: + pretty: 2.0.0 + react-dom: 18.2.0 + transitivePeerDependencies: + - react + dev: false + /@react-email/render/0.0.3_react@18.2.0: resolution: {integrity: sha512-+4eOrLGdTCJjoJU3PunekErjo3PFnhSDFjVINBHrfJT+1wlVdhWfDo7hFdzXJx/JOOYqmnZTilU/umGLdRumKQ==} engines: {node: '>=18.0.0'} @@ -5638,7 +5694,7 @@ packages: eslint: 8.31.0 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 3.5.3_vz4tyq5r7fh66imfi352lmrvhq - eslint-plugin-import: 2.27.5_qdjeohovcytra7xto5vgmxssaq + eslint-plugin-import: 2.27.5_2ac3tknkazjoq5fxmuugu665ny eslint-plugin-jest: 26.9.0_y6565ziejixavcuubgd3r7fqr4 eslint-plugin-jest-dom: 4.0.3_eslint@8.31.0 eslint-plugin-jsx-a11y: 6.7.1_eslint@8.31.0 @@ -6354,6 +6410,10 @@ packages: '@types/ms': 0.7.31 dev: true + /@types/degit/2.8.3: + resolution: {integrity: sha512-CL7y71j2zaDmtPLD5Xq5S1Gv2dFoHl0/GBZm6s39Mj/ls28L3NzAOqf7H4H0/2TNVMgMjMVf9CAFYSjmXhi3bw==} + dev: false + /@types/eslint/8.4.10: resolution: {integrity: sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==} dependencies: @@ -6400,12 +6460,25 @@ packages: '@types/node': 18.14.0 dev: true + /@types/fs-extra/11.0.1: + resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} + dependencies: + '@types/jsonfile': 6.1.1 + '@types/node': 18.14.0 + dev: true + /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 '@types/node': 18.14.0 + /@types/gradient-string/1.1.2: + resolution: {integrity: sha512-zIet2KvHr2dkOCPI5ggQQ+WJVyfBSFaqK9sNelhgDjlE2K3Fu2muuPJwu5aKM3xoWuc3WXudVEMUwI1QWhykEQ==} + dependencies: + '@types/tinycolor2': 1.4.3 + dev: true + /@types/hast/2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} dependencies: @@ -6420,6 +6493,13 @@ packages: resolution: {integrity: sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w==} dev: true + /@types/inquirer/9.0.3: + resolution: {integrity: sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==} + dependencies: + '@types/through': 0.0.30 + rxjs: 7.8.0 + dev: true + /@types/is-ci/3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: @@ -6468,6 +6548,12 @@ 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': 18.14.0 + dev: true + /@types/jsonwebtoken/9.0.1: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: @@ -6671,6 +6757,15 @@ packages: '@types/jest': 29.2.5 dev: true + /@types/through/0.0.30: + resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} + dependencies: + '@types/node': 18.14.0 + dev: true + + /@types/tinycolor2/1.4.3: + resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==} + /@types/unist/2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true @@ -6731,7 +6826,7 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin/5.53.0_bzepuo66bcyj4mepwnxofjvdli: + /@typescript-eslint/eslint-plugin/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm: resolution: {integrity: sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -6742,7 +6837,6 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_7kw3g6rralp5ps6mg3uyzz6azm '@typescript-eslint/scope-manager': 5.53.0 '@typescript-eslint/type-utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm '@typescript-eslint/utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm @@ -7005,18 +7099,17 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@uiw/codemirror-extensions-basic-setup/4.19.5_tbeldtdcrf45b35pezgkzq2u4e: + /@uiw/codemirror-extensions-basic-setup/4.19.5_wd2tsis3in55bkaiwnc2c46tom: resolution: {integrity: sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' '@codemirror/search': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' dependencies: - '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde + '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 @@ -7025,14 +7118,11 @@ packages: '@codemirror/view': 6.7.2 dev: false - /@uiw/react-codemirror/4.19.5_aguurb4bmecpxzejz52amioxne: + /@uiw/react-codemirror/4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle: resolution: {integrity: sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==} peerDependencies: - '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: @@ -7041,14 +7131,13 @@ packages: '@codemirror/state': 6.2.0 '@codemirror/theme-one-dark': 6.1.0 '@codemirror/view': 6.7.2 - '@uiw/codemirror-extensions-basic-setup': 4.19.5_tbeldtdcrf45b35pezgkzq2u4e - codemirror: 6.0.1_@lezer+common@1.0.2 + '@uiw/codemirror-extensions-basic-setup': 4.19.5_wd2tsis3in55bkaiwnc2c46tom + codemirror: 6.0.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 transitivePeerDependencies: - '@codemirror/autocomplete' - '@codemirror/language' - - '@codemirror/lint' - '@codemirror/search' dev: false @@ -7261,6 +7350,20 @@ packages: type-fest: 0.21.3 dev: true + /ansi-escapes/5.0.0: + resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==} + engines: {node: '>=12'} + dependencies: + type-fest: 1.4.0 + dev: false + + /ansi-escapes/6.0.0: + resolution: {integrity: sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 3.6.0 + dev: false + /ansi-regex/2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} @@ -7273,7 +7376,6 @@ packages: /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: true /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -7295,7 +7397,6 @@ packages: /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: true /ansicolors/0.3.2: resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} @@ -7751,6 +7852,14 @@ packages: inherits: 2.0.4 readable-stream: 3.6.0 + /bl/5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false + /blob-util/2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} dev: true @@ -7879,6 +7988,13 @@ packages: base64-js: 1.5.1 ieee754: 1.2.1 + /buffer/6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /buffers/0.1.1: resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} engines: {node: '>=0.2.0'} @@ -8135,6 +8251,11 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk/5.2.0: + resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + /character-entities-html4/2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} dev: true @@ -8234,6 +8355,13 @@ packages: dependencies: restore-cursor: 3.1.0 + /cli-cursor/4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: false + /cli-progress/3.11.2: resolution: {integrity: sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA==} engines: {node: '>=4'} @@ -8307,6 +8435,11 @@ packages: engines: {node: '>= 10'} dev: true + /cli-width/4.0.0: + resolution: {integrity: sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==} + engines: {node: '>= 12'} + dev: false + /client-only/0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false @@ -8378,18 +8511,16 @@ packages: engines: {node: '>=0.10.0'} dev: false - /codemirror/6.0.1_@lezer+common@1.0.2: + /codemirror/6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: - '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde + '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 '@codemirror/search': 6.2.3 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 - transitivePeerDependencies: - - '@lezer/common' dev: false /collection-visit/1.0.0: @@ -8454,7 +8585,6 @@ packages: /commander/9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - dev: true /common-tags/1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} @@ -9016,6 +9146,12 @@ packages: vm2: 3.9.13 dev: true + /degit/2.8.4: + resolution: {integrity: sha512-vqYuzmSA5I50J882jd+AbAhQtgK6bdKUJIex1JNfEUPENCgYsxugzKVZlFyMwV4i06MmnV47/Iqi5Io86zf3Ng==} + engines: {node: '>=8.0.0'} + hasBin: true + dev: false + /delayed-stream/1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -9180,7 +9316,6 @@ packages: /eastasianwidth/0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true /ecc-jsbn/0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} @@ -9224,7 +9359,6 @@ packages: /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true /emojis-list/3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} @@ -9699,6 +9833,11 @@ packages: engines: {node: '>=10'} dev: true + /escape-string-regexp/5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: false + /escodegen/1.14.3: resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} engines: {node: '>=4.0'} @@ -9740,11 +9879,11 @@ packages: eslint-plugin-promise: ^6.0.0 typescript: '*' dependencies: - '@typescript-eslint/eslint-plugin': 5.53.0_bzepuo66bcyj4mepwnxofjvdli + '@typescript-eslint/eslint-plugin': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm '@typescript-eslint/parser': 5.48.1_7kw3g6rralp5ps6mg3uyzz6azm eslint: 8.34.0 eslint-config-standard: 17.0.0_rwq7hzy2vtlwiajbw6pmw3rkzy - eslint-plugin-import: 2.27.5_zycheyzypw6s5ouujsf5akzhsy + eslint-plugin-import: 2.27.5_eslint@8.34.0 eslint-plugin-n: 15.6.1_eslint@8.34.0 eslint-plugin-promise: 6.1.1_eslint@8.34.0 typescript: 4.9.5 @@ -9761,7 +9900,7 @@ packages: eslint-plugin-promise: ^6.0.0 dependencies: eslint: 8.34.0 - eslint-plugin-import: 2.27.5_zycheyzypw6s5ouujsf5akzhsy + eslint-plugin-import: 2.27.5_eslint@8.34.0 eslint-plugin-n: 15.6.1_eslint@8.34.0 eslint-plugin-promise: 6.1.1_eslint@8.34.0 dev: true @@ -9804,7 +9943,7 @@ packages: debug: 4.3.4 enhanced-resolve: 5.12.0 eslint: 8.31.0 - eslint-plugin-import: 2.27.5_qdjeohovcytra7xto5vgmxssaq + eslint-plugin-import: 2.27.5_2ac3tknkazjoq5fxmuugu665ny get-tsconfig: 4.3.0 globby: 13.1.3 is-core-module: 2.11.0 @@ -9814,7 +9953,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_fqawsowff3yxll7f25e3byprdq: + /eslint-module-utils/2.7.4_eyqnu5kib2hfrvsonwfdq4ojse: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -9835,7 +9974,6 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_7kw3g6rralp5ps6mg3uyzz6azm debug: 3.2.7 eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 @@ -9843,7 +9981,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_sqt5xxn4ciiurbqrzlaarm6ama: + /eslint-module-utils/2.7.4_v73lhamtbyinynmwa5fn7kpmfq: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -9868,6 +10006,7 @@ packages: debug: 3.2.7 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 + eslint-import-resolver-typescript: 3.5.3_vz4tyq5r7fh66imfi352lmrvhq transitivePeerDependencies: - supports-color dev: true @@ -9903,7 +10042,7 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-import/2.27.5_qdjeohovcytra7xto5vgmxssaq: + /eslint-plugin-import/2.27.5_2ac3tknkazjoq5fxmuugu665ny: resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -9921,7 +10060,7 @@ packages: doctrine: 2.1.0 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_sqt5xxn4ciiurbqrzlaarm6ama + eslint-module-utils: 2.7.4_v73lhamtbyinynmwa5fn7kpmfq has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -9936,7 +10075,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.27.5_zycheyzypw6s5ouujsf5akzhsy: + /eslint-plugin-import/2.27.5_eslint@8.34.0: resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -9946,7 +10085,6 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_7kw3g6rralp5ps6mg3uyzz6azm array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 @@ -9954,7 +10092,7 @@ packages: doctrine: 2.1.0 eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_fqawsowff3yxll7f25e3byprdq + eslint-module-utils: 2.7.4_eyqnu5kib2hfrvsonwfdq4ojse has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -10464,6 +10602,21 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + /execa/7.0.0: + resolution: {integrity: sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: false + /executable/4.1.1: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} engines: {node: '>=4'} @@ -10727,6 +10880,14 @@ packages: escape-string-regexp: 1.0.5 dev: true + /figures/5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + dev: false + /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -10968,6 +11129,15 @@ packages: universalify: 2.0.0 dev: true + /fs-extra/11.1.0: + resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: false + /fs-extra/7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -11379,6 +11549,14 @@ packages: /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + /gradient-string/2.0.2: + resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + tinygradient: 1.1.5 + dev: false + /grapheme-splitter/1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} @@ -11639,6 +11817,11 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + /human-signals/4.3.0: + resolution: {integrity: sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==} + engines: {node: '>=14.18.0'} + dev: false + /humanize-duration/3.27.3: resolution: {integrity: sha512-iimHkHPfIAQ8zCDQLgn08pRqSVioyWvnGfaQ8gond2wf7Jq2jJ+24ykmnRyiz3fIldcn4oUuQXpjqKLhSVR7lw==} dev: false @@ -11743,6 +11926,27 @@ packages: wrap-ansi: 7.0.0 dev: true + /inquirer/9.1.4: + resolution: {integrity: sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 6.0.0 + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-width: 4.0.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 6.1.2 + run-async: 2.4.1 + rxjs: 7.8.0 + string-width: 5.1.2 + strip-ansi: 7.0.1 + through: 2.3.8 + wrap-ansi: 8.1.0 + dev: false + /internal-slot/1.0.4: resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} engines: {node: '>= 0.4'} @@ -12004,6 +12208,11 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + /is-interactive/2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: false + /is-invalid-path/0.1.0: resolution: {integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==} engines: {node: '>=0.10.0'} @@ -12119,6 +12328,11 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + /is-stream/3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -12156,6 +12370,11 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + /is-unicode-supported/1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: false + /is-valid-path/0.1.1: resolution: {integrity: sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==} engines: {node: '>=0.10.0'} @@ -12557,7 +12776,6 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.10 - dev: true /jsonpath-plus/5.1.0: resolution: {integrity: sha512-890w2Pjtj0iswAxalRlt2kHthi6HKrXEfZcn+ZNZptv7F3rUGIeDuZo+C+h4vXBHLEsVjJrHeCm35nYeZLzSBQ==} @@ -12850,6 +13068,14 @@ packages: chalk: 4.1.2 is-unicode-supported: 0.1.0 + /log-symbols/5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.2.0 + is-unicode-supported: 1.3.0 + dev: false + /log-update/4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -13532,6 +13758,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-fn/4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: false + /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -13725,7 +13956,6 @@ packages: /mute-stream/0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - dev: true /mz/2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -13935,6 +14165,13 @@ packages: dependencies: path-key: 3.1.1 + /npm-run-path/5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: false + /npmlog/4.1.2: resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} dependencies: @@ -14117,6 +14354,13 @@ packages: dependencies: mimic-fn: 2.1.0 + /onetime/6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: false + /ono/4.0.11: resolution: {integrity: sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==} dependencies: @@ -14174,6 +14418,21 @@ packages: strip-ansi: 6.0.1 wcwidth: 1.0.1 + /ora/6.1.2: + resolution: {integrity: sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-spinners: 2.7.0 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + strip-ansi: 7.0.1 + wcwidth: 1.0.1 + dev: false + /os-tmpdir/1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -14429,6 +14688,11 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + /path-key/4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: false + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -14968,6 +15232,15 @@ packages: shallow-equal: 1.2.1 dev: false + /react-dom/18.2.0: + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + scheduler: 0.23.0 + dev: false + /react-dom/18.2.0_react@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -15550,6 +15823,14 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 + /restore-cursor/4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: false + /ret/0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} engines: {node: '>=0.12'} @@ -15643,7 +15924,6 @@ packages: /run-async/2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} - dev: true /run-exclusive/2.2.18: resolution: {integrity: sha512-TXr1Gkl1iEAOCCpBTRm/2m0+1KGjORcWpZZ+VGGTe7dYX8E4y8/fMvrHk0zf+kclec2R//tpvdBxgG0bDgaJfw==} @@ -15660,7 +15940,6 @@ packages: resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} dependencies: tslib: 2.4.1 - dev: true /sade/1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} @@ -16201,7 +16480,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.0.1 - dev: true /string.prototype.matchall/4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} @@ -16278,7 +16556,6 @@ packages: engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: true /strip-bom/3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -16288,6 +16565,11 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + /strip-final-newline/3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: false + /strip-indent/3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -16371,7 +16653,6 @@ packages: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - dev: true /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -16518,6 +16799,14 @@ packages: engines: {node: '>=8'} dev: false + /terminal-link/3.0.0: + resolution: {integrity: sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==} + engines: {node: '>=12'} + dependencies: + ansi-escapes: 5.0.0 + supports-hyperlinks: 2.3.0 + dev: false + /test-exclude/6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -16565,7 +16854,6 @@ packages: /through/2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true /through2/2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -16595,6 +16883,17 @@ packages: resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} dev: true + /tinycolor2/1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + dev: false + + /tinygradient/1.1.5: + resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==} + dependencies: + '@types/tinycolor2': 1.4.3 + tinycolor2: 1.6.0 + dev: false + /tinypool/0.3.0: resolution: {integrity: sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==} engines: {node: '>=14.0.0'} @@ -17102,10 +17401,19 @@ packages: engines: {node: '>=8'} dev: false + /type-fest/1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: false + /type-fest/2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + /type-fest/3.6.0: + resolution: {integrity: sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==} + engines: {node: '>=14.16'} + /type-is/1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -17299,7 +17607,6 @@ packages: /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - dev: true /unload/2.2.0: resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} @@ -17997,6 +18304,15 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 + /wrap-ansi/8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.0.1 + dev: false + /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} From 26e69cb659015900b7fbe9f9303d82ffb5e8556c Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 13:15:46 +0000 Subject: [PATCH 11/59] Added cli changeset --- .changeset/new-students-double.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/new-students-double.md diff --git a/.changeset/new-students-double.md b/.changeset/new-students-double.md new file mode 100644 index 000000000..74db5e6dc --- /dev/null +++ b/.changeset/new-students-double.md @@ -0,0 +1,5 @@ +--- +"create-trigger": minor +--- + +Easily scaffold out standalone trigger.dev projects using create-trigger and our templates From b2f14cbea35e923fab88b2a72286efbaa16af94c Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 13:29:08 +0000 Subject: [PATCH 12/59] Make the SDK a patch --- .changeset/ten-dancers-hang.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/ten-dancers-hang.md b/.changeset/ten-dancers-hang.md index 4673a42dd..26de4246a 100644 --- a/.changeset/ten-dancers-hang.md +++ b/.changeset/ten-dancers-hang.md @@ -1,5 +1,5 @@ --- -"@trigger.dev/sdk": minor +"@trigger.dev/sdk": patch --- Added handly links to the dashboard in log feedback From aa295134ff9b87d26dfc6fd558cd860bd5d61451 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 13:29:36 +0000 Subject: [PATCH 13/59] Changeset: prerelease the next version --- .changeset/pre.json | 31 ++++++++++++++++++++ generated-integrations/airtable/CHANGELOG.md | 8 +++++ generated-integrations/airtable/package.json | 2 +- generated-integrations/sendgrid/CHANGELOG.md | 8 +++++ generated-integrations/sendgrid/package.json | 2 +- integrations/github/CHANGELOG.md | 8 +++++ integrations/github/package.json | 2 +- integrations/resend/CHANGELOG.md | 8 +++++ integrations/resend/package.json | 2 +- integrations/shopify/CHANGELOG.md | 8 +++++ integrations/shopify/package.json | 2 +- integrations/slack/CHANGELOG.md | 8 +++++ integrations/slack/package.json | 2 +- integrations/whatsapp/CHANGELOG.md | 8 +++++ integrations/whatsapp/package.json | 2 +- packages/create-trigger/CHANGELOG.md | 7 +++++ packages/create-trigger/package.json | 2 +- packages/trigger-sdk/CHANGELOG.md | 7 +++++ packages/trigger-sdk/package.json | 2 +- 19 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 .changeset/pre.json create mode 100644 packages/create-trigger/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000..cfd2fa188 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,31 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "integrations": "1.0.0", + "webapp": "1.0.0", + "wss": "1.0.0", + "@trigger.dev/airtable": "0.1.22", + "@trigger.dev/sendgrid": "0.1.22", + "@trigger.dev/github": "0.1.22", + "@trigger.dev/resend": "0.1.22", + "@trigger.dev/shopify": "0.1.22", + "@trigger.dev/slack": "0.1.22", + "@trigger.dev/whatsapp": "0.1.20", + "@trigger.dev/common-schemas": "0.1.1", + "create-trigger": "0.1.0", + "emails": "1.0.0", + "integration-catalog": "0.1.16", + "@trigger.dev/integration-sdk": "0.1.17", + "internal-bridge": "0.0.1", + "internal-cli": "0.0.1", + "internal-platform": "0.0.3", + "internal-pulsar": "0.0.1", + "@trigger.dev/sdk": "0.2.15" + }, + "changesets": [ + "big-apples-reflect", + "new-students-double", + "ten-dancers-hang" + ] +} diff --git a/generated-integrations/airtable/CHANGELOG.md b/generated-integrations/airtable/CHANGELOG.md index 4d88de699..6c1fea1e5 100644 --- a/generated-integrations/airtable/CHANGELOG.md +++ b/generated-integrations/airtable/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/airtable +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/generated-integrations/airtable/package.json b/generated-integrations/airtable/package.json index 9ed13da90..73433fb24 100644 --- a/generated-integrations/airtable/package.json +++ b/generated-integrations/airtable/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/airtable", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official Airtable integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/generated-integrations/sendgrid/CHANGELOG.md b/generated-integrations/sendgrid/CHANGELOG.md index 18256ec43..e9dfbf220 100644 --- a/generated-integrations/sendgrid/CHANGELOG.md +++ b/generated-integrations/sendgrid/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/sendgrid +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/generated-integrations/sendgrid/package.json b/generated-integrations/sendgrid/package.json index 488c6a264..53c1d5610 100644 --- a/generated-integrations/sendgrid/package.json +++ b/generated-integrations/sendgrid/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sendgrid", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official SendGrid integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/integrations/github/CHANGELOG.md b/integrations/github/CHANGELOG.md index 3e65b2ffd..079ad66fa 100644 --- a/integrations/github/CHANGELOG.md +++ b/integrations/github/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/github +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/integrations/github/package.json b/integrations/github/package.json index e4e2f516f..3edde75bd 100644 --- a/integrations/github/package.json +++ b/integrations/github/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/github", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official GitHub integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/integrations/resend/CHANGELOG.md b/integrations/resend/CHANGELOG.md index 21586b1d4..535c68c5a 100644 --- a/integrations/resend/CHANGELOG.md +++ b/integrations/resend/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/resend +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/integrations/resend/package.json b/integrations/resend/package.json index 61003ecb9..d7bf4ae07 100644 --- a/integrations/resend/package.json +++ b/integrations/resend/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/resend", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official resend.com integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/integrations/shopify/CHANGELOG.md b/integrations/shopify/CHANGELOG.md index 3eee6a081..e67fa6dca 100644 --- a/integrations/shopify/CHANGELOG.md +++ b/integrations/shopify/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/shopify +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/integrations/shopify/package.json b/integrations/shopify/package.json index 7b5b4ab49..7263a6997 100644 --- a/integrations/shopify/package.json +++ b/integrations/shopify/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/shopify", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official Shopify integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/integrations/slack/CHANGELOG.md b/integrations/slack/CHANGELOG.md index 763ef22cc..34efa0a40 100644 --- a/integrations/slack/CHANGELOG.md +++ b/integrations/slack/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/slack +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.22 ### Patch Changes diff --git a/integrations/slack/package.json b/integrations/slack/package.json index af5178299..da69d8da9 100644 --- a/integrations/slack/package.json +++ b/integrations/slack/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/slack", - "version": "0.1.22", + "version": "0.1.23-next.0", "description": "The official Slack integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/integrations/whatsapp/CHANGELOG.md b/integrations/whatsapp/CHANGELOG.md index 3008774d5..5d699a9e5 100644 --- a/integrations/whatsapp/CHANGELOG.md +++ b/integrations/whatsapp/CHANGELOG.md @@ -1,5 +1,13 @@ # @trigger.dev/whatsapp +## 0.1.21-next.0 + +### Patch Changes + +- Updated dependencies [ee20f921] +- Updated dependencies [51f9bc9d] + - @trigger.dev/sdk@0.2.16-next.0 + ## 0.1.20 ### Patch Changes diff --git a/integrations/whatsapp/package.json b/integrations/whatsapp/package.json index 8816a6f04..6895d80f9 100644 --- a/integrations/whatsapp/package.json +++ b/integrations/whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/whatsapp", - "version": "0.1.20", + "version": "0.1.21-next.0", "description": "The official WhatsApp Business integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/create-trigger/CHANGELOG.md b/packages/create-trigger/CHANGELOG.md new file mode 100644 index 000000000..5277c4343 --- /dev/null +++ b/packages/create-trigger/CHANGELOG.md @@ -0,0 +1,7 @@ +# create-trigger + +## 0.2.0-next.0 + +### Minor Changes + +- 26e69cb6: Easily scaffold out standalone trigger.dev projects using create-trigger and our templates diff --git a/packages/create-trigger/package.json b/packages/create-trigger/package.json index 8ca3151a5..cea02ddcb 100644 --- a/packages/create-trigger/package.json +++ b/packages/create-trigger/package.json @@ -1,6 +1,6 @@ { "name": "create-trigger", - "version": "0.1.0", + "version": "0.2.0-next.0", "description": "The Trigger.dev CLI to easily create and manage a Trigger.dev project", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/trigger-sdk/CHANGELOG.md b/packages/trigger-sdk/CHANGELOG.md index 3bac3c8ee..0624b7789 100644 --- a/packages/trigger-sdk/CHANGELOG.md +++ b/packages/trigger-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/sdk +## 0.2.16-next.0 + +### Patch Changes + +- ee20f921: Make the schema an optional param for customEvent and webhookEvent +- 51f9bc9d: Added handly links to the dashboard in log feedback + ## 0.2.15 ### Patch Changes diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index 8d7465762..bdad67624 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sdk", - "version": "0.2.15", + "version": "0.2.16-next.0", "description": "trigger.dev Node.JS SDK", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 719ef2a3a93f51a81b5497a845ca4087c28d0640 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 15:23:50 +0000 Subject: [PATCH 14/59] Improved the grid template styles --- apps/webapp/app/components/templates/TemplatesGrid.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/components/templates/TemplatesGrid.tsx b/apps/webapp/app/components/templates/TemplatesGrid.tsx index 60084452a..5d4d9afa0 100644 --- a/apps/webapp/app/components/templates/TemplatesGrid.tsx +++ b/apps/webapp/app/components/templates/TemplatesGrid.tsx @@ -56,7 +56,7 @@ export function TemplatesGrid({
- + {template.title} @@ -64,7 +64,7 @@ export function TemplatesGrid({
@@ -102,7 +102,7 @@ function TemplateButtonOrLink({ key={template.title} type="button" onClick={onClick} - className="group flex w-full flex-col self-stretch overflow-hidden rounded-md border border-slate-700 bg-slate-800 text-left text-slate-200 shadow-md transition hover:cursor-pointer hover:border-slate-500 hover:bg-slate-700/30 disabled:opacity-50" + className={classNames} > {children} From 14031a8c6edbbe2fb05c6eebade78b51d8162678 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 15:24:06 +0000 Subject: [PATCH 15/59] Title component now accepts className --- apps/webapp/app/components/primitives/text/Title.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/primitives/text/Title.tsx b/apps/webapp/app/components/primitives/text/Title.tsx index 70a7f8602..f25a477a8 100644 --- a/apps/webapp/app/components/primitives/text/Title.tsx +++ b/apps/webapp/app/components/primitives/text/Title.tsx @@ -1,8 +1,15 @@ +import classNames from "classnames"; import { Header1 } from "./Headers"; -export function Title({ children }: { children: React.ReactNode }) { +export function Title({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { return ( - + {children} ); From 26ac1fa57c62b54aca0b1f016dee1267435ffdb7 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 15:24:56 +0000 Subject: [PATCH 16/59] CopyText ignores default and propagation clicks --- apps/webapp/app/components/CopyText.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/webapp/app/components/CopyText.tsx b/apps/webapp/app/components/CopyText.tsx index 5f3ac68aa..9987f6cde 100644 --- a/apps/webapp/app/components/CopyText.tsx +++ b/apps/webapp/app/components/CopyText.tsx @@ -13,12 +13,17 @@ export function CopyText({ className, onCopied, }: CopyTextProps) { - const onClick = useCallback(() => { - navigator.clipboard.writeText(value); - if (onCopied) { - onCopied(); - } - }, [value, onCopied]); + const onClick = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + navigator.clipboard.writeText(value); + if (onCopied) { + onCopied(); + } + }, + [value, onCopied] + ); return (
From ae550df37d952ee690d3c3bedf74036df2a12f25 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 15:25:43 +0000 Subject: [PATCH 17/59] Updated the Workflows Overview page if you have no workflows --- .../workflowListPresenter.server.ts | 13 ++++++- .../orgs/$organizationSlug/__org/index.tsx | 37 +++++++++++-------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/apps/webapp/app/presenters/workflowListPresenter.server.ts b/apps/webapp/app/presenters/workflowListPresenter.server.ts index 584a5a028..aded85da4 100644 --- a/apps/webapp/app/presenters/workflowListPresenter.server.ts +++ b/apps/webapp/app/presenters/workflowListPresenter.server.ts @@ -3,10 +3,11 @@ import type { PrismaClient } from "~/db.server"; import { prisma } from "~/db.server"; import { getRuntimeEnvironment } from "~/models/runtimeEnvironment.server"; import { WorkflowsPresenter } from "../presenters/workflowsPresenter.server"; +import { TemplateListPresenter } from "./templateListPresenter.server"; export type WorkflowListItem = Awaited< ReturnType ->[number]; +>["workflows"][number]; export class WorkflowListPresenter { #prismaClient: PrismaClient; @@ -28,11 +29,19 @@ export class WorkflowListPresenter { }); invariant(runtimeEnvironment, "Runtime environment not found"); + const templatesPresenter = new TemplateListPresenter(); + const workflowsPresenter = new WorkflowsPresenter(); - return workflowsPresenter.data( + const workflows = await workflowsPresenter.data( { organization: { slug: organizationSlug }, isArchived: false }, runtimeEnvironment.id ); + const { templates } = await templatesPresenter.data(); + + return { + workflows, + templates, + }; } } diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx index 8ca9fe7ce..e6458797d 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx @@ -1,12 +1,13 @@ import type { LoaderArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; +import { CopyTextPanel } from "~/components/CopyTextButton"; import { CreateNewWorkflow } from "~/components/CreateNewWorkflow"; import { Container } from "~/components/layout/Container"; -import { PanelInfo } from "~/components/layout/PanelInfo"; -import { PrimaryLink } from "~/components/primitives/Buttons"; +import { Panel } from "~/components/layout/Panel"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; +import { TemplatesGrid } from "~/components/templates/TemplatesGrid"; import { WorkflowList } from "~/components/workflows/workflowList"; import { useCurrentOrganization } from "~/hooks/useOrganizations"; import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server"; @@ -22,8 +23,11 @@ export const loader = async ({ request, params }: LoaderArgs) => { const presenter = new WorkflowListPresenter(); try { - const workflows = await presenter.data(params.organizationSlug, currentEnv); - return typedjson({ workflows }); + const { workflows, templates } = await presenter.data( + params.organizationSlug, + currentEnv + ); + return typedjson({ workflows, templates }); } catch (error: any) { console.error(error); throw new Response("Error ", { status: 400 }); @@ -31,7 +35,7 @@ export const loader = async ({ request, params }: LoaderArgs) => { }; export default function Page() { - const { workflows } = useTypedLoaderData(); + const { workflows, templates } = useTypedLoaderData(); const currentOrganization = useCurrentOrganization(); if (currentOrganization === undefined) { return <>; @@ -39,22 +43,23 @@ export default function Page() { return ( - Workflows {workflows.length === 0 ? ( <> - 0 workflows - - - Create first workflow - - + Create your first workflow +
+ Install the Trigger.dev package + + + + Or clone a template +
+ +
+
) : ( <> + Workflows {workflows.length} active workflow{workflows.length > 1 ? "s" : ""} From e6550d156ee6acf2767e990315255144e94fae3a Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 15:29:23 +0000 Subject: [PATCH 18/59] Convert basic starter to hello world and added isLive to templates to filter out the existing basic starter --- .../templateListPresenter.server.ts | 1 + .../workflowStartPresenter.server.ts | 1 + apps/webapp/app/routes/api/v1/templates.ts | 1 + .../migration.sql | 2 + apps/webapp/prisma/schema.prisma | 12 +-- apps/webapp/prisma/seed.ts | 66 ++++++++++++---- .../templates/docs/basic-starter-local.md | 31 -------- .../{basic-starter.md => blank-starter.md} | 77 ++++++++---------- apps/webapp/templates/docs/hello-world.md | 79 +++++++++++++++++++ 9 files changed, 174 insertions(+), 96 deletions(-) create mode 100644 apps/webapp/prisma/migrations/20230224144605_add_is_live_to_templates/migration.sql delete mode 100644 apps/webapp/templates/docs/basic-starter-local.md rename apps/webapp/templates/docs/{basic-starter.md => blank-starter.md} (71%) create mode 100644 apps/webapp/templates/docs/hello-world.md diff --git a/apps/webapp/app/presenters/templateListPresenter.server.ts b/apps/webapp/app/presenters/templateListPresenter.server.ts index e510984a0..f0fed6386 100644 --- a/apps/webapp/app/presenters/templateListPresenter.server.ts +++ b/apps/webapp/app/presenters/templateListPresenter.server.ts @@ -20,6 +20,7 @@ export class TemplateListPresenter { async data(): Promise<{ templates: Array }> { const templates = await this.#prismaClient.template.findMany({ orderBy: { priority: "asc" }, + where: { isLive: true }, }); const serviceMetadatas = await getServiceMetadatas(true); diff --git a/apps/webapp/app/presenters/workflowStartPresenter.server.ts b/apps/webapp/app/presenters/workflowStartPresenter.server.ts index 0cf6a9067..c90addfef 100644 --- a/apps/webapp/app/presenters/workflowStartPresenter.server.ts +++ b/apps/webapp/app/presenters/workflowStartPresenter.server.ts @@ -45,6 +45,7 @@ export class WorkflowStartPresenter { orderBy: { priority: "asc", }, + where: { isLive: true }, }); return { diff --git a/apps/webapp/app/routes/api/v1/templates.ts b/apps/webapp/app/routes/api/v1/templates.ts index daf95e1b5..c4615f254 100644 --- a/apps/webapp/app/routes/api/v1/templates.ts +++ b/apps/webapp/app/routes/api/v1/templates.ts @@ -4,6 +4,7 @@ import { prisma } from "~/db.server"; export async function loader() { const templates = await prisma.template.findMany({ orderBy: { priority: "asc" }, + where: { isLive: true }, }); return json(templates); diff --git a/apps/webapp/prisma/migrations/20230224144605_add_is_live_to_templates/migration.sql b/apps/webapp/prisma/migrations/20230224144605_add_is_live_to_templates/migration.sql new file mode 100644 index 000000000..e98880ee8 --- /dev/null +++ b/apps/webapp/prisma/migrations/20230224144605_add_is_live_to_templates/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Template" ADD COLUMN "isLive" BOOLEAN NOT NULL DEFAULT true; diff --git a/apps/webapp/prisma/schema.prisma b/apps/webapp/prisma/schema.prisma index 868e6a113..a776ea0a1 100644 --- a/apps/webapp/prisma/schema.prisma +++ b/apps/webapp/prisma/schema.prisma @@ -342,7 +342,7 @@ model IntegrationRequest { params Json endpoint String - version String @default("1") + version String @default("1") externalService ExternalService @relation(fields: [externalServiceId], references: [id], onDelete: Cascade, onUpdate: Cascade) externalServiceId String @@ -485,10 +485,10 @@ model WorkflowRunStep { idempotencyKey String ts String - type WorkflowRunStepType - input Json? - output Json? - context Json + type WorkflowRunStepType + input Json? + output Json? + context Json displayProperties Json? createdAt DateTime @default(now()) @@ -625,6 +625,8 @@ model Template { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + isLive Boolean @default(true) + organizationTemplates OrganizationTemplate[] } diff --git a/apps/webapp/prisma/seed.ts b/apps/webapp/prisma/seed.ts index 2a4a2d0e1..55395c1f6 100644 --- a/apps/webapp/prisma/seed.ts +++ b/apps/webapp/prisma/seed.ts @@ -22,18 +22,32 @@ async function readTemplateDocsFile(slug: string) { async function seed() { console.log(`Database has been seeded. 🌱`); - const basicStarter = { - repositoryUrl: "https://github.com/triggerdotdev/basic-starter", + const blankStarter = { + repositoryUrl: "https://github.com/triggerdotdev/blank-starter", imageUrl: "https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/51a2a621-577a-4648-a087-bc5381259a00/public", - title: "A blank starter project with a simple Custom Event trigger", - shortTitle: "Basic Starter", - description: "This is a great place to start if you're new to Trigger.", + title: "A blank starter ready to run your own workflow", + shortTitle: "Blank Starter", + description: + "This is a great place to start if you want to build your own workflow from scratch.", priority: 0, services: [], - workflowIds: ["basic-starter"], - markdownDocs: await readTemplateDocsFile("basic-starter"), - runLocalDocs: await readTemplateDocsFile("basic-starter-local"), + workflowIds: ["blank-starter"], + markdownDocs: await readTemplateDocsFile("blank-starter"), + }; + + const helloWorld = { + repositoryUrl: "https://github.com/triggerdotdev/hello-world", + imageUrl: + "https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/51a2a621-577a-4648-a087-bc5381259a00/public", + title: "A Hello World with a simple custom event trigger", + shortTitle: "Hello World", + description: + "This is a great place to start if you're new to Trigger.dev and want to learn how to build a simple workflow.", + priority: 10, + services: [], + workflowIds: ["hello-world"], + markdownDocs: await readTemplateDocsFile("hello-world"), }; const scheduledHealthcheck = { @@ -44,7 +58,7 @@ async function seed() { shortTitle: "Scheduled Healthcheck", description: "This will run every 5 minutes and send a Slack message if a website url returns a non-200 response.", - priority: 1, + priority: 20, services: ["slack"], workflowIds: ["scheduled-healthcheck"], markdownDocs: await readTemplateDocsFile("scheduled-healthcheck"), @@ -59,7 +73,7 @@ async function seed() { shortTitle: "GitHub stars to Slack", description: "When a GitHub repo is starred, post information about the user to Slack.", - priority: 1, + priority: 30, services: ["github", "slack"], workflowIds: ["github-stars-to-slack"], markdownDocs: await readTemplateDocsFile("github-stars-to-slack"), @@ -74,7 +88,7 @@ async function seed() { shortTitle: "GitHub issues to Slack", description: "When a GitHub issue is created or modified, post a message and link to the issue in a specific Slack channel.", - priority: 1, + priority: 40, services: ["github", "slack"], workflowIds: ["github-issues-to-slack"], markdownDocs: await readTemplateDocsFile("github-issues-to-slack"), @@ -90,7 +104,7 @@ async function seed() { shortTitle: "Resend.com drip campaign", description: "When a new user is created, send them a welcome drip campaign from Resend.com and react.email.", - priority: 2, + priority: 50, services: ["resend"], workflowIds: ["resend-welcome-drip-campaign"], markdownDocs: await readTemplateDocsFile("resend-welcome-drip-campaign"), @@ -99,12 +113,30 @@ async function seed() { ), }; - await prisma.template.upsert({ - where: { slug: "basic-starter" }, - update: basicStarter, - create: { + await prisma.template.updateMany({ + where: { slug: "basic-starter", - ...basicStarter, + }, + data: { + isLive: false, + }, + }); + + await prisma.template.upsert({ + where: { slug: "blank-starter" }, + update: blankStarter, + create: { + slug: "blank-starter", + ...blankStarter, + }, + }); + + await prisma.template.upsert({ + where: { slug: "hello-world" }, + update: helloWorld, + create: { + slug: "hello-world", + ...helloWorld, }, }); diff --git a/apps/webapp/templates/docs/basic-starter-local.md b/apps/webapp/templates/docs/basic-starter-local.md deleted file mode 100644 index 1ba0fb892..000000000 --- a/apps/webapp/templates/docs/basic-starter-local.md +++ /dev/null @@ -1,31 +0,0 @@ -## πŸ’» Run locally - -First, in your terminal of choice, clone the repo and install dependencies: - -```sh -git clone https://github.com/triggerdotdev/basic-starter.git -cd basic-starter -npm install -``` - -Then execute the following command to create a `.env` file with your development Trigger.dev API Key: - -```sh -echo "TRIGGER_API_KEY=" >> .env -``` - -And finally you are ready to run the process: - -```sh -npm run dev -``` - -You should see a message output in your terminal like the following: - -``` -[trigger.dev] ✨ Connected and listening for events [basic-starter] -``` - -## πŸ§ͺ Test it - -The [Basic Starter README](https://github.com/triggerdotdev/basic-starter) has more details on how to test this template. diff --git a/apps/webapp/templates/docs/basic-starter.md b/apps/webapp/templates/docs/blank-starter.md similarity index 71% rename from apps/webapp/templates/docs/basic-starter.md rename to apps/webapp/templates/docs/blank-starter.md index c768af212..aa2852c5a 100644 --- a/apps/webapp/templates/docs/basic-starter.md +++ b/apps/webapp/templates/docs/blank-starter.md @@ -1,32 +1,52 @@ -This repo is a very simple starting point for creating your Trigger.dev workflows. - Currently this repo only has a single [customEvent](https://docs.trigger.dev/triggers/custom-events) trigger: ```ts import { Trigger, customEvent } from "@trigger.dev/sdk"; -import { z } from "zod"; new Trigger({ // Give your Trigger a stable ID - id: "basic-starter", - name: "Basic Starter", - // Trigger on a custom event, see https://docs.trigger.dev/triggers/custom-events + id: "hello-world", + name: "Template: Hello World", + // Trigger on the custom event named "your.event", see https://docs.trigger.dev/triggers/custom-events on: customEvent({ - name: "basic.starter", - // Use zod to verify event payload. See https://docs.trigger.dev/guides/zod - schema: z.object({ id: z.string() }), + name: "your.event", }), - // The run functions gets called once per "basic.starter" event + // The run functions gets called once per "your.event" event async run(event, ctx) { - // Call external services, add delays, and more here. - await ctx.logger.info("Hello world from inside trigger.dev"); + await ctx.waitFor("waiting...", { seconds: 10 }); - // Returned data will become the run "output" and is optional - return event; + await ctx.logger.info("Hello world from inside trigger.dev"); }, }).listen(); ``` +## πŸ“Ί Go Live + +After you are happy with your campaign and deploy it live to Render.com (or some other hosting service), you can send custom events that Trigger your workflow using the [sendEvent](https://docs.trigger.dev/reference/send-event) function from the `@trigger.dev/sdk`, or simply by making requests to our [`events`](https://docs.trigger.dev/api-reference/events/sendEvent) API endpoint. + +Here is an example of sending the custom event to trigger the workflow contained in this repo using `fetch`: + +```ts +const event = { + name: "your.event", + payload: { + hello: "world", + }, +}; + +const response = await fetch("https://app.trigger.dev/api/v1/events", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.TRIGGER_API_KEY}`, + }, + body: JSON.stringify({ + id: randomUUID(), + event, + }), +}); +``` + ## ✍️ Customize You can easily adapt this workflow to a different event relevant to your app. For example, we have a workflow that runs when a user is created and it looks like this: @@ -57,32 +77,3 @@ new Trigger({ ``` Be sure to check out more over on our [docs](https://docs.trigger.dev) - -## πŸ“Ί Go Live - -After you are happy with your campaign and deploy it live to Render.com (or some other hosting service), you can send custom events that Trigger your workflow using the [sendEvent](https://docs.trigger.dev/reference/send-event) function from the `@trigger.dev/sdk`, or simply by making requests to our [`events`](https://docs.trigger.dev/api-reference/events/sendEvent) API endpoint. - -Here is an example of sending the custom event to trigger the workflow contained in this repo using `fetch`: - -```ts -const eventId = ulid(); // Generate a unique event ID -const event = { - name: "basic.starter", - payload: { - // This should match the zod schema provided in the `customEvent.schema` option - id: "user_1234", - }, -}; - -const response = await fetch("https://app.trigger.dev/api/v1/events", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${process.env.TRIGGER_API_KEY}`, - }, - body: JSON.stringify({ - id: eventId, - event, - }), -}); -``` diff --git a/apps/webapp/templates/docs/hello-world.md b/apps/webapp/templates/docs/hello-world.md new file mode 100644 index 000000000..aa2852c5a --- /dev/null +++ b/apps/webapp/templates/docs/hello-world.md @@ -0,0 +1,79 @@ +Currently this repo only has a single [customEvent](https://docs.trigger.dev/triggers/custom-events) trigger: + +```ts +import { Trigger, customEvent } from "@trigger.dev/sdk"; + +new Trigger({ + // Give your Trigger a stable ID + id: "hello-world", + name: "Template: Hello World", + // Trigger on the custom event named "your.event", see https://docs.trigger.dev/triggers/custom-events + on: customEvent({ + name: "your.event", + }), + // The run functions gets called once per "your.event" event + async run(event, ctx) { + await ctx.waitFor("waiting...", { seconds: 10 }); + + await ctx.logger.info("Hello world from inside trigger.dev"); + }, +}).listen(); +``` + +## πŸ“Ί Go Live + +After you are happy with your campaign and deploy it live to Render.com (or some other hosting service), you can send custom events that Trigger your workflow using the [sendEvent](https://docs.trigger.dev/reference/send-event) function from the `@trigger.dev/sdk`, or simply by making requests to our [`events`](https://docs.trigger.dev/api-reference/events/sendEvent) API endpoint. + +Here is an example of sending the custom event to trigger the workflow contained in this repo using `fetch`: + +```ts +const event = { + name: "your.event", + payload: { + hello: "world", + }, +}; + +const response = await fetch("https://app.trigger.dev/api/v1/events", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.TRIGGER_API_KEY}`, + }, + body: JSON.stringify({ + id: randomUUID(), + event, + }), +}); +``` + +## ✍️ Customize + +You can easily adapt this workflow to a different event relevant to your app. For example, we have a workflow that runs when a user is created and it looks like this: + +```ts +import { Trigger, customEvent } from "@trigger.dev/sdk"; +import * as slack from "@trigger.dev/slack"; +import { z } from "zod"; + +new Trigger({ + id: "new-user", + name: "New user", + on: customEvent({ + name: "user.created", + schema: z.object({ id: z.string() }), + }), + async run(event, ctx) { + const user = await prisma.user.find({ + where: { id: event.id }, + }); + + await slack.postMessage("🚨", { + channelName: "new-users", + text: `New user signed up: ${user.email}`, + }); + }, +}).listen(); +``` + +Be sure to check out more over on our [docs](https://docs.trigger.dev) From f3ffffd50b7d8036b28bc31fceb4f4b4806a1851 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 24 Feb 2023 15:59:34 +0000 Subject: [PATCH 19/59] implement the New Workflow page and add the api key to the command to copy for templates and the main one --- apps/webapp/app/components/CopyTextButton.tsx | 8 +-- .../components/templates/TemplatesGrid.tsx | 7 ++- apps/webapp/app/hooks/useEnvironments.ts | 14 +++++ .../orgs/$organizationSlug/__org/index.tsx | 48 +++++++++++++---- .../__org/workflows/new/index.tsx | 53 +++++++++++++++++-- 5 files changed, 111 insertions(+), 19 deletions(-) diff --git a/apps/webapp/app/components/CopyTextButton.tsx b/apps/webapp/app/components/CopyTextButton.tsx index 2c473d6bf..5d8c84c28 100644 --- a/apps/webapp/app/components/CopyTextButton.tsx +++ b/apps/webapp/app/components/CopyTextButton.tsx @@ -16,6 +16,7 @@ const variantStyle = { export type CopyTextButtonProps = { value: string; + text?: string; className?: string; variant?: "slate" | "blue" | "darkTransparent" | "lightTransparent" | "text"; }; @@ -23,6 +24,7 @@ export type CopyTextButtonProps = { export function CopyTextButton({ value, className, + text, variant = "blue", }: CopyTextButtonProps) { const [copied, setCopied] = useState(false); @@ -58,7 +60,7 @@ export function CopyTextButton({ ); } -export function CopyTextPanel({ value, className }: CopyTextButtonProps) { +export function CopyTextPanel({ value, text, className }: CopyTextButtonProps) { const [copied, setCopied] = useState(false); const onCopied = useCallback(() => { setCopied(true); @@ -70,12 +72,12 @@ export function CopyTextPanel({ value, className }: CopyTextButtonProps) { {copied ? (
- {value} + {text ?? value}
) : (
- {value} + {text ?? value}
)} diff --git a/apps/webapp/app/components/templates/TemplatesGrid.tsx b/apps/webapp/app/components/templates/TemplatesGrid.tsx index 5d4d9afa0..157591aa9 100644 --- a/apps/webapp/app/components/templates/TemplatesGrid.tsx +++ b/apps/webapp/app/components/templates/TemplatesGrid.tsx @@ -11,9 +11,11 @@ import { TemplateOverview } from "./TemplateOverview"; export function TemplatesGrid({ templates, openInNewPage, + commandFlags, }: { templates: Array; openInNewPage: boolean; + commandFlags?: string; }) { const [openedTemplate, setOpenedTemplate] = useState( null @@ -64,7 +66,10 @@ export function TemplatesGrid({
diff --git a/apps/webapp/app/hooks/useEnvironments.ts b/apps/webapp/app/hooks/useEnvironments.ts index dc09c3687..0d64d7081 100644 --- a/apps/webapp/app/hooks/useEnvironments.ts +++ b/apps/webapp/app/hooks/useEnvironments.ts @@ -48,3 +48,17 @@ export function useCurrentEnvironment(): RuntimeEnvironment | undefined { ); return currentEnvironment; } + +export function useDevEnvironment(): RuntimeEnvironment | undefined { + const routeMatch = useMatchesData("routes/__app/orgs/$organizationSlug"); + + if ( + !routeMatch || + !isRuntimeEnvironments(routeMatch.data.organization.environments) + ) { + return undefined; + } + return routeMatch.data.organization.environments.find( + (environment: any) => environment.slug === "development" + ); +} diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx index e6458797d..3d0281834 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx @@ -1,3 +1,4 @@ +import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline"; import type { LoaderArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; @@ -5,10 +6,12 @@ import { CopyTextPanel } from "~/components/CopyTextButton"; import { CreateNewWorkflow } from "~/components/CreateNewWorkflow"; import { Container } from "~/components/layout/Container"; import { Panel } from "~/components/layout/Panel"; +import { ToxicA } from "~/components/primitives/Buttons"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; import { TemplatesGrid } from "~/components/templates/TemplatesGrid"; import { WorkflowList } from "~/components/workflows/workflowList"; +import { useDevEnvironment } from "~/hooks/useEnvironments"; import { useCurrentOrganization } from "~/hooks/useOrganizations"; import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server"; import { WorkflowListPresenter } from "~/presenters/workflowListPresenter.server"; @@ -23,11 +26,7 @@ export const loader = async ({ request, params }: LoaderArgs) => { const presenter = new WorkflowListPresenter(); try { - const { workflows, templates } = await presenter.data( - params.organizationSlug, - currentEnv - ); - return typedjson({ workflows, templates }); + return typedjson(await presenter.data(params.organizationSlug, currentEnv)); } catch (error: any) { console.error(error); throw new Response("Error ", { status: 400 }); @@ -37,23 +36,50 @@ export const loader = async ({ request, params }: LoaderArgs) => { export default function Page() { const { workflows, templates } = useTypedLoaderData(); const currentOrganization = useCurrentOrganization(); + const currentEnv = useDevEnvironment(); + if (currentOrganization === undefined) { return <>; } + if (currentEnv === undefined) { + return <>; + } + return ( {workflows.length === 0 ? ( <> Create your first workflow
- Install the Trigger.dev package - - - - Or clone a template + + Add Trigger.dev to an existing Node.js repo +
- + + Manual Setup docs + + +
+ + Or set up a Node.js project ready for Trigger.dev by running one + command + + + + + Or start from a template +
+
diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx index 93fa73c53..04458af62 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/workflows/new/index.tsx @@ -1,6 +1,12 @@ +import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; +import { CopyTextPanel } from "~/components/CopyTextButton"; +import { Panel } from "~/components/layout/Panel"; +import { ToxicA } from "~/components/primitives/Buttons"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { TemplatesGrid } from "~/components/templates/TemplatesGrid"; +import { useDevEnvironment } from "~/hooks/useEnvironments"; +import { useCurrentOrganization } from "~/hooks/useOrganizations"; import { TemplateListPresenter } from "~/presenters/templateListPresenter.server"; export const loader = async () => { @@ -10,12 +16,51 @@ export const loader = async () => { export default function NewWorkflowStep1Page() { const { templates } = useTypedLoaderData(); + const currentOrganization = useCurrentOrganization(); + const currentEnv = useDevEnvironment(); + + if (currentOrganization === undefined) { + return <>; + } + + if (currentEnv === undefined) { + return <>; + } return (
- - Install one of these Templates directly into your codebase - - + <> +
+ + Add Trigger.dev to an existing Node.js repo + +
+ + Manual Setup docs + + +
+ + Or set up a new Node.js project ready for Trigger.dev by running one + command + + + + + Or start from a template +
+ +
+
+
); } From ec3e98ca9fc4ce875811d58a30467b125d0fd4b8 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 24 Feb 2023 16:12:48 +0000 Subject: [PATCH 20/59] Updated the Orgs with more distinct icons --- .../navigation/OrganizationMenu.tsx | 36 ++++++++++++------- apps/webapp/app/routes/__app/index.tsx | 26 ++++++++++---- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/apps/webapp/app/components/navigation/OrganizationMenu.tsx b/apps/webapp/app/components/navigation/OrganizationMenu.tsx index df589ae7e..66b84ff5b 100644 --- a/apps/webapp/app/components/navigation/OrganizationMenu.tsx +++ b/apps/webapp/app/components/navigation/OrganizationMenu.tsx @@ -1,5 +1,10 @@ import { Popover, Transition } from "@headlessui/react"; -import { BookmarkIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline"; +import { + BookmarkIcon, + BuildingOffice2Icon, + ChevronUpDownIcon, + UserIcon, +} from "@heroicons/react/24/outline"; import { CheckIcon, PlusIcon } from "@heroicons/react/24/solid"; import { Link } from "@remix-run/react"; import classNames from "classnames"; @@ -33,10 +38,10 @@ export function OrganizationMenu() { -