Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70f9bd0d70 | |||
| a1860dbaae | |||
| d69e4e712d | |||
| 7fae67c47d | |||
| 9ae0ca64af | |||
| da69fa0613 | |||
| 336029b842 | |||
| 29edcd3df9 | |||
| a9ff32418e |
@@ -4,7 +4,6 @@ import {
|
||||
ArrowRightOnRectangleIcon,
|
||||
ChartBarIcon,
|
||||
CursorArrowRaysIcon,
|
||||
EllipsisHorizontalIcon,
|
||||
ShieldCheckIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { UserGroupIcon, UserPlusIcon } from "@heroicons/react/24/solid";
|
||||
@@ -15,6 +14,7 @@ import { useFeatures } from "~/hooks/useFeatures";
|
||||
import { MatchedOrganization } from "~/hooks/useOrganizations";
|
||||
import { MatchedProject } from "~/hooks/useProject";
|
||||
import { User } from "~/models/user.server";
|
||||
import { useV3Enabled } from "~/root";
|
||||
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
|
||||
import { cn } from "~/utils/cn";
|
||||
import {
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
organizationBillingPath,
|
||||
organizationIntegrationsPath,
|
||||
organizationPath,
|
||||
organizationSettingsPath,
|
||||
organizationTeamPath,
|
||||
personalAccessTokensPath,
|
||||
projectEnvironmentsPath,
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
projectHttpEndpointsPath,
|
||||
projectPath,
|
||||
projectRunsPath,
|
||||
projectSettingsPath,
|
||||
projectSetupPath,
|
||||
projectTriggersPath,
|
||||
} from "~/utils/pathBuilder";
|
||||
@@ -56,9 +58,8 @@ import {
|
||||
PopoverSectionHeader,
|
||||
} from "../primitives/Popover";
|
||||
import { StepNumber } from "../primitives/StepNumber";
|
||||
import { MenuCount, SideMenuItem } from "./SideMenuItem";
|
||||
import { SideMenuHeader } from "./SideMenuHeader";
|
||||
import { useV3Enabled } from "~/root";
|
||||
import { MenuCount, SideMenuItem } from "./SideMenuItem";
|
||||
|
||||
type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
|
||||
type SideMenuProject = Pick<
|
||||
@@ -106,11 +107,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
showHeaderDivider ? " border-border" : "border-transparent"
|
||||
)}
|
||||
>
|
||||
<ProjectSelector
|
||||
organization={organization}
|
||||
organizations={organizations}
|
||||
project={project}
|
||||
/>
|
||||
<ProjectSelector organizations={organizations} project={project} />
|
||||
<UserMenu user={user} />
|
||||
</div>
|
||||
<div
|
||||
@@ -118,7 +115,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
ref={borderRef}
|
||||
>
|
||||
<div className="mb-6 flex flex-col gap-1 px-1">
|
||||
<SideMenuHeader title={project.name || "No project found"}>
|
||||
<SideMenuHeader title={"Project"}>
|
||||
<PopoverMenuItem
|
||||
to={projectSetupPath(organization, project)}
|
||||
title="Framework setup"
|
||||
@@ -168,9 +165,16 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
to={projectEnvironmentsPath(organization, project)}
|
||||
data-action="environments & api keys"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Project settings"
|
||||
icon="settings"
|
||||
iconColor="text-teal-500"
|
||||
to={projectSettingsPath(organization, project)}
|
||||
data-action="project-settings"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-1 flex flex-col gap-1 px-1">
|
||||
<SideMenuHeader title={organization.title}>
|
||||
<SideMenuHeader title={"Organization"}>
|
||||
<PopoverMenuItem to={newProjectPath(organization)} title="New Project" icon="plus" />
|
||||
<PopoverMenuItem
|
||||
to={inviteTeamMemberPath(organization)}
|
||||
@@ -206,10 +210,17 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
iconColor="text-green-600"
|
||||
data-action="usage & billing"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Organization settings"
|
||||
icon="settings"
|
||||
iconColor="text-teal-500"
|
||||
to={organizationSettingsPath(organization)}
|
||||
data-action="organization-settings"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 border-t border-border p-1">
|
||||
{currentPlan?.subscription?.isPaying === true ? (
|
||||
{currentPlan?.subscription?.isPaying === true && (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
@@ -265,16 +276,14 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : (
|
||||
<SideMenuItem
|
||||
name="Join our Discord"
|
||||
icon={DiscordIcon}
|
||||
to="https://trigger.dev/discord"
|
||||
data-action="join our discord"
|
||||
target="_blank"
|
||||
/>
|
||||
)}
|
||||
|
||||
<SideMenuItem
|
||||
name="Join our Discord"
|
||||
icon={DiscordIcon}
|
||||
to="https://trigger.dev/discord"
|
||||
data-action="join our discord"
|
||||
target="_blank"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Documentation"
|
||||
icon="docs"
|
||||
@@ -317,11 +326,9 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
|
||||
|
||||
function ProjectSelector({
|
||||
project,
|
||||
organization,
|
||||
organizations,
|
||||
}: {
|
||||
project: SideMenuProject;
|
||||
organization: MatchedOrganization;
|
||||
organizations: MatchedOrganization[];
|
||||
}) {
|
||||
const [isOrgMenuOpen, setOrgMenuOpen] = useState(false);
|
||||
@@ -339,7 +346,7 @@ function ProjectSelector({
|
||||
className="h-7 w-full justify-between overflow-hidden py-1 pl-2"
|
||||
>
|
||||
<LogoIcon className="relative -top-px mr-2 h-4 w-4 min-w-[1rem]" />
|
||||
<span className="truncate">{organization.title ?? "Select an organization"}</span>
|
||||
<span className="truncate">{project.name ?? "Select a project"}</span>
|
||||
</PopoverArrowTrigger>
|
||||
<PopoverContent
|
||||
className="min-w-[16rem] overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700"
|
||||
@@ -350,23 +357,31 @@ function ProjectSelector({
|
||||
<Fragment key={organization.id}>
|
||||
<PopoverSectionHeader title={organization.title} />
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
{organization.projects.map((p) => {
|
||||
const isSelected = p.id === project.id;
|
||||
return (
|
||||
<PopoverMenuItem
|
||||
key={p.id}
|
||||
to={projectPath(organization, p)}
|
||||
title={
|
||||
<div className="flex w-full items-center justify-between text-bright">
|
||||
<span className="grow truncate text-left">{p.name}</span>
|
||||
<MenuCount count={p.jobCount} />
|
||||
</div>
|
||||
}
|
||||
isSelected={isSelected}
|
||||
icon="folder"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{organization.projects.length > 0 ? (
|
||||
organization.projects.map((p) => {
|
||||
const isSelected = p.id === project.id;
|
||||
return (
|
||||
<PopoverMenuItem
|
||||
key={p.id}
|
||||
to={projectPath(organization, p)}
|
||||
title={
|
||||
<div className="flex w-full items-center justify-between text-bright">
|
||||
<span className="grow truncate text-left">{p.name}</span>
|
||||
<MenuCount count={p.jobCount} />
|
||||
</div>
|
||||
}
|
||||
isSelected={isSelected}
|
||||
icon="folder"
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<PopoverMenuItem
|
||||
to={newProjectPath(organization)}
|
||||
title="New project"
|
||||
icon="plus"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
@@ -130,6 +130,9 @@ export async function getUsersInvites({ email }: { email: string }) {
|
||||
return await prisma.orgMemberInvite.findMany({
|
||||
where: {
|
||||
email,
|
||||
organization: {
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
organization: true,
|
||||
|
||||
@@ -14,6 +14,11 @@ export async function findEnvironmentByApiKey(apiKey: string) {
|
||||
},
|
||||
});
|
||||
|
||||
//don't return deleted projects
|
||||
if (environment?.project.deletedAt !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
@@ -28,24 +33,10 @@ export async function findEnvironmentByPublicApiKey(apiKey: string) {
|
||||
},
|
||||
});
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
export async function getEnvironmentForOrganization(organizationSlug: string, slug: string) {
|
||||
const organization = await prisma.organization.findUnique({
|
||||
where: {
|
||||
slug: organizationSlug,
|
||||
},
|
||||
include: {
|
||||
environments: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
return;
|
||||
//don't return deleted projects
|
||||
if (environment?.project.deletedAt !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const environment = organization.environments.find((environment) => environment.slug === slug);
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
@@ -98,27 +98,36 @@ export class JobListPresenter {
|
||||
orderBy: [{ title: "asc" }],
|
||||
});
|
||||
|
||||
const latestRuns = await this.#prismaClient.$queryRaw<
|
||||
{
|
||||
createdAt: Date;
|
||||
status: JobRunStatus;
|
||||
jobId: string;
|
||||
rn: BigInt;
|
||||
}[]
|
||||
>`
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
"id",
|
||||
"createdAt",
|
||||
"status",
|
||||
"jobId",
|
||||
ROW_NUMBER() OVER(PARTITION BY "jobId" ORDER BY "createdAt" DESC) as rn
|
||||
FROM
|
||||
"public"."JobRun"
|
||||
WHERE
|
||||
"jobId" IN (${Prisma.join(jobs.map((j) => j.id))})
|
||||
) t
|
||||
WHERE rn = 1;`;
|
||||
let latestRuns = [] as {
|
||||
createdAt: Date;
|
||||
status: JobRunStatus;
|
||||
jobId: string;
|
||||
rn: BigInt;
|
||||
}[];
|
||||
|
||||
if (jobs.length > 0) {
|
||||
latestRuns = await this.#prismaClient.$queryRaw<
|
||||
{
|
||||
createdAt: Date;
|
||||
status: JobRunStatus;
|
||||
jobId: string;
|
||||
rn: BigInt;
|
||||
}[]
|
||||
>`
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
"id",
|
||||
"createdAt",
|
||||
"status",
|
||||
"jobId",
|
||||
ROW_NUMBER() OVER(PARTITION BY "jobId" ORDER BY "createdAt" DESC) as rn
|
||||
FROM
|
||||
"JobRun"
|
||||
WHERE
|
||||
"jobId" IN (${Prisma.join(jobs.map((j) => j.id))})
|
||||
) t
|
||||
WHERE rn = 1;`;
|
||||
}
|
||||
|
||||
return jobs
|
||||
.flatMap((job) => {
|
||||
|
||||
@@ -10,11 +10,16 @@ export class NewOrganizationPresenter {
|
||||
|
||||
public async call({ userId }: { userId: User["id"] }) {
|
||||
const organizations = await this.#prismaClient.organization.findMany({
|
||||
select: {
|
||||
projects: {
|
||||
where: { deletedAt: null },
|
||||
},
|
||||
},
|
||||
where: { members: { some: { userId } } },
|
||||
});
|
||||
|
||||
return {
|
||||
hasOrganizations: organizations.length > 0,
|
||||
hasOrganizations: organizations.filter((o) => o.projects.length > 0).length > 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { PrismaClient } from "@trigger.dev/database";
|
||||
import { redirect } from "remix-typedjson";
|
||||
import { prisma } from "~/db.server";
|
||||
import {
|
||||
clearCurrentProjectId,
|
||||
commitCurrentProjectSession,
|
||||
getCurrentProjectId,
|
||||
setCurrentProjectId,
|
||||
@@ -9,6 +10,8 @@ import {
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { newProjectPath } from "~/utils/pathBuilder";
|
||||
import { ProjectPresenter } from "./ProjectPresenter.server";
|
||||
import { redirectWithErrorMessage } from "~/models/message.server";
|
||||
import { match } from "assert";
|
||||
|
||||
export class OrganizationsPresenter {
|
||||
#prismaClient: PrismaClient;
|
||||
@@ -54,7 +57,11 @@ export class OrganizationsPresenter {
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new Response("Project not found", { status: 404 });
|
||||
throw redirectWithErrorMessage(
|
||||
newProjectPath({ slug: organizationSlug }),
|
||||
request,
|
||||
"No projects found in organization"
|
||||
);
|
||||
}
|
||||
|
||||
return { organizations, organization, project };
|
||||
@@ -75,16 +82,21 @@ export class OrganizationsPresenter {
|
||||
|
||||
//no project in session, let's set one
|
||||
if (!sessionProjectId) {
|
||||
//no session id and no project slug so we need to select the best project
|
||||
if (!projectSlug) {
|
||||
const bestProject = await this.#selectBestProjectForOrganization(organizationSlug, userId);
|
||||
const bestProject = await this.#selectBestProjectForOrganization(
|
||||
organizationSlug,
|
||||
userId,
|
||||
request
|
||||
);
|
||||
const session = await setCurrentProjectId(bestProject.id, request);
|
||||
throw redirect(request.url, {
|
||||
headers: { "Set-Cookie": await commitCurrentProjectSession(session) },
|
||||
});
|
||||
}
|
||||
|
||||
//use the project param to find the project
|
||||
const project = await prisma.project.findFirst({
|
||||
//get all the projects
|
||||
const projects = await prisma.project.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
@@ -93,21 +105,37 @@ export class OrganizationsPresenter {
|
||||
organization: {
|
||||
slug: organizationSlug,
|
||||
},
|
||||
deletedAt: null,
|
||||
slug: projectSlug,
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw redirect(newProjectPath({ slug: organizationSlug }));
|
||||
if (projects.length === 0) {
|
||||
throw redirectWithErrorMessage(
|
||||
newProjectPath({ slug: organizationSlug }),
|
||||
request,
|
||||
"No projects in this organization"
|
||||
);
|
||||
}
|
||||
|
||||
const session = await setCurrentProjectId(project.id, request);
|
||||
//try get the project which matches the URL
|
||||
let matchingProject = projects.find((p) => p.slug === projectSlug);
|
||||
|
||||
//if there's no matching project, just use the most recently updated one
|
||||
if (!matchingProject) {
|
||||
matchingProject = projects[0];
|
||||
}
|
||||
|
||||
//set the session
|
||||
const session = await setCurrentProjectId(matchingProject.id, request);
|
||||
throw redirect(request.url, {
|
||||
headers: { "Set-Cookie": await commitCurrentProjectSession(session) },
|
||||
});
|
||||
}
|
||||
|
||||
//no project slug, so just return the session id
|
||||
if (!projectSlug) {
|
||||
return sessionProjectId;
|
||||
}
|
||||
@@ -123,6 +151,7 @@ export class OrganizationsPresenter {
|
||||
organization: {
|
||||
slug: organizationSlug,
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -142,7 +171,7 @@ export class OrganizationsPresenter {
|
||||
|
||||
async #getOrganizations(userId: string) {
|
||||
const orgs = await this.#prismaClient.organization.findMany({
|
||||
where: { members: { some: { userId } } },
|
||||
where: { members: { some: { userId } }, deletedAt: null },
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
@@ -150,6 +179,7 @@ export class OrganizationsPresenter {
|
||||
title: true,
|
||||
runsEnabled: true,
|
||||
projects: {
|
||||
where: { deletedAt: null },
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
@@ -202,14 +232,20 @@ export class OrganizationsPresenter {
|
||||
});
|
||||
}
|
||||
|
||||
async #selectBestProjectForOrganization(organizationSlug: string, userId: string) {
|
||||
async #selectBestProjectForOrganization(
|
||||
organizationSlug: string,
|
||||
userId: string,
|
||||
request: Request
|
||||
) {
|
||||
const projects = await this.#prismaClient.project.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
},
|
||||
where: {
|
||||
deletedAt: null,
|
||||
organization: {
|
||||
deletedAt: null,
|
||||
slug: organizationSlug,
|
||||
members: { some: { userId } },
|
||||
},
|
||||
@@ -223,8 +259,7 @@ export class OrganizationsPresenter {
|
||||
});
|
||||
|
||||
if (projects.length === 0) {
|
||||
logger.info("Didn't find a project in this org", { organizationSlug, projects });
|
||||
throw new Response("Not Found", { status: 404 });
|
||||
throw redirect(newProjectPath({ slug: organizationSlug }), request);
|
||||
}
|
||||
|
||||
return projects[0];
|
||||
|
||||
@@ -23,6 +23,7 @@ export class ProjectPresenter {
|
||||
organizationId: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
deletedAt: true,
|
||||
_count: {
|
||||
select: {
|
||||
sources: {
|
||||
@@ -53,7 +54,7 @@ export class ProjectPresenter {
|
||||
},
|
||||
},
|
||||
},
|
||||
where: { id, organization: { members: { some: { userId } } } },
|
||||
where: { id, deletedAt: null, organization: { members: { some: { userId } } } },
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
@@ -67,6 +68,7 @@ export class ProjectPresenter {
|
||||
organizationId: project.organizationId,
|
||||
createdAt: project.createdAt,
|
||||
updatedAt: project.updatedAt,
|
||||
deletedAt: project.deletedAt,
|
||||
hasInactiveExternalTriggers: project._count.sources > 0,
|
||||
jobCount: project._count.jobs,
|
||||
httpEndpointCount: project._count.httpEndpoints,
|
||||
|
||||
@@ -14,7 +14,7 @@ export class SelectBestProjectPresenter {
|
||||
const projectId = await getCurrentProjectId(request);
|
||||
if (projectId) {
|
||||
const project = await this.#prismaClient.project.findUnique({
|
||||
where: { id: projectId, organization: { members: { some: { userId } } } },
|
||||
where: { id: projectId, deletedAt: null, organization: { members: { some: { userId } } } },
|
||||
include: { organization: true },
|
||||
});
|
||||
if (project) {
|
||||
@@ -28,6 +28,7 @@ export class SelectBestProjectPresenter {
|
||||
organization: true,
|
||||
},
|
||||
where: {
|
||||
deletedAt: null,
|
||||
organization: {
|
||||
members: { some: { userId } },
|
||||
},
|
||||
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { Form, useActionData, useNavigation } from "@remix-run/react";
|
||||
import { ActionFunction, json } from "@remix-run/server-runtime";
|
||||
import { redirect } from "remix-typedjson";
|
||||
import { z } from "zod";
|
||||
import { InlineCode } from "~/components/code/InlineCode";
|
||||
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
|
||||
import { Button } from "~/components/primitives/Buttons";
|
||||
import { Fieldset } from "~/components/primitives/Fieldset";
|
||||
import { FormButtons } from "~/components/primitives/FormButtons";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
import { Header2 } from "~/components/primitives/Headers";
|
||||
import { Hint } from "~/components/primitives/Hint";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
import { PageHeader, PageTitle, PageTitleRow } from "~/components/primitives/PageHeader";
|
||||
import { prisma } from "~/db.server";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import {
|
||||
clearCurrentProjectId,
|
||||
commitCurrentProjectSession,
|
||||
} from "~/services/currentProject.server";
|
||||
import { DeleteProjectService } from "~/services/deleteProject.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { organizationPath, projectPath } from "~/utils/pathBuilder";
|
||||
|
||||
export function createSchema(
|
||||
constraints: {
|
||||
getSlugMatch?: (slug: string) => { isMatch: boolean; projectSlug: string };
|
||||
} = {}
|
||||
) {
|
||||
return z.discriminatedUnion("action", [
|
||||
z.object({
|
||||
action: z.literal("rename"),
|
||||
projectName: z.string().min(3, "Project name must have at least 3 characters").max(50),
|
||||
}),
|
||||
z.object({
|
||||
action: z.literal("delete"),
|
||||
projectSlug: z.string().superRefine((slug, ctx) => {
|
||||
if (constraints.getSlugMatch === undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: conform.VALIDATION_UNDEFINED,
|
||||
});
|
||||
} else {
|
||||
const { isMatch, projectSlug } = constraints.getSlugMatch(slug);
|
||||
if (isMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `The slug must match ${projectSlug}`,
|
||||
});
|
||||
}
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export const action: ActionFunction = async ({ request, params }) => {
|
||||
const userId = await requireUserId(request);
|
||||
const { organizationSlug, projectParam } = params;
|
||||
if (!organizationSlug || !projectParam) {
|
||||
return json({ errors: { body: "organizationSlug is required" } }, { status: 400 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
|
||||
const schema = createSchema({
|
||||
getSlugMatch: (slug) => {
|
||||
return { isMatch: slug === projectParam, projectSlug: projectParam };
|
||||
},
|
||||
});
|
||||
const submission = parse(formData, { schema });
|
||||
|
||||
if (!submission.value || submission.intent !== "submit") {
|
||||
return json(submission);
|
||||
}
|
||||
|
||||
try {
|
||||
switch (submission.value.action) {
|
||||
case "rename": {
|
||||
await prisma.project.update({
|
||||
where: {
|
||||
slug: projectParam,
|
||||
organization: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
name: submission.value.projectName,
|
||||
},
|
||||
});
|
||||
|
||||
return redirectWithSuccessMessage(
|
||||
projectPath({ slug: organizationSlug }, { slug: projectParam }),
|
||||
request,
|
||||
`Project renamed to ${submission.value.projectName}`
|
||||
);
|
||||
}
|
||||
case "delete": {
|
||||
const deleteProjectService = new DeleteProjectService();
|
||||
try {
|
||||
await deleteProjectService.call({ projectSlug: projectParam, userId });
|
||||
|
||||
//we need to clear the project from the session
|
||||
const removeProjectIdSession = await clearCurrentProjectId(request);
|
||||
return redirect(organizationPath({ slug: organizationSlug }), {
|
||||
headers: { "Set-Cookie": await commitCurrentProjectSession(removeProjectIdSession) },
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
logger.error("Project could not be deleted", {
|
||||
error: error instanceof Error ? error.message : JSON.stringify(error),
|
||||
});
|
||||
return redirectWithErrorMessage(
|
||||
organizationPath({ slug: organizationSlug }),
|
||||
request,
|
||||
`Project ${projectParam} could not be deleted`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
return json({ errors: { body: error.message } }, { status: 400 });
|
||||
}
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const project = useProject();
|
||||
const lastSubmission = useActionData();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [renameForm, { projectName }] = useForm({
|
||||
id: "rename-project",
|
||||
// TODO: type this
|
||||
lastSubmission: lastSubmission as any,
|
||||
shouldRevalidate: "onSubmit",
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
schema: createSchema(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [deleteForm, { projectSlug }] = useForm({
|
||||
id: "delete-project",
|
||||
// TODO: type this
|
||||
lastSubmission: lastSubmission as any,
|
||||
shouldValidate: "onInput",
|
||||
shouldRevalidate: "onSubmit",
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
schema: createSchema({
|
||||
getSlugMatch: (slug) => ({ isMatch: slug === project.slug, projectSlug: project.slug }),
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isRenameLoading =
|
||||
navigation.formData?.get("action") === "rename" &&
|
||||
(navigation.state === "submitting" || navigation.state === "loading");
|
||||
|
||||
const isDeleteLoading =
|
||||
navigation.formData?.get("action") === "delete" &&
|
||||
(navigation.state === "submitting" || navigation.state === "loading");
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader>
|
||||
<PageTitleRow>
|
||||
<PageTitle title={`${project.name} project settings`} />
|
||||
</PageTitleRow>
|
||||
</PageHeader>
|
||||
|
||||
<PageBody>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<Form method="post" {...renameForm.props} className="max-w-md">
|
||||
<input type="hidden" name="action" value="rename" />
|
||||
<Fieldset>
|
||||
<InputGroup>
|
||||
<Label htmlFor={projectName.id}>Rename your project</Label>
|
||||
<Input
|
||||
{...conform.input(projectName, { type: "text" })}
|
||||
defaultValue={project.name}
|
||||
placeholder="Your project name"
|
||||
icon="folder"
|
||||
autoFocus
|
||||
/>
|
||||
<FormError id={projectName.errorId}>{projectName.error}</FormError>
|
||||
</InputGroup>
|
||||
<FormButtons
|
||||
confirmButton={
|
||||
<Button
|
||||
type="submit"
|
||||
variant={"primary/small"}
|
||||
disabled={isRenameLoading}
|
||||
LeadingIcon={isRenameLoading ? "spinner-white" : undefined}
|
||||
>
|
||||
Rename project
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Header2 spacing>Danger zone</Header2>
|
||||
<Form
|
||||
method="post"
|
||||
{...deleteForm.props}
|
||||
className="max-w-md rounded-sm border border-rose-500/40"
|
||||
>
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<Fieldset className="p-4">
|
||||
<InputGroup>
|
||||
<Label htmlFor={projectSlug.id}>Delete project</Label>
|
||||
<Input
|
||||
{...conform.input(projectSlug, { type: "text" })}
|
||||
placeholder="Your project slug"
|
||||
icon="warning"
|
||||
autoFocus
|
||||
/>
|
||||
<FormError id={projectSlug.errorId}>{projectSlug.error}</FormError>
|
||||
<FormError>{deleteForm.error}</FormError>
|
||||
<Hint>
|
||||
This change is irreversible, so please be certain. Type in the Project slug
|
||||
<InlineCode variant="extra-small">{project.slug}</InlineCode> and then press
|
||||
Delete.
|
||||
</Hint>
|
||||
</InputGroup>
|
||||
<FormButtons
|
||||
confirmButton={
|
||||
<Button
|
||||
type="submit"
|
||||
variant={"danger/small"}
|
||||
LeadingIcon={isDeleteLoading ? "spinner-white" : "trash-can"}
|
||||
leadingIconClassName="text-white"
|
||||
disabled={isDeleteLoading}
|
||||
>
|
||||
Delete project
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</PageBody>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,9 @@
|
||||
import { Outlet } from "@remix-run/react";
|
||||
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { typedjson } from "remix-typedjson";
|
||||
import invariant from "tiny-invariant";
|
||||
import { RouteErrorDisplay } from "~/components/ErrorDisplay";
|
||||
import { BreadcrumbLink } from "~/components/navigation/Breadcrumb";
|
||||
import { organizationMatchId, useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useTypedMatchData } from "~/hooks/useTypedMatchData";
|
||||
import { ProjectPresenter } from "~/presenters/ProjectPresenter.server";
|
||||
import { commitCurrentProjectSession, setCurrentProjectId } from "~/services/currentProject.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { telemetry } from "~/services/telemetry.server";
|
||||
import { Handle } from "~/utils/handle";
|
||||
import { projectPath } from "~/utils/pathBuilder";
|
||||
import { loader as orgLoader } from "../_app.orgs.$organizationSlug/route";
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { Form, useActionData, useNavigation } from "@remix-run/react";
|
||||
import { ActionFunction, json } from "@remix-run/server-runtime";
|
||||
import { redirect } from "remix-typedjson";
|
||||
import { r } from "tar";
|
||||
import { z } from "zod";
|
||||
import { InlineCode } from "~/components/code/InlineCode";
|
||||
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
|
||||
import { Button } from "~/components/primitives/Buttons";
|
||||
import { Fieldset } from "~/components/primitives/Fieldset";
|
||||
import { FormButtons } from "~/components/primitives/FormButtons";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
import { Header2 } from "~/components/primitives/Headers";
|
||||
import { Hint } from "~/components/primitives/Hint";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
import { PageHeader, PageTitle, PageTitleRow } from "~/components/primitives/PageHeader";
|
||||
import { prisma } from "~/db.server";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import {
|
||||
clearCurrentProjectId,
|
||||
commitCurrentProjectSession,
|
||||
} from "~/services/currentProject.server";
|
||||
import { DeleteOrganizationService } from "~/services/deleteOrganization.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { organizationPath, organizationSettingsPath, rootPath } from "~/utils/pathBuilder";
|
||||
|
||||
export function createSchema(
|
||||
constraints: {
|
||||
getSlugMatch?: (slug: string) => { isMatch: boolean; organizationSlug: string };
|
||||
} = {}
|
||||
) {
|
||||
return z.discriminatedUnion("action", [
|
||||
z.object({
|
||||
action: z.literal("rename"),
|
||||
organizationName: z
|
||||
.string()
|
||||
.min(3, "Organization name must have at least 3 characters")
|
||||
.max(50),
|
||||
}),
|
||||
z.object({
|
||||
action: z.literal("delete"),
|
||||
organizationSlug: z.string().superRefine((slug, ctx) => {
|
||||
if (constraints.getSlugMatch === undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: conform.VALIDATION_UNDEFINED,
|
||||
});
|
||||
} else {
|
||||
const { isMatch, organizationSlug } = constraints.getSlugMatch(slug);
|
||||
if (isMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `The slug must match ${organizationSlug}`,
|
||||
});
|
||||
}
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export const action: ActionFunction = async ({ request, params }) => {
|
||||
const userId = await requireUserId(request);
|
||||
const { organizationSlug } = params;
|
||||
if (!organizationSlug) {
|
||||
return json({ errors: { body: "organizationSlug is required" } }, { status: 400 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const schema = createSchema({
|
||||
getSlugMatch: (slug) => {
|
||||
return { isMatch: slug === organizationSlug, organizationSlug };
|
||||
},
|
||||
});
|
||||
const submission = parse(formData, { schema });
|
||||
|
||||
if (!submission.value || submission.intent !== "submit") {
|
||||
return json(submission);
|
||||
}
|
||||
|
||||
try {
|
||||
switch (submission.value.action) {
|
||||
case "rename": {
|
||||
await prisma.organization.update({
|
||||
where: {
|
||||
slug: organizationSlug,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
title: submission.value.organizationName,
|
||||
},
|
||||
});
|
||||
|
||||
return redirectWithSuccessMessage(
|
||||
organizationPath({ slug: organizationSlug }),
|
||||
request,
|
||||
`Organization renamed to ${submission.value.organizationName}`
|
||||
);
|
||||
}
|
||||
case "delete": {
|
||||
const deleteOrganizationService = new DeleteOrganizationService();
|
||||
try {
|
||||
await deleteOrganizationService.call({ organizationSlug, userId, request });
|
||||
|
||||
//we need to clear the project from the session
|
||||
const removeProjectIdSession = await clearCurrentProjectId(request);
|
||||
return redirect(rootPath(), {
|
||||
headers: {
|
||||
"Set-Cookie": await commitCurrentProjectSession(removeProjectIdSession),
|
||||
},
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
||||
logger.error("Organization could not be deleted", {
|
||||
error: errorMessage,
|
||||
});
|
||||
return redirectWithErrorMessage(
|
||||
organizationSettingsPath({ slug: organizationSlug }),
|
||||
request,
|
||||
errorMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
return json({ errors: { body: error.message } }, { status: 400 });
|
||||
}
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const organization = useOrganization();
|
||||
const lastSubmission = useActionData();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [renameForm, { organizationName }] = useForm({
|
||||
id: "rename-organization",
|
||||
// TODO: type this
|
||||
lastSubmission: lastSubmission as any,
|
||||
shouldRevalidate: "onSubmit",
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
schema: createSchema(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [deleteForm, { organizationSlug }] = useForm({
|
||||
id: "delete-organization",
|
||||
// TODO: type this
|
||||
lastSubmission: lastSubmission as any,
|
||||
shouldValidate: "onInput",
|
||||
shouldRevalidate: "onSubmit",
|
||||
onValidate({ formData }) {
|
||||
return parse(formData, {
|
||||
schema: createSchema({
|
||||
getSlugMatch: (slug) => ({
|
||||
isMatch: slug === organization.slug,
|
||||
organizationSlug: organization.slug,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isRenameLoading =
|
||||
navigation.formData?.get("action") === "rename" &&
|
||||
(navigation.state === "submitting" || navigation.state === "loading");
|
||||
|
||||
const isDeleteLoading =
|
||||
navigation.formData?.get("action") === "delete" &&
|
||||
(navigation.state === "submitting" || navigation.state === "loading");
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader>
|
||||
<PageTitleRow>
|
||||
<PageTitle title={`${organization.title} organization settings`} />
|
||||
</PageTitleRow>
|
||||
</PageHeader>
|
||||
|
||||
<PageBody>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<Form method="post" {...renameForm.props} className="max-w-md">
|
||||
<input type="hidden" name="action" value="rename" />
|
||||
<Fieldset>
|
||||
<InputGroup>
|
||||
<Label htmlFor={organizationName.id}>Rename your organization</Label>
|
||||
<Input
|
||||
{...conform.input(organizationName, { type: "text" })}
|
||||
defaultValue={organization.title}
|
||||
placeholder="Your organization name"
|
||||
icon="folder"
|
||||
autoFocus
|
||||
/>
|
||||
<FormError id={organizationName.errorId}>{organizationName.error}</FormError>
|
||||
</InputGroup>
|
||||
<FormButtons
|
||||
confirmButton={
|
||||
<Button
|
||||
type="submit"
|
||||
variant={"primary/small"}
|
||||
disabled={isRenameLoading}
|
||||
LeadingIcon={isRenameLoading ? "spinner-white" : undefined}
|
||||
>
|
||||
Rename organization
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Header2 spacing>Danger zone</Header2>
|
||||
<Form
|
||||
method="post"
|
||||
{...deleteForm.props}
|
||||
className="max-w-md rounded-sm border border-rose-500/40"
|
||||
>
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<Fieldset className="p-4">
|
||||
<InputGroup>
|
||||
<Label htmlFor={organizationSlug.id}>Delete organization</Label>
|
||||
<Input
|
||||
{...conform.input(organizationSlug, { type: "text" })}
|
||||
placeholder="Your organization slug"
|
||||
icon="warning"
|
||||
autoFocus
|
||||
/>
|
||||
<FormError id={organizationSlug.errorId}>{organizationSlug.error}</FormError>
|
||||
<FormError>{deleteForm.error}</FormError>
|
||||
<Hint>
|
||||
This change is irreversible, so please be certain. Type in the Organization slug
|
||||
<InlineCode variant="extra-small">{organization.slug}</InlineCode> and then
|
||||
press Delete.
|
||||
</Hint>
|
||||
</InputGroup>
|
||||
<FormButtons
|
||||
confirmButton={
|
||||
<Button
|
||||
type="submit"
|
||||
variant={"danger/small"}
|
||||
LeadingIcon={isDeleteLoading ? "spinner-white" : "trash-can"}
|
||||
leadingIconClassName="text-white"
|
||||
disabled={isDeleteLoading}
|
||||
>
|
||||
Delete organization
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</PageBody>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
+51
-9
@@ -1,12 +1,14 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import type { ActionFunction } from "@remix-run/node";
|
||||
import type { ActionFunction, LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { json } from "@remix-run/node";
|
||||
import { Form, useActionData } from "@remix-run/react";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import invariant from "tiny-invariant";
|
||||
import { z } from "zod";
|
||||
import { MainCenteredContainer } from "~/components/layout/AppLayout";
|
||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||
import { Callout } from "~/components/primitives/Callout";
|
||||
import { Fieldset } from "~/components/primitives/Fieldset";
|
||||
import { FormButtons } from "~/components/primitives/FormButtons";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
@@ -14,11 +16,44 @@ import { FormTitle } from "~/components/primitives/FormTitle";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { prisma } from "~/db.server";
|
||||
import { redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { createProject } from "~/models/project.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { organizationPath, projectPath } from "~/utils/pathBuilder";
|
||||
import { OrganizationParamsSchema, organizationPath, projectPath } from "~/utils/pathBuilder";
|
||||
|
||||
export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
const userId = await requireUserId(request);
|
||||
const { organizationSlug } = OrganizationParamsSchema.parse(params);
|
||||
|
||||
const organization = await prisma.organization.findUnique({
|
||||
where: { slug: organizationSlug, members: { some: { userId } } },
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
_count: {
|
||||
select: {
|
||||
projects: {
|
||||
where: { deletedAt: null },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
throw new Response(null, { status: 404, statusText: "Organization not found" });
|
||||
}
|
||||
|
||||
return typedjson({
|
||||
organization: {
|
||||
id: organization.id,
|
||||
title: organization.title,
|
||||
slug: organizationSlug,
|
||||
projectsCount: organization._count.projects,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const schema = z.object({
|
||||
projectName: z.string().min(3, "Project name must have at least 3 characters").max(50),
|
||||
@@ -54,7 +89,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
};
|
||||
|
||||
export default function NewOrganizationPage() {
|
||||
const organization = useOrganization();
|
||||
const { organization } = useTypedLoaderData<typeof loader>();
|
||||
const lastSubmission = useActionData();
|
||||
|
||||
const [form, { projectName }] = useForm({
|
||||
@@ -71,10 +106,15 @@ export default function NewOrganizationPage() {
|
||||
<div>
|
||||
<FormTitle
|
||||
LeadingIcon="folder"
|
||||
title="Create a new Project"
|
||||
description="Create a new Project to help you organize the Jobs you create."
|
||||
title="Create a new project"
|
||||
description={`This will create a new project in your "${organization.title}" organization. `}
|
||||
/>
|
||||
<Form method="post" {...form.props}>
|
||||
{organization.projectsCount === 0 && (
|
||||
<Callout variant="info" className="mb-4">
|
||||
Organizations require at least one project, please create one to continue.
|
||||
</Callout>
|
||||
)}
|
||||
<Fieldset>
|
||||
<InputGroup>
|
||||
<Label htmlFor={projectName.id}>Project name</Label>
|
||||
@@ -93,9 +133,11 @@ export default function NewOrganizationPage() {
|
||||
</Button>
|
||||
}
|
||||
cancelButton={
|
||||
<LinkButton to={organizationPath(organization)} variant={"secondary/small"}>
|
||||
Cancel
|
||||
</LinkButton>
|
||||
organization.projectsCount > 0 ? (
|
||||
<LinkButton to={organizationPath(organization)} variant={"secondary/small"}>
|
||||
Cancel
|
||||
</LinkButton>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Fieldset>
|
||||
@@ -3,7 +3,7 @@ import { parse } from "@conform-to/zod";
|
||||
import { RadioGroup } from "@radix-ui/react-radio-group";
|
||||
import type { ActionFunction, LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { json, redirect } from "@remix-run/node";
|
||||
import { Form, useActionData } from "@remix-run/react";
|
||||
import { Form, useActionData, useNavigation } from "@remix-run/react";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { z } from "zod";
|
||||
import { MainCenteredContainer } from "~/components/layout/AppLayout";
|
||||
@@ -23,7 +23,7 @@ import { createOrganization } from "~/models/organization.server";
|
||||
import { NewOrganizationPresenter } from "~/presenters/NewOrganizationPresenter.server";
|
||||
import { commitCurrentProjectSession, setCurrentProjectId } from "~/services/currentProject.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { plansPath, projectPath, rootPath, selectPlanPath } from "~/utils/pathBuilder";
|
||||
import { projectPath, rootPath, selectPlanPath } from "~/utils/pathBuilder";
|
||||
|
||||
const schema = z.object({
|
||||
orgName: z.string().min(3).max(50),
|
||||
@@ -86,6 +86,7 @@ export default function NewOrganizationPage() {
|
||||
const { hasOrganizations } = useTypedLoaderData<typeof loader>();
|
||||
const lastSubmission = useActionData();
|
||||
const { isManagedCloud } = useFeatures();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const [form, { orgName, projectName }] = useForm({
|
||||
id: "create-organization",
|
||||
@@ -95,8 +96,11 @@ export default function NewOrganizationPage() {
|
||||
return parse(formData, { schema });
|
||||
},
|
||||
shouldRevalidate: "onSubmit",
|
||||
shouldValidate: "onSubmit",
|
||||
});
|
||||
|
||||
const isLoading = navigation.state === "submitting" || navigation.state === "loading";
|
||||
|
||||
return (
|
||||
<MainCenteredContainer className="max-w-[22rem]">
|
||||
<FormTitle LeadingIcon="organization" title="Create an Organization" />
|
||||
@@ -161,7 +165,12 @@ export default function NewOrganizationPage() {
|
||||
|
||||
<FormButtons
|
||||
confirmButton={
|
||||
<Button type="submit" variant={"primary/small"} TrailingIcon="arrow-right">
|
||||
<Button
|
||||
type="submit"
|
||||
variant={"primary/small"}
|
||||
TrailingIcon="arrow-right"
|
||||
disabled={isLoading}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export default function Page() {
|
||||
<Checkbox
|
||||
id="marketingEmails"
|
||||
{...conform.input(marketingEmails, { type: "checkbox" })}
|
||||
label="Receive product updates"
|
||||
label="Receive onboarding emails"
|
||||
variant="simple/small"
|
||||
defaultChecked={user.marketingEmails}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { ActionFunctionArgs, json } from "@remix-run/server-runtime";
|
||||
import { z } from "zod";
|
||||
import { DeleteEndpointIndexService } from "~/services/endpoints/deleteEndpointService";
|
||||
import { DeleteEndpointService } from "~/services/endpoints/deleteEndpointService";
|
||||
import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { workerQueue } from "~/services/worker.server";
|
||||
@@ -42,7 +42,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
|
||||
return json({ success: true });
|
||||
}
|
||||
case "delete": {
|
||||
const service = new DeleteEndpointIndexService();
|
||||
const service = new DeleteEndpointService();
|
||||
await service.call(endpointParam, userId);
|
||||
return json({ success: true });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, json } from "@remix-run/server-runtime";
|
||||
import { PlainClient, uiComponent } from "@team-plain/typescript-sdk";
|
||||
import { inspect } from "util";
|
||||
import { z } from "zod";
|
||||
import { env } from "~/env.server";
|
||||
import { redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { requireUser } from "~/services/session.server";
|
||||
import crypto from "node:crypto";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { prisma } from "~/db.server";
|
||||
import { AppContainer, MainCenteredContainer } from "~/components/layout/AppLayout";
|
||||
import { Header1 } from "~/components/primitives/Headers";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { TextLink } from "~/components/primitives/TextLink";
|
||||
import { LinkButton } from "~/components/primitives/Buttons";
|
||||
import { rootPath } from "~/utils/pathBuilder";
|
||||
import { FormTitle } from "~/components/primitives/FormTitle";
|
||||
import { EnvelopeIcon } from "@heroicons/react/24/solid";
|
||||
|
||||
export const ParamsSchema = z.object({
|
||||
userId: z.string(),
|
||||
token: z.string(),
|
||||
});
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const { userId, token } = ParamsSchema.parse(params);
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return typedjson({
|
||||
success: false as const,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
//check that the token is valid for the userId
|
||||
const hashedUserId = crypto
|
||||
.createHash("sha256")
|
||||
.update(`${userId}-${env.MAGIC_LINK_SECRET}`)
|
||||
.digest("hex");
|
||||
if (hashedUserId !== token) {
|
||||
return typedjson({
|
||||
success: false as const,
|
||||
message: "This unsubscribe link was invalid so we can't unsubscribe you.",
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: { marketingEmails: false },
|
||||
});
|
||||
|
||||
return typedjson({ success: true as const, email: user.email });
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
|
||||
return typedjson({ success: false as const, message: errorMessage });
|
||||
}
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const result = useTypedLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<AppContainer>
|
||||
<MainCenteredContainer className="max-w-[22rem]">
|
||||
{result.success ? (
|
||||
<div>
|
||||
<FormTitle LeadingIcon="envelope" title="Unsubscribed" />
|
||||
<Paragraph spacing>
|
||||
You have unsubscribed from onboarding emails, {result.email}.
|
||||
</Paragraph>
|
||||
<LinkButton variant="primary/medium" to={rootPath()}>
|
||||
Dashboard
|
||||
</LinkButton>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<FormTitle LeadingIcon="envelope" title="Unsubscribe failed" />
|
||||
<Paragraph spacing>{result.message}</Paragraph>
|
||||
<Paragraph spacing>
|
||||
If you believe this is a bug, please{" "}
|
||||
<TextLink href="https://trigger.dev/contact">contact support</TextLink>.
|
||||
</Paragraph>
|
||||
<LinkButton variant="primary/medium" to={rootPath()}>
|
||||
Dashboard
|
||||
</LinkButton>
|
||||
</div>
|
||||
)}
|
||||
</MainCenteredContainer>
|
||||
</AppContainer>
|
||||
);
|
||||
}
|
||||
@@ -31,3 +31,9 @@ export async function setCurrentProjectId(id: string, request: Request) {
|
||||
session.set("currentProjectId", id);
|
||||
return session;
|
||||
}
|
||||
|
||||
export async function clearCurrentProjectId(request: Request) {
|
||||
const session = await getCurrentProjectSession(request);
|
||||
session.unset("currentProjectId");
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { DateFormatter } from "@internationalized/date";
|
||||
import { PrismaClient } from "@trigger.dev/database";
|
||||
import { prisma } from "~/db.server";
|
||||
import { featuresForRequest } from "~/features.server";
|
||||
import { BillingService } from "./billing.server";
|
||||
import { DeleteProjectService } from "./deleteProject.server";
|
||||
|
||||
export class DeleteOrganizationService {
|
||||
#prismaClient: PrismaClient;
|
||||
|
||||
constructor(prismaClient: PrismaClient = prisma) {
|
||||
this.#prismaClient = prismaClient;
|
||||
}
|
||||
|
||||
public async call({
|
||||
organizationSlug,
|
||||
userId,
|
||||
request,
|
||||
}: {
|
||||
organizationSlug: string;
|
||||
userId: string;
|
||||
request: Request;
|
||||
}) {
|
||||
const organization = await this.#prismaClient.organization.findFirst({
|
||||
include: {
|
||||
projects: true,
|
||||
members: true,
|
||||
},
|
||||
where: {
|
||||
slug: organizationSlug,
|
||||
members: { some: { userId: userId } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
throw new Error("Organization not found");
|
||||
}
|
||||
|
||||
if (organization.deletedAt) {
|
||||
throw new Error("Organization already deleted");
|
||||
}
|
||||
|
||||
//check if they have an active subscription
|
||||
const { isManagedCloud } = featuresForRequest(request);
|
||||
const billingPresenter = new BillingService(isManagedCloud);
|
||||
const currentPlan = await billingPresenter.currentPlan(organization.id);
|
||||
|
||||
if (currentPlan && currentPlan.subscription && currentPlan.subscription.isPaying) {
|
||||
//they've cancelled and that date hasn't passed yet
|
||||
if (
|
||||
currentPlan.subscription.canceledAt &&
|
||||
new Date(currentPlan.subscription.canceledAt) > new Date()
|
||||
) {
|
||||
//a dateformatter that produces results like "Jan 1 2024"
|
||||
const dateFormatter = new DateFormatter("en-us", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
throw new Error(
|
||||
`This Organization has a canceled subscription. You can delete it when the cancelation date (${dateFormatter.format(
|
||||
new Date(currentPlan.subscription.canceledAt)
|
||||
)}) is in the past.`
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("You can't delete an Organization that has an active subscription");
|
||||
}
|
||||
|
||||
// loop through the projects and delete them
|
||||
const projectDeleteService = new DeleteProjectService();
|
||||
for (const project of organization.projects) {
|
||||
await projectDeleteService.call({ projectId: project.id, userId });
|
||||
}
|
||||
|
||||
//set all the integrations to disabled
|
||||
await this.#prismaClient.integrationConnection.updateMany({
|
||||
where: {
|
||||
organizationId: organization.id,
|
||||
},
|
||||
data: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
//mark the organization as deleted
|
||||
await this.#prismaClient.organization.update({
|
||||
where: {
|
||||
id: organization.id,
|
||||
},
|
||||
data: {
|
||||
runsEnabled: false,
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { PrismaClient } from "@trigger.dev/database";
|
||||
import { prisma } from "~/db.server";
|
||||
import { DeleteEndpointService } from "./endpoints/deleteEndpointService";
|
||||
import { logger } from "./logger.server";
|
||||
import { DisableScheduleSourceService } from "./schedules/disableScheduleSource.server";
|
||||
|
||||
type Options = ({ projectId: string } | { projectSlug: string }) & {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export class DeleteProjectService {
|
||||
#prismaClient: PrismaClient;
|
||||
|
||||
constructor(prismaClient: PrismaClient = prisma) {
|
||||
this.#prismaClient = prismaClient;
|
||||
}
|
||||
|
||||
public async call(options: Options) {
|
||||
const projectId = await this.#getProjectId(options);
|
||||
const project = await this.#prismaClient.project.findFirst({
|
||||
include: {
|
||||
environments: {
|
||||
include: {
|
||||
endpoints: true,
|
||||
},
|
||||
},
|
||||
jobs: {
|
||||
where: { deletedAt: null },
|
||||
include: {
|
||||
aliases: {
|
||||
where: {
|
||||
name: "latest",
|
||||
},
|
||||
include: {
|
||||
version: true,
|
||||
},
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
organization: true,
|
||||
},
|
||||
where: {
|
||||
id: projectId,
|
||||
organization: { members: { some: { userId: options.userId } } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new Error("Project not found");
|
||||
}
|
||||
|
||||
if (project.deletedAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
//disable and delete all jobs
|
||||
const service = new DisableScheduleSourceService();
|
||||
for (const environment of project.environments) {
|
||||
//disable the event dispatchers
|
||||
await this.#prismaClient.eventDispatcher.updateMany({
|
||||
where: {
|
||||
environmentId: environment.id,
|
||||
},
|
||||
data: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
const eventDispatchers = await this.#prismaClient.eventDispatcher.findMany({
|
||||
where: {
|
||||
environmentId: environment.id,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Deleting jobs", { jobs: project.jobs });
|
||||
for (const job of project.jobs) {
|
||||
//disable all the job versions
|
||||
await this.#prismaClient.jobVersion.updateMany({
|
||||
where: {
|
||||
jobId: job.id,
|
||||
},
|
||||
data: {
|
||||
status: "DISABLED",
|
||||
},
|
||||
});
|
||||
|
||||
await this.#prismaClient.job.update({
|
||||
where: {
|
||||
id: job.id,
|
||||
},
|
||||
data: {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
//disable scheduled sources
|
||||
for (const eventDispatcher of eventDispatchers) {
|
||||
await service.call({
|
||||
key: job.id,
|
||||
dispatcher: eventDispatcher,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//delete all endpoints
|
||||
const deleteEndpointService = new DeleteEndpointService();
|
||||
for (const environment of project.environments) {
|
||||
for (const endpoint of environment.endpoints) {
|
||||
await deleteEndpointService.call(endpoint.id, options.userId);
|
||||
}
|
||||
}
|
||||
|
||||
//mark the project as deleted
|
||||
await this.#prismaClient.project.update({
|
||||
where: {
|
||||
id: project.id,
|
||||
},
|
||||
data: {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async #getProjectId(options: Options) {
|
||||
if ("projectId" in options) {
|
||||
return options.projectId;
|
||||
}
|
||||
|
||||
const { id } = await this.#prismaClient.project.findFirstOrThrow({
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
slug: options.projectSlug,
|
||||
},
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PrismaClient } from "@trigger.dev/database";
|
||||
import { prisma } from "~/db.server";
|
||||
|
||||
export class DeleteEndpointIndexService {
|
||||
export class DeleteEndpointService {
|
||||
#prismaClient: PrismaClient;
|
||||
|
||||
constructor(prismaClient: PrismaClient = prisma) {
|
||||
|
||||
@@ -577,6 +577,13 @@ export class IntegrationAuthRepository {
|
||||
throw new Error(`Connection ${connectionId} not found`);
|
||||
}
|
||||
|
||||
if (!connection.enabled) {
|
||||
logger.info("Connection is disabled", {
|
||||
connection,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let customOAuthClient: OAuthClient | undefined;
|
||||
if (connection.integration.customClientReference) {
|
||||
const secretStore = getSecretStore(env.SECRET_STORE);
|
||||
@@ -687,9 +694,15 @@ export class IntegrationAuthRepository {
|
||||
if (connection.expiresAt) {
|
||||
const refreshBy = new Date(connection.expiresAt.getTime() - tokenRefreshThreshold * 1000);
|
||||
if (refreshBy < new Date()) {
|
||||
connection = await this.refreshConnection({
|
||||
const refreshedConnection = await this.refreshConnection({
|
||||
connectionId: connection.id,
|
||||
});
|
||||
|
||||
if (!refreshedConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection = refreshedConnection;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Job } from "@trigger.dev/database";
|
||||
import type { PrismaClient } from "~/db.server";
|
||||
import { prisma } from "~/db.server";
|
||||
import { telemetry } from "../telemetry.server";
|
||||
import { logger } from "../logger.server";
|
||||
|
||||
export class DeleteJobService {
|
||||
#prismaClient: PrismaClient;
|
||||
@@ -25,6 +26,7 @@ export class DeleteJobService {
|
||||
const allDisabled = latestVersions.every((alias) => alias.version.status === "DISABLED");
|
||||
|
||||
if (!allDisabled) {
|
||||
logger.info("Not all latest versions are disabled, cannot delete job", { jobId: job.id });
|
||||
throw new Error("All latest versions must be disabled before deleting a job");
|
||||
}
|
||||
|
||||
|
||||
@@ -207,6 +207,19 @@ export class PerformRunExecutionV3Service {
|
||||
|
||||
forceYieldCoordinator.deregisterRun(run.id);
|
||||
|
||||
//if the run has been canceled while it's being executed, we shouldn't do anything more
|
||||
const updatedRun = await this.#prismaClient.jobRun.findUnique({
|
||||
select: {
|
||||
status: true,
|
||||
},
|
||||
where: {
|
||||
id: run.id,
|
||||
},
|
||||
});
|
||||
if (!updatedRun || updatedRun.status === "CANCELED") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return await this.#failRunExecutionWithRetry(
|
||||
run,
|
||||
|
||||
@@ -131,6 +131,10 @@ export function organizationBillingPath(organization: OrgForPath) {
|
||||
return `${organizationPath(organization)}/billing`;
|
||||
}
|
||||
|
||||
export function organizationSettingsPath(organization: OrgForPath) {
|
||||
return `${organizationPath(organization)}/settings`;
|
||||
}
|
||||
|
||||
export function usagePath(organization: OrgForPath) {
|
||||
return `${organizationPath(organization)}/billing`;
|
||||
}
|
||||
@@ -216,6 +220,10 @@ export function projectEventsPath(organization: OrgForPath, project: ProjectForP
|
||||
return `${projectPath(organization, project)}/events`;
|
||||
}
|
||||
|
||||
export function projectSettingsPath(organization: OrgForPath, project: ProjectForPath) {
|
||||
return `${projectPath(organization, project)}/settings`;
|
||||
}
|
||||
|
||||
export function projectEventPath(
|
||||
organization: OrgForPath,
|
||||
project: ProjectForPath,
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/airtable
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/airtable",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for airtable",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"airtable": "^0.12.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/github",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official GitHub integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -30,8 +30,8 @@
|
||||
"@octokit/request-error": "^5.0.1",
|
||||
"@octokit/webhooks": "^12.0.10",
|
||||
"octokit": "^3.1.2",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/linear
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/linear",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for @linear/sdk",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@linear/sdk": "^8.0.0",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d69e4e71: Retry 400 status code, OpenAI returns this sometimes for no reason...
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/openai",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official OpenAI integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"openai": "^4.16.1",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -60,11 +60,11 @@ function createTaskUsageProperties(
|
||||
},
|
||||
...("completion_tokens" in usage
|
||||
? [
|
||||
{
|
||||
label: "Completion Usage",
|
||||
text: String(usage.completion_tokens),
|
||||
},
|
||||
]
|
||||
{
|
||||
label: "Completion Usage",
|
||||
text: String(usage.completion_tokens),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
@@ -83,35 +83,35 @@ function createTaskRateLimitProperties(headers: Headers | undefined) {
|
||||
return [
|
||||
...(remainingRequests
|
||||
? [
|
||||
{
|
||||
label: "Remaining Requests",
|
||||
text: remainingRequests ?? "Unknown",
|
||||
},
|
||||
]
|
||||
{
|
||||
label: "Remaining Requests",
|
||||
text: remainingRequests ?? "Unknown",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(resetRequests
|
||||
? [
|
||||
{
|
||||
label: "Reset Requests",
|
||||
text: resetRequests ?? "Unknown",
|
||||
},
|
||||
]
|
||||
{
|
||||
label: "Reset Requests",
|
||||
text: resetRequests ?? "Unknown",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(remainingTokens
|
||||
? [
|
||||
{
|
||||
label: "Remaining Tokens",
|
||||
text: remainingTokens ?? "Unknown",
|
||||
},
|
||||
]
|
||||
{
|
||||
label: "Remaining Tokens",
|
||||
text: remainingTokens ?? "Unknown",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(resetTokens
|
||||
? [
|
||||
{
|
||||
label: "Reset Tokens",
|
||||
text: resetTokens ?? "Unknown",
|
||||
},
|
||||
]
|
||||
{
|
||||
label: "Reset Tokens",
|
||||
text: resetTokens ?? "Unknown",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
@@ -128,6 +128,8 @@ export function handleOpenAIError(error: unknown) {
|
||||
}
|
||||
|
||||
return (
|
||||
//sometimes OpenAI returns a 400 that when retried becomes a 200…
|
||||
error.status === 400 ||
|
||||
error.status === 429 ||
|
||||
error.status === 408 ||
|
||||
error.status === 409 ||
|
||||
@@ -295,7 +297,7 @@ const requestOptionsKeys: KeysEnum<OpenAIRequestOptions> = {
|
||||
|
||||
export const isRequestOptions = (obj: unknown): obj is OpenAIRequestOptions => {
|
||||
return (
|
||||
typeof obj === 'object' &&
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
!isEmptyObj(obj) &&
|
||||
Object.keys(obj).every((k) => hasOwn(requestOptionsKeys, k))
|
||||
@@ -310,4 +312,4 @@ function isEmptyObj(obj: Object | null | undefined): boolean {
|
||||
|
||||
function hasOwn(obj: Object, key: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/plain
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/plain",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official Plain.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@team-plain/typescript-sdk": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/replicate
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/replicate",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for replicate",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"replicate": "^0.18.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/resend",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official Resend.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"resend": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sendgrid",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for @sendgrid/mail",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.8.0"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/shopify
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/shopify",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for @shopify/shopify-api",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@shopify/shopify-api": "^8.0.2",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@slack/web-api": "^6.8.1",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/stripe
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/stripe",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for stripe",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"stripe": "^12.14.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/supabase
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/supabase",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev integration for @supabase/supabase-js",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.26.0",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"supabase-management-js": "^1.0.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/typeform
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/typeform",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The official Typeform integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.18",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@typeform/api-client": "^1.8.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/astro
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/astro",
|
||||
"description": "An Astro-native integration for Trigger.dev background jobs platform",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^3.0.12",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# trigger.dev
|
||||
|
||||
## 1.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
|
||||
## 1.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trigger.dev",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"description": "A Command-Line Interface for Trigger.dev (v3) projects",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# create-trigger
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
- @trigger.dev/yalt@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/cli",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "The Trigger.dev CLI",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @trigger.dev/core-backend
|
||||
|
||||
## 2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
## 2.3.16
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/core-backend",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Core code used across `@trigger.dev/sdk` and Trigger.dev server",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# internal-platform
|
||||
|
||||
## 2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
## 2.3.16
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/core",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Core code used across the Trigger.dev SDK and platform",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- DropIndex
|
||||
DROP INDEX "idx_jobrun_jobid_createdat";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Project" ADD COLUMN "deletedAt" TIMESTAMP(3);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_jobrun_jobId_createdAt" ON "JobRun"("jobId", "createdAt" DESC);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Organization" ADD COLUMN "deletedAt" TIMESTAMP(3);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "IntegrationConnection" ADD COLUMN "enabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
@@ -109,8 +109,9 @@ model Organization {
|
||||
|
||||
maximumExecutionTimePerRunInMs Int @default(900000) // 15 minutes
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
companySize String?
|
||||
|
||||
@@ -271,6 +272,9 @@ model IntegrationConnection {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
/// If enabled is false, OAuth refreshing will not be attempted
|
||||
enabled Boolean @default(true)
|
||||
|
||||
runConnections RunConnection[]
|
||||
}
|
||||
|
||||
@@ -398,8 +402,9 @@ model Project {
|
||||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
organizationId String
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
environments RuntimeEnvironment[]
|
||||
endpoints Endpoint[]
|
||||
@@ -848,6 +853,8 @@ model JobRun {
|
||||
statuses JobRunStatusRecord[]
|
||||
autoYieldExecution JobRunAutoYieldExecution[]
|
||||
subscriptions JobRunSubscription[]
|
||||
|
||||
@@index([jobId, createdAt(sort: Desc)], map: "idx_jobrun_jobId_createdAt")
|
||||
}
|
||||
|
||||
enum JobRunStatus {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @trigger.dev/eslint-plugin
|
||||
|
||||
## 2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
## 2.3.16
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/eslint-plugin",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "ESLint plugin with trigger.dev best practices",
|
||||
"keywords": [
|
||||
"eslint",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/express
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/express",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Official Express adapter for Trigger.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/hono
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/hono",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "A Trigger.dev adapter for Hono.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "3.x",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/integration-kit
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/integration-kit",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev Integration Kit has helpers to make creating integrations easier",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/nestjs
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/nestjs",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Official NestJS adapter for Trigger.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": ">=10.0.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.2.4",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/nextjs
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/nextjs",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev Next.js integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -41,7 +41,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"next": ">=12.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/react
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/react",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev React SDK",
|
||||
"license": "MIT",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "5.0.0-beta.2",
|
||||
"@trigger.dev/core": "workspace:^2.3.17",
|
||||
"@trigger.dev/core": "workspace:^2.3.18",
|
||||
"debug": "^4.3.4",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/remix
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/remix",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev Remix integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18",
|
||||
"@remix-run/server-runtime": ">1.19.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @trigger.dev/sveltekit
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sveltekit",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "Trigger.dev svelteKit integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.3.17"
|
||||
"@trigger.dev/sdk": "workspace:^2.3.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/testing
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
- @trigger.dev/sdk@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/testing",
|
||||
"description": "A collection of useful tools to write tests for Trigger.dev.",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @trigger.dev/sdk
|
||||
|
||||
## 2.3.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.3.18
|
||||
- @trigger.dev/core-backend@2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sdk",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "trigger.dev Node.JS SDK",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -30,8 +30,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/core": "workspace:^2.3.17",
|
||||
"@trigger.dev/core-backend": "workspace:^2.3.17",
|
||||
"@trigger.dev/core": "workspace:^2.3.18",
|
||||
"@trigger.dev/core-backend": "workspace:^2.3.18",
|
||||
"chalk": "^5.2.0",
|
||||
"cronstrue": "^2.21.0",
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @trigger.dev/yalt
|
||||
|
||||
## 2.3.18
|
||||
|
||||
## 2.3.17
|
||||
|
||||
## 2.3.16
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/yalt",
|
||||
"version": "2.3.17",
|
||||
"version": "2.3.18",
|
||||
"description": "yalt.dev client library",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
Generated
+36
-36
@@ -430,7 +430,7 @@ importers:
|
||||
devDependencies:
|
||||
eslint: 8.31.0
|
||||
eslint-config-prettier: 8.6.0_eslint@8.31.0
|
||||
eslint-config-turbo: 1.11.3_eslint@8.31.0
|
||||
eslint-config-turbo: 1.12.3_eslint@8.31.0
|
||||
eslint-plugin-react: 7.31.8_eslint@8.31.0
|
||||
typescript: 4.9.4
|
||||
|
||||
@@ -458,8 +458,8 @@ importers:
|
||||
|
||||
integrations/airtable:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
@@ -488,8 +488,8 @@ importers:
|
||||
'@octokit/types': ^12.4.0
|
||||
'@octokit/webhooks': ^12.0.10
|
||||
'@octokit/webhooks-types': ^7.3.1
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': '18'
|
||||
@@ -519,8 +519,8 @@ importers:
|
||||
integrations/linear:
|
||||
specifiers:
|
||||
'@linear/sdk': ^8.0.0
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
@@ -543,8 +543,8 @@ importers:
|
||||
|
||||
integrations/openai:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/jest': ^29.5.3
|
||||
@@ -573,8 +573,8 @@ importers:
|
||||
integrations/plain:
|
||||
specifiers:
|
||||
'@team-plain/typescript-sdk': ^2.7.0
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': '18'
|
||||
@@ -595,8 +595,8 @@ importers:
|
||||
|
||||
integrations/replicate:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
@@ -620,8 +620,8 @@ importers:
|
||||
|
||||
integrations/resend:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': '18'
|
||||
@@ -644,8 +644,8 @@ importers:
|
||||
integrations/sendgrid:
|
||||
specifiers:
|
||||
'@sendgrid/mail': ^7.7.0
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
@@ -667,8 +667,8 @@ importers:
|
||||
integrations/shopify:
|
||||
specifiers:
|
||||
'@shopify/shopify-api': ^8.0.2
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
@@ -692,7 +692,7 @@ importers:
|
||||
integrations/slack:
|
||||
specifiers:
|
||||
'@slack/web-api': ^6.8.1
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': '18'
|
||||
@@ -714,8 +714,8 @@ importers:
|
||||
|
||||
integrations/stripe:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 16.x
|
||||
rimraf: ^3.0.2
|
||||
@@ -740,8 +740,8 @@ importers:
|
||||
integrations/supabase:
|
||||
specifiers:
|
||||
'@supabase/supabase-js': ^2.26.0
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': 18.x
|
||||
@@ -766,8 +766,8 @@ importers:
|
||||
|
||||
integrations/typeform:
|
||||
specifiers:
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/integration-kit': workspace:^2.3.18
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@typeform/api-client': ^1.8.0
|
||||
'@types/node': 16.x
|
||||
@@ -1112,7 +1112,7 @@ importers:
|
||||
|
||||
packages/express:
|
||||
specifiers:
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/debug': ^4.1.7
|
||||
@@ -1182,7 +1182,7 @@ importers:
|
||||
specifiers:
|
||||
'@nestjs/common': ^10.2.4
|
||||
'@remix-run/web-fetch': ^4.3.5
|
||||
'@trigger.dev/sdk': workspace:^2.3.17
|
||||
'@trigger.dev/sdk': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/debug': ^4.1.7
|
||||
@@ -1241,7 +1241,7 @@ importers:
|
||||
packages/react:
|
||||
specifiers:
|
||||
'@tanstack/react-query': 5.0.0-beta.2
|
||||
'@trigger.dev/core': workspace:^2.3.17
|
||||
'@trigger.dev/core': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/debug': ^4.1.7
|
||||
'@types/react': 18.2.17
|
||||
@@ -1344,8 +1344,8 @@ importers:
|
||||
|
||||
packages/trigger-sdk:
|
||||
specifiers:
|
||||
'@trigger.dev/core': workspace:^2.3.17
|
||||
'@trigger.dev/core-backend': workspace:^2.3.17
|
||||
'@trigger.dev/core': workspace:^2.3.18
|
||||
'@trigger.dev/core-backend': workspace:^2.3.18
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/debug': ^4.1.7
|
||||
@@ -20591,13 +20591,13 @@ packages:
|
||||
eslint: 8.45.0
|
||||
dev: true
|
||||
|
||||
/eslint-config-turbo/1.11.3_eslint@8.31.0:
|
||||
resolution: {integrity: sha512-v7CHpAHodBKlj+r+R3B2DJlZbCjpZLnK7gO/vCRk/Lc+tlD/f04wM6rmHlerevOlchtmwARilRLBnmzNLffTyQ==}
|
||||
/eslint-config-turbo/1.12.3_eslint@8.31.0:
|
||||
resolution: {integrity: sha512-Q46MEOiNJpJWC3Et5/YEuIYYhbOieS04yZwQOinO2hpZw3folEXV+hbwVo8M+ap/q8gtpjIWiRMZ1A4QxmhEqQ==}
|
||||
peerDependencies:
|
||||
eslint: '>6.6.0'
|
||||
dependencies:
|
||||
eslint: 8.31.0
|
||||
eslint-plugin-turbo: 1.11.3_eslint@8.31.0
|
||||
eslint-plugin-turbo: 1.12.3_eslint@8.31.0
|
||||
dev: true
|
||||
|
||||
/eslint-doc-generator/1.4.3_eslint@8.45.0:
|
||||
@@ -21300,8 +21300,8 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-turbo/1.11.3_eslint@8.31.0:
|
||||
resolution: {integrity: sha512-R5ftTTWQzEYaKzF5g6m/MInCU8pIN+2TLL+S50AYBr1enwUovdZmnZ1HDwFMaxIjJ8x5ah+jvAzql5IJE9VWaA==}
|
||||
/eslint-plugin-turbo/1.12.3_eslint@8.31.0:
|
||||
resolution: {integrity: sha512-7hEyxa+oP898EFNoxVenHlH8jtBwV1hbbIkdQWgqDcB0EmVNGVEZkYRo5Hm6BuMAjR433B+NISBJdj0bQo4/Lg==}
|
||||
peerDependencies:
|
||||
eslint: '>6.6.0'
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user