Merge pull request #71 from triggerdotdev/dev
Onboarding improvements using the new create-trigger CLI
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Make the schema an optional param for customEvent and webhookEvent
|
||||
@@ -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<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
navigator.clipboard.writeText(value);
|
||||
if (onCopied) {
|
||||
onCopied();
|
||||
}
|
||||
},
|
||||
[value, onCopied]
|
||||
);
|
||||
|
||||
return (
|
||||
<div onClick={onClick} className={`${className}`}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 { EnvironmentIcon } from "~/routes/resources/environment";
|
||||
import { CopyText } from "./CopyText";
|
||||
|
||||
const variantStyle = {
|
||||
@@ -16,6 +17,7 @@ const variantStyle = {
|
||||
|
||||
export type CopyTextButtonProps = {
|
||||
value: string;
|
||||
text?: string;
|
||||
className?: string;
|
||||
variant?: "slate" | "blue" | "darkTransparent" | "lightTransparent" | "text";
|
||||
};
|
||||
@@ -23,6 +25,7 @@ export type CopyTextButtonProps = {
|
||||
export function CopyTextButton({
|
||||
value,
|
||||
className,
|
||||
text,
|
||||
variant = "blue",
|
||||
}: CopyTextButtonProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
@@ -57,3 +60,48 @@ export function CopyTextButton({
|
||||
</CopyText>
|
||||
);
|
||||
}
|
||||
|
||||
const panelVariantStyle = {
|
||||
primary:
|
||||
"truncate text-indigo-300 bg-indigo-700/50 pl-3.5 pr-2 py-3 rounded border border-indigo-600 flex items-center justify-between gap-2 hover:cursor-pointer hover:bg-indigo-600/50 hover:border-indigo-600 transition",
|
||||
slate:
|
||||
"flex w-full items-center justify-between gap-2 truncate rounded bg-slate-850 py-2 pl-2.5 pr-1 transition hover:cursor-pointer hover:bg-slate-800 hover:text-slate-300",
|
||||
text: "flex w-full items-center justify-between gap-2 truncate rounded bg-transparent py-2 pl-2.5 pr-1 transition hover:cursor-pointer hover:border-slate-700/50 hover:bg-slate-700/50",
|
||||
};
|
||||
|
||||
export type CopyTextPanelProps = {
|
||||
value: string;
|
||||
text?: string;
|
||||
className?: string;
|
||||
variant?: "primary" | "slate" | "text";
|
||||
};
|
||||
|
||||
export function CopyTextPanel({
|
||||
value,
|
||||
text,
|
||||
className,
|
||||
variant = "primary",
|
||||
}: CopyTextPanelProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const onCopied = useCallback(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500);
|
||||
}, [setCopied]);
|
||||
return (
|
||||
<CopyText value={value} onCopied={onCopied} className="w-full">
|
||||
{copied ? (
|
||||
<div className={classNames(className, panelVariantStyle[variant])}>
|
||||
<span className="truncate font-mono text-sm">{text ?? value}</span>
|
||||
<CheckIcon className="h-5 w-5 min-w-[1.25rem] text-green-500" />
|
||||
</div>
|
||||
) : (
|
||||
<div className={classNames(className, panelVariantStyle[variant])}>
|
||||
<span className="truncate font-mono text-sm">{text ?? value}</span>
|
||||
<ClipboardIcon className="h-4 w-4 min-w-[1.25rem]" />
|
||||
</div>
|
||||
)}
|
||||
</CopyText>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import classNames from "classnames";
|
||||
|
||||
const baseClasses = "px-12 py-10";
|
||||
const baseClasses = "px-4 py-4 md:px-8 md:py-6 lg:px-12 lg:py-10";
|
||||
|
||||
export function Container({
|
||||
children,
|
||||
|
||||
@@ -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() {
|
||||
<Popover.Button
|
||||
className={`
|
||||
${open ? "" : "text-opacity-90"}
|
||||
inline-flex justify-between items-center rounded text-white bg-transparent pl-2.5 pr-2 py-2 text-sm hover:bg-slate-800 focus:outline-none`}
|
||||
inline-flex items-center justify-between rounded bg-transparent py-2 pl-2.5 pr-2 text-sm text-white hover:bg-slate-800 focus:outline-none`}
|
||||
>
|
||||
<BookmarkIcon
|
||||
className={`h-5 w-5 mr-2 ${dimmedClassNames}`}
|
||||
<BuildingOffice2Icon
|
||||
className={`mr-2 h-5 w-5 ${dimmedClassNames}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="transition">
|
||||
@@ -67,9 +72,9 @@ export function OrganizationMenu() {
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute left-0 z-30 mt-3 w-screen min-w-max max-w-xs max-h-[70vh] translate-x-0 transform px-4 sm:px-0">
|
||||
<Popover.Panel className="absolute left-0 z-30 mt-3 max-h-[70vh] w-screen min-w-max max-w-xs translate-x-0 transform px-4 sm:px-0">
|
||||
<div className="overflow-hidden rounded-lg ring-1 ring-black ring-opacity-5">
|
||||
<div className="relative grid gap-y-1 py-1 bg-slate-700 grid-cols-1">
|
||||
<div className="relative grid grid-cols-1 gap-y-1 bg-slate-700 py-1">
|
||||
{organizations.map((organization) => {
|
||||
return (
|
||||
<Popover.Button
|
||||
@@ -77,16 +82,23 @@ export function OrganizationMenu() {
|
||||
as={Link}
|
||||
to={`/orgs/${organization.slug}`}
|
||||
className={classNames(
|
||||
"flex items-center justify-between gap-1.5 mx-1 px-3 py-2 text-white rounded hover:bg-slate-800 transition",
|
||||
"mx-1 flex items-center justify-between gap-1.5 rounded px-3 py-2 text-white transition hover:bg-slate-800",
|
||||
organization.slug === currentOrganization?.slug &&
|
||||
"!bg-slate-800"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<BookmarkIcon
|
||||
className="h-5 w-5 z-100"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{organization.title === "Personal Workspace" ? (
|
||||
<UserIcon
|
||||
className="z-100 h-5 w-5 text-slate-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<BuildingOffice2Icon
|
||||
className="z-100 h-5 w-5 text-slate-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<span className="block truncate">
|
||||
{organization.title}
|
||||
</span>
|
||||
@@ -99,7 +111,7 @@ export function OrganizationMenu() {
|
||||
);
|
||||
})}
|
||||
<Popover.Button as={Link} to={`/orgs/new`}>
|
||||
<div className="flex items-center gap-2 mx-1 pl-2.5 py-2 rounded hover:bg-slate-800 transition">
|
||||
<div className="mx-1 flex items-center gap-2 rounded py-2 pl-2.5 transition hover:bg-slate-800">
|
||||
<PlusIcon
|
||||
className="h-5 w-5 text-green-500"
|
||||
aria-hidden="true"
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
import {
|
||||
SquaresPlusIcon,
|
||||
EyeIcon,
|
||||
EyeSlashIcon,
|
||||
InformationCircleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import {
|
||||
ArrowsRightLeftIcon,
|
||||
UsersIcon,
|
||||
ForwardIcon,
|
||||
ChevronLeftIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
PhoneArrowUpRightIcon,
|
||||
EnvelopeIcon,
|
||||
BeakerIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
Squares2X2Icon,
|
||||
ChevronLeftIcon,
|
||||
Cog6ToothIcon,
|
||||
EnvelopeIcon,
|
||||
ForwardIcon,
|
||||
PhoneArrowUpRightIcon,
|
||||
PlusCircleIcon,
|
||||
Squares2X2Icon,
|
||||
SquaresPlusIcon,
|
||||
UsersIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Link, NavLink } from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useCurrentEnvironment } from "~/hooks/useEnvironments";
|
||||
import {
|
||||
useCurrentOrganization,
|
||||
useOrganizations,
|
||||
} from "~/hooks/useOrganizations";
|
||||
import { useCurrentWorkflow } from "~/hooks/useWorkflows";
|
||||
import { EnvironmentIcon } from "~/routes/resources/environment";
|
||||
import { titleCase } from "~/utils";
|
||||
import { CopyTextPanel } from "../CopyTextButton";
|
||||
import { TertiaryA, TertiaryButton } from "../primitives/Buttons";
|
||||
import { Body } from "../primitives/text/Body";
|
||||
import { Header1 } from "../primitives/text/Headers";
|
||||
import invariant from "tiny-invariant";
|
||||
import { CopyText } from "../CopyText";
|
||||
import { useCurrentEnvironment } from "~/hooks/useEnvironments";
|
||||
|
||||
export function SideMenuContainer({ children }: { children: React.ReactNode }) {
|
||||
return <div className="grid h-full grid-cols-[300px_2fr]">{children}</div>;
|
||||
@@ -160,6 +168,8 @@ function SideMenu({
|
||||
const organization = useCurrentOrganization();
|
||||
invariant(organization, "Organization must be defined");
|
||||
|
||||
const [isShowingKeys, setIsShowingKeys] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col border-r border-slate-800 bg-slate-950">
|
||||
<div className="flex flex-1 flex-col overflow-y-auto pb-4">
|
||||
@@ -199,34 +209,59 @@ function SideMenu({
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col gap-6">
|
||||
<ul className="ml-3 mr-2 flex flex-col gap-6">
|
||||
<ul className="ml-3 mr-2 flex flex-col gap-2">
|
||||
<li className="flex w-full items-center justify-between">
|
||||
<TertiaryA
|
||||
href="https://docs.trigger.dev/guides/environments"
|
||||
target="_blank"
|
||||
className="group flex items-center gap-1 transition"
|
||||
>
|
||||
<InformationCircleIcon className="h-4 w-4 text-slate-500 transition group-hover:text-slate-400" />
|
||||
<Body
|
||||
size="extra-small"
|
||||
className={`overflow-hidden text-slate-300 transition group-hover:text-slate-400 ${menuSmallTitleStyle}`}
|
||||
>
|
||||
API keys
|
||||
</Body>
|
||||
</TertiaryA>
|
||||
|
||||
{!isShowingKeys ? (
|
||||
<TertiaryButton
|
||||
onClick={() => setIsShowingKeys(true)}
|
||||
className="group mr-1.5 transition before:text-xs before:text-slate-400 hover:before:content-['Show_keys']"
|
||||
>
|
||||
<EyeIcon className="h-4 w-4 text-slate-500 transition group-hover:text-slate-400" />
|
||||
</TertiaryButton>
|
||||
) : (
|
||||
<TertiaryButton
|
||||
onClick={() => setIsShowingKeys(false)}
|
||||
className="group mr-1.5 transition before:text-xs before:text-slate-400 hover:before:content-['Hide_keys']"
|
||||
>
|
||||
<EyeSlashIcon className="h-4 w-4 text-slate-500 transition group-hover:text-slate-400" />
|
||||
</TertiaryButton>
|
||||
)}
|
||||
</li>
|
||||
{organization.environments.map((environment) => {
|
||||
return (
|
||||
<li
|
||||
key={environment.id}
|
||||
className="flex w-full flex-col justify-between gap-1.5"
|
||||
className="flex w-full flex-col justify-between"
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<Body
|
||||
size="extra-small"
|
||||
className={`overflow-hidden text-slate-300 ${menuSmallTitleStyle}`}
|
||||
>
|
||||
{environment.slug} api key
|
||||
</Body>
|
||||
{/* <CopyTextButton
|
||||
variant="text"
|
||||
<div className="relative flex items-center">
|
||||
<EnvironmentIcon
|
||||
slug={environment.slug}
|
||||
className="absolute top-4 left-2"
|
||||
/>
|
||||
<CopyTextPanel
|
||||
value={environment.apiKey}
|
||||
/> */}
|
||||
</div>
|
||||
<div className="relative select-all overflow-hidden rounded-sm border border-slate-800 p-1 pl-2 text-sm text-slate-400">
|
||||
<span className="pointer-events-none absolute right-7 top-0 block h-6 w-20 bg-gradient-to-r from-transparent to-slate-950"></span>
|
||||
<CopyText
|
||||
value={environment.apiKey}
|
||||
className="group absolute right-0 top-0 flex h-full w-7 items-center justify-center rounded-sm border-l border-slate-800 bg-slate-950 transition hover:cursor-pointer hover:bg-slate-900 active:bg-green-900"
|
||||
>
|
||||
<ClipboardDocumentCheckIcon className="h-5 w-5 group-active:text-green-500" />
|
||||
</CopyText>
|
||||
{environment.apiKey}
|
||||
text={
|
||||
isShowingKeys
|
||||
? environment.apiKey
|
||||
: `${titleCase(environment.slug)}`
|
||||
}
|
||||
variant="slate"
|
||||
className="pl-6 text-slate-300 hover:text-slate-300"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<Header1 size="extra-large" className="mb-6 text-slate-200">
|
||||
<Header1 size="extra-large" className={classNames("mb-6 text-slate-200")}>
|
||||
{children}
|
||||
</Header1>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,7 @@ export function TemplateCard({
|
||||
{template.description}
|
||||
</Body>
|
||||
</div>
|
||||
<div className="flex flex-row gap-x-1">
|
||||
<div className="flex gap-x-1">
|
||||
{template.services.map((service) => (
|
||||
<div key={service.service} className="">
|
||||
<ApiLogoIcon
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { ArrowRightIcon } from "@heroicons/react/20/solid";
|
||||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
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";
|
||||
|
||||
export function TemplateOverview({
|
||||
template,
|
||||
className,
|
||||
commandFlags,
|
||||
}: {
|
||||
template: TemplateListItem;
|
||||
className?: string;
|
||||
commandFlags?: string;
|
||||
}) {
|
||||
const { docsHTML, imageUrl } = template;
|
||||
|
||||
@@ -21,21 +23,25 @@ export function TemplateOverview({
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
"grid w-full grid-cols-1 gap-8 rounded-lg bg-slate-850 pl-8 text-left md:grid-cols-[20rem_minmax(0,_1fr)]"
|
||||
"grid w-full grid-cols-1 gap-8 rounded-lg bg-slate-850 pl-8 text-left lg:grid-cols-[24rem_minmax(0,_1fr)]"
|
||||
)}
|
||||
>
|
||||
<div className="sticky top-4 flex h-max flex-col rounded-r">
|
||||
<TemplateDetails template={template} className="hidden md:flex" />
|
||||
<div className="flex h-max flex-col rounded-r lg:sticky lg:top-4">
|
||||
<TemplateDetails
|
||||
template={template}
|
||||
commandFlags={commandFlags}
|
||||
className="hidden lg:flex"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col rounded">
|
||||
<div className="z-90 h-fit w-full transition group-hover:opacity-90">
|
||||
<div className="hidden h-fit w-full transition group-hover:opacity-90 lg:block">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt=""
|
||||
className="h-full w-full rounded-t object-cover"
|
||||
alt="Template hero image"
|
||||
className="h-full w-full rounded-t-md object-cover"
|
||||
/>
|
||||
</div>
|
||||
<TemplateDetails template={template} className="md:hidden" />
|
||||
<TemplateDetails template={template} className="lg:hidden" />
|
||||
<div className="flex rounded-b bg-slate-900/75 p-8">
|
||||
<div
|
||||
className="prose prose-sm prose-invert min-w-full [&>pre]:bg-[rgb(17,23,41)]"
|
||||
@@ -52,9 +58,11 @@ export function TemplateOverview({
|
||||
function TemplateDetails({
|
||||
className,
|
||||
template,
|
||||
commandFlags,
|
||||
}: {
|
||||
className?: string;
|
||||
template: TemplateListItem;
|
||||
commandFlags?: string;
|
||||
}) {
|
||||
const { title, description, repositoryUrl, id } = template;
|
||||
return (
|
||||
@@ -74,7 +82,7 @@ function TemplateDetails({
|
||||
</Body>
|
||||
<div className="ml-2 h-px w-full bg-slate-800" />
|
||||
</div>
|
||||
<div className="mb-4 flex gap-x-1">
|
||||
<div className="mb-6 flex gap-x-1">
|
||||
{template.services.map((service) => (
|
||||
<Fragment key={service.service}>
|
||||
<ApiLogoIcon
|
||||
@@ -92,26 +100,50 @@ function TemplateDetails({
|
||||
<div className="mb-2 flex items-center">
|
||||
<Body
|
||||
size="extra-small"
|
||||
className="uppercase tracking-wide text-slate-500"
|
||||
className="whitespace-nowrap uppercase tracking-wide text-slate-500"
|
||||
>
|
||||
Repo
|
||||
Help and guides
|
||||
</Body>
|
||||
<div className="ml-2 h-px w-full bg-slate-800" />
|
||||
</div>
|
||||
<TertiaryA href={repositoryUrl} target="_blank" className="mb-8">
|
||||
<OctoKitty className="h-4 w-4" />
|
||||
<Body size="small" className="truncate font-mono">
|
||||
{repositoryUrl.replace("https://github.com/triggerdotdev", "")}
|
||||
<div className="mb-8 grid grid-cols-2 gap-2">
|
||||
<SecondaryA
|
||||
href={repositoryUrl}
|
||||
target="_blank"
|
||||
className="!max-w-full"
|
||||
>
|
||||
View Repo
|
||||
<ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4" />
|
||||
</SecondaryA>
|
||||
<SecondaryA
|
||||
href="https://docs.trigger.dev"
|
||||
target="_blank"
|
||||
className="!max-w-full"
|
||||
>
|
||||
View Docs
|
||||
<ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4" />
|
||||
</SecondaryA>
|
||||
</div>
|
||||
<div className="mb-2 flex items-center">
|
||||
<Body
|
||||
size="extra-small"
|
||||
className="whitespace-nowrap uppercase tracking-wide text-slate-500"
|
||||
>
|
||||
Get started
|
||||
</Body>
|
||||
</TertiaryA>
|
||||
<ToxicLink
|
||||
size="large"
|
||||
className="group flex h-12 min-w-full"
|
||||
to={`../../templates/add?templateId=${id}`}
|
||||
>
|
||||
<span> Use this template </span>
|
||||
<ArrowRightIcon className="ml-1 h-5 w-5 transition group-hover:translate-x-0.5" />
|
||||
</ToxicLink>
|
||||
<div className="ml-2 h-px w-full bg-slate-800" />
|
||||
</div>
|
||||
<Body className="mb-4 text-slate-400">
|
||||
Run this command in your terminal to create a new project using this
|
||||
template.
|
||||
</Body>
|
||||
<CopyTextPanel
|
||||
text={`npm create trigger ${template.slug}`}
|
||||
value={`npm create trigger@latest ${template.slug} ${
|
||||
commandFlags ? ` ${commandFlags}` : ""
|
||||
}`}
|
||||
className="mb-8"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { XCircleIcon } from "@heroicons/react/24/solid";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { Link } from "@remix-run/react";
|
||||
import classNames from "classnames";
|
||||
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";
|
||||
import { TemplateOverview } from "./TemplateOverview";
|
||||
|
||||
export function TemplatesGrid({
|
||||
templates,
|
||||
openInNewPage,
|
||||
commandFlags,
|
||||
}: {
|
||||
templates: Array<TemplateListItem>;
|
||||
openInNewPage: boolean;
|
||||
commandFlags?: string;
|
||||
}) {
|
||||
const [openedTemplate, setOpenedTemplate] = useState<TemplateListItem | null>(
|
||||
null
|
||||
@@ -28,17 +30,22 @@ export function TemplatesGrid({
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
>
|
||||
<StyledDialog.Panel className="relative mx-auto flex max-h-[80vh] max-w-5xl items-start gap-2 overflow-hidden overflow-y-auto rounded-md">
|
||||
{openedTemplate && <TemplateOverview template={openedTemplate} />}
|
||||
<StyledDialog.Panel className="relative mx-auto flex max-h-[80vh] max-w-6xl items-start gap-2 overflow-hidden overflow-y-auto rounded-md border border-slate-700">
|
||||
{openedTemplate && (
|
||||
<TemplateOverview
|
||||
template={openedTemplate}
|
||||
commandFlags={commandFlags}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setOpenedTemplate(null)}
|
||||
className="sticky top-0 text-slate-600 transition hover:text-slate-500"
|
||||
className="group sticky top-2 -ml-[48px] rounded text-slate-400 transition hover:bg-slate-800/70 hover:text-slate-500"
|
||||
>
|
||||
<XCircleIcon className="h-10 w-10" />
|
||||
<XMarkIcon className="h-8 w-8 transition group-hover:text-slate-300" />
|
||||
</button>
|
||||
</StyledDialog.Panel>
|
||||
</StyledDialog.Dialog>
|
||||
<div className="grid w-full grid-cols-1 items-start justify-start gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="grid w-full grid-cols-1 items-start justify-start gap-5 md:grid-cols-2 lg:grid-cols-3">
|
||||
{templates.map((template) => {
|
||||
return (
|
||||
<TemplateButtonOrLink
|
||||
@@ -46,34 +53,26 @@ export function TemplatesGrid({
|
||||
template={template}
|
||||
openInNewPage={openInNewPage}
|
||||
onClick={() => setOpenedTemplate(template)}
|
||||
className="p-5"
|
||||
>
|
||||
<div className="h-32 w-full bg-slate-600 transition group-hover:opacity-90">
|
||||
<div className="w-full transition group-hover:opacity-90 group-hover:shadow-lg">
|
||||
<img
|
||||
src={template.imageUrl}
|
||||
alt=""
|
||||
className="h-32 w-full object-cover"
|
||||
alt={template.title}
|
||||
className="h-32 w-full rounded-md object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-full flex-col place-content-between p-4">
|
||||
<div className="flex flex-col gap-y-2 ">
|
||||
<Header1 size="small" className="font-semibold">
|
||||
{template.title}
|
||||
</Header1>
|
||||
<Body size="small" className="text-slate-400">
|
||||
{template.description}
|
||||
</Body>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-row gap-x-1">
|
||||
{template.services.map((service) => (
|
||||
<div key={service.service} className="">
|
||||
<ApiLogoIcon
|
||||
integration={service}
|
||||
size="regular"
|
||||
className="mt-2 flex h-8 w-8 items-center justify-center rounded border-[1px] border-slate-700 bg-slate-900 transition group-hover:border-slate-600 group-hover:bg-slate-900/80"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<Header1 size="regular" className="py-6 text-slate-100">
|
||||
{template.title}
|
||||
</Header1>
|
||||
<CopyTextPanel
|
||||
value={`npm create trigger@latest ${template.slug}${
|
||||
commandFlags ? ` ${commandFlags}` : ``
|
||||
}`}
|
||||
text={`npm create trigger ${template.slug}`}
|
||||
className=""
|
||||
/>
|
||||
</div>
|
||||
</TemplateButtonOrLink>
|
||||
);
|
||||
@@ -88,18 +87,20 @@ function TemplateButtonOrLink({
|
||||
openInNewPage,
|
||||
onClick,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
template: TemplateListItem;
|
||||
openInNewPage: boolean;
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
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";
|
||||
const cardStyles =
|
||||
"group flex w-full p-5 flex-col self-stretch overflow-hidden rounded-md border border-slate-700/70 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 (
|
||||
<Link to={template.slug} className={classNames}>
|
||||
<Link to={template.slug} className={classNames(cardStyles, className)}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
@@ -109,7 +110,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={cardStyles}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useState } from "react";
|
||||
import type { TemplateListItem } from "~/presenters/templateListPresenter.server";
|
||||
import { CopyTextPanel } from "../CopyTextButton";
|
||||
import { PrimaryA, TertiaryButton } from "../primitives/Buttons";
|
||||
import { StyledDialog } from "../primitives/Dialog";
|
||||
import { Body } from "../primitives/text/Body";
|
||||
import { Header4 } from "../primitives/text/Headers";
|
||||
import { SubTitle } from "../primitives/text/SubTitle";
|
||||
import { TemplatesGrid } from "../templates/TemplatesGrid";
|
||||
|
||||
export function WorkflowOnboarding({
|
||||
apiKey,
|
||||
templates,
|
||||
}: {
|
||||
apiKey: string;
|
||||
templates: TemplateListItem[];
|
||||
}) {
|
||||
let [isOpen, setIsOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<StyledDialog.Dialog
|
||||
onClose={(e) => setIsOpen(false)}
|
||||
appear
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center">
|
||||
<StyledDialog.Panel className="mx-auto flex max-w-2xl flex-col justify-center overflow-hidden rounded-md border border-slate-700 bg-slate-850 text-left">
|
||||
<div className="flex w-full items-center justify-between py-3 pr-3 pl-5">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Header4 size="small" className="text-slate-300">
|
||||
Setup Trigger.dev manually
|
||||
</Header4>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => setIsOpen(false)}
|
||||
className="group rounded p-1 text-slate-600 transition hover:bg-slate-700 hover:text-slate-500"
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6 text-slate-600 transition group-hover:text-slate-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden bg-slate-800 p-5">
|
||||
<div className="grid grid-cols-[minmax(0,_1fr),_4rem,_minmax(0,_1fr)]">
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<Body className="mb-4 text-slate-400">
|
||||
Add Trigger.dev to an existing Node.js repo.
|
||||
</Body>
|
||||
<PrimaryA
|
||||
href="https://docs.trigger.dev/getting-started#manual-setup"
|
||||
target="_blank"
|
||||
>
|
||||
Manual setup docs
|
||||
<ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4" />
|
||||
</PrimaryA>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="h-full w-px bg-slate-700"></div>
|
||||
<Body size="small" className="uppercase text-slate-600">
|
||||
or
|
||||
</Body>
|
||||
<div className="h-full w-px bg-slate-700"></div>
|
||||
</div>
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<Body className="mb-4 text-slate-400">
|
||||
Setup a new Node.js project ready for Trigger.dev by
|
||||
running this command.
|
||||
</Body>
|
||||
<CopyTextPanel
|
||||
value={`npm create trigger@latest --apiKey ${apiKey}`}
|
||||
text={`npm create trigger --apiKey ${apiKey}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StyledDialog.Panel>
|
||||
</div>
|
||||
</div>
|
||||
</StyledDialog.Dialog>
|
||||
|
||||
<div className="mb-2 flex w-full items-center justify-between">
|
||||
<SubTitle className="mb-0">Get started with a template</SubTitle>
|
||||
<TertiaryButton onClick={(e) => setIsOpen(true)}>
|
||||
<InformationCircleIcon className="h-4 w-4" />
|
||||
Setup manually instead
|
||||
</TertiaryButton>
|
||||
</div>
|
||||
<div>
|
||||
<TemplatesGrid
|
||||
openInNewPage={false}
|
||||
templates={templates}
|
||||
commandFlags={`-k ${apiKey}`}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const LIVE_ENVIRONMENT = "live";
|
||||
export const DEV_ENVIRONMENT = "development";
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DEV_ENVIRONMENT } from "~/consts";
|
||||
import type { RuntimeEnvironment } from "~/models/runtimeEnvironment.server";
|
||||
import { useMatchesData } from "~/utils";
|
||||
|
||||
@@ -48,3 +49,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 === DEV_ENVIRONMENT
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ import type { loader as appLoader } from "~/routes/__app";
|
||||
import type { loader as orgLoader } from "~/routes/__app/orgs/$organizationSlug";
|
||||
import { hydrateObject, useMatchesData } from "~/utils";
|
||||
|
||||
export type MatchedOrganization = UseDataFunctionReturn<
|
||||
typeof appLoader
|
||||
>["organizations"][number];
|
||||
|
||||
export function useOrganizations() {
|
||||
return (
|
||||
getOrganizationsFromMatchesData("routes/__app") ??
|
||||
getOrganizationsFromMatchesData("routes/__public")
|
||||
);
|
||||
return useOrganizationsFromMatchesData(["routes/__app", "routes/__public"]);
|
||||
}
|
||||
|
||||
export function useCurrentOrganization() {
|
||||
@@ -34,8 +35,8 @@ export function useIsNewOrganizationPage(): boolean {
|
||||
return !!routeMatch;
|
||||
}
|
||||
|
||||
function getOrganizationsFromMatchesData(path: string) {
|
||||
const routeMatch = useMatchesData(path);
|
||||
function useOrganizationsFromMatchesData(paths: string[]) {
|
||||
const routeMatch = useMatchesData(paths);
|
||||
|
||||
if (!routeMatch || !routeMatch.data.organizations) {
|
||||
return undefined;
|
||||
|
||||
@@ -4,6 +4,7 @@ import slug from "slug";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { generateTwoRandomWords } from "~/utils/randomWords";
|
||||
import { taskQueue } from "~/services/messageBroker.server";
|
||||
import { DEV_ENVIRONMENT, LIVE_ENVIRONMENT } from "~/consts";
|
||||
|
||||
export type { Organization } from ".prisma/client";
|
||||
|
||||
@@ -67,6 +68,11 @@ export function getOrganizations({ userId }: { userId: User["id"] }) {
|
||||
return prisma.organization.findMany({
|
||||
where: { users: { some: { id: userId } } },
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: {
|
||||
environments: {
|
||||
orderBy: { slug: "asc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -144,8 +150,8 @@ export async function createOrganization({
|
||||
|
||||
if (organization) {
|
||||
// Create the dev and prod environments
|
||||
await createEnvironment(organization, "development");
|
||||
await createEnvironment(organization, "live");
|
||||
await createEnvironment(organization, DEV_ENVIRONMENT);
|
||||
await createEnvironment(organization, LIVE_ENVIRONMENT);
|
||||
|
||||
await taskQueue.publish("ORGANIZATION_CREATED", {
|
||||
id: organization.id,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { renderMarkdown } from "~/services/renderMarkdown.server";
|
||||
import type { TemplateListItem } from "./templateListPresenter.server";
|
||||
import { WorkflowsPresenter } from "../presenters/workflowsPresenter.server";
|
||||
import { getServiceMetadatas } from "~/models/integrations.server";
|
||||
import { DEV_ENVIRONMENT, LIVE_ENVIRONMENT } from "~/consts";
|
||||
|
||||
export class OrganizationTemplatePresenter {
|
||||
#prismaClient: PrismaClient;
|
||||
@@ -71,10 +72,10 @@ export class OrganizationTemplatePresenter {
|
||||
|
||||
const developmentApiKey =
|
||||
organizationTemplate.organization.environments.find(
|
||||
(e) => e.slug === "development"
|
||||
(e) => e.slug === DEV_ENVIRONMENT
|
||||
)?.apiKey;
|
||||
const liveApiKey = organizationTemplate.organization.environments.find(
|
||||
(e) => e.slug === "live"
|
||||
(e) => e.slug === LIVE_ENVIRONMENT
|
||||
)?.apiKey;
|
||||
|
||||
return {
|
||||
|
||||
@@ -20,6 +20,7 @@ export class TemplateListPresenter {
|
||||
async data(): Promise<{ templates: Array<TemplateListItem> }> {
|
||||
const templates = await this.#prismaClient.template.findMany({
|
||||
orderBy: { priority: "asc" },
|
||||
where: { isLive: true },
|
||||
});
|
||||
|
||||
const serviceMetadatas = await getServiceMetadatas(true);
|
||||
|
||||
@@ -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<WorkflowListPresenter["data"]>
|
||||
>[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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export class WorkflowStartPresenter {
|
||||
orderBy: {
|
||||
priority: "asc",
|
||||
},
|
||||
where: { isLive: true },
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
import { BookmarkIcon, PlusIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
BuildingOffice2Icon,
|
||||
PlusIcon,
|
||||
UserIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Link } from "@remix-run/react";
|
||||
import classNames from "classnames";
|
||||
import { CopyTextPanel } from "~/components/CopyTextButton";
|
||||
import { Body } from "~/components/primitives/text/Body";
|
||||
import { Header4 } from "~/components/primitives/text/Headers";
|
||||
import type { MatchedOrganization } from "~/hooks/useOrganizations";
|
||||
import { useOrganizations } from "~/hooks/useOrganizations";
|
||||
import type { Organization } from "~/models/organization.server";
|
||||
import { environmentShortName } from "~/utils";
|
||||
|
||||
export default function AppLayout() {
|
||||
const organizations = useOrganizations();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-center m-20">
|
||||
<ul className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 max-w-8xl gap-2">
|
||||
<div className="flex h-80 w-full items-center justify-center bg-slate-900/50">
|
||||
<h1 className="relative bottom-6 text-4xl text-slate-400">
|
||||
Your Organizations
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<ul className="-mt-24 grid max-w-7xl grid-cols-2 gap-2 lg:grid-cols-3">
|
||||
{organizations ? (
|
||||
<OrganizationGrid organizations={organizations} />
|
||||
) : (
|
||||
@@ -23,12 +35,14 @@ export default function AppLayout() {
|
||||
<Link
|
||||
to="orgs/new"
|
||||
className={classNames(
|
||||
"border-2 border-slate-800 text-center hover:border-transparent hover:bg-slate-800/50 hover:shadow-md",
|
||||
"h-full border border-slate-700 hover:border-transparent hover:bg-[rgb(38,51,71)] hover:shadow-md",
|
||||
boxClasses
|
||||
)}
|
||||
>
|
||||
<PlusIcon className="h-10 w-10 text-green-500" />
|
||||
New Organization
|
||||
<Header4 size="small" className="mb-10">
|
||||
New Organization
|
||||
</Header4>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -40,7 +54,7 @@ export default function AppLayout() {
|
||||
function OrganizationGrid({
|
||||
organizations,
|
||||
}: {
|
||||
organizations: Organization[];
|
||||
organizations: MatchedOrganization[];
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
@@ -54,26 +68,47 @@ function OrganizationGrid({
|
||||
);
|
||||
}
|
||||
|
||||
const boxClasses =
|
||||
"flex flex-col gap-4 items-center justify-center min-h-40 rounded-lg px-6 py-6 min-h-[15rem] transition";
|
||||
|
||||
function OrganizationGridItem({
|
||||
organization,
|
||||
}: {
|
||||
organization: Organization;
|
||||
organization: MatchedOrganization;
|
||||
}) {
|
||||
return (
|
||||
<li key={organization.id} className="w-full h-full">
|
||||
<li key={organization.id} className="h-full w-full">
|
||||
<Link
|
||||
to={`orgs/${organization.slug}`}
|
||||
className={classNames(
|
||||
"bg-slate-800 shadow-md text-center hover:bg-slate-800/50",
|
||||
"border border-slate-700 bg-slate-800 hover:bg-[rgb(38,51,71)]",
|
||||
boxClasses
|
||||
)}
|
||||
>
|
||||
<BookmarkIcon className="h-10 w-10" />
|
||||
{organization.title}{" "}
|
||||
{organization.title === "Personal Workspace" ? (
|
||||
<UserIcon className="h-10 w-10 text-slate-300" aria-hidden="true" />
|
||||
) : (
|
||||
<BuildingOffice2Icon
|
||||
className="h-10 w-10 text-blue-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<Header4 size="large" className="mb-10 text-slate-300">
|
||||
{organization.title}
|
||||
</Header4>
|
||||
<div className="grid w-full grid-cols-2 gap-2">
|
||||
{organization.environments.map((environment) => (
|
||||
<div key={environment.id} className="flex w-full items-center">
|
||||
<CopyTextPanel
|
||||
value={environment.apiKey}
|
||||
text={`${environmentShortName(environment.slug)} API Key`}
|
||||
variant="slate"
|
||||
className="w-full text-slate-500"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const boxClasses =
|
||||
"flex flex-col gap-4 w-80 text-center shadow-md items-center justify-center rounded-lg px-2 pb-2 pt-14 min-h-full transition";
|
||||
|
||||
@@ -3,11 +3,11 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import invariant from "tiny-invariant";
|
||||
import { CreateNewWorkflow } from "~/components/CreateNewWorkflow";
|
||||
import { Container } from "~/components/layout/Container";
|
||||
import { PanelInfo } from "~/components/layout/PanelInfo";
|
||||
import { PrimaryLink } from "~/components/primitives/Buttons";
|
||||
import { SubTitle } from "~/components/primitives/text/SubTitle";
|
||||
import { Title } from "~/components/primitives/text/Title";
|
||||
import { WorkflowList } from "~/components/workflows/workflowList";
|
||||
import { WorkflowOnboarding } from "~/components/workflows/WorkflowOnboarding";
|
||||
import { useDevEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useCurrentOrganization } from "~/hooks/useOrganizations";
|
||||
import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server";
|
||||
import { WorkflowListPresenter } from "~/presenters/workflowListPresenter.server";
|
||||
@@ -22,8 +22,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
|
||||
const presenter = new WorkflowListPresenter();
|
||||
|
||||
try {
|
||||
const workflows = await presenter.data(params.organizationSlug, currentEnv);
|
||||
return typedjson({ workflows });
|
||||
return typedjson(await presenter.data(params.organizationSlug, currentEnv));
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
throw new Response("Error ", { status: 400 });
|
||||
@@ -31,30 +30,33 @@ export const loader = async ({ request, params }: LoaderArgs) => {
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const { workflows } = useTypedLoaderData<typeof loader>();
|
||||
const { workflows, templates } = useTypedLoaderData<typeof loader>();
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
const currentEnv = useDevEnvironment();
|
||||
|
||||
if (currentOrganization === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (currentEnv === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Title>Workflows</Title>
|
||||
{workflows.length === 0 ? (
|
||||
<>
|
||||
<SubTitle>0 workflows</SubTitle>
|
||||
<PanelInfo
|
||||
message="You don't have any workflows yet. They will appear here once
|
||||
connected."
|
||||
className="mb-4 max-w-4xl p-4 pr-6"
|
||||
>
|
||||
<PrimaryLink to={`/orgs/${currentOrganization.slug}/workflows/new`}>
|
||||
Create first workflow
|
||||
</PrimaryLink>
|
||||
</PanelInfo>
|
||||
<Title>Create your first workflow</Title>
|
||||
<div className="max-w-6xl">
|
||||
<WorkflowOnboarding
|
||||
templates={templates}
|
||||
apiKey={currentEnv.apiKey}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Title>Workflows</Title>
|
||||
<SubTitle>
|
||||
{workflows.length} active workflow{workflows.length > 1 ? "s" : ""}
|
||||
</SubTitle>
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function Integrations() {
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center text-center leading-relaxed text-slate-400">
|
||||
<span className="px-2.5">Need an integration?</span>
|
||||
<span className="px-6 text-base text-slate-200">
|
||||
<span className="mb-4 px-6 text-base text-slate-200">
|
||||
Let us know!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
+22
-118
@@ -1,125 +1,29 @@
|
||||
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 { SubTitle } from "~/components/primitives/text/SubTitle";
|
||||
import { useUser } from "~/hooks/useUser";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { WorkflowOnboarding } from "~/components/workflows/WorkflowOnboarding";
|
||||
import { useDevEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useCurrentOrganization } from "~/hooks/useOrganizations";
|
||||
import { TemplateListPresenter } from "~/presenters/templateListPresenter.server";
|
||||
|
||||
export const loader = async () => {
|
||||
const presenter = new TemplateListPresenter();
|
||||
return typedjson(await presenter.data());
|
||||
};
|
||||
|
||||
export default function NewWorkflowStep1Page() {
|
||||
return <Step1 />;
|
||||
}
|
||||
const { templates } = useTypedLoaderData<typeof loader>();
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
const currentEnv = useDevEnvironment();
|
||||
|
||||
function Step1() {
|
||||
const user = useUser();
|
||||
const fetcher = useFetcher();
|
||||
let [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetcher.state === "submitting") {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}, [fetcher.state, setIsOpen]);
|
||||
if (currentOrganization === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (currentEnv === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<StyledDialog.Dialog
|
||||
onClose={(e) => setIsOpen(false)}
|
||||
appear
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4">
|
||||
<StyledDialog.Panel className="mx-auto flex max-w-xl items-start gap-2 overflow-hidden">
|
||||
<div className="flex h-full w-full flex-col overflow-hidden rounded-md bg-slate-800 text-left">
|
||||
<div className="relative flex flex-col items-center justify-between gap-5 overflow-hidden border-b border-slate-850/80 bg-blue-400 px-4 py-12">
|
||||
<CloudIcon className="absolute top-2 -left-4 h-28 w-28 animate-pulse text-white/70" />
|
||||
<CloudIcon className="absolute top-16 right-16 h-16 w-16 animate-pulse text-white/70" />
|
||||
<RocketLaunchIcon className="h-20 w-20 animate-[float_3s_ease-in-out_infinite] text-slate-800" />
|
||||
<Header3 className="font-semibold">
|
||||
Cloud hosting coming soon…
|
||||
</Header3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<Body className="mb-4 text-slate-400">
|
||||
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.
|
||||
</Body>
|
||||
<div className="flex w-full justify-end">
|
||||
<fetcher.Form
|
||||
action="/resources/cloud-waitlist"
|
||||
method="post"
|
||||
>
|
||||
{user.isOnCloudWaitlist ? (
|
||||
<PrimaryButton
|
||||
type="submit"
|
||||
className="mt-2 w-full"
|
||||
disabled
|
||||
>
|
||||
<CheckIcon className="-m-1 h-4 w-4 text-green-500" />
|
||||
Already on the waitlist
|
||||
</PrimaryButton>
|
||||
) : (
|
||||
<PrimaryButton type="submit" className="mt-2 w-full">
|
||||
Notify me when it's ready
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</fetcher.Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="sticky top-0 text-slate-300 transition hover:text-slate-200"
|
||||
>
|
||||
<XCircleIcon className="h-10 w-10" />
|
||||
</button>
|
||||
</StyledDialog.Panel>
|
||||
</div>
|
||||
</div>
|
||||
</StyledDialog.Dialog>
|
||||
<div className={classNames(onboarding.maxWidth, "mb-6")}>
|
||||
<SubTitle className="flex items-center">
|
||||
<StepNumber active stepNumber="1" />
|
||||
Where do you want your workflow hosted?
|
||||
</SubTitle>
|
||||
<Panel className="flex w-full items-center justify-between">
|
||||
<div className="grid w-full grid-cols-2 gap-x-4">
|
||||
<Link to="step2" className={onboarding.buttonStyles}>
|
||||
<HomeIcon className="h-10 w-10 text-green-400" />
|
||||
<Header3>I'll host the workflow myself</Header3>
|
||||
<Body size="small" className="text-slate-400">
|
||||
I will deploy the code to my own servers.
|
||||
</Body>
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => setIsOpen(true)}
|
||||
className={onboarding.buttonStyles}
|
||||
>
|
||||
<CloudIcon className="h-10 w-10 text-blue-400" />
|
||||
<Header3>Host the workflow for me in the cloud</Header3>
|
||||
<Body size="small" className="text-slate-400">
|
||||
Trigger.dev can host and handle the servers for me.
|
||||
</Body>
|
||||
</button>
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
</>
|
||||
<div className="max-w-6xl">
|
||||
<WorkflowOnboarding templates={templates} apiKey={currentEnv.apiKey} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<typeof loader>();
|
||||
|
||||
return (
|
||||
<div className={classNames(onboarding.maxWidth)}>
|
||||
<div className="flex items-center justify-between">
|
||||
<BackToStep1 />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<BackToStep2 text="I'll start with a template" />
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<SubTitle className="flex items-center">
|
||||
<StepNumber active stepNumber="3" />
|
||||
Which template would you like to use?
|
||||
</SubTitle>
|
||||
<TemplatesGrid templates={templates} openInNewPage={false} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+3
-2
@@ -41,6 +41,7 @@ import { RunsTable } from "~/components/runs/RunsTable";
|
||||
import { TriggerBody } from "~/components/triggers/Trigger";
|
||||
import { TriggerTypeIcon } from "~/components/triggers/TriggerIcons";
|
||||
import { triggerLabel } from "~/components/triggers/triggerLabel";
|
||||
import { DEV_ENVIRONMENT } from "~/consts";
|
||||
import { useConnectionSlots } from "~/hooks/useConnectionSlots";
|
||||
import { useCurrentEnvironment } from "~/hooks/useEnvironments";
|
||||
import { useCurrentOrganization } from "~/hooks/useOrganizations";
|
||||
@@ -107,7 +108,7 @@ export default function Page() {
|
||||
className="mb-6"
|
||||
message={`This workflow hasn't been connected in the ${environment.slug} environment yet.`}
|
||||
></PanelWarning>
|
||||
{environment.slug === "development" ? (
|
||||
{environment.slug === DEV_ENVIRONMENT ? (
|
||||
<ConnectToDevelopmentInstructions environment={environment} />
|
||||
) : (
|
||||
<ConnectToLiveInstructions environment={environment} />
|
||||
@@ -399,7 +400,7 @@ export default function Page() {
|
||||
<Disclosure.Panel className="p-6">
|
||||
<div className="mb-1 flex items-baseline gap-2">
|
||||
<SubTitle className="text-slate-300">
|
||||
Trigger your workflow from the source
|
||||
Trigger your workflow for real
|
||||
</SubTitle>
|
||||
<span className="relative -top-0.5 rounded-full bg-blue-700 px-2 pt-1 pb-0.5 text-[0.6rem] font-medium uppercase tracking-wider text-blue-200">
|
||||
Recommended
|
||||
|
||||
+26
-9
@@ -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() {
|
||||
</TertiaryLink>
|
||||
</PanelInfo>
|
||||
) : (
|
||||
<Panel className="mt-4">
|
||||
<Tester
|
||||
organizationSlug={organization.slug}
|
||||
workflowSlug={workflow.slug}
|
||||
eventNames={workflow.eventNames}
|
||||
initialValue={JSON.stringify(payload, null, 2)}
|
||||
/>
|
||||
</Panel>
|
||||
<>
|
||||
<SubTitle>{workflowType(workflow)}</SubTitle>
|
||||
<Panel>
|
||||
<Tester
|
||||
organizationSlug={organization.slug}
|
||||
workflowSlug={workflow.slug}
|
||||
eventNames={workflow.eventNames}
|
||||
initialValue={JSON.stringify(payload, null, 2)}
|
||||
/>
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -135,3 +139,16 @@ function Tester({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Outlet } from "@remix-run/react";
|
||||
import { Container } from "~/components/layout/Container";
|
||||
|
||||
export default function TemplatesLayout() {
|
||||
return (
|
||||
<Container>
|
||||
<div className="px-4 py-6 md:px-8 md:py-6 lg:px-12 lg:py-10">
|
||||
<Outlet />
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function TemplateSlugPage() {
|
||||
<div className="mx-auto max-w-[1188px]">
|
||||
<Link
|
||||
to="/templates"
|
||||
className="mb-4 -ml-1 flex w-max items-center justify-start gap-2 text-sm text-slate-500 transition hover:text-slate-300"
|
||||
className="mb-4 ml-4 flex w-max items-center justify-start gap-2 text-sm text-slate-500 transition hover:text-slate-300 lg:-ml-1"
|
||||
>
|
||||
<ArrowLeftIcon className="h-3 w-3" />
|
||||
Choose a different Template
|
||||
|
||||
@@ -15,10 +15,10 @@ export default function TemplateList() {
|
||||
return (
|
||||
<div className="mx-auto mt-4 flex max-w-6xl flex-col lg:mt-6">
|
||||
<h1 className="mb-6 text-center font-title text-5xl font-semibold text-slate-200">
|
||||
Choose your Template
|
||||
Choose a template
|
||||
</h1>
|
||||
<Header2 size="small" className="mb-8 text-center text-slate-400">
|
||||
Quickly get started with your workflow by using a pre-built example.
|
||||
<Header2 size="small" className="mb-16 text-center text-slate-400">
|
||||
Quickly get started with your workflow by using a pre-built template.
|
||||
</Header2>
|
||||
<TemplatesGrid templates={templates} openInNewPage={true} />
|
||||
</div>
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
@@ -14,5 +14,6 @@ export async function loader({ request }: LoaderArgs) {
|
||||
return json({
|
||||
organizationId: authenticatedEnv.organizationId,
|
||||
env: authenticatedEnv.slug,
|
||||
organizationSlug: authenticatedEnv.organization.slug,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
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" },
|
||||
where: { isLive: true },
|
||||
});
|
||||
|
||||
return json(templates);
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export function EnvironmentMenu() {
|
||||
<Popover.Button
|
||||
className={`
|
||||
${open ? "" : ""}
|
||||
inline-flex justify-between gap-2 items-center rounded text-white bg-transparent pl-3.5 pr-2 py-2 text-sm hover:bg-slate-800 focus:outline-none`}
|
||||
inline-flex items-center justify-between gap-2 rounded bg-transparent py-2 pl-3.5 pr-2 text-sm text-white hover:bg-slate-800 focus:outline-none`}
|
||||
>
|
||||
<EnvironmentIcon slug={currentEnvironment.slug} />
|
||||
<span className="transition">
|
||||
@@ -90,7 +90,7 @@ export function EnvironmentMenu() {
|
||||
</span>
|
||||
<ChevronUpDownIcon
|
||||
className={`${open ? "" : ""}
|
||||
ml-1 h-5 w-5 transition duration-150 ease-in-out text-slate-500`}
|
||||
ml-1 h-5 w-5 text-slate-500 transition duration-150 ease-in-out`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Popover.Button>
|
||||
@@ -105,7 +105,7 @@ export function EnvironmentMenu() {
|
||||
>
|
||||
<Popover.Panel className="absolute left-0 z-30 mt-3 w-screen min-w-max max-w-xs translate-x-0 transform px-4 sm:px-0">
|
||||
<div className="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
|
||||
<div className="relative grid gap-y-1 py-1 bg-slate-700 grid-cols-1">
|
||||
<div className="relative grid grid-cols-1 gap-y-1 bg-slate-700 py-1">
|
||||
{environments.map((environment) => {
|
||||
return (
|
||||
<Popover.Button
|
||||
@@ -115,7 +115,7 @@ export function EnvironmentMenu() {
|
||||
name="environment"
|
||||
value={environment.slug}
|
||||
className={classNames(
|
||||
"flex items-center justify-between gap-1.5 mx-1 px-3 py-2 text-white rounded hover:bg-slate-800 transition",
|
||||
"mx-1 flex items-center justify-between gap-1.5 rounded px-3 py-2 text-white transition hover:bg-slate-800",
|
||||
environment.slug === currentEnvironment?.slug &&
|
||||
"!bg-slate-800"
|
||||
)}
|
||||
@@ -144,7 +144,13 @@ export function EnvironmentMenu() {
|
||||
);
|
||||
}
|
||||
|
||||
function EnvironmentIcon({ slug }: { slug: string }) {
|
||||
export function EnvironmentIcon({
|
||||
slug,
|
||||
className,
|
||||
}: {
|
||||
slug: string;
|
||||
className?: string;
|
||||
}) {
|
||||
let color = "bg-emerald-500";
|
||||
if (slug === "live") {
|
||||
color = "bg-orange-500";
|
||||
@@ -152,8 +158,9 @@ function EnvironmentIcon({ slug }: { slug: string }) {
|
||||
return (
|
||||
<span
|
||||
className={classNames(
|
||||
"rounded-full block w-[0.35rem] h-[0.35rem]",
|
||||
color
|
||||
"block h-[0.35rem] w-[0.35rem] rounded-full",
|
||||
color,
|
||||
className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -223,6 +223,30 @@ class BehaviouralAnalytics {
|
||||
},
|
||||
};
|
||||
|
||||
telemetry = {
|
||||
capture: ({
|
||||
userId,
|
||||
event,
|
||||
properties,
|
||||
organizationId,
|
||||
environmentId,
|
||||
}: {
|
||||
userId: string;
|
||||
event: string;
|
||||
properties: Record<string | number, any>;
|
||||
organizationId?: string;
|
||||
environmentId?: string;
|
||||
}) => {
|
||||
this.#capture({
|
||||
userId,
|
||||
event,
|
||||
eventProperties: properties,
|
||||
organizationId,
|
||||
environmentId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
#capture(event: CaptureEvent) {
|
||||
if (this.client === undefined) return;
|
||||
let groups: Record<string, string> = {};
|
||||
|
||||
@@ -791,6 +791,7 @@ function createTaskQueue() {
|
||||
"x-ttl": run.workflow.triggerTtlInSeconds,
|
||||
"x-is-test": run.isTest ? "true" : "false",
|
||||
"x-app-origin": env.APP_ORIGIN,
|
||||
"x-attempt": String(run.attemptCount),
|
||||
},
|
||||
{
|
||||
eventTimestamp: run.event.timestamp.getTime(),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { RouteMatch } from "@remix-run/react";
|
||||
import { useMatches } from "@remix-run/react";
|
||||
import { useMemo } from "react";
|
||||
import { DEV_ENVIRONMENT } from "./consts";
|
||||
|
||||
const DEFAULT_REDIRECT = "/";
|
||||
|
||||
@@ -33,18 +33,23 @@ export function safeRedirect(
|
||||
* @returns {JSON|undefined} The router data or undefined if not found
|
||||
*/
|
||||
export function useMatchesData(
|
||||
id: string,
|
||||
id: string | string[],
|
||||
debug: boolean = false
|
||||
): RouteMatch | undefined {
|
||||
const matchingRoutes = useMatches();
|
||||
|
||||
if (debug) {
|
||||
console.log("matchingRoutes", matchingRoutes);
|
||||
}
|
||||
|
||||
const route = useMemo(
|
||||
() => matchingRoutes.find((route) => route.id === id),
|
||||
[matchingRoutes, id]
|
||||
);
|
||||
const paths = Array.isArray(id) ? id : [id];
|
||||
|
||||
// Get the first matching route
|
||||
const route = paths.reduce((acc, path) => {
|
||||
if (acc) return acc;
|
||||
return matchingRoutes.find((route) => route.id === path);
|
||||
}, undefined as RouteMatch | undefined);
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
@@ -124,3 +129,12 @@ export function titleCase(original: string): string {
|
||||
export function dateDifference(date1: Date, date2: Date) {
|
||||
return Math.abs(date1.getTime() - date2.getTime());
|
||||
}
|
||||
|
||||
export const environmentShortName = (slug: string) =>
|
||||
slug === DEV_ENVIRONMENT ? "Dev" : "Live";
|
||||
|
||||
// Takes an api key (either trigger_live_xxxx or trigger_development_xxxx) and returns trigger_live_********
|
||||
export const obfuscateApiKey = (apiKey: string) => {
|
||||
const [prefix, slug, secretPart] = apiKey.split("_");
|
||||
return `${prefix}_${slug}_${"*".repeat(secretPart.length)}`;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Template" ADD COLUMN "isLive" BOOLEAN NOT NULL DEFAULT true;
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
|
||||
+52
-20
@@ -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.",
|
||||
"https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/3812f076-38a2-4456-b809-2fc61dd77800/public",
|
||||
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: [],
|
||||
markdownDocs: await readTemplateDocsFile("blank-starter"),
|
||||
};
|
||||
|
||||
const helloWorld = {
|
||||
repositoryUrl: "https://github.com/triggerdotdev/hello-world",
|
||||
imageUrl:
|
||||
"https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/634cfd11-e499-48ca-aaf5-b2404642f600/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"),
|
||||
@@ -55,11 +69,11 @@ async function seed() {
|
||||
repositoryUrl: "https://github.com/triggerdotdev/github-stars-to-slack",
|
||||
imageUrl:
|
||||
"https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/5b3964be-9a7b-4a7e-1837-b283e501b900/public",
|
||||
title: "Slack notifications when a GitHub repo is starred",
|
||||
title: "Post to Slack every time a GitHub repo is starred",
|
||||
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"),
|
||||
@@ -86,11 +100,11 @@ async function seed() {
|
||||
"https://github.com/triggerdotdev/resend-welcome-drip-campaign",
|
||||
imageUrl:
|
||||
"https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/cce3b770-b6f9-40ef-baf3-f01c20686700/public",
|
||||
title: "Send a welcome drip campaign to new users",
|
||||
title: "Send an email drip campaign when a new user signs up",
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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=<APIKEY>" >> .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.
|
||||
+34
-43
@@ -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,
|
||||
}),
|
||||
});
|
||||
```
|
||||
@@ -1,6 +1,4 @@
|
||||
## ✨ Trigger.dev GitHub Issues to Slack
|
||||
|
||||
This repo contains a [GitHub IssueEvent](https://docs.trigger.dev/integrations/apis/github/events/issues) Trigger that will run whenever an issue action is performed in a GitHub repository:
|
||||
This template contains a [GitHub IssueEvent](https://docs.trigger.dev/integrations/apis/github/events/issues) Trigger that will run whenever an issue action is performed in a GitHub repository:
|
||||
|
||||
```ts
|
||||
import { Trigger } from "@trigger.dev/sdk";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
This repo contains a [GitHub newStarEvent](https://docs.trigger.dev/integrations/apis/github/events/new-star) Trigger that will run whenever the specified repository gets a new ⭐️:
|
||||
This template contains a [GitHub newStarEvent](https://docs.trigger.dev/integrations/apis/github/events/new-star) Trigger that will run whenever the specified repository gets a new ⭐️:
|
||||
|
||||
```ts
|
||||
import { Trigger } from "@trigger.dev/sdk";
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
Currently this template 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)
|
||||
@@ -1,4 +1,4 @@
|
||||
This repo contains a [customEvent](https://docs.trigger.dev/triggers/custom-events) Trigger that will send an example drip email campaign using [Resend.com](https://resend.com/) and [react.email](https://react.email/)
|
||||
This template contains a [customEvent](https://docs.trigger.dev/triggers/custom-events) Trigger that will send an example drip email campaign using [Resend.com](https://resend.com/) and [react.email](https://react.email/)
|
||||
|
||||
> Resend.com is currently in private beta, but if you signup for their waitlist, give us a shout on [our Discord](https://discord.gg/CzBqJnYq9r) and we'll help you get in.
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
## ✨ Trigger.dev Scheduled Healthcheck
|
||||
|
||||
This repo contains a [Scheduled](https://docs.trigger.dev/triggers/scheduled) Trigger that will run every 5 minutes and send a Slack message if a website url returns a non-200 response:
|
||||
This template contains a [Scheduled](https://docs.trigger.dev/triggers/scheduled) Trigger that will run every 5 minutes and send a Slack message if a website url returns a non-200 response:
|
||||
|
||||
```ts
|
||||
new Trigger({
|
||||
@@ -40,15 +38,3 @@ new Trigger({
|
||||
- Update the frequency (you can go as frequent as once per minute)
|
||||
|
||||
Be sure to check out more over on our [docs](https://docs.trigger.dev)
|
||||
|
||||
## 🚀 Deploy
|
||||
|
||||
We've made it really easy to deploy this repo to Render.com, if you don't already have a Node.js server to host your triggers.
|
||||
|
||||
[Render.com](https://render.com) is a super-fast way to deploy webapps and servers (think of it like a modern Heroku)
|
||||
|
||||
<a href="https://render.com/deploy?repo=https://github.com/triggerdotdev/scheduled-healthcheck">
|
||||
<img width="144px" src="https://render.com/images/deploy-to-render-button.svg" alt="Deploy to Render">
|
||||
</a>
|
||||
|
||||
> **Note** Make sure you use your "live" trigger.dev API Key when deploying to a server
|
||||
|
||||
@@ -26,6 +26,7 @@ export type WorkflowRunControllerOptions = {
|
||||
organizationId: string;
|
||||
isTest: boolean;
|
||||
appOrigin: string;
|
||||
attempt: number;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -41,6 +42,7 @@ export class WorkflowRunController {
|
||||
organizationId: string;
|
||||
isTest: boolean;
|
||||
appOrigin: string;
|
||||
attempt: number;
|
||||
};
|
||||
|
||||
#logger: Logger;
|
||||
|
||||
+26
-6
@@ -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",
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -337,12 +350,15 @@ export class TriggerServer {
|
||||
}
|
||||
);
|
||||
|
||||
this.#socket.close(4001, "Client not authenticated");
|
||||
this.#socket.close(
|
||||
4001,
|
||||
"Could not authenticate to the server because the API key is invalid"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 +389,7 @@ export class TriggerServer {
|
||||
triggerTTL: data.triggerTTL,
|
||||
});
|
||||
|
||||
this.#workflowId = response.id;
|
||||
this.#workflowId = response.workflow.id;
|
||||
|
||||
this.#logger.debug("Initializing trigger subscriber...");
|
||||
|
||||
@@ -455,6 +471,10 @@ export class TriggerServer {
|
||||
typeof properties["x-is-test"] === "string"
|
||||
? properties["x-is-test"] === "true"
|
||||
: false,
|
||||
attempt:
|
||||
typeof properties["x-attempt"] === "string"
|
||||
? Number(properties["x-attempt"])
|
||||
: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -487,7 +507,7 @@ export class TriggerServer {
|
||||
|
||||
this.#isInitialized = true;
|
||||
|
||||
return true;
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
this.#logger.error(
|
||||
|
||||
+152
-152
@@ -15,7 +15,7 @@ const userCreatedEvent = z.object({
|
||||
const trigger = new Trigger({
|
||||
id: "my-workflow",
|
||||
name: "My workflow",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// apiKey: "<enter your API key here>",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
triggerTTL: 60 * 60 * 24,
|
||||
@@ -44,159 +44,159 @@ const trigger = new Trigger({
|
||||
|
||||
trigger.listen();
|
||||
|
||||
new Trigger({
|
||||
id: "smoke-test",
|
||||
name: "Smoke Test",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
on: customEvent({
|
||||
name: "smoke.test",
|
||||
schema: z.object({ baz: z.string() }),
|
||||
}),
|
||||
run: async (event, ctx) => {
|
||||
await ctx.logger.info("Inside the smoke test workflow, received event", {
|
||||
event,
|
||||
myDate: new Date(),
|
||||
});
|
||||
},
|
||||
}).listen();
|
||||
// new Trigger({
|
||||
// id: "smoke-test",
|
||||
// name: "Smoke Test",
|
||||
// apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// endpoint: "ws://localhost:8889/ws",
|
||||
// logLevel: "debug",
|
||||
// on: customEvent({
|
||||
// name: "smoke.test",
|
||||
// schema: z.object({ baz: z.string() }),
|
||||
// }),
|
||||
// run: async (event, ctx) => {
|
||||
// await ctx.logger.info("Inside the smoke test workflow, received event", {
|
||||
// event,
|
||||
// myDate: new Date(),
|
||||
// });
|
||||
// },
|
||||
// }).listen();
|
||||
|
||||
new Trigger({
|
||||
id: "log-tests",
|
||||
name: "My logs",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
on: customEvent({ name: "user.created", schema: z.any() }),
|
||||
run: async (event, ctx) => {
|
||||
await ctx.logger.info("It's been 5 minutes since the last run!");
|
||||
await ctx.logger.debug("This is a debug log");
|
||||
await ctx.logger.warn("This is a warning");
|
||||
await ctx.logger.error("This is an error");
|
||||
},
|
||||
}).listen();
|
||||
// new Trigger({
|
||||
// id: "log-tests",
|
||||
// name: "My logs",
|
||||
// apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// endpoint: "ws://localhost:8889/ws",
|
||||
// logLevel: "debug",
|
||||
// on: customEvent({ name: "user.created", schema: z.any() }),
|
||||
// run: async (event, ctx) => {
|
||||
// await ctx.logger.info("It's been 5 minutes since the last run!");
|
||||
// await ctx.logger.debug("This is a debug log");
|
||||
// await ctx.logger.warn("This is a warning");
|
||||
// await ctx.logger.error("This is an error");
|
||||
// },
|
||||
// }).listen();
|
||||
|
||||
export const bookingPayloadSchema = z.object({
|
||||
triggerEvent: z.string(),
|
||||
createdAt: z.coerce.date(),
|
||||
payload: z.object({
|
||||
type: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
additionalNotes: z.string(),
|
||||
customInputs: z.object({}),
|
||||
startTime: z.coerce.date(),
|
||||
endTime: z.coerce.date(),
|
||||
organizer: z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
timeZone: z.string(),
|
||||
language: z.object({ locale: z.string() }),
|
||||
}),
|
||||
attendees: z.array(
|
||||
z.object({
|
||||
email: z.string(),
|
||||
name: z.string(),
|
||||
timeZone: z.string(),
|
||||
language: z.object({ locale: z.string() }),
|
||||
})
|
||||
),
|
||||
location: z.string(),
|
||||
destinationCalendar: z.object({
|
||||
id: z.number(),
|
||||
integration: z.string(),
|
||||
externalId: z.string(),
|
||||
userId: z.number(),
|
||||
eventTypeId: z.null(),
|
||||
credentialId: z.number(),
|
||||
}),
|
||||
hideCalendarNotes: z.boolean(),
|
||||
requiresConfirmation: z.null(),
|
||||
eventTypeId: z.number(),
|
||||
seatsShowAttendees: z.boolean(),
|
||||
uid: z.string(),
|
||||
conferenceData: z.object({
|
||||
createRequest: z.object({ requestId: z.string() }),
|
||||
}),
|
||||
videoCallData: z.object({
|
||||
type: z.string(),
|
||||
id: z.string(),
|
||||
password: z.string(),
|
||||
url: z.string(),
|
||||
}),
|
||||
appsStatus: z.array(
|
||||
z.object({
|
||||
appName: z.string(),
|
||||
type: z.string(),
|
||||
success: z.number(),
|
||||
failures: z.number(),
|
||||
errors: z.array(z.any()).optional(),
|
||||
warnings: z.array(z.any()).optional(),
|
||||
})
|
||||
),
|
||||
eventTitle: z.string(),
|
||||
eventDescription: z.null(),
|
||||
price: z.number(),
|
||||
currency: z.string(),
|
||||
length: z.number(),
|
||||
bookingId: z.number(),
|
||||
metadata: z.object({ videoCallUrl: z.string() }),
|
||||
status: z.string(),
|
||||
}),
|
||||
});
|
||||
// export const bookingPayloadSchema = z.object({
|
||||
// triggerEvent: z.string(),
|
||||
// createdAt: z.coerce.date(),
|
||||
// payload: z.object({
|
||||
// type: z.string(),
|
||||
// title: z.string(),
|
||||
// description: z.string(),
|
||||
// additionalNotes: z.string(),
|
||||
// customInputs: z.object({}),
|
||||
// startTime: z.coerce.date(),
|
||||
// endTime: z.coerce.date(),
|
||||
// organizer: z.object({
|
||||
// id: z.number(),
|
||||
// name: z.string(),
|
||||
// email: z.string(),
|
||||
// timeZone: z.string(),
|
||||
// language: z.object({ locale: z.string() }),
|
||||
// }),
|
||||
// attendees: z.array(
|
||||
// z.object({
|
||||
// email: z.string(),
|
||||
// name: z.string(),
|
||||
// timeZone: z.string(),
|
||||
// language: z.object({ locale: z.string() }),
|
||||
// })
|
||||
// ),
|
||||
// location: z.string(),
|
||||
// destinationCalendar: z.object({
|
||||
// id: z.number(),
|
||||
// integration: z.string(),
|
||||
// externalId: z.string(),
|
||||
// userId: z.number(),
|
||||
// eventTypeId: z.null(),
|
||||
// credentialId: z.number(),
|
||||
// }),
|
||||
// hideCalendarNotes: z.boolean(),
|
||||
// requiresConfirmation: z.null(),
|
||||
// eventTypeId: z.number(),
|
||||
// seatsShowAttendees: z.boolean(),
|
||||
// uid: z.string(),
|
||||
// conferenceData: z.object({
|
||||
// createRequest: z.object({ requestId: z.string() }),
|
||||
// }),
|
||||
// videoCallData: z.object({
|
||||
// type: z.string(),
|
||||
// id: z.string(),
|
||||
// password: z.string(),
|
||||
// url: z.string(),
|
||||
// }),
|
||||
// appsStatus: z.array(
|
||||
// z.object({
|
||||
// appName: z.string(),
|
||||
// type: z.string(),
|
||||
// success: z.number(),
|
||||
// failures: z.number(),
|
||||
// errors: z.array(z.any()).optional(),
|
||||
// warnings: z.array(z.any()).optional(),
|
||||
// })
|
||||
// ),
|
||||
// eventTitle: z.string(),
|
||||
// eventDescription: z.null(),
|
||||
// price: z.number(),
|
||||
// currency: z.string(),
|
||||
// length: z.number(),
|
||||
// bookingId: z.number(),
|
||||
// metadata: z.object({ videoCallUrl: z.string() }),
|
||||
// status: z.string(),
|
||||
// }),
|
||||
// });
|
||||
|
||||
new Trigger({
|
||||
id: "calcom-booking-custom-event",
|
||||
name: "Cal.com booking custom event",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
triggerTTL: 60 * 60 * 24,
|
||||
on: customEvent({ name: "calcom.booking", schema: bookingPayloadSchema }),
|
||||
run: async (event, ctx) => {
|
||||
return event;
|
||||
},
|
||||
}).listen();
|
||||
// new Trigger({
|
||||
// id: "calcom-booking-custom-event",
|
||||
// name: "Cal.com booking custom event",
|
||||
// apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// endpoint: "ws://localhost:8889/ws",
|
||||
// logLevel: "debug",
|
||||
// triggerTTL: 60 * 60 * 24,
|
||||
// on: customEvent({ name: "calcom.booking", schema: bookingPayloadSchema }),
|
||||
// run: async (event, ctx) => {
|
||||
// return event;
|
||||
// },
|
||||
// }).listen();
|
||||
|
||||
new Trigger({
|
||||
id: "testing-schedule-test-events",
|
||||
name: "Testing scheduled test payloads",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
triggerTTL: 60 * 60 * 24,
|
||||
on: scheduleEvent({
|
||||
rateOf: { hours: 1 },
|
||||
}),
|
||||
run: async (event, ctx) => {
|
||||
return event;
|
||||
},
|
||||
}).listen();
|
||||
// new Trigger({
|
||||
// id: "testing-schedule-test-events",
|
||||
// name: "Testing scheduled test payloads",
|
||||
// apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// endpoint: "ws://localhost:8889/ws",
|
||||
// logLevel: "debug",
|
||||
// triggerTTL: 60 * 60 * 24,
|
||||
// on: scheduleEvent({
|
||||
// rateOf: { hours: 1 },
|
||||
// }),
|
||||
// run: async (event, ctx) => {
|
||||
// return event;
|
||||
// },
|
||||
// }).listen();
|
||||
|
||||
new Trigger({
|
||||
id: "smoke-test-webhook-schema-test",
|
||||
name: "Smoke Test Webhook Schema Test",
|
||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
endpoint: "ws://localhost:8889/ws",
|
||||
logLevel: "debug",
|
||||
on: webhookEvent({
|
||||
service: "cal.com",
|
||||
eventName: "BOOKING_CREATED",
|
||||
filter: {
|
||||
triggerEvent: ["BOOKING_CREATED"],
|
||||
},
|
||||
schema: bookingPayloadSchema,
|
||||
verifyPayload: {
|
||||
enabled: true,
|
||||
header: "X-Cal-Signature-256",
|
||||
},
|
||||
}),
|
||||
run: async (event, ctx) => {
|
||||
await ctx.logger.info("Received a cal.com booking", {
|
||||
event,
|
||||
wallTime: new Date(),
|
||||
});
|
||||
},
|
||||
}).listen();
|
||||
// new Trigger({
|
||||
// id: "smoke-test-webhook-schema-test",
|
||||
// name: "Smoke Test Webhook Schema Test",
|
||||
// apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||
// endpoint: "ws://localhost:8889/ws",
|
||||
// logLevel: "debug",
|
||||
// on: webhookEvent({
|
||||
// service: "cal.com",
|
||||
// eventName: "BOOKING_CREATED",
|
||||
// filter: {
|
||||
// triggerEvent: ["BOOKING_CREATED"],
|
||||
// },
|
||||
// schema: bookingPayloadSchema,
|
||||
// verifyPayload: {
|
||||
// enabled: true,
|
||||
// header: "X-Cal-Signature-256",
|
||||
// },
|
||||
// }),
|
||||
// run: async (event, ctx) => {
|
||||
// await ctx.logger.info("Received a cal.com booking", {
|
||||
// event,
|
||||
// wallTime: new Date(),
|
||||
// });
|
||||
// },
|
||||
// }).listen();
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/airtable
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/airtable",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official Airtable integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sendgrid",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official SendGrid integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/github",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official GitHub integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/resend",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official resend.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/shopify
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/shopify",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official Shopify integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.23-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.23-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.23-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
# @trigger.dev/whatsapp
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3a2cf0dd]
|
||||
- @trigger.dev/sdk@0.2.17
|
||||
|
||||
## 0.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ee20f921]
|
||||
- Updated dependencies [4f47d031]
|
||||
- Updated dependencies [87a3bbee]
|
||||
- Updated dependencies [51f9bc9d]
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16
|
||||
|
||||
## 0.1.21-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @trigger.dev/sdk@0.2.16-next.3
|
||||
|
||||
## 0.1.21-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [87a3bbee]
|
||||
- @trigger.dev/sdk@0.2.16-next.2
|
||||
|
||||
## 0.1.21-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0932ae7d]
|
||||
- @trigger.dev/sdk@0.2.16-next.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/whatsapp",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.22",
|
||||
"description": "The official WhatsApp Business integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# create-trigger
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 26e69cb6: Easily scaffold out standalone trigger.dev projects using create-trigger and our templates
|
||||
|
||||
## 0.2.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 26e69cb6: Easily scaffold out standalone trigger.dev projects using create-trigger and our templates
|
||||
@@ -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)
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "create-trigger",
|
||||
"version": "0.2.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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
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 <project-name>",
|
||||
"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 <api-key>",
|
||||
"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<string> => {
|
||||
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<string> => {
|
||||
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<string | undefined> => {
|
||||
// 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_******** or trigger_dev_********
|
||||
if (input) {
|
||||
if (
|
||||
!input.startsWith("trigger_development_") ||
|
||||
!input.startsWith("trigger_dev_")
|
||||
) {
|
||||
return "Please enter a valid development API key or leave blank to skip";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
if (apiKey) {
|
||||
logger.success(
|
||||
`Fantastic! We'll save the API key (${obfuscateApiKey(
|
||||
apiKey
|
||||
)}) in the .env file.`
|
||||
);
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
};
|
||||
|
||||
const promptGit = async (): Promise<boolean> => {
|
||||
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<boolean> => {
|
||||
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;
|
||||
};
|
||||
|
||||
export const obfuscateApiKey = (apiKey: string) => {
|
||||
const [prefix, slug, secretPart] = apiKey.split("_") as [
|
||||
string,
|
||||
string,
|
||||
string
|
||||
];
|
||||
return `${prefix}_${slug}_${"*".repeat(secretPart.length)}`;
|
||||
};
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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=<enter your API key here>";
|
||||
await fs.writeFile(envPath, envContents);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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(),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
};
|
||||
@@ -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";
|
||||
};
|
||||
@@ -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<boolean> => {
|
||||
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`
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -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<Ora | null> {
|
||||
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<void>((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<void>((res, rej) => {
|
||||
yarnSubprocess.stdout?.on("data", (data: Buffer) => {
|
||||
yarnSpinner.text = data.toString();
|
||||
});
|
||||
yarnSubprocess.on("error", (e) => rej(e));
|
||||
yarnSubprocess.on("close", () => res());
|
||||
});
|
||||
|
||||
return yarnSpinner;
|
||||
}
|
||||
}
|
||||
@@ -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`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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));
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import { TEMPLATE_ORGANIZATION } from "../consts.js";
|
||||
|
||||
export function createTemplateRef(templateName: string): string {
|
||||
return `github:${TEMPLATE_ORGANIZATION}/${templateName}`;
|
||||
}
|
||||
@@ -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<WhoamiResponse | undefined> {
|
||||
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<WhoamiResponse>;
|
||||
}
|
||||
|
||||
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<Array<TriggerTemplate>> {
|
||||
const response = await fetch(`${TRIGGER_BASE_URL}/api/v1/templates`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return response.json() as Promise<Array<TriggerTemplate>>;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
export type TelemetryEvent = {
|
||||
id: string;
|
||||
event: string;
|
||||
properties: Record<string | number, any>;
|
||||
};
|
||||
|
||||
export async function sendTelemetry(event: TelemetryEvent, apiKey?: string) {
|
||||
const headers: Record<string, string> = {
|
||||
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),
|
||||
});
|
||||
}
|
||||
@@ -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 <T | undefined> 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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const logLevels = [
|
||||
"disabled",
|
||||
"log",
|
||||
"error",
|
||||
"log",
|
||||
"warn",
|
||||
"info",
|
||||
"debug",
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export const HostRPCSchema = {
|
||||
apiKey: z.string(),
|
||||
isTest: z.boolean().default(false),
|
||||
appOrigin: z.string(),
|
||||
attempt: z.number().optional(),
|
||||
}),
|
||||
}),
|
||||
response: z.boolean(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<string, string>) => ({
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${this.#apiKey}`,
|
||||
|
||||
@@ -9,6 +9,7 @@ const Catalog = {
|
||||
"x-ttl": z.coerce.number().optional(),
|
||||
"x-is-test": z.string().default("false"),
|
||||
"x-app-origin": z.string().default("https://app.trigger.dev"),
|
||||
"x-attempt": z.string().optional(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,46 @@
|
||||
# @trigger.dev/sdk
|
||||
|
||||
## 0.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3a2cf0dd: Fixed the missing error message when logging invalid API key and improved the error message
|
||||
|
||||
## 0.2.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ee20f921: Make the schema an optional param for customEvent and webhookEvent
|
||||
- 4f47d031: Give a better error message when the API key is invalid
|
||||
- 87a3bbee: Added a more helpful error message when missing an API key
|
||||
- 51f9bc9d: Added handly links to the dashboard in log feedback
|
||||
- 0932ae7d: Log out when a run first starts as well
|
||||
|
||||
## 0.2.16-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Give a better error message when the API key is invalid
|
||||
|
||||
## 0.2.16-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 87a3bbee: Added a more helpful error message when missing an API key
|
||||
|
||||
## 0.2.16-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0932ae7d: Log out when a run first starts as well
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sdk",
|
||||
"version": "0.2.15",
|
||||
"version": "0.2.17",
|
||||
"description": "trigger.dev Node.JS SDK",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -37,10 +37,13 @@
|
||||
"build:tsup": "tsup --dts-resolve"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^5.2.0",
|
||||
"debug": "^4.3.4",
|
||||
"evt": "^2.4.13",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"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",
|
||||
|
||||
@@ -15,6 +15,8 @@ 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";
|
||||
import chalk from "chalk";
|
||||
|
||||
const zodErrorMessageOptions: ErrorMessageOptions = {
|
||||
delimiter: {
|
||||
@@ -39,6 +41,23 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
#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 +119,25 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
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`);
|
||||
|
||||
@@ -144,11 +181,11 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#logger.error(`🚩 Could not connect to trigger.dev (code ${code})`);
|
||||
|
||||
if (reason) {
|
||||
this.#logger.error("Reason:", reason);
|
||||
}
|
||||
this.#logger.error(
|
||||
`${chalk.red("error")} Could not connect to trigger.dev${
|
||||
reason ? `: ${reason}` : `(code ${code})`
|
||||
}`
|
||||
);
|
||||
|
||||
// If #isConnected is already false, that means we are already trying to reconnect
|
||||
if (!this.#isConnected) return;
|
||||
@@ -590,6 +627,19 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
() => {
|
||||
this.#logger.debug("Running trigger...");
|
||||
|
||||
if (
|
||||
typeof data.meta.attempt === "number" &&
|
||||
data.meta.attempt === 0
|
||||
) {
|
||||
this.#logger.log(
|
||||
`Run ${data.id} started 👉 ${terminalLink(
|
||||
"View on dashboard",
|
||||
`${this.#registerResponse!.url}/runs/${data.id}`,
|
||||
{ fallback: (text, url) => `${text}: (${url})` }
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
serverRPC
|
||||
.send("START_WORKFLOW_RUN", {
|
||||
runId: data.id,
|
||||
@@ -599,7 +649,13 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
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 +732,7 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
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 +742,18 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
||||
triggerTTL: this.#options.triggerTTL,
|
||||
});
|
||||
|
||||
console.log(response);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { TriggerClient } from "../client";
|
||||
import { LogLevel } from "internal-bridge";
|
||||
import { TriggerEvent } from "../events";
|
||||
import chalk from "chalk";
|
||||
|
||||
import type { TriggerContext } from "../types";
|
||||
import { z } from "zod";
|
||||
import terminalLink from "terminal-link";
|
||||
|
||||
export type TriggerOptions<TSchema extends z.ZodTypeAny> = {
|
||||
id: string;
|
||||
@@ -31,6 +33,44 @@ export class Trigger<TSchema extends z.ZodTypeAny> {
|
||||
}
|
||||
|
||||
async listen() {
|
||||
const apiKey = this.#getApiKey();
|
||||
|
||||
if (apiKey.status === "invalid") {
|
||||
console.log(
|
||||
`${chalk.red("Trigger.dev error")}: ${chalk.bold(
|
||||
this.id
|
||||
)} is has an invalid API key ("${chalk.italic(
|
||||
apiKey.apiKey
|
||||
)}"), please set the TRIGGER_API_KEY environment variable or pass the apiKey option to a valid value. ${terminalLink(
|
||||
"Get your API key here",
|
||||
"https://app.trigger.dev",
|
||||
{
|
||||
fallback(text, url) {
|
||||
return `${text} 👉 ${url}`;
|
||||
},
|
||||
}
|
||||
)}`
|
||||
);
|
||||
|
||||
return;
|
||||
} else if (apiKey.status === "missing") {
|
||||
console.log(
|
||||
`${chalk.red("Trigger.dev error")}: ${chalk.bold(
|
||||
this.id
|
||||
)} is missing an API key, please set the TRIGGER_API_KEY environment variable or pass the apiKey option to the Trigger constructor. ${terminalLink(
|
||||
"Get your API key here",
|
||||
"https://app.trigger.dev",
|
||||
{
|
||||
fallback(text, url) {
|
||||
return `${text} 👉 ${url}`;
|
||||
},
|
||||
}
|
||||
)}`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#client) {
|
||||
this.#client = new TriggerClient(this, this.options);
|
||||
}
|
||||
@@ -53,4 +93,21 @@ export class Trigger<TSchema extends z.ZodTypeAny> {
|
||||
get on() {
|
||||
return this.options.on;
|
||||
}
|
||||
|
||||
#getApiKey() {
|
||||
const apiKey = this.options.apiKey ?? process.env.TRIGGER_API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
return { status: "missing" as const };
|
||||
}
|
||||
|
||||
// Validate the api_key format (should be trigger_{env}_XXXXX)
|
||||
const isValid = apiKey.match(/^trigger_[a-z]+_[a-zA-Z0-9]+$/);
|
||||
|
||||
if (!isValid) {
|
||||
return { status: "invalid" as const, apiKey };
|
||||
}
|
||||
|
||||
return { status: "valid" as const, apiKey };
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+371
-51
@@ -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
|
||||
@@ -1142,12 +1187,15 @@ importers:
|
||||
'@types/slug': ^5.0.3
|
||||
'@types/uuid': ^9.0.0
|
||||
'@types/ws': ^8.5.3
|
||||
chalk: ^5.2.0
|
||||
debug: ^4.3.4
|
||||
evt: ^2.4.13
|
||||
get-caller-file: ^2.0.5
|
||||
internal-bridge: workspace:*
|
||||
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
|
||||
@@ -1157,10 +1205,13 @@ importers:
|
||||
zod-error: ^1.1.0
|
||||
zod-to-json-schema: ^3.20.2
|
||||
dependencies:
|
||||
chalk: 5.2.0
|
||||
debug: 4.3.4
|
||||
evt: 2.4.13
|
||||
get-caller-file: 2.0.5
|
||||
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 +3835,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 +3860,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 +5573,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 +5698,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 +6414,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 +6464,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 +6497,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 +6552,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 +6761,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 +6830,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 +6841,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 +7103,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 +7122,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 +7135,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 +7354,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 +7380,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 +7401,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 +7856,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 +7992,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 +8255,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 +8359,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 +8439,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 +8515,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 +8589,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 +9150,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 +9320,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 +9363,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 +9837,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 +9883,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 +9904,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 +9947,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 +9957,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 +9978,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 +9985,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 +10010,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 +10046,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 +10064,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 +10079,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 +10089,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 +10096,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 +10606,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 +10884,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 +11133,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 +11553,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 +11821,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 +11930,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 +12212,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 +12332,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 +12374,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 +12780,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 +13072,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 +13762,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 +13960,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 +14169,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 +14358,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 +14422,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 +14692,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 +15236,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 +15827,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 +15928,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 +15944,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 +16484,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 +16560,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 +16569,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 +16657,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 +16803,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 +16858,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 +16887,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 +17405,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 +17611,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 +18308,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==}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user