Files
triggerdotdev--trigger.dev/apps/webapp/app/utils/pathBuilder.ts
Oskar Otwinowski 69dc7bcde8 feat(webapp): Vercel / Slack integrations improvements (#3108)
##  Checklist

- [x] I have followed every step in the [contributing
guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md)
- [x] The PR title follows the convention.
- [x] I ran and tested the code works

---

## Testing

Slack + GitHub + Vercel + Builds + Deployments

---

## Changelog

Settings changes:
- Split general from integrations
- Add new Slack section to org level integrations
Vercel improvements:
- bugfix for TRIGGER_SECRET_KEY collision
- onboarding improvements for connecting to projects
- new loops event
Slack improvements:
- nicer alerts
Webhook/Email alerts:
- rich events with Github & Vercel integration data

---

## Screenshots


<img width="2550" height="652" alt="Screenshot 2026-02-20 at 21 53 34"
src="https://github.com/user-attachments/assets/8d7c9f1d-5fe9-4516-8fb3-885460b4207f"
/>
<img width="843" height="710" alt="Screenshot 2026-02-23 at 10 55 54"
src="https://github.com/user-attachments/assets/8ea72c1f-431b-493c-b9a9-8076cce12262"
/>
<img width="765" height="466" alt="Screenshot 2026-02-20 at 21 52 46"
src="https://github.com/user-attachments/assets/157fafb8-b7bf-499d-8953-c2aed5e44ce0"
/>
<img width="691" height="261" alt="Screenshot 2026-02-20 at 22 04 24"
src="https://github.com/user-attachments/assets/3aea7369-2008-4af8-a9c0-5fbfa2cc381d"
/>
<img width="2032" height="1114" alt="Screenshot 2026-02-19 at 14 48 49"
src="https://github.com/user-attachments/assets/dc10c14e-cd15-445a-b5be-d694d29d20e5"
/>
<img width="2032" height="1114" alt="Screenshot 2026-02-19 at 14 49 04"
src="https://github.com/user-attachments/assets/1ef591fd-fd00-430a-9649-8b18cff9586d"
/>
<img width="1583" height="1115" alt="Screenshot 2026-02-19 at 17 32 56"
src="https://github.com/user-attachments/assets/c5c8f318-d193-4dd4-86f7-1cc4bbcc4e0c"
/>
<img width="422" height="187" alt="Screenshot 2026-02-20 at 21 57 41"
src="https://github.com/user-attachments/assets/37865cb6-4c0d-40ef-9c60-7b057d546c61"
/>
<img width="1583" height="1115" alt="Screenshot 2026-02-19 at 17 33 06"
src="https://github.com/user-attachments/assets/e9180e8e-e611-4734-9232-80c62ff863ad"
/>

💯
2026-02-23 13:48:09 +00:00

624 lines
17 KiB
TypeScript

import type { RuntimeEnvironment, TaskRun, WorkerDeployment } from "@trigger.dev/database";
import { z } from "zod";
import { type TaskRunListSearchFilters } from "~/components/runs/v3/RunFilters";
import type { Organization } from "~/models/organization.server";
import type { Project } from "~/models/project.server";
import { objectToSearchParams } from "./searchParams";
import { type WaitpointSearchParams } from "~/components/runs/v3/WaitpointTokenFilters";
export type OrgForPath = Pick<Organization, "slug">;
export type ProjectForPath = Pick<Project, "slug">;
export type EnvironmentForPath = Pick<RuntimeEnvironment, "slug">;
export type v3RunForPath = Pick<TaskRun, "friendlyId">;
export type v3SpanForPath = Pick<TaskRun, "spanId">;
export type DeploymentForPath = Pick<WorkerDeployment, "shortCode">;
export type TaskForPath = {
taskIdentifier: string;
};
export const OrganizationParamsSchema = z.object({
organizationSlug: z.string(),
});
export const ProjectParamSchema = OrganizationParamsSchema.extend({
projectParam: z.string(),
});
export const EnvironmentParamSchema = ProjectParamSchema.extend({
envParam: z.string(),
});
//v3
export const v3TaskParamsSchema = EnvironmentParamSchema.extend({
taskParam: z.string(),
});
export const v3RunParamsSchema = EnvironmentParamSchema.extend({
runParam: z.string(),
});
export const v3SpanParamsSchema = v3RunParamsSchema.extend({
spanParam: z.string(),
});
export const v3RunStreamParamsSchema = v3RunParamsSchema.extend({
streamKey: z.string(),
});
export const v3DeploymentParams = EnvironmentParamSchema.extend({
deploymentParam: z.string(),
});
export const v3ScheduleParams = EnvironmentParamSchema.extend({
scheduleParam: z.string(),
});
export function rootPath() {
return `/`;
}
export function accountPath() {
return `/account`;
}
export function personalAccessTokensPath() {
return `/account/tokens`;
}
export function accountSecurityPath() {
return `/account/security`;
}
export function invitesPath() {
return `/invites`;
}
export function confirmBasicDetailsPath() {
return `/confirm-basic-details`;
}
export function acceptInvitePath(token: string) {
return `/invite-accept?token=${token}`;
}
export function resendInvitePath() {
return `/invite-resend`;
}
export function logoutPath() {
return `/logout`;
}
export function revokeInvitePath() {
return `/invite-revoke`;
}
// Org
export function organizationPath(organization: OrgForPath) {
return `/orgs/${organizationParam(organization)}`;
}
export function newOrganizationPath() {
return `/orgs/new`;
}
export function selectPlanPath(organization: OrgForPath) {
return `${organizationPath(organization)}/select-plan`;
}
export function organizationTeamPath(organization: OrgForPath) {
return `${organizationPath(organization)}/settings/team`;
}
export function inviteTeamMemberPath(organization: OrgForPath) {
return `${organizationPath(organization)}/invite`;
}
export function organizationBillingPath(organization: OrgForPath) {
return `${organizationPath(organization)}/billing`;
}
export function organizationSettingsPath(organization: OrgForPath) {
return `${organizationPath(organization)}/settings`;
}
export function organizationIntegrationsPath(organization: OrgForPath) {
return `${organizationPath(organization)}/settings/integrations`;
}
export function organizationVercelIntegrationPath(organization: OrgForPath) {
return `${organizationIntegrationsPath(organization)}/vercel`;
}
export function organizationSlackIntegrationPath(organization: OrgForPath) {
return `${organizationIntegrationsPath(organization)}/slack`;
}
function organizationParam(organization: OrgForPath) {
return organization.slug;
}
// Project
export function newProjectPath(organization: OrgForPath, message?: string) {
return `${organizationPath(organization)}/projects/new${
message ? `?message=${encodeURIComponent(message)}` : ""
}`;
}
function projectParam(project: ProjectForPath) {
return project.slug;
}
function environmentParam(environment: EnvironmentForPath) {
return environment.slug;
}
//v3 project
export function v3ProjectPath(organization: OrgForPath, project: ProjectForPath) {
return `/orgs/${organizationParam(organization)}/projects/${projectParam(project)}`;
}
export function githubAppInstallPath(organizationSlug: string, redirectTo: string) {
return `/github/install?org_slug=${organizationSlug}&redirect_to=${encodeURIComponent(
redirectTo
)}`;
}
export function vercelAppInstallPath(organizationSlug: string, projectSlug: string) {
return `/vercel/install?org_slug=${organizationSlug}&project_slug=${projectSlug}`;
}
export function vercelCallbackPath() {
return `/vercel/callback`;
}
export function vercelResourcePath(
organizationSlug: string,
projectSlug: string,
environmentSlug: string
) {
return `/resources/orgs/${organizationSlug}/projects/${projectSlug}/env/${environmentSlug}/vercel`;
}
export function v3EnvironmentPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `/orgs/${organizationParam(organization)}/projects/${projectParam(
project
)}/env/${environmentParam(environment)}`;
}
export function v3TasksStreamingPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/tasks/stream`;
}
export function v3ApiKeysPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/apikeys`;
}
export function v3BulkActionsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/bulk-actions`;
}
export function v3BulkActionPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
bulkAction: { friendlyId: string }
) {
return `${v3BulkActionsPath(organization, project, environment)}/${bulkAction.friendlyId}`;
}
export function v3EnvironmentVariablesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/environment-variables`;
}
export function v3NewEnvironmentVariablesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentVariablesPath(organization, project, environment)}/new`;
}
export function v3ProjectAlertsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/alerts`;
}
export function v3NewProjectAlertPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3ProjectAlertsPath(organization, project, environment)}/new`;
}
export function v3NewProjectAlertPathConnectToSlackPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3ProjectAlertsPath(organization, project, environment)}/new/connect-to-slack`;
}
export function v3TestPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/test`;
}
export function queryPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/query`;
}
export function v3CustomDashboardPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
dashboard: { friendlyId: string }
) {
return `${v3EnvironmentPath(organization, project, environment)}/metrics/custom/${
dashboard.friendlyId
}`;
}
export function v3BuiltInDashboardPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
key: string
) {
return `${v3EnvironmentPath(organization, project, environment)}/metrics/${key}`;
}
export function v3TestTaskPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
task: TaskForPath
) {
return `${v3TestPath(organization, project, environment)}/tasks/${encodeURIComponent(
task.taskIdentifier
)}`;
}
export function v3RunsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
filters?: TaskRunListSearchFilters
) {
const searchParams = objectToSearchParams(filters);
const query = searchParams ? `?${searchParams.toString()}` : "";
return `${v3EnvironmentPath(organization, project, environment)}/runs${query}`;
}
export function v3CreateBulkActionPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
filters?: TaskRunListSearchFilters,
mode?: "selected" | "filters",
action?: "replay" | "cancel"
) {
const searchParams = objectToSearchParams(filters) ?? new URLSearchParams();
searchParams.set("bulkInspector", "show");
if (mode) {
searchParams.set("mode", mode);
}
if (action) {
searchParams.set("action", action);
}
const query = `?${searchParams.toString()}`;
return `${v3RunsPath(organization, project, environment)}${query}`;
}
export function v3RunPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
run: v3RunForPath,
searchParams?: URLSearchParams
) {
const query = searchParams ? `?${searchParams.toString()}` : "";
return `${v3RunsPath(organization, project, environment)}/${run.friendlyId}${query}`;
}
export function v3RunRedirectPath(
organization: OrgForPath,
project: ProjectForPath,
run: v3RunForPath
) {
return `${v3ProjectPath(organization, project)}/runs/${run.friendlyId}`;
}
export function v3RunPathFromFriendlyId(runId: string) {
return `/runs/${runId}`;
}
export function v3RunDownloadLogsPath(run: v3RunForPath) {
return `/resources/runs/${run.friendlyId}/logs/download`;
}
export function v3RunSpanPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
run: v3RunForPath,
span: v3SpanForPath,
searchParams?: URLSearchParams
) {
searchParams = searchParams ?? new URLSearchParams();
searchParams.set("span", span.spanId);
return `${v3RunPath(organization, project, environment, run, searchParams)}`;
}
export function v3RunStreamingPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
run: v3RunForPath
) {
return `${v3RunPath(organization, project, environment, run)}/stream`;
}
export function v3RunIdempotencyKeyResetPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
run: v3RunForPath
) {
return `/resources/orgs/${organizationParam(organization)}/projects/${projectParam(
project
)}/env/${environmentParam(environment)}/runs/${run.friendlyId}/idempotencyKey/reset`;
}
export function v3SchedulesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/schedules`;
}
export function v3SchedulePath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
schedule: { friendlyId: string }
) {
return `${v3EnvironmentPath(organization, project, environment)}/schedules/${
schedule.friendlyId
}`;
}
export function v3EditSchedulePath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
schedule: { friendlyId: string }
) {
return `${v3EnvironmentPath(organization, project, environment)}/schedules/edit/${
schedule.friendlyId
}`;
}
export function v3NewSchedulePath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/schedules/new`;
}
export function v3QueuesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/queues`;
}
export function v3WaitpointTokensPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
filters?: WaitpointSearchParams
) {
const searchParams = objectToSearchParams(filters);
const query = searchParams ? `?${searchParams.toString()}` : "";
return `${v3EnvironmentPath(organization, project, environment)}/waitpoints/tokens${query}`;
}
export function v3WaitpointTokenPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
token: { id: string },
filters?: WaitpointSearchParams
) {
const searchParams = objectToSearchParams(filters);
const query = searchParams ? `?${searchParams.toString()}` : "";
return `${v3WaitpointTokensPath(organization, project, environment)}/${token.id}${query}`;
}
export function v3BatchesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/batches`;
}
export function v3BatchPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
batch: { friendlyId: string }
) {
return `${v3BatchesPath(organization, project, environment)}/${batch.friendlyId}`;
}
export function v3BatchRunsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
batch: { friendlyId: string }
) {
return `${v3RunsPath(organization, project, environment, { batchId: batch.friendlyId })}`;
}
export function v3ProjectSettingsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/settings`;
}
export function v3ProjectSettingsGeneralPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3ProjectSettingsPath(organization, project, environment)}/general`;
}
export function v3ProjectSettingsIntegrationsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3ProjectSettingsPath(organization, project, environment)}/integrations`;
}
export function v3LogsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/logs`;
}
export function v3DeploymentsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/deployments`;
}
export function v3DeploymentPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
deployment: DeploymentForPath,
currentPage: number
) {
const query = currentPage ? `?page=${currentPage}` : "";
return `${v3DeploymentsPath(organization, project, environment)}/${deployment.shortCode}${query}`;
}
export function v3DeploymentVersionPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
version: string
) {
return `${v3DeploymentsPath(organization, project, environment)}?version=${version}`;
}
export function branchesPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/branches`;
}
export function concurrencyPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/concurrency`;
}
export function limitsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/limits`;
}
export function regionsPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath
) {
return `${v3EnvironmentPath(organization, project, environment)}/regions`;
}
export function v3BillingPath(organization: OrgForPath, message?: string) {
return `${organizationPath(organization)}/settings/billing${
message ? `?message=${encodeURIComponent(message)}` : ""
}`;
}
export function v3BillingAlertsPath(organization: OrgForPath) {
return `${organizationPath(organization)}/settings/billing-alerts`;
}
export function v3StripePortalPath(organization: OrgForPath) {
return `/resources/${organization.slug}/subscription/portal`;
}
export function v3UsagePath(organization: OrgForPath) {
return `${organizationPath(organization)}/settings/usage`;
}
// Docs
export function docsRoot() {
return "https://trigger.dev/docs";
}
export function docsPath(path: string) {
return `${docsRoot()}/${path}`;
}
export function docsTroubleshootingPath(path: string) {
return `${docsRoot()}/v3/troubleshooting`;
}
export function adminPath() {
return `/@`;
}