Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 63b1c2237a | |||
| c72120eab0 | |||
| 313b7163e8 | |||
| b0e64937a0 | |||
| 3a2cf0ddb7 | |||
| 1579cdbfb2 | |||
| f1e81137b7 | |||
| 45b2bea39f | |||
| 4f47d03114 | |||
| 618cca3304 | |||
| 5706c3ab52 | |||
| e8c81cafb8 | |||
| a0d062c977 | |||
| 87a3bbee8e | |||
| a5ff50f737 | |||
| 763dd8b55a | |||
| 9e5c687174 | |||
| dcbb0f5287 | |||
| d64cfe5add | |||
| 1fa52f1c8c | |||
| ea28f3dd47 | |||
| 2c563ce998 | |||
| 7dd0102579 | |||
| 01bc7df3e2 | |||
| 6d4f71d22c | |||
| cc002ef707 | |||
| f552af517b | |||
| 8aff958bd3 | |||
| ad189b1f4b | |||
| e54ba7cbed | |||
| 1d056b5328 | |||
| cf7c413c87 | |||
| ac87a634a3 | |||
| c9e411be9b | |||
| ee4e181fc3 | |||
| f51f8f3ece | |||
| 12f4f3bc68 | |||
| 894a5dcb5d | |||
| 3af9b63a9c | |||
| 0932ae7d89 | |||
| 12056d888b | |||
| 63e004594c | |||
| ec3e98ca9f | |||
| f3ffffd50b | |||
| e6550d156e | |||
| ae550df37d | |||
| 26ac1fa57c | |||
| 14031a8c6e | |||
| 719ef2a3a9 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Make the schema an optional param for customEvent and webhookEvent
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-trigger": minor
|
||||
---
|
||||
|
||||
Easily scaffold out standalone trigger.dev projects using create-trigger and our templates
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"integrations": "1.0.0",
|
||||
"webapp": "1.0.0",
|
||||
"wss": "1.0.0",
|
||||
"@trigger.dev/airtable": "0.1.22",
|
||||
"@trigger.dev/sendgrid": "0.1.22",
|
||||
"@trigger.dev/github": "0.1.22",
|
||||
"@trigger.dev/resend": "0.1.22",
|
||||
"@trigger.dev/shopify": "0.1.22",
|
||||
"@trigger.dev/slack": "0.1.22",
|
||||
"@trigger.dev/whatsapp": "0.1.20",
|
||||
"@trigger.dev/common-schemas": "0.1.1",
|
||||
"create-trigger": "0.1.0",
|
||||
"emails": "1.0.0",
|
||||
"integration-catalog": "0.1.16",
|
||||
"@trigger.dev/integration-sdk": "0.1.17",
|
||||
"internal-bridge": "0.0.1",
|
||||
"internal-cli": "0.0.1",
|
||||
"internal-platform": "0.0.3",
|
||||
"internal-pulsar": "0.0.1",
|
||||
"@trigger.dev/sdk": "0.2.15"
|
||||
},
|
||||
"changesets": [
|
||||
"big-apples-reflect",
|
||||
"new-students-double",
|
||||
"ten-dancers-hang"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Added handly links to the dashboard in log feedback
|
||||
@@ -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 { 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);
|
||||
@@ -58,7 +61,27 @@ export function CopyTextButton({
|
||||
);
|
||||
}
|
||||
|
||||
export function CopyTextPanel({ value, className }: CopyTextButtonProps) {
|
||||
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);
|
||||
@@ -67,21 +90,18 @@ export function CopyTextPanel({ value, className }: CopyTextButtonProps) {
|
||||
}, 1500);
|
||||
}, [setCopied]);
|
||||
return (
|
||||
<CopyText className={`${className}`} value={value} onCopied={onCopied}>
|
||||
<CopyText value={value} onCopied={onCopied} className="w-full">
|
||||
{copied ? (
|
||||
<div className={copyTextPanelStyles}>
|
||||
<span className="truncate font-mono text-sm">{value}</span>
|
||||
<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={copyTextPanelStyles}>
|
||||
<span className="truncate font-mono text-sm">{value}</span>
|
||||
<ClipboardIcon className="h-5 w-5 min-w-[1.25rem]" />
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
const copyTextPanelStyles =
|
||||
"truncate bg-indigo-700/50 pl-3.5 pr-2 py-3 rounded border border-indigo-600 truncate flex items-center justify-between gap-2 hover:cursor-pointer hover:bg-indigo-600/50 hover:border-indigo-600 transition";
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
import classNames from "classnames";
|
||||
import { Fragment } from "react";
|
||||
import type { TemplateListItem } from "~/presenters/templateListPresenter.server";
|
||||
@@ -10,9 +11,11 @@ import { Header1 } from "../primitives/text/Headers";
|
||||
export function TemplateOverview({
|
||||
template,
|
||||
className,
|
||||
commandFlags,
|
||||
}: {
|
||||
template: TemplateListItem;
|
||||
className?: string;
|
||||
commandFlags?: string;
|
||||
}) {
|
||||
const { docsHTML, imageUrl } = template;
|
||||
|
||||
@@ -20,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)]"
|
||||
@@ -51,9 +58,11 @@ export function TemplateOverview({
|
||||
function TemplateDetails({
|
||||
className,
|
||||
template,
|
||||
commandFlags,
|
||||
}: {
|
||||
className?: string;
|
||||
template: TemplateListItem;
|
||||
commandFlags?: string;
|
||||
}) {
|
||||
const { title, description, repositoryUrl, id } = template;
|
||||
return (
|
||||
@@ -104,6 +113,7 @@ function TemplateDetails({
|
||||
className="!max-w-full"
|
||||
>
|
||||
View Repo
|
||||
<ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4" />
|
||||
</SecondaryA>
|
||||
<SecondaryA
|
||||
href="https://docs.trigger.dev"
|
||||
@@ -111,6 +121,7 @@ function TemplateDetails({
|
||||
className="!max-w-full"
|
||||
>
|
||||
View Docs
|
||||
<ArrowTopRightOnSquareIcon className="ml-1 h-4 w-4" />
|
||||
</SecondaryA>
|
||||
</div>
|
||||
<div className="mb-2 flex items-center">
|
||||
@@ -122,7 +133,17 @@ function TemplateDetails({
|
||||
</Body>
|
||||
<div className="ml-2 h-px w-full bg-slate-800" />
|
||||
</div>
|
||||
<CopyTextPanel value={`npm create trigger@latest ${template.slug}`} />
|
||||
<Body className="mb-4 text-slate-400">
|
||||
Run this command in your terminal to create a new project using this
|
||||
template.
|
||||
</Body>
|
||||
<CopyTextPanel
|
||||
text={`npx create-trigger ${template.slug}`}
|
||||
value={`npx 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 { 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,26 +53,25 @@ export function TemplatesGrid({
|
||||
template={template}
|
||||
openInNewPage={openInNewPage}
|
||||
onClick={() => setOpenedTemplate(template)}
|
||||
className="p-5"
|
||||
>
|
||||
<div className="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={template.title}
|
||||
className="h-32 w-full object-cover"
|
||||
className="h-32 w-full rounded-md object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col justify-between p-5">
|
||||
<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="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}`}
|
||||
className="mt-5"
|
||||
value={`npx create-trigger@latest ${template.slug}${
|
||||
commandFlags ? ` ${commandFlags}` : ``
|
||||
}`}
|
||||
text={`npx create-trigger ${template.slug}`}
|
||||
className=""
|
||||
/>
|
||||
</div>
|
||||
</TemplateButtonOrLink>
|
||||
@@ -81,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-600 hover:bg-slate-700/50 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>
|
||||
);
|
||||
@@ -102,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={`npx create-trigger@latest --apiKey ${apiKey}`}
|
||||
text={`npx 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>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { SubTitle } from "~/components/primitives/text/SubTitle";
|
||||
import { TemplatesGrid } from "~/components/templates/TemplatesGrid";
|
||||
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 () => {
|
||||
@@ -10,12 +11,19 @@ export const loader = async () => {
|
||||
|
||||
export default function NewWorkflowStep1Page() {
|
||||
const { templates } = useTypedLoaderData<typeof loader>();
|
||||
const currentOrganization = useCurrentOrganization();
|
||||
const currentEnv = useDevEnvironment();
|
||||
|
||||
if (currentOrganization === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (currentEnv === undefined) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className="max-w-5xl">
|
||||
<SubTitle>
|
||||
Install one of these Templates directly into your codebase
|
||||
</SubTitle>
|
||||
<TemplatesGrid templates={templates} openInNewPage={false} />
|
||||
<div className="max-w-6xl">
|
||||
<WorkflowOnboarding templates={templates} apiKey={currentEnv.apiKey} />
|
||||
</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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { prisma } from "~/db.server";
|
||||
export async function loader() {
|
||||
const templates = await prisma.template.findMany({
|
||||
orderBy: { priority: "asc" },
|
||||
where: { isLive: true },
|
||||
});
|
||||
|
||||
return json(templates);
|
||||
|
||||
@@ -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
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -350,7 +350,10 @@ 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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+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,51 @@
|
||||
# @trigger.dev/airtable
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/airtable",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official Airtable integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sendgrid",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official SendGrid integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/github",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official GitHub integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/resend",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official resend.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/shopify
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/shopify",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official Shopify integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 0.1.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "0.1.23-next.0",
|
||||
"version": "0.1.25",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
# @trigger.dev/whatsapp
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c72120ea]
|
||||
- @trigger.dev/sdk@0.2.18
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/whatsapp",
|
||||
"version": "0.1.21-next.0",
|
||||
"version": "0.1.23",
|
||||
"description": "The official WhatsApp Business integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 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
|
||||
|
||||
@@ -11,7 +11,7 @@ To scaffold out a new project using `create-trigger`, run any of the following t
|
||||
### npm
|
||||
|
||||
```sh
|
||||
npm create trigger@latest
|
||||
npx create-trigger@latest
|
||||
```
|
||||
|
||||
### yarn
|
||||
@@ -29,7 +29,7 @@ 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
|
||||
npx create-trigger@latest github-stars-to-slack
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-trigger",
|
||||
"version": "0.2.0-next.0",
|
||||
"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",
|
||||
|
||||
@@ -230,9 +230,14 @@ const promptApiKey = async (): Promise<string | undefined> => {
|
||||
message: "Enter your development API key (optional)",
|
||||
default: undefined,
|
||||
validate: (input) => {
|
||||
// Make sure they enter something like trigger_development_********
|
||||
if (input && !input.startsWith("trigger_development_")) {
|
||||
return "Please enter a valid API key (e.g. trigger_development_********) or leave blank to skip";
|
||||
// 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;
|
||||
@@ -241,7 +246,9 @@ const promptApiKey = async (): Promise<string | undefined> => {
|
||||
|
||||
if (apiKey) {
|
||||
logger.success(
|
||||
`Fantastic! We'll save the API key (trigger_development_********) in the .env file.`
|
||||
`Fantastic! We'll save the API key (${obfuscateApiKey(
|
||||
apiKey
|
||||
)}) in the .env file.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -293,3 +300,12 @@ const promptInstall = async (): Promise<boolean> => {
|
||||
|
||||
return install;
|
||||
};
|
||||
|
||||
export const obfuscateApiKey = (apiKey: string) => {
|
||||
const [prefix, slug, secretPart] = apiKey.split("_") as [
|
||||
string,
|
||||
string,
|
||||
string
|
||||
];
|
||||
return `${prefix}_${slug}_${"*".repeat(secretPart.length)}`;
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const logLevels = [
|
||||
"disabled",
|
||||
"log",
|
||||
"error",
|
||||
"log",
|
||||
"warn",
|
||||
"info",
|
||||
"debug",
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,45 @@
|
||||
# @trigger.dev/sdk
|
||||
|
||||
## 0.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c72120ea: Removed accidental log statement
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sdk",
|
||||
"version": "0.2.16-next.0",
|
||||
"version": "0.2.18",
|
||||
"description": "trigger.dev Node.JS SDK",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -37,8 +37,10 @@
|
||||
"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",
|
||||
|
||||
@@ -16,6 +16,7 @@ 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: {
|
||||
@@ -180,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;
|
||||
@@ -626,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,
|
||||
|
||||
@@ -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
+4
@@ -1187,8 +1187,10 @@ 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
|
||||
@@ -1203,8 +1205,10 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user