Conversation
🦋 Changeset detectedLatest commit: d23d10d The changes in this PR will be included in the next version bump. This PR includes changesets to release 28 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds a Vercel integration end-to-end: database migrations and Prisma schema updates (integration tables, deployment tracking, env var versioning); new server-side modules and services for OAuth state, token exchange, Vercel API repository, and integration service; presenters and API routes for settings, onboarding, connect/install/callback flows, per-resource management, and project integrations; UI components (onboarding modal, settings panel, logos, build-settings, menu item, modals, checkboxes); environment/schema updates and referral/telemetry helpers; environment variable repository changes with versioning and lastUpdatedBy; path helpers, tests, and an added Estimated code review effort🎯 5 (Critical) | ⏱️ ~150 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
183-210:⚠️ Potential issue | 🟠 MajorDon’t skip
isSecretchanges when the value is unchanged.If
options.isSecretis provided (e.g., overriding to secret) and the secret value matches, the earlycontinueprevents updatingisSecret(and skips creation when a value record is missing). This breaks “mark as secret” with unchanged values.🔧 Proposed fix
- // Check if value already exists and is the same - skip update if unchanged - const existingSecret = await secretStore.getSecret(SecretValue, key); - if (existingSecret && existingSecret.secret === variable.value) { - // Value is unchanged, skip this variable for this environment - continue; - } - - const existingValueRecord = await tx.environmentVariableValue.findFirst({ + const existingValueRecord = await tx.environmentVariableValue.findFirst({ where: { variableId: environmentVariable.id, environmentId, }, }); + + // Check if value already exists and is the same - skip update if unchanged *and* + // there is no metadata change (e.g. isSecret toggle) + const existingSecret = await secretStore.getSecret(SecretValue, key); + const canSkip = + existingSecret && + existingSecret.secret === variable.value && + existingValueRecord && + (options.isSecret === undefined || + existingValueRecord.isSecret === options.isSecret); + if (canSkip) { + continue; + }
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 323-324: Remove the debug console.log call in
VercelOnboardingModal and either delete the line or replace it with your app's
proper logging utility (e.g., processLogger.info or useLogger().info) before
calling onDataReload(); specifically remove the line containing
console.log("Vercel onboarding: Reloading data...") in the block that calls
onDataReload() so production code has no console.log usage and any message uses
the established logger API.
In `@apps/webapp/app/models/vercelIntegration.server.ts`:
- Around line 22-48: The isVercelSecretType function currently returns true only
for "secret" and "sensitive", so Vercel env vars with type "encrypted" are
treated as non-secret; update isVercelSecretType to also return true for
"encrypted" (i.e., treat "encrypted" as a secret-intended type) so secret
detection in sync logic (isVercelSecretType) correctly classifies encrypted
variables as secrets.
In `@apps/webapp/app/presenters/v3/BranchesPresenter.server.ts`:
- Line 45: The GitMeta schema's source enum is missing "trigger_vercel_app",
causing GitMeta.safeParse in processGitMetadata to reject Vercel-origin
metadata; update the GitMeta z.enum definition in the common schema (the one
referenced by GitMeta.safeParse and used alongside GitMetaLinks) to include
"trigger_vercel_app" alongside "trigger_github_app", "github_actions", and
"local" so Vercel-sourced git metadata is validated and preserved.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:
- Around line 650-651: The delete Button currently renders an empty label when
idle ({isLoading ? "Deleting" : ""}), making the action undiscoverable; update
the Button's children to show a default label (e.g., {isLoading ? "Deleting" :
"Delete"}) and add an explicit accessible name if missing (aria-label="Delete
variable" or similar) on the same Button instance to preserve clarity while
loading and improve accessibility.
- Around line 553-555: The menu Button (component named Button with props
LeadingIcon={PencilSquareIcon}) is rendering empty because it has no child/text;
restore the visible label by adding the string "Edit" as the Button's child (or
use the Button's text prop if that pattern is used elsewhere) so the menu item
shows "Edit" alongside the PencilSquareIcon in route.tsx where the Button is
declared.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:
- Line 315: The fetcher is typed as any—replace useTypedFetcher<any>() with a
correctly typed fetcher by importing the loader (response) type from the Vercel
resource route and using it as the generic parameter; e.g., import the loader's
Response/LoaderData type used by that route and change the declaration of
vercelFetcher (the call to useTypedFetcher) to use that specific type instead of
any so TypeScript enforces the actual data shape.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.integrations.vercel.tsx:
- Around line 118-120: ActionSchema is defined but never used; update the
route's action function to parse and validate the incoming form data with
ActionSchema (e.g. call await request.formData(), extract the "intent" value,
and use ActionSchema.parse or safeParse). If validation fails return a 400
response or handle the error path; on success proceed with the existing
uninstall logic. Reference ActionSchema and the action function exported from
this route when making the change.
- Around line 350-354: There is a duplicate empty-state branch: the conditional
rendering checking connectedProjects.length === 0 that returns a <TableBlankRow>
is unreachable because an earlier check already renders a different empty state;
remove the redundant block (the conditional containing TableBlankRow) so only
the original empty-state UI remains and ensure no other JSX references the
removed fragment (look for connectedProjects and TableBlankRow usages to locate
the block).
In
`@apps/webapp/app/routes/api.v1.orgs`.$organizationSlug.projects.$projectParam.vercel.projects.ts:
- Around line 27-71: The loader currently returns Vercel integration metadata
without authenticating or checking org membership; call
authenticateApiRequestWithPersonalAccessToken at the start of loader (after the
OPTIONS/CORS handling) to authenticate the requester, then verify the
authenticated user's membership in the organization identified by
organizationSlug (use the same membership-check logic/pattern used in
api.v1.orgs.$orgParam.projects.ts — e.g., lookup organization membership via
prisma using the authenticated user's id and organizationSlug) and return a
403/json error via apiCors if not a member; only after authentication and
membership check proceed to call
VercelIntegrationService.getVercelProjectIntegration and return the integration
details.
In `@apps/webapp/app/routes/login.mfa/route.tsx`:
- Around line 166-190: The session.unset(...) changes to the user session are
not being persisted because only authSession is committed; after you call
session.unset for the pending-mfa-* fields (the session object used for the
logged-in user), call await sessionStorage.commitSession(session) and append its
Set-Cookie header (e.g. headers.append("Set-Cookie", await
sessionStorage.commitSession(session))) so the cleared pending-mfa-* values are
written to the cookie; ensure you commit this user session before
returning/setting other cookies (keep existing authSession commit intact).
In
`@apps/webapp/app/routes/resources.environments`.$environmentId.regenerate-api-key.tsx:
- Around line 27-33: The call to syncApiKeyToVercelInBackground is
fire-and-forget and may be terminated in serverless runtimes; change the code to
await syncApiKeyToVercelInBackground(...) inside a try/catch so the request
waits for completion and logs any errors (use updatedEnvironment.projectId,
updatedEnvironment.type, updatedEnvironment.apiKey), or alternatively dispatch
the same payload to a durable background job/queue and await enqueue
confirmation; ensure you do not leave an unhandled promise and add error
handling/logging around the call.
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 772-773: The envVarsConfigLink prop is missing the environment
scope; update the usage of envVarsConfigLink (where it's currently built from
organizationSlug and projectSlug) to include the environmentSlug from the loader
so the path becomes the canonical
/orgs/{organizationSlug}/projects/{projectSlug}/env/{environmentSlug}/environment-variables;
locate the JSX/prop that sets envVarsConfigLink and insert environmentSlug
between projectSlug and environment-variables (using the existing
environmentSlug variable).
In `@apps/webapp/app/routes/vercel.callback.ts`:
- Around line 9-18: The VercelCallbackSchema currently declares configurationId
as required (VercelCallbackSchema -> configurationId: z.string()) but the
handler logic later treats configurationId as optional (checks if
(configurationId)); make these consistent by updating the schema to
configurationId: z.string().optional() so the parsed payload matches the
conditional, and keep the existing runtime check, or alternatively remove the
runtime if (configurationId) guard and leave the schema required—choose the
former if the code must accept callbacks without configurationId.
In `@apps/webapp/app/routes/vercel.onboarding.tsx`:
- Line 268: The isInstalling loading flag (useState in this file) is set true on
form submit but never reset on error; update the submit flow that calls
setIsInstalling(true) so it also sets setIsInstalling(false) when the action
returns an error or throws—e.g., after awaiting the action response (or in the
catch/finally) inspect actionData or the thrown error and call
setIsInstalling(false) to unblock the UI; reference the isInstalling state and
setIsInstalling function and the form submit/submit handler where you currently
set it true to add this reset.
In `@apps/webapp/app/services/vercelIntegration.server.ts`:
- Around line 398-408: The code building updatedData ignores
params.syncEnvVarsMapping so onboarding selections aren't persisted; update the
construction of updatedData (VercelProjectIntegrationData) to set
syncEnvVarsMapping using params.syncEnvVarsMapping when provided (e.g.,
syncEnvVarsMapping: params.syncEnvVarsMapping ??
existing.parsedIntegrationData.syncEnvVarsMapping) instead of always copying
existing.parsedIntegrationData.syncEnvVarsMapping so new mappings from params
are saved.
In `@apps/webapp/app/v3/vercel/index.ts`:
- Around line 3-14: getVercelInstallParams currently returns the raw next query
param; update it to sanitize the next value at the source by importing and using
sanitizeRedirectPath (same helper used by other routes) and only include next in
the returned object if sanitizeRedirectPath(next) yields a truthy/safe value;
keep existing checks on code/configurationId/integration and return null
otherwise.
In
`@internal-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql`:
- Around line 14-18: Move the two index creations out of the current migration
and into a new, separate migration file and create them with CONCURRENTLY to
avoid table locks: remove the CREATE INDEX
"IntegrationDeployment_deploymentId_idx" and CREATE INDEX
"IntegrationDeployment_commitSHA_idx" statements from the current migration and
add a new migration containing CREATE INDEX CONCURRENTLY
"IntegrationDeployment_deploymentId_idx" ON
"public"."IntegrationDeployment"("deploymentId"); and CREATE INDEX CONCURRENTLY
"IntegrationDeployment_commitSHA_idx" ON
"public"."IntegrationDeployment"("commitSHA");; ensure the new migration is
executed non-transactionally (since CONCURRENTLY cannot run inside a
transaction) and update any migration metadata accordingly.
In `@internal-packages/database/prisma/schema.prisma`:
- Around line 1861-1862: The inline indexes for IntegrationDeployment
(IntegrationDeployment_commitSHA_idx and IntegrationDeployment_deploymentId_idx)
must be removed from the model (remove the @@index([...]) entries in the
IntegrationDeployment model in schema.prisma) and instead added in a separate
SQL migration file that runs outside a transaction and uses CREATE INDEX
CONCURRENTLY for both indexes (mirror how WorkerDeployment_commitSHA_idx and
OrganizationIntegration_externalOrganizationId_idx were separated). Update the
migration named 20260129162810_add_integration_deployment by removing any
bundled index creation and create a new migration that executes "CREATE INDEX
CONCURRENTLY ..." for IntegrationDeployment_commitSHA_idx and
IntegrationDeployment_deploymentId_idx so the table is not locked during
deployment.
In `@packages/core/src/v3/schemas/api.ts`:
- Around line 1155-1160: Add a changeset documenting the schema modification to
the discriminated union in packages/core (the source schema defined with
z.discriminatedUnion("type", ...) in packages/core/src/v3/schemas/api.ts); run
`pnpm run changeset:add`, select the packages/core package, write a brief
description that this change adds/modifies the optional "source" discriminated
union (user vs integration) and mark it as a patch release, then commit the
generated changeset file with the PR.
🧹 Nitpick comments (23)
.vscode/settings.json (1)
10-11: Consider team impact of experimental settings.Enabling
typescript.experimental.useTsgomay cause inconsistent IDE behavior across team members with different VS Code versions. Consider moving experimental/personal preferences to user settings rather than workspace settings, or document the requirement in the README.apps/webapp/app/components/integrations/VercelLogo.tsx (1)
1-12: Consider adding accessibility attributes.The SVG lacks accessibility attributes. For decorative icons, add
aria-hidden="true"to hide from screen readers. If the icon conveys meaning, addrole="img"and anaria-label.♿ Proposed accessibility improvement
export function VercelLogo({ className }: { className?: string }) { return ( <svg viewBox="0 0 76 65" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} + aria-hidden="true" > <path d="M37.5274 0L75.0548 65H0L37.5274 0Z" fill="currentColor" /> </svg> ); }apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx (1)
540-544: Consider hiding the Cancel button whenpreventDismissis true.The
DialogCloseCancel button can still close the modal even whenpreventDismissis enabled. If the intent is to force users to complete the action, consider conditionally hiding or disabling this button:💡 Conditional Cancel button
cancelButton={ - <DialogClose asChild> - <Button variant="tertiary/medium">Cancel</Button> - </DialogClose> + !preventDismiss ? ( + <DialogClose asChild> + <Button variant="tertiary/medium">Cancel</Button> + </DialogClose> + ) : undefined }Note: If explicit cancellation is intentionally allowed while blocking accidental dismissal, this change is not needed.
apps/webapp/app/services/org.server.ts (1)
15-17: Consider using JSON error response for consistency.Other API routes in the codebase return JSON error responses (e.g.,
json({ error: "..." }, { status: 404 })). Using a plain text Response here may be inconsistent with the rest of the application.Suggested change
+import { json } from "@remix-run/node"; + export async function requireOrganization(request: Request, organizationSlug: string) { // ... if (!organization) { - throw new Response("Organization not found", { status: 404 }); + throw json({ error: "Organization not found" }, { status: 404 }); }apps/webapp/app/routes/vercel.configure.tsx (1)
16-18: UsesafeParsefor better error handling.Using
parsedirectly will throw an unhandled error, resulting in a 500 response. Consider usingsafeParseto return a proper 400 response for invalid/missing parameters.Suggested improvement
export const loader = async ({ request }: LoaderFunctionArgs) => { const url = new URL(request.url); const searchParams = Object.fromEntries(url.searchParams); - const { configurationId } = SearchParamsSchema.parse(searchParams); + const parsed = SearchParamsSchema.safeParse(searchParams); + + if (!parsed.success) { + throw new Response("Missing or invalid configurationId", { status: 400 }); + } + + const { configurationId } = parsed.data;apps/webapp/app/routes/api.v1.deployments.$deploymentId.ts (1)
73-82: Rename iterator variable for clarity.The variable
idis misleading since it represents anIntegrationDeploymentrecord, not an ID. This makesid.idon line 76 particularly confusing.Suggested improvement
integrationDeployments: deployment.integrationDeployments.length > 0 - ? deployment.integrationDeployments.map((id) => ({ - id: id.id, - integrationName: id.integrationName, - integrationDeploymentId: id.integrationDeploymentId, - commitSHA: id.commitSHA, - createdAt: id.createdAt, + ? deployment.integrationDeployments.map((integrationDeployment) => ({ + id: integrationDeployment.id, + integrationName: integrationDeployment.integrationName, + integrationDeploymentId: integrationDeployment.integrationDeploymentId, + commitSHA: integrationDeployment.commitSHA, + createdAt: integrationDeployment.createdAt, })) : undefined,apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx (1)
118-124: Consider linking to a generic integrations page.The menu item is labeled "Integrations" (plural) but links directly to the Vercel-specific page (
organizationVercelIntegrationPath). If additional integrations are planned, consider linking toorganizationIntegrationsPath(organization)instead, which would be the parent/settings/integrationsroute. This would allow for a landing page listing all available integrations.If Vercel is intended to be the only integration for now, this is acceptable, but the naming could be misleading to users.
apps/webapp/app/utils/pathBuilder.ts (1)
170-178: Duplication ofvercelResourcePathexists but may be contextually justified.Two versions exist: pathBuilder.ts expects typed objects (OrgForPath, ProjectForPath, EnvironmentForPath with slug properties), while the vercel.tsx version accepts raw strings (organizationSlug, projectSlug, environmentSlug). The vercel.tsx route receives route params as strings and has no need to construct full typed objects just to extract their slug property. While the duplication is real, consolidating toward the pathBuilder version would require unnecessary object construction in the route handler. Consider this acceptable duplication given the different input scenarios, or add a comment explaining why the local version exists.
apps/webapp/app/models/orgIntegration.server.ts (1)
101-106: Consider URL-encoding thestateparameter.The
stateparameter is passed directly into the URL without encoding. If the base64-encoded state contains URL-unsafe characters like+,/, or=, this could cause issues with the OAuth callback.♻️ Suggested fix
static vercelInstallUrl(state: string): string { // The user goes to Vercel's marketplace to install the integration // After installation, Vercel redirects to our callback with the authorization code const redirectUri = encodeURIComponent(`${env.APP_ORIGIN}/vercel/callback`); - return `https://vercel.com/integrations/${env.VERCEL_INTEGRATION_APP_SLUG}/new?state=${state}&redirect_uri=${redirectUri}`; + return `https://vercel.com/integrations/${env.VERCEL_INTEGRATION_APP_SLUG}/new?state=${encodeURIComponent(state)}&redirect_uri=${redirectUri}`; }apps/webapp/app/routes/auth.github.callback.tsx (1)
59-77: Duplicate user query could be consolidated.The user is queried twice: once at lines 24-32 (selecting only
idandmfaEnabledAt) and again at lines 61-63 (full user). Consider fetchingcreatedAtin the first query to avoid the redundant database call.♻️ Suggested optimization
const userRecord = await prisma.user.findFirst({ where: { id: auth.userId, }, select: { id: true, mfaEnabledAt: true, + createdAt: true, + email: true, + name: true, + admin: true, }, });Then reuse
userRecordinstead of querying again:const referralSource = await getReferralSource(request); if (referralSource) { - const user = await prisma.user.findUnique({ - where: { id: auth.userId }, - }); - if (user) { - const userAge = Date.now() - user.createdAt.getTime(); + if (userRecord) { + const userAge = Date.now() - userRecord.createdAt.getTime(); const isNewUser = userAge < 30 * 1000; if (isNewUser) { telemetry.user.identify({ - user, + user: userRecord, isNewUser: true, referralSource, }); } } headers.append("Set-Cookie", await clearReferralSourceCookie()); }apps/webapp/app/routes/magic.tsx (1)
58-79: Consider extracting referral/telemetry handling into a shared helper.The same block now appears in magic login, MFA completion, and Google callback. A shared helper would reduce duplication and keep behavior consistent.
apps/webapp/app/routes/vercel.connect.tsx (1)
20-63: Avoid the redundant project lookup.
project(withorganization) is already fetched in the loader; consider passing it intocreateOrFindVercelIntegrationto save a DB round‑trip.apps/webapp/app/routes/vercel.callback.ts (1)
5-5: Unused import:requireUserId.The
requireUserIdfunction is imported but never used in this file. OnlygetUserIdis called.🧹 Remove unused import
-import { getUserId, requireUserId } from "~/services/session.server"; +import { getUserId } from "~/services/session.server";apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.ts (2)
78-92: Consider extracting the type guard for cleaner code.The inline type guard for
lastUpdatedByis verbose. A named type guard function would improve readability and reusability.♻️ Extract type guard
function isUserUpdater( value: unknown ): value is { type: "user"; userId: string } { return ( value !== null && typeof value === "object" && "type" in value && value.type === "user" && "userId" in value && typeof value.userId === "string" ); } // Then use: const userIds = new Set( environmentVariables .flatMap((envVar) => envVar.values) .map((value) => value.lastUpdatedBy) .filter(isUserUpdater) .map((lastUpdatedBy) => lastUpdatedBy.userId) );
141-151: Service instantiation reduces testability.Creating
VercelIntegrationServiceinline makes the presenter harder to test in isolation. Consider injecting it through the constructor or as a parameter.♻️ Inject service for testability
export class EnvironmentVariablesPresenter { `#prismaClient`: PrismaClient; + `#vercelService`: VercelIntegrationService; - constructor(prismaClient: PrismaClient = prisma) { + constructor( + prismaClient: PrismaClient = prisma, + vercelService?: VercelIntegrationService + ) { this.#prismaClient = prismaClient; + this.#vercelService = vercelService ?? new VercelIntegrationService(prismaClient); } // Then use this.#vercelService instead of creating new instanceAs per coding guidelines: "Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts"apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (1)
343-381: Multiple overlapping useEffects for modal state create complexity.Three separate useEffects (lines 343-361, 365-371, 374-381) all manage modal opening based on query params. This creates hard-to-follow control flow and potential for race conditions.
Consider consolidating into a single effect or using a state machine pattern.
♻️ Consolidate modal state management
// Single effect to manage modal state based on query param and data useEffect(() => { if (!vercelIntegrationEnabled) return; if (hasQueryParam) { // Query param present - ensure modal is open and data is loaded if (!isModalOpen) { setIsModalOpen(true); } if (vercelFetcher.state === "idle" && !vercelFetcher.data?.onboardingData) { vercelFetcher.load( `${vercelResourcePath(organization.slug, project.slug, environment.slug)}?vercelOnboarding=true` ); } } else if (isModalOpen) { // Query param removed - close modal setIsModalOpen(false); } }, [hasQueryParam, vercelIntegrationEnabled, isModalOpen, vercelFetcher.state, vercelFetcher.data?.onboardingData]);apps/webapp/app/routes/vercel.onboarding.tsx (1)
290-301: Duplicated URL construction logic.The
newOrgUrlandnewProjectUrlconstruction share similar logic for building params with code, configurationId, integration, and next. Consider extracting a helper.♻️ Extract URL builder helper
function buildIntegrationParams(data: { code: string; configurationId: string | null; next: string | null; organizationId?: string; }): URLSearchParams { const params = new URLSearchParams(); params.set("code", data.code); if (data.configurationId) params.set("configurationId", data.configurationId); params.set("integration", "vercel"); if (data.organizationId) params.set("organizationId", data.organizationId); if (data.next) params.set("next", data.next); return params; }Also applies to: 363-375
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (2)
434-434: UnusednextUrlin dependency array.The
handleSkipOnboardingcallback includesnextUrlin its dependency array but doesn't use it in the function body.🧹 Remove unused dependency
- }, [actionUrl, fetcher, onClose, nextUrl, fromMarketplaceContext]); + }, [actionUrl, fetcher, onClose, fromMarketplaceContext]);
66-77: Complex state machine implemented with multiple useEffects.The onboarding flow uses ~10 useEffects to manage state transitions. This creates implicit control flow that's difficult to trace and test. Consider using a dedicated state machine library (like XState) or consolidating into a reducer pattern.
This is a significant refactor but would greatly improve maintainability. The current implementation works but the implicit dependencies between effects make it fragile to future changes.
Also applies to: 119-329
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx (2)
7-7: Unused import:useLoaderData.
useLoaderDatais imported butuseTypedLoaderDatais used instead.🧹 Remove unused import
-import { Form, useActionData, useLoaderData, useNavigation } from "@remix-run/react"; +import { Form, useActionData, useNavigation } from "@remix-run/react";
85-87: Avoidanycast for integration data.Casting
integrationDatatoanyloses type safety. Define a proper type for the Vercel integration data structure.🔧 Add proper typing
+type VercelIntegrationData = { + teamId?: string; + installationId?: string; +}; // Get team ID from integrationData -const integrationData = vercelIntegration.integrationData as any; +const integrationData = vercelIntegration.integrationData as VercelIntegrationData | null; const teamId = integrationData?.teamId ?? null; const installationId = integrationData?.installationId ?? null;apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (2)
3-6: DeriveALL_ENV_SLUGSfromEnvSlugSchemato prevent drift.This keeps the constant and schema aligned if new slugs are added.
♻️ Proposed change
-export const ALL_ENV_SLUGS: EnvSlug[] = ["dev", "stg", "prod", "preview"]; +export const ALL_ENV_SLUGS: EnvSlug[] = EnvSlugSchema.options;
125-149: Consider centralizing env type/slug mapping.
envTypeToSlugoverlaps withenvSluginapps/webapp/app/models/api-key.server.ts. Consolidating into a shared helper reduces drift risk.
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
Outdated
Show resolved
Hide resolved
....orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
Show resolved
Hide resolved
....orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
Show resolved
Hide resolved
...-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/webapp/app/models/orgIntegration.server.ts`:
- Around line 99-113: The vercelInstallUrl method interpolates a Base64 `state`
into the query string without URL-encoding, which can corrupt values containing
+, /, =; update static vercelInstallUrl(state: string) to URL-encode the `state`
(e.g., via encodeURIComponent) before building the returned URL (also keep the
existing redirectUri encoding), referencing the vercelInstallUrl method and
env.VERCEL_INTEGRATION_APP_SLUG to locate the change.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/webapp/app/models/orgIntegration.server.tsapps/webapp/app/v3/services/alerts/deliverAlert.server.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
apps/webapp/app/v3/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Organize services in the webapp following the pattern
app/v3/services/*/*.server.ts
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.tsapps/webapp/app/models/orgIntegration.server.ts
🧠 Learnings (3)
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL
Applied to files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/v3/services/alerts/deliverAlert.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/models/orgIntegration.server.ts
🧬 Code graph analysis (1)
apps/webapp/app/v3/services/alerts/deliverAlert.server.ts (1)
apps/webapp/app/models/orgIntegration.server.ts (1)
isIntegrationForService(50-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/webapp/app/v3/services/alerts/deliverAlert.server.ts (1)
24-28: Good guard for Slack-only integrations.
UsingisIntegrationForServiceprevents a non‑Slack integration from being used when a mismatchedintegrationIdis supplied.Also applies to: 648-654
apps/webapp/app/models/orgIntegration.server.ts (1)
50-55: Type guard looks solid.
Clear narrowing for service-specific integrations and keeps call sites clean.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/v3/schemas/api.ts (1)
680-724:⚠️ Potential issue | 🟠 MajorAllow integration deployment commitSHA to be nullish to avoid response validation failures.
GetDeploymentResponseBody.commitSHAis already nullish, butintegrationDeployments[].commitSHAis required. If an integration deployment lacks a commit (common for some Vercel builds or non‑git sources), the response will fail Zod validation and the API will 500. Consider aligning with the top‑level field.🔧 Suggested adjustment
- commitSHA: z.string(), + commitSHA: z.string().nullish(),
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 330-341: The useEffect that transitions state from
"loading-env-mapping" reads fromMarketplaceContext but doesn't include it in the
dependency array, which can cause stale closures; update the dependency array
for that useEffect to include fromMarketplaceContext (alongside state,
onboardingData, hasStagingEnvironment) so the effect re-runs when
fromMarketplaceContext changes and continue to use setState("env-mapping") /
setState("loading-env-vars") as before.
- Around line 992-994: The prop cast hides a true type mismatch between
onboardingData.gitHubAppInstallations (typed GitHubAppInstallationForVercel from
VercelSettingsPresenter) and the ConnectGitHubRepoModal prop type
(GitHubAppInstallation); fix by unifying the type definitions — either export
and reuse a single shared type (e.g., rename GitHubAppInstallationForVercel to
GitHubAppInstallation and use it in VercelSettingsPresenter and the GitHub
route) or update ConnectGitHubRepoModal to accept the presenter type; then
remove the unsafe `as GitHubAppInstallation[]` cast and update all affected
imports/exports so the types align without assertions.
In `@apps/webapp/app/models/vercelIntegration.server.ts`:
- Around line 68-75: The VercelEnvironmentVariable type is missing the
customEnvironmentIds property which getVercelEnvironmentVariables returns and
the onboarding presenter expects; update the VercelEnvironmentVariable type (the
exported type definition named VercelEnvironmentVariable) to include
customEnvironmentIds?: string[] (or string[] if you want it required) so callers
like getVercelEnvironmentVariables and the onboarding presenter can use it
without excess‑property errors or any casts.
In
`@apps/webapp/app/routes/resources.environments`.$environmentId.regenerate-api-key.tsx:
- Around line 54-65: The background sync ignores non-throwing failures from
VercelIntegrationRepository.syncSingleApiKeyToVercel; update
syncApiKeyToVercelInBackground to inspect the returned result (e.g., const
result = await VercelIntegrationRepository.syncSingleApiKeyToVercel(...)) and if
result.success is false, call your logger (or processLogger) to log a
descriptive error including result.error and context (projectId,
environmentType) so non-exception failures are visible; still keep the try/catch
for thrown errors.
In `@apps/webapp/app/services/referralSource.server.ts`:
- Around line 16-23: The getReferralSource function currently performs a manual
equality check after parsing the cookie; instead validate the parsed value using
the project's Zod schema so unexpected values are rejected. Update logic in
getReferralSource to call referralSourceCookie.parse(cookie) and then validate
the result against the ReferralSource Zod schema (or have
referralSourceCookie.parse already return the Zod-validated value) and only
return the typed ReferralSource when validation succeeds; otherwise return null.
Ensure you reference and use the existing referralSourceCookie and the
ReferralSource schema/type so the function returns a correctly validated
ReferralSource | null.
🧹 Nitpick comments (4)
apps/webapp/app/routes/vercel.onboarding.tsx (1)
283-289:window.close()may silently fail.The "Close" button calls
window.close()which only works if the window was opened programmatically (viawindow.open). If the user navigated directly to this page, the button will have no effect. Consider adding a fallback redirect.🔧 Suggested fallback
<Button variant="primary/medium" - onClick={() => window.close()} + onClick={() => { + window.close(); + // Fallback if window.close() doesn't work + setTimeout(() => { + window.location.href = "/"; + }, 100); + }} className="w-full" > Close </Button>apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (2)
243-246: Magic number in setTimeout delay.The 100ms delay before redirect seems arbitrary. Consider either removing it if unnecessary, or adding a comment explaining why it's needed (e.g., to ensure state updates complete).
📝 Add explanatory comment
hasTriggeredMarketplaceRedirectRef.current = true; + // Small delay to ensure state updates complete before navigation setTimeout(() => { window.location.href = nextUrl; }, 100);
420-434: Unused dependency in handleSkipOnboarding.
nextUrlis in the dependency array but is not used in the callback body. This suggests either dead code or a missing feature.🧹 Remove unused dependency
- }, [actionUrl, fetcher, onClose, nextUrl, fromMarketplaceContext]); + }, [actionUrl, fetcher, onClose, fromMarketplaceContext]);apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (1)
937-939: Import at end of file is unconventional.Moving the import to the top of the file with other imports would follow standard conventions and improve readability.
📝 Move import to top of file
Move line 937 to the imports section at the top of the file (after line 62):
import { VercelOnboardingModal } from "~/components/integrations/VercelOnboardingModal";
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.changeset/vercel-integration.mdapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/models/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/auth.github.callback.tsxapps/webapp/app/routes/auth.google.callback.tsxapps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/vercel.callback.tsapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/services/referralSource.server.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tspackages/core/src/v3/schemas/api.ts
✅ Files skipped from review due to trivial changes (1)
- .changeset/vercel-integration.md
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/webapp/app/routes/vercel.callback.ts
- apps/webapp/app/routes/auth.github.callback.tsx
- apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx
- apps/webapp/app/routes/auth.google.callback.tsx
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxpackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxpackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxpackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxpackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.tsapps/webapp/app/services/referralSource.server.tsapps/webapp/app/models/vercelIntegration.server.ts
{packages,integrations}/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Add a changeset when modifying any public package in
packages/*orintegrations/*usingpnpm run changeset:add
Files:
packages/core/src/v3/schemas/api.ts
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/services/referralSource.server.ts
🧠 Learnings (29)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:26:53.718Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/services/referralSource.server.ts
📚 Learning: 2025-09-02T11:18:06.602Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/services/gitHubSession.server.ts:31-36
Timestamp: 2025-09-02T11:18:06.602Z
Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
Applied to files:
apps/webapp/app/routes/login.mfa/route.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/login.mfa/route.tsxapps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/magic.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
Applied to files:
apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsx
📚 Learning: 2026-02-03T18:26:53.718Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:26:53.718Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The webapp at apps/webapp is a Remix 2.1 application using Node.js v20
Applied to files:
apps/webapp/app/routes/vercel.onboarding.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-04-17T10:27:25.337Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 1923
File: packages/core/src/v3/schemas/api.ts:813-827
Timestamp: 2025-04-17T10:27:25.337Z
Learning: Creating secret environment variables is restricted to the dashboard UI only, and not allowed via the API/SDK for now. The `EnvironmentVariableWithSecret` type in the API schema is for reading/displaying purposes only.
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxpackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-08-14T12:13:20.455Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2392
File: packages/cli-v3/src/utilities/gitMeta.ts:195-218
Timestamp: 2025-08-14T12:13:20.455Z
Learning: In the GitMeta schema (packages/core/src/v3/schemas/common.ts), all fields are intentionally optional to handle partial data from various deployment contexts (local, GitHub Actions, GitHub App). Functions like getGitHubAppMeta() are designed to work with missing environment variables rather than validate their presence.
Applied to files:
packages/core/src/v3/schemas/api.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
Applied to files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
packages/core/src/v3/schemas/api.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/services/referralSource.server.ts
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (like the Edit button with PencilSquareIcon) intentionally have no text labels - only icons are shown in the TableCellMenu. This is a deliberate UI design pattern for compact icon-only menu items.
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/services/referralSource.server.ts
📚 Learning: 2025-06-06T23:55:01.933Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2155
File: hosting/docker/.env.example:4-7
Timestamp: 2025-06-06T23:55:01.933Z
Learning: In the trigger.dev project, .env.example files should contain actual example secret values rather than placeholders, as these help users understand the expected format. The files include clear warnings about not using these defaults in production and instructions for generating proper secrets.
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
🧬 Code graph analysis (9)
apps/webapp/app/routes/vercel.onboarding.tsx (4)
apps/webapp/app/services/session.server.ts (1)
requireUserId(25-35)apps/webapp/app/models/message.server.ts (1)
redirectWithErrorMessage(201-218)apps/webapp/app/utils/pathBuilder.ts (2)
confirmBasicDetailsPath(75-77)newProjectPath(137-141)apps/webapp/app/v3/vercel/vercelOAuthState.server.ts (1)
generateVercelOAuthState(15-23)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
apps/webapp/app/v3/environmentVariables/repository.ts (8)
CreateEnvironmentVariables(21-28)CreateEnvironmentVariables(30-30)CreateResult(32-40)EditEnvironmentVariable(42-52)EditEnvironmentVariable(53-53)Result(75-82)EditEnvironmentVariableValue(67-72)EditEnvironmentVariableValue(73-73)
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (6)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
VercelOnboardingModal(939-939)vercelResourcePath(415-421)apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (6)
getAvailableEnvSlugs(96-105)getAvailableEnvSlugsForBuildSettings(107-112)EnvSlug(4-4)SyncEnvVarsMapping(45-45)shouldSyncEnvVarForAnyEnvironment(164-179)ALL_ENV_SLUGS(6-6)apps/webapp/app/utils/pathBuilder.ts (1)
vercelAppInstallPath(162-164)apps/webapp/app/models/vercelIntegration.server.ts (1)
VercelCustomEnvironment(77-85)apps/webapp/app/components/primitives/Paragraph.tsx (1)
Paragraph(88-107)apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx (1)
GitHubAppInstallation(65-71)
apps/webapp/app/routes/magic.tsx (1)
apps/webapp/app/services/referralSource.server.ts (1)
trackAndClearReferralSource(35-52)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (6)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (3)
loader(140-204)vercelResourcePath(415-421)VercelOnboardingModal(939-939)apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx (1)
loader(49-116)apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)
loader(38-89)apps/webapp/app/hooks/useProject.tsx (1)
useProject(20-24)apps/webapp/app/hooks/useEnvironment.tsx (1)
useEnvironment(19-23)apps/webapp/app/utils/pathBuilder.ts (1)
vercelResourcePath(170-178)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (1)
apps/webapp/app/utils/pathBuilder.ts (4)
EnvironmentParamSchema(26-28)v3ProjectSettingsPath(476-482)vercelResourcePath(170-178)vercelAppInstallPath(162-164)
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts (3)
packages/core/src/v3/apps/http.ts (1)
json(65-75)apps/webapp/app/services/personalAccessToken.server.ts (1)
authenticateApiRequestWithPersonalAccessToken(110-119)apps/webapp/app/services/vercelIntegration.server.ts (1)
VercelIntegrationService(38-599)
apps/webapp/app/services/referralSource.server.ts (1)
apps/webapp/app/services/telemetry.server.ts (1)
telemetry(239-245)
apps/webapp/app/models/vercelIntegration.server.ts (2)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (2)
projectId(644-684)EnvironmentVariablesRepository(48-864)apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (5)
envTypeToVercelTarget(80-94)TriggerEnvironmentType(35-35)TriggerEnvironmentType(36-36)SyncEnvVarsMapping(45-45)shouldSyncEnvVar(151-162)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: typecheck / typecheck
🔇 Additional comments (51)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (6)
9-9: LGTM!The method signatures are now cleaner with structured option types derived from Zod schemas. This aligns with the TypeScript guidelines for using types and Zod for validation.
Also applies to: 13-14, 51-51
159-177: LGTM!The inheritance logic correctly propagates
isSecret: truefrom parent variables while allowing non-secret status to fall through to the database default. This aligns with the established behavior that variables default toisSecret: falsewhen not explicitly marked as secrets. Based on learnings: "environment variables should default toisSecret: falsewhen not explicitly marked as secrets in the syncEnvVars functionality."
183-200: LGTM!The idempotent write check is well-designed. It correctly avoids unnecessary database operations when the secret value and metadata are unchanged, while still ensuring updates occur when
isSecretstatus changes or values differ.
214-243: LGTM!The branched upsert logic with version tracking is well-implemented. The pattern of using
undefinedfor updates (preserve existing) vsPrisma.JsonNullfor creates (explicit null) is consistent and correct. The version incrementing provides good audit trail support for the Vercel integration.
273-273: LGTM!The
editmethod now properly tracks version changes and updater information. The transaction ensures atomicity between secret store operations and version increments.Also applies to: 363-376, 394-395
416-416: LGTM!The
editValuemethod now correctly increments the version and tracks the updater, maintaining consistency with the other mutation methods.Also applies to: 475-489
apps/webapp/app/routes/login.mfa/route.tsx (2)
29-29: Referral tracking import looks good.
164-170: Referral tracking hook integrates cleanly with the session commits.apps/webapp/app/services/referralSource.server.ts (3)
1-14: Cookie configuration is sensible and secure.
25-33: Cookie set/clear helpers look good.
35-52: Referral tracking flow is clear and appropriately scoped.apps/webapp/app/routes/magic.tsx (2)
9-9: Import is appropriate for the new referral tracking hook.
57-57: Referral tracking call placement is correct.apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts (1)
25-132: Auth, membership verification, and CORS handling look solid.The loader enforces PAT auth, validates params with Zod, checks org membership, and wraps responses with CORS appropriately.
apps/webapp/app/services/vercelIntegration.server.ts (6)
45-125: Integration lookup + parse flow is clear and safe.Error handling and schema parsing are consistent and return null on invalid data.
127-239: Project selection and sync flow looks good.The update/create branching plus API‑key sync and logging are well structured.
241-356: Config and env‑var mapping updates are cleanly handled.The update pattern keeps integrationData consistent and returns parsed data.
385-479: Onboarding flow is robust with sensible logging and fallbacks.Pull‑sync and TRIGGER_VERSION propagation are handled defensively.
481-582: Production sync guardrails are sensible.Early returns on missing env/deployment prevent unnecessary API calls.
584-598: Disconnect flow is straightforward and safe.Soft‑delete keeps history while removing active linkage.
apps/webapp/app/models/vercelIntegration.server.ts (8)
21-47: Helper normalization and error handling utilities are clean.
137-249: OAuth exchange + token validation flow looks solid.
251-367: Configuration + environment variable fetches are well structured.
369-593: Shared env var retrieval and fallback logic is robust.
595-949: Org integration persistence and API‑key sync flows are clear.
951-1232: Pull‑sync pipeline handles filtering, overrides, and logging well.
1234-1417: Batch upsert + single upsert behavior is consistent with Vercel API constraints.
1419-1502: Project settings and uninstall flows are cleanly guarded.apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsx (1)
24-33: Awaiting the sync call is a good reliability improvement.apps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts (3)
18-71: Type surface for settings/onboarding is clear and well scoped.
73-315: Settings aggregation flow is robust with good guardrails.
320-573: Onboarding data assembly is comprehensive and well‑structured.apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (4)
65-105: Loader enhancement for Vercel enablement is clean and minimal.
303-417: Onboarding modal state + fetcher orchestration looks solid.
547-560: Settings panel wiring is straightforward and well‑gated.
615-635: Onboarding modal integration is neatly hooked up.apps/webapp/app/routes/vercel.onboarding.tsx (5)
1-24: Imports look good.The imports are well-organized and follow project conventions. Using Zod for validation and typedjson for typed responses aligns with coding guidelines.
25-53: Well-structured Zod schemas using discriminated union pattern.The schema design correctly uses
z.discriminatedUnionfor type-safe action handling. The validation is thorough.
55-164: Loader implementation is solid.Good patterns:
- Authentication via
requireUserId- Zod validation with proper error handling
- Authorization check via membership filtering
- Proper redirect handling for edge cases (no orgs, invalid params)
270-275: Loading state reset is properly implemented.The
useEffectcorrectly resetsisInstallingwhen navigation returns to idle, addressing the previous review concern about stuck loading states.
224-235: Hardcoded "prod" environment slug is intentional for Vercel integration.The environment lookup uses
slug: "prod"consistently across the Vercel integration flow. This is documented in the Vercel settings file with a comment ("Default to production environment") and follows the same pattern in project creation (_app.orgs.$organizationSlug_.projects.new/route.tsx). The design limits Vercel integrations to the production environment by default, which is the intended behavior.apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (3)
1-65: Imports and helper function look good.The
formatVercelTargetshelper provides clean target label formatting. Imports are well-organized.
66-78: Clean state machine type definition.Using a string union type for the state machine is appropriate and follows the coding guideline of preferring types over interfaces.
79-141: Component setup is well-structured.Props are clearly defined, initial state computation is properly memoized with
useCallback, and the marketplace context check is appropriately derived from search params.apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (7)
1-72: Imports and type definition look good.The
ConnectedVercelProjecttype properly usestypeinstead ofinterfaceper coding guidelines. Imports are well-organized.
73-139: Schemas and helper function are well-implemented.The
parseVercelStagingEnvironmenthelper safely handles JSON parsing with proper error handling. The discriminated union pattern for action routing is appropriate.
140-204: Loader implementation is solid.Good patterns:
- Proper authentication and authorization
- Conditional data fetching for onboarding
- Error handling distinguishes Response errors from unexpected errors
- Logging includes useful context
305-315: Good security practice on redirect URL validation.The https-only check for the
nextURL parameter prevents open redirect vulnerabilities to non-secure destinations. The try-catch handles malformed URLs gracefully.
411-412: Excellent exhaustiveness check.Using
submission.value satisfies neverensures TypeScript will error if a new action type is added to the schema but not handled in the action function.
762-762: Path now correctly includes environment slug.The
envVarsConfigLinkproperly includes all path segments includingenvironmentSlug, addressing the previous review feedback.
415-421: Do not apply this refactor — thevercelResourcePathfunctions inpathBuilder.tsand this file have incompatible signatures. The one inpathBuilder.tsaccepts typed objects (OrgForPath,ProjectForPath,EnvironmentForPath) and uses helper functions likeorganizationParam(), while this function accepts plain strings. They are not duplicates and cannot be interchanged.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.integrations.vercel.tsx:
- Around line 46-55: The loader is parsing configurationId from route params but
the value comes from the query string; update the loader to read configurationId
from the URL's search params (use the existing url variable and
url.searchParams.get("configurationId")) and pass that value into
SearchParamsSchema.parse (e.g., build an input object merging params with
configurationId from url.searchParams.get) before using it to filter
integrations; keep references to SearchParamsSchema, loader, configurationId and
requireOrganization so the change is local to that function.
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 553-559: The GitHub warning text in VercelGitHubWarning and the
corresponding hint (both in this file) incorrectly state that the Vercel
integration "spawns Trigger.dev builds"; update the copy to reflect the correct
scope: Vercel only links deployments and does not trigger builds or provide git
metadata (the GitHub app does that). Locate the VercelGitHubWarning component
and the hint text referenced near the other occurrence and replace phrases like
"spawns Trigger.dev builds" and any mention of providing git metadata with
wording such as "links deployments; builds and git metadata are provided by the
GitHub app" (or equivalent concise copy conveying that separation of
responsibilities).
In `@apps/webapp/app/routes/vercel.connect.tsx`:
- Around line 13-27: Update the VercelConnectSchema and the
createOrFindVercelIntegration signature to accept an optional configurationId:
make configurationId optional in VercelConnectSchema (change z.string() to an
optional string/nullable) and update the createOrFindVercelIntegration parameter
type for configurationId to be optional (e.g., configurationId?: string or
string | null) so dashboard flows that omit configurationId pass validation;
ensure callers (e.g., vercel.callback.ts) pass undefined/null when no
configurationId is present and downstream uses (installationId/integrationData)
can handle null/undefined.
In `@apps/webapp/app/routes/vercel.onboarding.tsx`:
- Around line 26-85: The loader validation fails for the expired path because
LoaderParamsSchema requires code; change code to optional (e.g., code:
z.string().optional().nullable()) in LoaderParamsSchema so the loader can accept
?error=expired without a code, and update the loader usages (in export async
function loader) to treat params.data.code as possibly null/undefined (e.g., use
params.data.code ?? null when returning typedjson) so the "Installation Expired"
UI can render.
🧹 Nitpick comments (1)
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts (1)
110-120: Consider includingtriggerProjectin the not-connected response for API consistency.The
projectis already available at line 108 when no integration exists. IncludingtriggerProjectin this response would provide a consistent shape regardless of connection status, which simplifies client-side type handling.♻️ Suggested change for consistent response shape
if (!integration) { return apiCors( request, json({ connected: false, vercelProject: null, config: null, syncEnvVarsMapping: null, + triggerProject: { + id: project.id, + name: project.name, + slug: project.slug, + }, }) ); }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
🧠 Learnings (15)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:26:53.718Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/vercel.connect.tsxapps/webapp/app/routes/vercel.onboarding.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The webapp at apps/webapp is a Remix 2.1 application using Node.js v20
Applied to files:
apps/webapp/app/routes/vercel.onboarding.tsx
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/routes/vercel.onboarding.tsx
📚 Learning: 2026-02-03T18:26:53.718Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:26:53.718Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
🧬 Code graph analysis (2)
apps/webapp/app/routes/vercel.connect.tsx (5)
apps/webapp/app/models/vercelIntegration.server.ts (1)
TokenResponse(60-66)apps/webapp/app/routes/vercel.callback.ts (1)
loader(20-74)apps/webapp/app/services/session.server.ts (1)
requireUserId(25-35)apps/webapp/app/v3/vercel/vercelOAuthState.server.ts (1)
validateVercelOAuthState(25-40)apps/webapp/app/utils/pathBuilder.ts (1)
v3ProjectSettingsPath(476-482)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx (1)
apps/webapp/app/utils/pathBuilder.ts (2)
OrganizationParamsSchema(18-20)v3ProjectSettingsPath(476-482)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts (2)
32-39: Authentication and authorization properly implemented.The endpoint now correctly authenticates requests using
authenticateApiRequestWithPersonalAccessTokenbefore any data access, and the Prisma query at lines 59-63 verifies organization membership viamembers: { some: { userId: authenticationResult.userId } }. This properly addresses the previously flagged security concern.
51-86: Clean neverthrow pattern with efficient org membership verification.The
fromPromisewrapper and discriminated union return type (type: "not_found" | "success") provide clear control flow. Embedding the membership check in the Prisma query (lines 59-63) is efficient—single query validates access and retrieves data.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx
Outdated
Show resolved
Hide resolved
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Show resolved
Hide resolved
29a3434 to
2953d4c
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 946-956: The current copy in the Vercel onboarding component
(around Header3, Paragraph, and Callout) incorrectly implies that connecting
GitHub will let Vercel trigger builds and perform build synchronization; update
the text to clarify that installing the GitHub app provides source and git
metadata (source: "trigger_github_app") and that the Vercel integration only
links deployments between platforms (does not trigger builds). Edit the
Paragraph and the Callout text to: state that connecting GitHub lets Trigger.dev
read repo metadata and enable deployment linking, and explicitly note that build
execution remains handled by the GitHub app/Vercel build settings—ensure Header3
stays “Connect GitHub Repository” and keep the info Callout tone but correct the
wording to avoid implying automatic builds are performed by the Vercel
integration.
In `@apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx`:
- Around line 118-124: The SideMenuItem labeled "Integrations" (in
OrganizationSettingsSideMenu, the SideMenuItem with props name="Integrations"
and to={organizationVercelIntegrationPath(organization)}) is misleading because
it links directly to the Vercel integration page; either rename this menu item
to "Vercel Integration" to match the destination, or implement a parent
integrations hub route (/settings/integrations) and update the to prop to that
hub while creating a child route for organizationVercelIntegrationPath; update
the data-action value if you rename to keep telemetry consistent.
In `@apps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts`:
- Around line 60-290: The call method is incorrectly declared async and mixes
synchronous Result returns with async ResultAsync flows; change
VercelSettingsPresenter.call to be non-async and return a ResultAsync throughout
(update its signature/return type accordingly), replace all early synchronous
ok({...}) returns with okAsync({...}) (and any err returns with errAsync if
present), and ensure the final flow returns the existing ResultAsync from
ResultAsync.combine() so callers can await presenter.call() directly; update any
related imports/types to match ResultAsync if needed and keep helper functions
(checkOrgIntegration, getVercelProjectIntegration, etc.) unchanged.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.integrations.vercel.tsx:
- Around line 119-143: The uninstall action currently finds the first Vercel
integration for the org without respecting configurationId, which can delete the
wrong installation; update the action that uses
prisma.organizationIntegration.findFirst (in the action function) to also read
configurationId from the request formData (use ActionSchema or parse
formData.get("configurationId")) and include configurationId in the where clause
when present (i.e., add configurationId: configurationIdValue to the where
filter for service "VERCEL" and organizationId), so marketplace installs are
targeted correctly; ensure any uninstall logic that calls repository methods
passes installationId/configurationId only when defined (omit it otherwise).
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 160-179: The code wraps presenter.call(...) (which returns a
ResultAsync) with fromPromise, then treats the unwrapped value as a Result and
calls isOk(), causing runtime errors; change to await the ResultAsync directly
by doing const resultOrFail = await presenter.call({ projectId: project.id,
organizationId: project.organizationId }); then check resultOrFail.isErr() /
resultOrFail.isOk() (or !resultOrFail.isOk()) and update the logger to use
resultOrFail.error when present; remove the fromPromise import/usage and ensure
subsequent uses (resultOrFail.value → resultOrFail.value or resultOrFail.unwrap/
.value as appropriate) operate on the actual Result returned from awaiting
VercelSettingsPresenter.call.
In `@apps/webapp/app/routes/vercel.connect.tsx`:
- Around line 164-167: Validate the incoming next query before adding it to
URLSearchParams to prevent open redirects: in the code that constructs params
(where URLSearchParams, vercelOnboarding, origin and next are handled), only
call params.set("next", next) after ensuring next is a safe relative or
same-origin path (e.g., allow strings that start with "/" and do not contain a
scheme/host, or parse with new URL(next, origin) and confirm url.origin ===
origin and url.pathname startsWith("/")). If validation fails, omit the next
param or replace it with a safe fallback so the downstream settings/redirect
logic cannot be used for an open redirect.
In `@apps/webapp/app/routes/vercel.onboarding.tsx`:
- Around line 265-272: The current onboarding handler always sets params.origin
= "marketplace" which breaks dashboard installs that omit configurationId;
change the logic in app/routes/vercel.onboarding.tsx so origin is set to
"marketplace" only when configurationId is present and to "dashboard" (or omit
marketplace) when configurationId is absent. Also update the corresponding
vercel.connect.tsx validation: add a Zod superRefine to enforce that
configurationId must exist for marketplace flows and that installationId (or
configurationId) is only passed into repository methods when configurationId is
defined, omitting that field for dashboard flows. Ensure you reference the
params, configurationId, origin, vercel.connect.tsx validation, and repository
call sites when making the changes.
🧹 Nitpick comments (6)
apps/webapp/app/routes/auth.github.callback.tsx (1)
58-58: Optional: avoid the extra user lookup insidetrackAndClearReferralSource.You already fetched
userRecordabove; consider extending the helper to accept user metadata (e.g.,createdAt) to skip a second DB query on every successful login.apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
214-243: Simplify redundant ternary expressions.The ternary
options.lastUpdatedBy ? options.lastUpdatedBy : undefinedis equivalent to justoptions.lastUpdatedBysinceundefinedis returned when the value is falsy anyway. This pattern appears in the update path here and in theedit/editValuemethods.Note: The
Prisma.JsonNullusage in the create path (line 240) is correct and should remain as-is.♻️ Suggested simplification
await tx.environmentVariableValue.update({ where: { id: existingValueRecord.id, }, data: { version: { increment: 1, }, - lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : undefined, + lastUpdatedBy: options.lastUpdatedBy, valueReferenceId: secretReference.id, ...(options.isSecret !== undefined ? { isSecret: options.isSecret, } : {}), }, });apps/webapp/app/routes/confirm-basic-details.tsx (1)
115-122: Guardnextbefore propagating it across redirects.
If/orgs/newlater usesnextfor navigation, ensure it’s restricted to internal paths to avoid open-redirect risks. Consider a minimal guard here and confirm downstream validation.🔒 Suggested guard for internal-only
next- if (vercelParams.next) { - params.set("next", vercelParams.next); - } + if (vercelParams.next?.startsWith("/")) { + params.set("next", vercelParams.next); + }apps/webapp/app/routes/api.v1.deployments.$deploymentId.ts (1)
73-82: Optional: rename the map variable for clarity.Using a more descriptive variable name makes the mapping easier to read.
♻️ Suggested refactor
- ? deployment.integrationDeployments.map((id) => ({ - id: id.id, - integrationName: id.integrationName, - integrationDeploymentId: id.integrationDeploymentId, - commitSHA: id.commitSHA, - createdAt: id.createdAt, - })) + ? deployment.integrationDeployments.map((integrationDeployment) => ({ + id: integrationDeployment.id, + integrationName: integrationDeployment.integrationName, + integrationDeploymentId: integrationDeployment.integrationDeploymentId, + commitSHA: integrationDeployment.commitSHA, + createdAt: integrationDeployment.createdAt, + }))apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)
34-36: Unused import detected.
v3ProjectSettingsPathis imported but not used anywhere in this file. Consider removing it to keep imports clean.♻️ Suggested fix
v3ProjectPath, - v3ProjectSettingsPath, } from "~/utils/pathBuilder";apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx (1)
81-85: ValidateintegrationDatabefore readingteamId/installationId.Casting to
anyhides malformed data and can leakundefinedinto the UI. A small Zod schema keeps this safe and consistent.♻️ Suggested refactor
+const OrgVercelIntegrationDataSchema = z.object({ + teamId: z.string().nullable().optional(), + installationId: z.string().nullable().optional(), +}); // ... -const integrationData = vercelIntegration.integrationData as any; -const teamId = integrationData?.teamId ?? null; -const installationId = integrationData?.installationId ?? null; +const parsed = OrgVercelIntegrationDataSchema.safeParse(vercelIntegration.integrationData); +const teamId = parsed.success ? parsed.data.teamId ?? null : null; +const installationId = parsed.success ? parsed.data.installationId ?? null : null;As per coding guidelines: Use zod for validation in packages/core and apps/webapp.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx
Show resolved
Hide resolved
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Show resolved
Hide resolved
|
@coderabbitai help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsx (1)
16-18:⚠️ Potential issue | 🟡 MinorReturn a proper Response object for 405 status.
Returning a plain object
{ status: 405, body: "..." }won't set the HTTP status code correctly. Remix expects aResponseor a result fromjson()/redirect()helpers.🔧 Proposed fix
// Ensure this is a POST request if (request.method.toUpperCase() !== "POST") { - return { status: 405, body: "Method Not Allowed" }; + return new Response("Method Not Allowed", { status: 405 }); }
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelBuildSettings.tsx`:
- Around line 23-25: The local slugToEnvType function is incomplete and should
be replaced by the canonical envSlugToType from vercelProjectIntegrationSchema;
remove the slugToEnvType definition, import envSlugToType from
vercelProjectIntegrationSchema, and call envSlugToType(slug) wherever
slugToEnvType(slug) was used so all slugs (dev, stg, prod, preview) map
correctly to DEVELOPMENT/STAGING/PRODUCTION/PREVIEW.
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 228-245: The client-side redirect in the useEffect (involving
hasTriggeredMarketplaceRedirectRef, isOpen, fromMarketplaceContext, nextUrl,
hasProjectSelected, isGitHubConnectedForOnboarding) assigns query-derived
nextUrl directly to window.location.href which can allow unsafe schemes; before
setting window.location.href validate nextUrl by constructing a URL object and
ensure its protocol is either "https:" or it shares the current origin
(same-origin), and reject or ignore redirects with other schemes (e.g.,
"javascript:")—only after passing this guard set
hasTriggeredMarketplaceRedirectRef.current = true and invoke the delayed
redirect.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:
- Around line 190-205: Wrap the Vercel cleanup calls in a try/catch so failures
are best-effort: when using VercelIntegrationService (calls to
getVercelProjectIntegration and removeSyncEnvVarForEnvironment) and the
prisma.runtimeEnvironment lookup, catch any errors from
removeSyncEnvVarForEnvironment (and optionally
getVercelProjectIntegration/runtimeEnvironment) and log them (e.g.,
processLogger.error or console.error) instead of rethrowing, ensuring the
surrounding delete action that uses submission.value.key and
submission.value.environmentId still returns success even if the Vercel sync
cleanup fails.
- Around line 120-125: The schema for the action payload currently defines
syncEnabled as z.string().transform(...) which accepts any string and coerces
non-"true" to false; update the schema definition for syncEnabled in the
z.object used for the "update-vercel-sync" action to first constrain values to
z.union([z.literal("true"), z.literal("false")]) and then apply the transform to
convert the literal "true"/"false" to a boolean, so only "true" or "false"
inputs are valid before coercion.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:
- Around line 342-357: The effect currently treats { onboardingData: null } as
"not loaded" and repeatedly calls vercelFetcher.load; update the conditions in
the useEffect (the block using hasQueryParam, vercelIntegrationEnabled,
vercelFetcher, isModalOpen, openVercelOnboarding) so you only call
vercelFetcher.load when vercelFetcher.data is strictly undefined (i.e., hasn't
been fetched yet) and vercelFetcher.state === "idle", and treat onboardingData
=== null as a valid loaded response (do not re-load); keep the existing behavior
that opens the modal only when vercelFetcher.data?.onboardingData is truthy and
isModalOpen is false.
In `@apps/webapp/app/routes/login._index/route.tsx`:
- Line 170: The GitHub and Google auth link constructions in this route (the to=
values that build `/login/github?...` and `/login/google?...` around the GitHub
and Google auth buttons) do not encode data.redirectTo, causing malformed URLs
when redirectTo contains special chars; update those constructions to wrap
redirectTo with encodeURIComponent (same as used for the magic link) so both
`/login/github?redirectTo=${encodeURIComponent(data.redirectTo)}` and
`/login/google?redirectTo=${encodeURIComponent(data.redirectTo)}` are used
(ensure you handle the conditional absence of data.redirectTo the same way as
the magic link).
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx:
- Around line 391-418: The Cancel button currently wrapped in DialogClose
bypasses the preventDismiss guard; update the modal footer so the Cancel button
does not close when preventDismiss is true by conditionally rendering or
altering it: if preventDismiss is true, render a disabled or non-DialogClose
plain Button (or a Button with an onClick that checks preventDismiss and returns
early) instead of the DialogClose-wrapped Cancel, otherwise keep the
DialogClose-wrapped Cancel; reference the preventDismiss prop, the DialogClose
component, and the setIsModalOpen/isModalOpen state to implement the conditional
behavior.
🧹 Nitpick comments (10)
apps/webapp/app/services/telemetry.server.ts (1)
59-70: Consider includingreferralSourcein the "user created" event.The
referralSourceis passed toidentify()but not included in the "user created" capture event. If attribution analysis requires correlating referral sources with user creation events, you may want to include it here as well.💡 Optional enhancement
this.#capture({ userId: user.id, event: "user created", eventProperties: { email: user.email, name: user.name, authenticationMethod: user.authenticationMethod, admin: user.admin, createdAt: user.createdAt, + ...(referralSource && { referralSource }), }, });apps/webapp/app/services/referralSource.server.ts (1)
49-50: Consider extracting the magic number as a named constant.The 30-second threshold for determining a "new user" is a domain-specific value that would benefit from a descriptive name.
♻️ Suggested improvement
+const NEW_USER_THRESHOLD_MS = 30 * 1000; // 30 seconds + export async function trackAndClearReferralSource( request: Request, userId: string, headers: Headers ): Promise<void> { const referralSource = await getReferralSource(request); if (!referralSource) return; headers.append("Set-Cookie", await clearReferralSourceCookie()); const user = await prisma.user.findUnique({ where: { id: userId } }); if (!user) return; const userAge = Date.now() - user.createdAt.getTime(); - if (userAge >= 30 * 1000) return; + if (userAge >= NEW_USER_THRESHOLD_MS) return; telemetry.user.identify({ user, isNewUser: true, referralSource }); }apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)
34-34: Unused import detected.
v3ProjectSettingsPathis imported but never used in this file. Consider removing it to keep imports clean.🧹 Remove unused import
import { newProjectPath, OrganizationParamsSchema, organizationPath, selectPlanPath, v3ProjectPath, - v3ProjectSettingsPath, } from "~/utils/pathBuilder";apps/webapp/app/routes/vercel.configure.tsx (1)
14-44: Consider usingsafeParsefor more informative error responses.Using
parsethrows a generic Zod error on invalid input. UsingsafeParsewould allow returning a more descriptive 400 response whenconfigurationIdis missing.🛡️ Optional: Better error handling with safeParse
export const loader = async ({ request }: LoaderFunctionArgs) => { const url = new URL(request.url); const searchParams = Object.fromEntries(url.searchParams); - const { configurationId } = SearchParamsSchema.parse(searchParams); + const parsed = SearchParamsSchema.safeParse(searchParams); + if (!parsed.success) { + throw new Response("Missing or invalid configurationId", { status: 400 }); + } + const { configurationId } = parsed.data;apps/webapp/app/components/integrations/VercelLogo.tsx (1)
1-12: Consider addingaria-hidden="true"for accessibility.Since this is a decorative logo that doesn't convey information, adding
aria-hidden="true"prevents screen readers from announcing it unnecessarily.♿ Proposed accessibility improvement
export function VercelLogo({ className }: { className?: string }) { return ( <svg viewBox="0 0 76 65" fill="none" xmlns="http://www.w3.org/2000/svg" className={className} + aria-hidden="true" > <path d="M37.5274 0L75.0548 65H0L37.5274 0Z" fill="currentColor" /> </svg> ); }apps/webapp/app/routes/resources.environments.$environmentId.regenerate-api-key.tsx (1)
59-62: Type signature includes "DEVELOPMENT" but it's never passed.The function signature accepts
"DEVELOPMENT"as a validenvironmentType, but the caller explicitly excludes it. Consider removing it from the union for consistency, or add a guard inside the function.♻️ Option 1: Narrow the type signature
async function syncApiKeyToVercel( projectId: string, - environmentType: "PRODUCTION" | "STAGING" | "PREVIEW" | "DEVELOPMENT", + environmentType: "PRODUCTION" | "STAGING" | "PREVIEW", apiKey: string ): Promise<void> {apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (1)
422-428: Reuse the shared path builder to avoid drift.Now that
apps/webapp/app/utils/pathBuilder.tsexportsvercelResourcePath, consider delegating to it here so the route and UI stay in sync.♻️ Suggested refactor
-import { EnvironmentParamSchema, v3ProjectSettingsPath, vercelAppInstallPath } from "~/utils/pathBuilder"; +import { + EnvironmentParamSchema, + v3ProjectSettingsPath, + vercelAppInstallPath, + vercelResourcePath as vercelResourcePathFromBuilder, +} from "~/utils/pathBuilder"; @@ export function vercelResourcePath( organizationSlug: string, projectSlug: string, environmentSlug: string ) { - return `/resources/orgs/${organizationSlug}/projects/${projectSlug}/env/${environmentSlug}/vercel`; + return vercelResourcePathFromBuilder( + { slug: organizationSlug }, + { slug: projectSlug }, + { slug: environmentSlug } + ); }internal-packages/database/prisma/schema.prisma (1)
2119-2139: Inconsistent indentation inOrganizationProjectIntegrationmodel.This model uses tabs while the rest of the schema uses spaces. Standardize to spaces for consistency with the rest of the file.
🔧 Suggested fix
model OrganizationProjectIntegration { - id String `@id` `@default`(cuid()) - - organizationIntegration OrganizationIntegration `@relation`(fields: [organizationIntegrationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - organizationIntegrationId String - - project Project `@relation`(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) + id String `@id` `@default`(cuid()) + + organizationIntegration OrganizationIntegration `@relation`(fields: [organizationIntegrationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + organizationIntegrationId String + + project Project `@relation`(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String - + externalEntityId String /// Identifier for webhooks, for example Vercel's projectId integrationData Json /// Save useful data like config or external entity name installedBy String? /// UserId who installed the integration createdAt DateTime `@default`(now()) updatedAt DateTime `@updatedAt` deletedAt DateTime? - + @@index([projectId]) @@index([projectId, organizationIntegrationId]) @@index([externalEntityId]) }apps/webapp/app/services/vercelIntegration.server.ts (1)
571-584: Type castas anyfor Vercel SDK target array.The cast
target: ["production"] as anyworks around SDK typing constraints. This is acceptable given SDK limitations, but consider adding a comment explaining why the cast is needed.🔧 Suggested improvement
requestBody: { key: "TRIGGER_VERSION", value: currentDeployment.version, + // Cast required: SDK expects a specific union type but string[] is valid at runtime target: ["production"] as any, type: "encrypted", },apps/webapp/app/models/vercelIntegration.server.ts (1)
376-381: Consider stricter typing for integration configuration response.The response type includes
[key: string]: anywhich bypasses type safety. Consider defining a more specific type or using Zod for runtime validation.return response.json() as Promise<{ id: string; teamId?: string | null; projects?: string[]; - [key: string]: any; }>;
apps/webapp/app/components/integrations/VercelBuildSettings.tsx
Outdated
Show resolved
Hide resolved
....orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
Show resolved
Hide resolved
....orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
Outdated
Show resolved
Hide resolved
...p/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx
Show resolved
Hide resolved
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx
Show resolved
Hide resolved
|
@coderabbitai resolve |
✅ Actions performedComments resolved. |
71f4625 to
2f03e2a
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 1031-1048: The modal currently assigns the raw nextUrl to
window.location.href in both the "Complete" and "Skip for now" button handlers,
which bypasses the existing redirect guard; update both handlers (the branch
that runs when isGitHubConnectedForOnboarding && fromMarketplaceContext &&
nextUrl and the else branch that checks fromMarketplaceContext && nextUrl) to
call safeRedirectUrl(nextUrl) and only set window.location.href to the returned
value when it is a non-empty/safe URL, keeping setState("completed") behavior
unchanged; ensure you reference safeRedirectUrl, nextUrl, setState, and
window.location.href in the VercelOnboardingModal button click handlers.
In `@apps/webapp/app/routes/login.magic/route.tsx`:
- Around line 63-71: The code stores an unchecked redirectTo query parameter
into the session (via setRedirectTo and commitRedirectSession) and later uses it
in magic.tsx's redirect call, allowing open redirects; fix by
validating/sanitizing redirectTo before storing (or before use) — implement or
call a helper like isSafeRedirect that ensures redirectTo is either a
same-origin relative path (starts with "/" and not "//", no protocol/host) or
matches a whitelist of allowed paths, and if validation fails use "/" as the
default; update the logic around setRedirectTo/commitRedirectSession in
route.tsx to only store the sanitized value (and likewise enforce the same check
in magic.tsx if stored values might be untrusted).
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 279-290: The parsed syncEnvVarsMapping is currently coerced with a
cast and may be malformed; validate it with the existing
SyncEnvVarsMappingSchema instead of blind casting: use
safeJsonParse(syncEnvVarsMapping) to get the parsed value, then call
SyncEnvVarsMappingSchema.safeParse (or parse) to validate and handle errors
(fail early or return a user-friendly error) and pass the validated value
(replace parsedSyncEnvVarsMapping) into vercelService.completeOnboarding; update
any variable name references (parsedSyncEnvVarsMapping) accordingly so only a
zod-validated object is sent.
In `@apps/webapp/app/utils/pathBuilder.ts`:
- Around line 170-178: Remove the unused vercelResourcePath function from
pathBuilder.ts: locate the exported function named vercelResourcePath, delete
its definition and any related export entry so it no longer contributes dead
code or duplication, and run a quick grep for vercelResourcePath to confirm
there are no remaining references; keep the active implementation used by the
settings route and VercelOnboardingModal untouched.
🧹 Nitpick comments (4)
apps/webapp/app/routes/_app.orgs.new/route.tsx (1)
72-91: Consider using Zod to validate the Vercel integration search params.The logic is correct and the redirect is safely constructed. One small suggestion: the coding guidelines call for Zod-based validation in the webapp. You could parse the search params through a small Zod schema for consistency with the rest of the codebase (e.g.,
vercel.connect.tsx).♻️ Optional refactor
+ const VercelRedirectParams = z.object({ + code: z.string(), + configurationId: z.string(), + integration: z.literal("vercel"), + next: z.string().optional(), + }); + const url = new URL(request.url); - const code = url.searchParams.get("code"); - const configurationId = url.searchParams.get("configurationId"); - const integration = url.searchParams.get("integration"); - const next = url.searchParams.get("next"); - - if (code && configurationId && integration === "vercel") { - // Redirect to projects/new with params preserved - const params = new URLSearchParams({ - code, - configurationId, - integration, - }); - if (next) { - params.set("next", next); + const vercelResult = VercelRedirectParams.safeParse( + Object.fromEntries(url.searchParams) + ); + + if (vercelResult.success) { + const { code, configurationId, integration, next } = vercelResult.data; + const params = new URLSearchParams({ code, configurationId, integration }); + if (next) params.set("next", next); const redirectUrl = `${organizationPath(organization)}/projects/new?${params.toString()}`; return redirect(redirectUrl); }As per coding guidelines, "Use zod for validation in packages/core and apps/webapp".
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (2)
183-200: The skip-if-unchanged check does not account forlastUpdatedBychanges.If a caller sends the same value and same
isSecretbut a differentlastUpdatedBy(e.g., a Vercel integration re-sync that should update the audit trail), the write will be skipped andlastUpdatedBywon't be updated. This is fine iflastUpdatedByis intended to mean "who last changed the value," but could be surprising if it means "who last set the value."Worth confirming this is the intended semantic — if so, a brief comment in code would help future maintainers.
214-243: Consistent create/update pattern with a minor readability nit.The differentiation between
Prisma.JsonNullfor creates (Line 240) andundefinedfor updates (Line 223) is correct — Prisma requires an explicit JSON null for new rows whileundefinedmeans "don't touch the field" on updates. Theversion: { increment: 1 }/version: 1split is also correct.The ternary
options.lastUpdatedBy ? options.lastUpdatedBy : <fallback>can be simplified tooptions.lastUpdatedBy ?? <fallback>since the value is either an object (always truthy) orundefined. This applies to all four occurrences (Lines 223, 240, 374, 395, 487).✏️ Optional: use nullish coalescing for clarity
- lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : undefined, + lastUpdatedBy: options.lastUpdatedBy ?? undefined,- lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : Prisma.JsonNull, + lastUpdatedBy: options.lastUpdatedBy ?? Prisma.JsonNull,packages/core/src/v3/schemas/api.ts (1)
1154-1159: RenameintegrationtointegrationNamefor clarity and consistency.The
"user"variant usesuserId, which is explicit. The"integration"variant should similarly clarify what type of value it holds. The codebase already usesintegrationNameelsewhere in the same file (line 718) for integration identifiers, and the actual values passed (e.g.,"vercel") are integration names, not IDs. This naming aligns with the established pattern.♻️ Suggested rename for clarity
source: z .discriminatedUnion("type", [ z.object({ type: z.literal("user"), userId: z.string() }), - z.object({ type: z.literal("integration"), integration: z.string() }), + z.object({ type: z.literal("integration"), integrationName: z.string() }), ]) .optional(),
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Show resolved
Hide resolved
Add a full Remix route and UI for managing Vercel integration settings for a project environment. Implement client and server pieces including forms, validation schemas, presenters, and service calls so users can view and update connection details, toggle sync/build/deploy options, select staging environment, and complete or skip onboarding. Key changes: - Add new route file handling Vercel settings and onboarding flows. - Introduce form schemas (zod) for update, disconnect, complete and skip onboarding actions and parsing with conform/to-zod. - Implement UI components and layout using existing primitives (Dialog, Button, Fieldset, Select, Switch, etc.) and helper functions (formatVercelTargets, cn). - Wire server-side logic to look up projects, environments, and org integrations and to call VercelIntegrationService and presenters. - Add types for ConnectedVercelProject, integration data and mappings. - Add imports for utilities: path builders, message redirects, logging, session requirement, and typed fetcher helpers. Reasoning: - Provide a unified interface to configure and manage Vercel integrations per environment, improving developer experience when syncing envvars, triggering builds/deploys, and completing onboarding.
Add routes, loader/action, and path helpers to support Vercel integration
management under organization settings.
- Add organizationIntegrationsPath and
organizationVercelIntegrationPath helpers to pathBuilder for consistent
settings URLs.
- Implement a new route
_app.orgs.$organizationSlug.settings.integrations.vercel.tsx that:
- validates org params and optional configurationId query param,
- enforces organization membership and returns 404 if not found,
- loads the organization's Vercel integration, its team/installation
IDs, and connected projects,
- provides action handling scaffolding for uninstall intent and
integrates with existing services/repositories.
- Include types, UI primitives, and data formatting via remix-typedjson
to render integration details and manage deletion.
Motivation: expose a dedicated settings page to view and manage Vercel
integrations per organization, enable deep linking to specific
integration configurations, and centralize route/path construction for
consistency across the app.
…r Vercel integration Add optional referralSource handling across auth flows and telemetry to record where new users originate. Extend telemetry.user.identify to accept referralSource and consolidate identify properties into a single object before sending to PostHog. Invoke this when users sign in via GitHub or magic link: read referralSource cookie, attach it for users created within the last 30 seconds (treating them as new users), and clear the cookie after use. This improves attribution for signups by capturing and sending referral information for new users while ensuring the referral cookie is removed after consumption.
Preserve full search params when redirecting unauthenticated users to login so Vercel callback params (code, configurationId, etc.) are not lost. Add handling for Vercel callbacks that arrive without a state parameter (common for Vercel-side installations). When state is absent but configurationId is present: - Query the user's organizations and projects. - If the user has no organizations, redirect to basic details onboarding with the Vercel params preserved. - If the user has organizations but no projects, redirect to the new project creation page for the first org with the Vercel params. - If the user has orgs and projects, exchange the code for a token, fetch the Vercel integration configuration, find the default project and environment, and continue the installation flow (including generating a state JWT). Add imports for new path helpers and OAuth state generator, and wire in logic to build redirect URLs with next param when present. Improve error handling and user-facing error redirects for token and configuration fetch failures.
- replace vercelResourcePath with v3ProjectSettingsPath for linking to project settings pages so routing uses the v3 path helper. - move DialogDescription out of DialogHeader and into DialogContent to correct markup/structure and restore the form buttons layout inside the dialog content. - remove unused import (confirmBasicDetailsPath) and unused OAuth state generator import to keep callback route imports minimal. - change secret type from "sensitive"/"plain" to "encrypted" when syncing Vercel trigger keys to standardize storage semantics. - fix a stray/partial line in the Vercel callback file (cleanup after parameter parsing). These changes improve routing consistency, correct dialog markup for accessibility and layout, standardize secret typing, and clean up unused imports and stray code.
Introduce utilities to robustly handle Vercel API response shapes: - add normalizeTarget to coerce target values into string arrays - add extractEnvs to safely pull envs arrays from union responses Replace ad-hoc existence checks and inline normalization with these helpers in getVercelEnvironmentVariables and getVercelEnvironmentVariableValues to avoid runtime errors when API returns varying shapes (string vs array vs missing). This simplifies mapping logic and centralizes normalization. Remove several outdated JSDoc comments and compress inline documentation to keep the implementation focused and more readable.
- Add getVercelInstallParams(request) to extract Vercel installation parameters (code, configurationId, next) from a request URL and only return them when the integration param is "vercel" or absent. This centralizes install URL parsing for Vercel flows. - Add requireOrganization(request, organizationSlug) helper that ensures the caller is an authenticated member of the organization, returning the organization and userId or throwing a 404 response. This enforces access control for org-scoped routes. - Refactor EnvironmentVariablesPresenter to use a Vercel integration service for fetching project integration and remove in-presenter legacy migration/parsing logic. The presenter now relies on VercelIntegrationService.getVercelProjectIntegration(projectId, true) to obtain parsed integration data, simplifying responsibilities and consolidating Vercel-related parsing/migration in the service layer. These changes improve separation of concerns, centralize Vercel integration parsing, and add a reusable org auth helper for request handlers.
Update Vercel org integration handling to update existing integration tokens instead of always creating a new record. When an existing org integration is detected, log the update, call the repository update method with the new token and metadata, and re-fetch the integration to use the up-to-date record. This avoids duplicate integrations and keeps stored tokens current. Refactor Vercel repo imports and environment lookup to improve error handling and typing. Return early when getVercelCustomEnvironments fails, use the typed result (.data) and VercelCustomEnvironment for safer lookup, and remove several commented section headers and unused helpers to simplify the module. Also remove an unused presenter import and tidy up minor formatting. Why: prevent duplicate org integrations, ensure tokens are refreshed correctly, and make environment name resolution more robust and typed.
- return uninstall result from uninstallVercelIntegration so caller can react when Vercel rejects the request due to invalid credentials. - detect auth errors more robustly in isVercelAuthError by checking statusCode fields on non-Axios error shapes. - treat 401/403 from Vercel as soft-fail: log a warning and continue to clean up local DB records instead of aborting, and surface authInvalid flag to the route handler. - log a warning when uninstall succeeds but the token is invalid; log normal info for successful uninstalls.
Add typed structures, schema validation, and token exchange helper for the Vercel OAuth callback flow. Introduce TokenResponse, ProjectWithOrganization, StateData, and CallbackParams types to make intent explicit and reduce implicit any usage. Add VercelCallbackSchema (zod) to validate incoming callback query parameters and ensure safer parsing of code/state/error payloads. Implement exchangeCodeForToken helper that posts to Vercel's token endpoint, handles errors, logs failures, and returns a normalized token shape (including raw response). This centralizes HTTP logic and improves error handling. Refactor createOrFindVercelIntegration to use the new TokenResponse and ProjectWithOrganization types, simplify flow comments, and avoid repetitive code by updating/creating integrations with consistent data. Overall this change improves type safety, error reporting, and readability of the Vercel OAuth callback route.
Add a new Remix route apps/webapp/app/routes/onboarding.vercel.tsx that implements server loader and token exchange to support Vercel integration onboarding. Introduce schemas (zod) for loader query parameters and form actions, and add helper exchangeCodeForToken to call Vercel's OAuth token endpoint with robust error logging. Fetch the current user's organizations and projects from Prisma in the loader, validate incoming query params, and handle missing/misconfigured Vercel client credentials with clear log messages. Add typed TokenResponse shape, use env variables for client credentials and redirect URI, and use redirectWithErrorMessage for user-facing failures. Motivation: enable users to connect a Vercel integration during onboarding by validating parameters, exchanging OAuth codes, and presenting organization/project selection backed by the app database.
Change default Vercel integration data to enable atomic builds only for production and keep pull env vars enabled for all non-dev environments. This tightens build behavior to match expected defaults and avoids enabling atomic builds for staging/preview by default. Add optional integrationDeployments field to deployment schema and include it in the deployment API response. This introduces a typed array of integration deployment records (id, integrationName, integrationDeploymentId, commitSHA, createdAt) so downstream clients can surface related integration deployment metadata. Expose Vercel project setting autoAssignCustomDomains in presenter by fetching it alongside custom environments. Combine fetching of custom environments and the auto-assign setting into a single helper that returns both values (or null when unknown), and thread the new autoAssignCustomDomains flag through the presenter types. Also rename a UI label from "Pull from Vercel" to "Sync" for clarity.
Adjust column widths and consolidate the "Updated" columns into a single cell to simplify the table header and row layout. Increase Key/Value/Environment header widths slightly to better match content when Vercel integration is enabled. Replace separate columns for version, updated-by, and updated-at with one "Updated" column that renders the actor (user avatar/name or Vercel integration) and the timestamp together. Also simplify the Actions header sizing to remove a tiny fixed width and use hiddenLabel with zero width, improving responsiveness. These changes clean up the table markup, reduce cluttered columns, and improve alignment and responsiveness of the environment variables list.
Add an optional discriminated `source` field to the import request schema to capture whether variables are imported by a user or an integration and to include the corresponding identifier (userId or integration). Propagate this source into the database payloads (`lastUpdatedBy`) when creating or syncing environment variables so the system records who/what initiated the import.
Add a new component, BuildSettingsFields, to manage Vercel build settings, including options for pulling environment variables before builds, discovering new environment variables, and enabling atomic deployments. Update related routes and schemas to accommodate the new discoverEnvVars functionality, replacing the previous pullNewEnvVars option. This enhances the Vercel integration by providing a more granular control over environment variable management during builds.
Introduce a new VercelOnboardingModal component to streamline the onboarding process for Vercel integration. This modal manages project selection, environment variable synchronization, and build settings configuration. It enhances user experience by providing a structured flow for onboarding, including handling custom environments and GitHub integration. Additionally, refactor related logic in the Vercel integration service and update schemas to support the new onboarding features.
…c and improve error management - Updated VercelSettingsPresenter to utilize ResultAsync for better error handling and flow control. - Refactored API key regeneration to sync directly with Vercel, improving reliability. - Enhanced onboarding and connection routes to handle optional parameters and errors more gracefully. - Improved JSON parsing with safe handling using Result from neverthrow. - Updated referral source management to use Zod for type safety. - General code cleanup and consistency improvements across Vercel-related services and routes.
…tion options and sync functionality
…nd update related components
…ctionality and error handling
…ling with improved filtering and error management
Add vercelTeamSlug to OrganizationProjectIntegration data and persist it when creating or updating Vercel project integrations. Retrieve the team slug (when available) from the Vercel client and store it in the integration record so downstream logic can reference the Vercel team identifier without making extra API calls. Enhance DeploymentListPresenter to detect presence of a Vercel project integration and, when available, include a LEFT JOIN to fetch the most recent integration deployment id for each worker deployment. Parse the stored integration data using the VercelProjectIntegrationDataSchema and expose hasVercelIntegration, integrationDeploymentId, and cached Vercel fields for use when rendering deployment entries. These changes reduce repeated Vercel API calls and enable linking of deployments to their Vercel counterparts in the UI.
2f03e2a to
05d877a
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (1)
108-158:⚠️ Potential issue | 🟡 MinorMissing
integration === "vercel"guard — inconsistent with org creation route.In
_app.orgs.new/route.tsx(line 79), the Vercel flow is gated oncode && configurationId && integration === "vercel", but here (line 123) onlycode && configurationIdis checked. This makes the condition fragile if another integration ever uses similar URL params.♻️ Suggested fix
+ const integration = url.searchParams.get("integration"); + try { const project = await createProject({ ... }); // If this is a Vercel integration flow, generate state and redirect to connect - if (code && configurationId) { + if (code && configurationId && integration === "vercel") {
🤖 Fix all issues with AI agents
In `@apps/webapp/app/models/vercelIntegration.server.ts`:
- Around line 223-233: The code is incorrectly using truthy checks that drop
empty-string env values; update the checks to explicitly test for null or
undefined so "" is preserved: in toVercelEnvironmentVariableValue replace "if
(!env.value) return null" with an explicit null/undefined check (e.g., env.value
=== undefined || env.value === null); similarly change the other occurrences
where "if (listValue)" and "if (!getResult.value.value) return null" to explicit
null/undefined checks for listValue and getResult.value.value respectively,
keeping the rest of the function logic intact (references:
toVercelEnvironmentVariableValue, listValue usage, getResult.value.value).
In `@apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts`:
- Around line 233-237: The code currently strips the "dpl_" prefix when building
vercelDeploymentUrl which yields an invalid Vercel link; update the construction
so vercelDeploymentUrl uses deployment.integrationDeploymentId directly (remove
the .replace(/^dpl_/, "") call) while keeping the same null handling and
condition checks (hasVercelIntegration, deployment.integrationDeploymentId,
vercelTeamSlug, vercelProjectName) so the final URL is
`https://vercel.com/${vercelTeamSlug}/${vercelProjectName}/${deployment.integrationDeploymentId}`.
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx:
- Line 34: Remove the unused import symbol v3ProjectSettingsPath from the
imports in route.tsx; locate the import list that currently includes
v3ProjectSettingsPath and delete that identifier so the file no longer
references an unused symbol and ESLint/type-checking errors are resolved.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx:
- Around line 314-335: The Vercel link is icon-only and needs an accessible
name; update the anchor rendered inside SimpleTooltip (the element using
deployment.vercelDeploymentUrl and VercelLogo) to include an appropriate
aria-label (e.g., "View on Vercel") so screen readers announce its purpose; keep
the existing onClick={(e) => e.stopPropagation()} and target/rel attributes
unchanged, and ensure the aria-label text matches the SimpleTooltip content for
consistency.
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:
- Around line 122-126: The schema uses z.enum for environmentType inside the
z.object for the "update-vercel-sync" action; replace
z.enum(["PRODUCTION","STAGING","PREVIEW","DEVELOPMENT"]) with a string-literal
union like z.union([z.literal("PRODUCTION"), z.literal("STAGING"),
z.literal("PREVIEW"), z.literal("DEVELOPMENT")]) so the schema follows the
project's preference for string unions over enums (leave the surrounding
z.object, action literal, key, and syncEnabled transform as-is).
🧹 Nitpick comments (10)
apps/webapp/app/routes/api.v1.deployments.$deploymentId.ts (1)
73-82: Confusing map parameter nameid— consider renaming.The callback parameter
idrepresents a fullIntegrationDeploymentrecord, not an identifier. This makes expressions likeid.idunnecessarily confusing. A more descriptive name (e.g.,integrationDeploymentorintDep) would improve readability.✏️ Suggested rename
integrationDeployments: deployment.integrationDeployments.length > 0 - ? deployment.integrationDeployments.map((id) => ({ - id: id.id, - integrationName: id.integrationName, - integrationDeploymentId: id.integrationDeploymentId, - commitSHA: id.commitSHA, - createdAt: id.createdAt, + ? deployment.integrationDeployments.map((intDep) => ({ + id: intDep.id, + integrationName: intDep.integrationName, + integrationDeploymentId: intDep.integrationDeploymentId, + commitSHA: intDep.commitSHA, + createdAt: intDep.createdAt, })) : undefined,apps/webapp/app/routes/_app.orgs.new/route.tsx (1)
72-91: Validate the Vercel integration parameters with Zod.The
code,configurationId, andintegrationvalues are extracted from the URL without validation. Per project coding guidelines, Zod should be used for validation in the webapp. Additionally, consider that the OAuthcodeis being forwarded through an extra redirect hop (org creation → projects/new → /vercel/connect), which increases the chance of code expiry for slow users.♻️ Suggested validation
+ const VercelIntegrationParams = z.object({ + code: z.string(), + configurationId: z.string(), + integration: z.literal("vercel"), + next: z.string().optional(), + }); + // Preserve Vercel integration params if present const url = new URL(request.url); - const code = url.searchParams.get("code"); - const configurationId = url.searchParams.get("configurationId"); - const integration = url.searchParams.get("integration"); - const next = url.searchParams.get("next"); - - if (code && configurationId && integration === "vercel") { + const vercelParams = VercelIntegrationParams.safeParse(Object.fromEntries(url.searchParams)); + + if (vercelParams.success) { + const { code, configurationId, integration, next } = vercelParams.data; // Redirect to projects/new with params preserved const params = new URLSearchParams({ code, configurationId, integration, }); if (next) { params.set("next", next); }As per coding guidelines: "Use zod for validation in packages/core and apps/webapp".
apps/webapp/app/components/integrations/VercelBuildSettings.tsx (1)
155-173: Atomic deployments toggle renders unconditionally regardless ofavailableEnvSlugs.If
"prod"is not inavailableEnvSlugs, this toggle still renders and can be toggled on—yet the other two sections scope their per-env switches toavailableEnvSlugs. Consider guarding this section:Suggested guard
+ {availableEnvSlugs.includes("prod") && ( {/* Atomic deployments */} <div> ... </div> + )}apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts (1)
109-152: Conditional SQL construction is well-structured, but ensure the lateral join has index support.The
LEFT JOIN LATERALonIntegrationDeploymentfilters bydeploymentIdandintegrationNamewith anORDER BY createdAt DESC LIMIT 1. If this table grows, the query will benefit from a composite index on(deploymentId, integrationName, createdAt).#!/bin/bash # Check existing indexes on IntegrationDeployment table rg -n "IntegrationDeployment" internal-packages/database/prisma/schema.prisma -A 5 -B 2 | head -40apps/webapp/app/routes/api.v1.orgs.$organizationSlug.projects.$projectParam.vercel.projects.ts (1)
108-146: The endpoint is protected and returns a redundant identifier — consider usingslugalone.This PAT-authenticated endpoint (requires org membership verification) returns both
triggerProject.id(the internal database UUID) andtriggerProject.slug. Since no consuming code found uses theidfield andslugalready serves as the public project identifier, either removeidfrom the response or confirm it's necessary for a specific use case. The risk is low due to authentication requirements, but the redundancy suggests unintended exposure.apps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts (1)
296-583:getOnboardingDatais ~280 lines of sequential logic — consider decomposing.This method handles GitHub installations, token validation, client creation, project discovery, env var fetching/merging, and existing-variable mapping all inline. Extracting helpers (e.g.,
#fetchAndMergeEnvVars,#buildExistingVariablesRecord) would improve readability and testability.apps/webapp/app/services/vercelIntegration.server.ts (1)
196-231: Duplicated sync-result extraction logic.Lines 198–207 and 222–231 contain identical patterns for extracting
syncResultfrom theResultAsync. Extract a small helper to DRY this up.♻️ Example helper
function toSyncResult(result: Result<{ errors: string[] }, { message: string }>): { success: boolean; errors: string[] } { return result.isOk() ? { success: result.value.errors.length === 0, errors: result.value.errors } : { success: false, errors: [result.error.message] }; }apps/webapp/app/routes/vercel.connect.tsx (1)
42-51: Consider omittinginstallationIdwhenconfigurationIdis undefined.Per the agreed approach,
installationIdshould only be passed whenconfigurationIdis defined, using conditional spread instead of passingundefineddirectly.Based on learnings: "pass installationId to repository methods only when configurationId is defined (omit the field otherwise)."
♻️ Proposed change
await VercelIntegrationRepository.updateVercelOrgIntegrationToken({ integrationId: orgIntegration.id, accessToken: tokenResponse.accessToken, tokenType: tokenResponse.tokenType, teamId: tokenResponse.teamId ?? null, userId: tokenResponse.userId, - installationId: configurationId, + ...(configurationId ? { installationId: configurationId } : {}), raw: tokenResponse.raw });Apply the same pattern to the
createVercelOrgIntegrationcall.Also applies to: 52-63
apps/webapp/app/routes/vercel.install.tsx (2)
16-16: Prefer a function declaration for consistency.
vercel.connect.tsxusesexport async function loader(...)while this file usesexport const loader = async (...). The coding guideline prefers function declarations. As per coding guidelines, "Use function declarations instead of default exports."♻️ Proposed change
-export const loader = async ({ request }: LoaderFunctionArgs) => { +export async function loader({ request }: LoaderFunctionArgs) {And close with
}instead of};at the end.
32-53: Remove redundant organization lookup — useproject.organizationIdinstead.
findProjectBySlugalready verifies org membership and returns the full project object includingorganizationId. The separate org query (lines 32–38) is unnecessary and makes an extra database call just to getorg.id, which is the same asproject.organizationId.♻️ Simplified flow
- // Find the organization - const org = await $replica.organization.findFirst({ - where: { slug: org_slug, members: { some: { userId: user.id } }, deletedAt: null }, - orderBy: { createdAt: "desc" }, - select: { - id: true, - }, - }); - - if (!org) { - throw redirect("/"); - } - // Find the project const project = await findProjectBySlug(org_slug, project_slug, user.id); if (!project) { logger.warn("Vercel App installation attempt for non-existent project", { org_slug, project_slug, userId: user.id, }); throw redirect("/"); } const stateToken = await generateVercelOAuthState({ - organizationId: org.id, + organizationId: project.organizationId, projectId: project.id, environmentSlug, organizationSlug: org_slug, projectSlug: project_slug, });
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
Outdated
Show resolved
Hide resolved
...outes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx
Show resolved
Hide resolved
....orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
Show resolved
Hide resolved
This comment was marked as resolved.
This comment was marked as resolved.
…ved environment variable handling, and pagination support
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 517-528: The useEffect handling completeOnboardingFetcher assigns
completeOnboardingFetcher.data.redirectTo directly to window.location.href
(creating an open redirect risk); update this branch to validate and sanitize
the server-provided redirect using the same safeRedirectUrl utility used
elsewhere before navigating: when "redirectTo" exists and is a string, call
safeRedirectUrl(completeOnboardingFetcher.data.redirectTo) (or the equivalent
project helper) and only set window.location.href to the sanitized result if it
returns a safe value; otherwise fall through to setState("completed") or handle
as a no-op. Ensure the change is applied inside the useEffect that reads
completeOnboardingFetcher.data and references completeOnboardingFetcher.state,
and keep the early return for state === "github-connection".
In `@apps/webapp/app/models/vercelIntegration.server.ts`:
- Around line 1412-1417: batchUpsertVercelEnvVars currently calls
client.projects.filterProjectEnvs once and uses extractVercelEnvs, which misses
paginated results; change the logic inside batchUpsertVercelEnvVars to
iterate/paginate through client.projects.filterProjectEnvs (using the same
pagination approach used by getVercelEnvironmentVariables) until all pages are
retrieved, aggregate all pages into a single list, then pass that full list into
extractVercelEnvs so dedupe/create logic sees every existing env var and avoids
duplicate creation.
- Around line 91-94: The current fallback string check on the error variable
(the typeof error === 'string' branch) is too brittle because
error.includes('401') / includes('403') matches any occurrence of those digits;
update this logic to (1) first try to read a numeric status from structured
properties if error is an object (check for error.status, error.statusCode, or
error.response?.status), and (2) if you must inspect a string, replace
error.includes checks with a stricter regex that only matches explicit status
patterns (e.g., /\b(?:HTTP[: ]|status[:= ]?)?(401|403)\b/) so only "HTTP 401" /
"status=401" style substrings are accepted; change the branch handling the
identified numbers to return 401/403 accordingly and leave all other cases
untouched.
In `@apps/webapp/app/services/vercelIntegration.server.ts`:
- Around line 46-48: Remove the unused migrateIfNeeded parameter from the
getVercelProjectIntegration signature and all call sites: update the function
declaration async getVercelProjectIntegration(projectId: string) to drop
migrateIfNeeded, remove the default value, and update callers (e.g., the call
from EnvironmentVariablesPresenter that currently passes true) to invoke
getVercelProjectIntegration(projectId) with no second argument; ensure
TypeScript signatures/imports are updated and run a build/tsc to catch any
remaining call sites to fix.
🧹 Nitpick comments (9)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
190-200: Skip logic doesn't account forlastUpdatedBychanges.When
canSkipis true (value andisSecretare unchanged), the loopcontinues and thelastUpdatedByfield is never updated. If a different actor (e.g., integration vs. user) syncs the same unchanged value, the audit trail will remain stale, pointing to the original updater.If accurate attribution matters, consider also comparing
lastUpdatedBy:Proposed fix
const canSkip = existingSecret && existingSecret.secret === variable.value && existingValueRecord && (options.isSecret === undefined || - existingValueRecord.isSecret === options.isSecret); + existingValueRecord.isSecret === options.isSecret) && + (!options.lastUpdatedBy || + JSON.stringify(existingValueRecord.lastUpdatedBy) === + JSON.stringify(options.lastUpdatedBy));apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (3)
122-122: Unused destructured variableCompleteOnboardingForm.
CompleteOnboardingFormis destructured fromcompleteOnboardingFetcherbut never used anywhere in the component's JSX or handlers. All submissions go throughcompleteOnboardingFetcher.submit(...)directly.🧹 Remove unused variable
const completeOnboardingFetcher = useFetcher(); - const { Form: CompleteOnboardingForm } = completeOnboardingFetcher;
440-453: UnnecessarynextUrlin dependency array.
handleSkipOnboardingreferencesnextUrlin its dependency array but never reads it in the function body. This is harmless but misleading to future readers.🧹 Remove stale dependency
- }, [actionUrl, fetcher, onClose, nextUrl, fromMarketplaceContext]); + }, [actionUrl, fetcher, onClose, fromMarketplaceContext]);
93-117: Consider decomposing this 1000+ line component into step sub-components.The modal manages ~15
useEffecthooks, 3 fetchers, and renders 5 distinct step UIs inline. Extracting each step (project selection, env mapping, env var sync, build settings, GitHub connection) into its own component would reduce cognitive load, make each step independently testable, and simplify the state machine transitions.This isn't urgent for a draft, but will pay off quickly as the flow evolves.
apps/webapp/test/vercelUrls.test.ts (2)
1-2: Test file should live beside the source file.Per coding guidelines, test files should live beside the files under test. This test should be at
apps/webapp/app/v3/vercel/vercelUrls.server.test.tsrather thanapps/webapp/test/vercelUrls.test.ts.
29-38: Consider adding a case-sensitivity test.The regex uses the
iflag, sohttps://VERCEL.COM/dashboardshould pass. Adding an explicit test case would document this intent and guard against regressions if the regex is ever edited.apps/webapp/app/models/vercelIntegration.server.ts (3)
1529-1579:upsertVercelEnvVarre-fetches all env vars for a single upsert.This private method fetches the full env var list (line 1540) just to find one matching key+target. When called from
syncSingleApiKeyToVercelthis is a single call, so it's acceptable now. But if it's ever called in a loop, it becomes an N+1 problem. Consider documenting this constraint or refactoring to accept pre-fetched env vars.
241-241: Repository uses a class with all static methods — consider using a plain module (namespace) or standalone functions.
VercelIntegrationRepositoryis a class with onlystaticmethods and no instance state. In TypeScript, a module of exported functions or a plain object is more idiomatic and avoids thethisambiguity with static methods (e.g.,this.getVercelClientin line 334 depends on call-site binding).
1586-1596: Refactor to use a proper Vercel SDK method for reading project settings.The current approach uses
updateProjectwith an empty body as a workaround to read project settings, which is fragile. The Vercel SDK provides better alternatives: usegetProjects()to list and filter by ID/name, or call the REST API directly withGET /v9/projects/{idOrName}. Either approach is more maintainable and correctly expresses the intent to read without modifying.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/models/vercelIntegration.server.tsapps/webapp/app/routes/api.v1.deployments.$deploymentId.tsapps/webapp/app/routes/vercel.callback.tsapps/webapp/app/routes/vercel.configure.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/webapp/app/routes/api.v1.deployments.$deploymentId.ts
- apps/webapp/app/routes/vercel.callback.ts
- apps/webapp/app/routes/vercel.configure.tsx
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/test/vercelUrls.test.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use vitest for all tests in the Trigger.dev repository
Files:
apps/webapp/test/vercelUrls.test.ts
apps/webapp/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Test files should only import classes and functions from
app/**/*.tsfiles and should not importenv.server.tsdirectly or indirectly; pass configuration through options insteadFor testable code in the webapp, never import env.server.ts in test files - pass configuration as options instead
Files:
apps/webapp/test/vercelUrls.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptivedescribeanditblocks
Tests should avoid mocks or stubs and use the helpers from@internal/testcontainerswhen Redis or Postgres are needed
Use vitest for running unit tests
**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g.,MyService.ts) →MyService.test.ts
Files:
apps/webapp/test/vercelUrls.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use testcontainers helpers (
redisTest,postgresTest,containerTest) from@internal/testcontainersfor Redis/PostgreSQL testing instead of mocks
Files:
apps/webapp/test/vercelUrls.test.ts
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.ts
🧠 Learnings (31)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/v3/vercel/vercelUrls.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository
Applied to files:
apps/webapp/test/vercelUrls.test.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.test.{ts,tsx} : Test files should only import classes and functions from `app/**/*.ts` files and should not import `env.server.ts` directly or indirectly; pass configuration through options instead
Applied to files:
apps/webapp/test/vercelUrls.test.ts
📚 Learning: 2026-01-15T10:48:02.687Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-15T10:48:02.687Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vitest for running unit tests
Applied to files:
apps/webapp/test/vercelUrls.test.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/test/vercelUrls.test.tsapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.test.{ts,tsx} : For testable code in the webapp, never import env.server.ts in test files - pass configuration as options instead
Applied to files:
apps/webapp/test/vercelUrls.test.ts
📚 Learning: 2026-01-15T10:48:02.687Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-15T10:48:02.687Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Test files should live beside the files under test and use descriptive `describe` and `it` blocks
Applied to files:
apps/webapp/test/vercelUrls.test.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2026-02-03T18:27:05.229Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-09-02T11:18:06.602Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/services/gitHubSession.server.ts:31-36
Timestamp: 2025-09-02T11:18:06.602Z
Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-06-06T23:55:01.933Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2155
File: hosting/docker/.env.example:4-7
Timestamp: 2025-06-06T23:55:01.933Z
Learning: In the trigger.dev project, .env.example files should contain actual example secret values rather than placeholders, as these help users understand the expected format. The files include clear warnings about not using these defaults in production and instructions for generating proper secrets.
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported and have a unique `id` property with no timeouts in the run function
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2024-10-16T12:24:37.279Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 1407
File: apps/kubernetes-provider/src/index.ts:37-39
Timestamp: 2024-10-16T12:24:37.279Z
Learning: In Node.js, when assigning default values to environment variables using the `||` operator (e.g., `const VAR = process.env.VAR || 'default'`), empty strings are treated as falsy values, so the default value will be used without additional checks.
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
📚 Learning: 2025-08-14T10:09:02.528Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: internal-packages/run-engine/src/engine/index.ts:466-467
Timestamp: 2025-08-14T10:09:02.528Z
Learning: In the triggerdotdev/trigger.dev codebase, it's acceptable to pass `string | undefined` types directly to Prisma operations (both create and update). The codebase consistently uses this pattern and the team is comfortable with how Prisma handles undefined values.
Applied to files:
apps/webapp/app/models/vercelIntegration.server.ts
🧬 Code graph analysis (4)
apps/webapp/test/vercelUrls.test.ts (1)
apps/webapp/app/v3/vercel/vercelUrls.server.ts (1)
sanitizeVercelNextUrl(5-26)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
apps/webapp/app/v3/environmentVariables/repository.ts (8)
CreateEnvironmentVariables(21-28)CreateEnvironmentVariables(30-30)CreateResult(32-40)EditEnvironmentVariable(42-52)EditEnvironmentVariable(53-53)Result(75-82)EditEnvironmentVariableValue(67-72)EditEnvironmentVariableValue(73-73)
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (8)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
VercelOnboardingModal(936-936)vercelResourcePath(412-418)apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (4)
getAvailableEnvSlugs(104-113)getAvailableEnvSlugsForBuildSettings(115-120)EnvSlug(5-5)ALL_ENV_SLUGS(7-7)apps/webapp/app/utils/pathBuilder.ts (2)
vercelAppInstallPath(162-164)githubAppInstallPath(156-160)apps/webapp/app/components/primitives/Dialog.tsx (3)
Dialog(117-117)DialogContent(119-119)DialogHeader(120-120)apps/webapp/app/components/integrations/VercelLogo.tsx (1)
VercelLogo(1-12)apps/webapp/app/components/primitives/FormButtons.tsx (1)
FormButtons(3-35)apps/webapp/app/components/primitives/Tooltip.tsx (4)
TooltipProvider(141-141)Tooltip(141-141)TooltipTrigger(141-141)TooltipContent(141-141)apps/webapp/app/components/integrations/VercelBuildSettings.tsx (1)
BuildSettingsFields(23-176)
apps/webapp/app/services/vercelIntegration.server.ts (3)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (9)
VercelProjectIntegrationData(61-61)VercelProjectIntegrationDataSchema(51-59)createDefaultVercelIntegrationData(63-82)VercelIntegrationConfig(37-37)SyncEnvVarsMapping(49-49)TriggerEnvironmentType(39-39)TriggerEnvironmentType(40-40)envTypeToSlug(133-144)EnvSlug(5-5)apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
projectId(644-684)apps/webapp/app/models/vercelIntegration.server.ts (1)
VercelIntegrationRepository(241-1659)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (3)
8-20: LGTM — Type imports align with Zod schemas.The strongly typed payload imports (
CreateEnvironmentVariables,EditEnvironmentVariable,EditEnvironmentVariableValue) correctly usetypekeyword and correspond to the Zod-inferred types in./repository.
363-397: LGTM — Edit method versioning and audit trail.Version increment on update and
version: 1on create are correctly applied. ThelastUpdatedByhandling is consistent: preserved on update when unspecified, explicitly set toPrisma.JsonNullon create.
475-489: LGTM — editValue versioning update.Version increment and
lastUpdatedByhandling are consistent with the other methods. The operation is correctly wrapped in the existing transaction.apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (4)
52-65: Well-implemented URL validation helper.The allowlist is correctly scoped to same-origin and
*.vercel.comsubdomains, and the regex anchoring prevents bypass via crafted hostnames likeevil-vercel.com.
375-401: Env var toggle logic is clean and consistent with opt-out semantics.The
handleToggleEnvVarandhandleToggleAllEnvVarscallbacks correctly implement an opt-out pattern where an empty mapping means "sync everything" and explicitfalseentries disable individual vars. The bulk toggle at line 406 (clear mapping = enable all) is consistent.
1032-1077: Redirect safety in GitHub connection buttons looks good.Both the "Complete" and "Skip for now" paths now correctly validate
nextUrlthroughsafeRedirectUrlbefore assigning towindow.location.href.
246-266: Marketplace auto-redirect properly usessafeRedirectUrl.The auto-redirect effect validates
nextUrlbefore navigation, and the guard ref prevents duplicate redirects.apps/webapp/app/v3/vercel/vercelUrls.server.ts (1)
1-26: Clean, well-scoped redirect-validation utility.The regex correctly anchors the hostname, the protocol-relative
//check prevents bypass, and wrappingnew URL()in try/catch handles malformed input. Implementation looks solid.apps/webapp/app/services/vercelIntegration.server.ts (4)
405-433:completeOnboardingintentionally preserves existingsyncEnvVarsMapping— verify this is the desired behavior.Line 431 always writes
existing.parsedIntegrationData.syncEnvVarsMapping(the old mapping) to the DB, while line 454 passesparams.syncEnvVarsMapping(potentially new) to the pull operation. The comment on line 430 explains the intent: avoid overwriting with an empty mapping during onboarding reset.However, this means any env-var sync selections made during onboarding are used for the initial pull but never persisted. If a user re-enters the settings page later, they'll see the old mapping, not what they selected during onboarding. If this is truly intentional, the comment is sufficient — but this is worth double-checking with the product requirements.
A previous review flagged this same area — noting it here for awareness given the updated comment.
39-44: Good testability pattern: injectablePrismaClientwith a default.The constructor accepting
PrismaClientwith a default follows the project's recommended testable-service pattern (perrealtimeClient.server.ts/realtimeClientGlobal.server.tsconvention).
23-37: Usestypeinstead ofinterface— compliant with coding guidelines.All three exported type aliases (
VercelProjectIntegrationWithParsedData,VercelProjectIntegrationWithData,VercelProjectIntegrationWithProject) correctly usetypeas required. As per coding guidelines,**/*.{ts,tsx}: Use types over interfaces for TypeScript.
559-565: Add a clarifying comment explaining the one-time TRIGGER_VERSION sync behavior.The early return at lines 563–565 prevents
TRIGGER_VERSIONfrom being updated if it already exists in Vercel. Since#syncTriggerVersionToVercelProductionis only called during integration setup (not on subsequent deployments), this means the Vercel env var is set once during onboarding and never updated, even when new versions are deployed. A brief comment explaining this intentional one-time-only behavior would help future maintainers.apps/webapp/app/models/vercelIntegration.server.ts (1)
50-52: No actionable issues found. TheisVercelSecretTypefunction correctly excludes"encrypted"by design—encrypted vars from Vercel are intentionally decrypted and synced as non-secret variables to Trigger.dev through a separate code path (lines 506–540), which is the intended behavior.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@apps/webapp/app/routes/_app.orgs`.$organizationSlug_.projects.new/route.tsx:
- Around line 122-137: When createProject succeeds but
runtimeEnvironment.findFirst (prod) returns null, the user is redirected without
context and the newly created project remains in the DB; update the error
handling around the environment lookup in the route (the block using
createProject, runtimeEnvironment.findFirst, and redirectWithErrorMessage) to
either (a) include in the error message that the project was created but not
linked to Vercel (mention project.id or project.name in the message) so the user
isn't confused, or (b) attempt cleanup by deleting the created project (call the
same project delete path in your service/Prisma for the project created by
createProject) before redirecting; implement one of these options in the code
paths handling code && configurationId where environment is falsy.
- Around line 107-111: Validate and sanitize the user-controlled query params
before propagating them: replace raw URLSearchParams.get() uses for code,
configurationId, and next with a zod schema (e.g., create a VercelQuerySchema)
that parses and enforces types and constraints, and specifically validate next
to only allow same-origin paths or entries from an explicit allowlist (or ensure
it is a relative path starting with "/") before attaching it to the redirect to
/vercel/connect; update the code that reads request.url and the variables code,
configurationId, next to use this schema and reject or normalize invalid values
so tainted next values never flow downstream.
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 836-840: The current useEffect sets setHasError(true) based solely
on fetcher.state === "idle" && fetcher.data === undefined && hasFetched, which
causes false positives; update the effect that references useEffect,
fetcher.state, fetcher.data, hasFetched, hasError, and setHasError so it only
marks an error when there is an explicit error signal from the fetcher (e.g.,
fetcher.data contains an error property or a non-2xx status) or when the fetcher
response is a thrown Response object (check for Response instance or an
ok/status flag), rather than whenever fetcher.data is undefined during transient
state transitions; in short, add a guard to inspect fetcher.data for concrete
error indicators (or Response type) before calling setHasError(true).
- Around line 244-248: The code is unsafely casting jsonArrayField results to
EnvSlug[] | null (atomicBuilds, pullEnvVarsBeforeBuild, discoverEnvVars) before
calling vercelService.updateVercelIntegrationConfig; replace those casts with a
Zod validation: define a Zod union/schema for EnvSlug and a
z.array(envSlugSchema).nullable(), parse/transform the jsonArrayField outputs
(e.g., atomicBuilds = envArraySchema.parseOrNull(jsonArrayField(...)) or use
safeParse and set null on failure) and pass the validated arrays into
updateVercelIntegrationConfig instead of using "as EnvSlug[] | null"; apply the
same change to the other occurrences (the other block that sets these three
variables).
In `@apps/webapp/app/services/vercelIntegration.server.ts`:
- Around line 154-258: selectVercelProject has a race where two concurrent calls
can both see getVercelProjectIntegration === null and call
createVercelProjectIntegration, creating duplicate
OrganizationProjectIntegration rows; fix by performing the read-then-write
inside a DB-level upsert/transaction: use Prisma transaction or a single upsert
on OrganizationProjectIntegration (or add a unique constraint on (projectId,
organizationIntegrationId) and catch unique-violation errors) so only one
integration is created; modify selectVercelProject to attempt an atomic upsert
(or run create inside a serializable transaction that re-reads with FOR UPDATE)
and return the existing row when a unique-constraint error occurs, keeping
subsequent syncApiKeysToVercel and disableAutoAssignCustomDomains logic
unchanged but invoked with the resolved integration.
🧹 Nitpick comments (7)
apps/webapp/app/services/vercelIntegration.server.ts (2)
197-230: Duplicated sync-result handling pattern — extract a helper.Lines 197–206 and 221–230 contain nearly identical logic for calling
syncApiKeysToVerceland normalizing the result. Consider extracting this into a small private method to reduce duplication.♻️ Suggested extraction
+ `#normalizeSyncResult`( + syncResultAsync: Awaited<ReturnType<typeof VercelIntegrationRepository.syncApiKeysToVercel>> + ): { success: boolean; errors: string[] } { + return syncResultAsync.isOk() + ? { success: syncResultAsync.value.errors.length === 0, errors: syncResultAsync.value.errors } + : { success: false, errors: [syncResultAsync.error.message] }; + }Then replace both occurrences with
this.#normalizeSyncResult(syncResultAsync).
567-580: Remove unnecessaryas anycast on Vercel SDK type.The Vercel SDK's
createProjectEnvtargetfield expects('production' | 'preview' | 'development')[]. Since["production"]already matches this type, theas anycast is unnecessary and masks type safety. Removing it allows TypeScript to catch potential issues if the SDK types change.♻️ Suggested fix
requestBody: { key: "TRIGGER_VERSION", value: currentDeployment.version, - target: ["production"] as any, + target: ["production"], type: "encrypted", },apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (1)
343-416: Excessive overlapping useEffects for modal state — high complexity and duplication risk.There are four separate useEffects (lines 343–361, 365–371, 374–381, 410–416) all managing the same modal open/close state. The second effect (lines 365–371) unconditionally opens the modal whenever
hasQueryParam && !isModalOpen, which makes effects#1and#3partially redundant — they also open the modal when data is loaded andhasQueryParamis present.This creates a chain where:
- Effect
#2opens modal → setsisModalOpen = trueisModalOpenchange triggers effect#1(it's in its dep array) → it may triggeropenVercelOnboarding()again, which is a no-op since modal is already openWhile not an infinite loop (since
openVercelOnboardingis guarded), the overlapping responsibility is fragile and hard to reason about. Consider consolidating into a single effect or at most two (one for query-param-driven state, one for button-click-driven state).apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (2)
439-452: UnusednextUrlin dependency array ofhandleSkipOnboarding.
nextUrlis listed in theuseCallbackdependency array (line 452) but is never referenced inside the callback body. This won't cause bugs but adds unnecessary re-creation of the callback whennextUrlchanges.♻️ Remove unused dependency
- }, [actionUrl, fetcher, onClose, nextUrl, fromMarketplaceContext]); + }, [actionUrl, fetcher, onClose, fromMarketplaceContext]);
92-116: Very large component (~1085 lines) — consider splitting into sub-components per step.The
VercelOnboardingModalcomponent manages ~15 useEffects, 3 fetchers, and renders 5+ distinct UI steps inline. This level of complexity makes it hard to test and maintain. Consider extracting each step (project selection, env mapping, env var sync, build settings, GitHub connection) into focused sub-components and/or extracting the state machine logic into a custom hook.This was also noted in the PR review objectives as recommendation
#10.Also applies to: 572-577
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
925-927: Move the import to the top of the file with other imports.The
import { VercelOnboardingModal }at line 925 is separated from other imports at the top of the file. This is unconventional and can confuse readers or linters.♻️ Move import to top
// At the top of the file, with other component imports: +import { VercelOnboardingModal } from "~/components/integrations/VercelOnboardingModal"; // Remove from bottom: -import { VercelOnboardingModal } from "~/components/integrations/VercelOnboardingModal"; - export { VercelSettingsPanel, VercelOnboardingModal };
578-591: Change-detection withJSON.stringify([...arr].sort())works but is fragile.The array comparison logic in
useEffect(lines 578–591) creates sorted copies and stringifies for comparison. This works forEnvSlug[]but is sensitive to object references in the dependency array —originalAtomicBuildsetc. are re-created on every render since they're derived fromconnectedProject.integrationData.configvia?? []. This means theuseEffectfires on every render.Consider memoizing the original values or using a deep comparison utility.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/utils/pathBuilder.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/utils/pathBuilder.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/services/vercelIntegration.server.ts
🧠 Learnings (34)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-02-03T18:27:05.229Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-02T11:18:06.602Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/services/gitHubSession.server.ts:31-36
Timestamp: 2025-09-02T11:18:06.602Z
Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The webapp at apps/webapp is a Remix 2.1 application using Node.js v20
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Keep the `.claude/skills/trigger-dev-tasks/` skill in sync with the latest rules version when SDK features change
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Import from `trigger.dev/core` using subpaths only, never import from root
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
🧬 Code graph analysis (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (6)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
loader(146-201)VercelOnboardingModal(927-927)apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.integrations.vercel.tsx (1)
loader(46-113)apps/webapp/app/routes/vercel.onboarding.tsx (1)
loader(56-176)apps/webapp/app/hooks/useSearchParam.ts (1)
useSearchParams(7-64)apps/webapp/app/utils/pathBuilder.ts (1)
vercelResourcePath(170-176)apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (1)
VercelOnboardingModal(92-1085)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (3)
apps/webapp/app/models/message.server.ts (1)
redirectWithErrorMessage(201-218)apps/webapp/app/utils/pathBuilder.ts (1)
newProjectPath(137-141)apps/webapp/app/v3/vercel/vercelOAuthState.server.ts (1)
generateVercelOAuthState(15-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: typecheck / typecheck
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (2)
35-35: LGTM!Import is used at line 139 and correctly references the server-side module.
139-157: LGTM — OAuth state construction and redirect look correct.The JWT-based state with 15-minute expiry is a solid pattern. Hardcoding
origin: "marketplace"is appropriate here since the presence ofconfigurationIdindicates a marketplace flow. The internal redirect to/vercel/connectcorrectly bundles all necessary parameters.apps/webapp/app/services/vercelIntegration.server.ts (3)
39-44: LGTM — Service class with dependency injection.Clean constructor-based DI for
PrismaClientwith a sensible default. This follows the project's testable service pattern.
46-82: LGTM — Safe parsing with proper error logging.
getVercelProjectIntegrationcorrectly usessafeParsewith error logging and returnsnullon parse failure rather than throwing. ThedeletedAt: nullfilters are properly applied.
598-612: LGTM — Soft-delete for disconnect.
disconnectVercelProjectcorrectly setsdeletedAtrather than hard-deleting the row, which is consistent with thedeletedAt: nullfilters used elsewhere.apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (2)
613-635: LGTM — Modal rendering with safe defaults.The
VercelOnboardingModalis properly gated byvercelIntegrationEnabled, uses safe defaults (?? false,?? null), and passesnextUrlthrough. ThesafeRedirectUrlguard in the modal handles URL validation.
546-559: Vercel integration settings nesting creates inconsistent access patterns.The Vercel settings panel (line 546) is nested inside the
githubAppEnabledconditional, but the VercelOnboardingModal (line 615) is rendered independently with onlyvercelIntegrationEnabled. While Vercel functionally requires GitHub to sync environment variables and link deployments (as documented in the VercelGitHubWarning component), this dependency is only enforced for the settings panel, not the onboarding flow.Either:
- Move the settings panel outside the GitHub conditional and add an explicit
isGitHubConnectedcheck in the VercelSettingsPanel component (consistent with how VercelGitHubWarning is used), or- Gate the VercelOnboardingModal with the same GitHub check to maintain consistency across the UI
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (4)
51-64: LGTM —safeRedirectUrlproperly restricts redirects.The function correctly limits to same-origin and
*.vercel.comover HTTPS, with proper hostname validation via regex. Thetry/catchhandles malformed URLs.
516-530: LGTM —safeRedirectUrlapplied to server-providedredirectTo.The
completeOnboardingFetcher.data.redirectTois now validated throughsafeRedirectUrlbefore assignment towindow.location.href. This addresses the open-redirect concern from the prior review.
244-265: LGTM — Marketplace auto-redirect usessafeRedirectUrl.The
nextUrlis validated throughsafeRedirectUrlbefore being assigned towindow.location.href, and the redirect guard ref prevents duplicate redirects.
1034-1065: LGTM — Both "Complete" and "Skip for now" buttons usesafeRedirectUrl.Both button handlers now validate
nextUrlthroughsafeRedirectUrlbefore navigating. This addresses the prior open-redirect concern.apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
146-201: LGTM — Loader with proper auth, validation, and error handling.The loader correctly requires authentication (
requireUserId), validates params with Zod (EnvironmentParamSchema), checks project/environment existence, and handles presenter errors with appropriate HTTP status codes. ThefromPromisewrapping issue from the prior review appears resolved.
297-305:⚠️ Potential issue | 🟠 MajorServer-side redirect allows any HTTPS host — should match client-side restriction.
The server returns
redirectTo: nextifurlResult.value.protocol === "https:"(line 299), which allows redirects to any HTTPS host. The client-sidesafeRedirectUrlis more restrictive (same-origin +*.vercel.com). While the client re-validates, defense-in-depth means the server should not return unsafe redirects. Tighten the server check to match.🔒 Suggested fix
if (next) { const urlResult = Result.fromThrowable(() => new URL(next), (e) => e)(); - if (urlResult.isOk() && urlResult.value.protocol === "https:") { + if (urlResult.isOk() && urlResult.value.protocol === "https:" && + /^([a-z0-9-]+\.)*vercel\.com$/i.test(urlResult.value.hostname)) { return json({ success: true, redirectTo: next }); }⛔ Skipped due to learnings
Learnt from: myftija Repo: triggerdotdev/trigger.dev PR: 2463 File: apps/webapp/app/services/gitHubSession.server.ts:31-36 Timestamp: 2025-09-02T11:18:06.602Z Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
Show resolved
Hide resolved
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
Show resolved
Hide resolved
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Show resolved
Hide resolved
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Show resolved
Hide resolved
0df16b5 to
5762f53
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/integrations/VercelOnboardingModal.tsx`:
- Around line 516-528: The effect handling completeOnboardingFetcher currently
returns early when safeRedirectUrl(completeOnboardingFetcher.data.redirectTo) is
falsy, leaving the modal stuck; update the useEffect in VercelOnboardingModal so
that after detecting "redirectTo" you still call setState("completed") when
safeRedirectUrl returns null (and only perform window.location.href when
validRedirect is truthy), removing the early return path that prevents state
advancement; reference the useEffect, completeOnboardingFetcher,
safeRedirectUrl, setState, and state symbols to locate and change the logic.
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 297-302: The server-side check for the `next` redirect is
currently allowing any HTTPS host (via `urlResult.value.protocol === "https:"`);
tighten it to match the client allowlist by only permitting redirects when the
parsed URL's origin equals the incoming request's origin or its hostname ends
with "vercel.com". Update the logic around `urlResult` (the
`Result.fromThrowable(() => new URL(next), ...)` block) to compute the request
origin (e.g., from the incoming `request` or its headers) and then return
success only if `urlResult.isOk()` and ( `urlResult.value.origin ===
requestOrigin` OR `urlResult.value.hostname.endsWith("vercel.com")` ); treat any
other case (including `urlResult.isErr()`) as a disallowed redirect.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/utils/pathBuilder.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/utils/pathBuilder.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/services/vercelIntegration.server.ts
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.ts
🧠 Learnings (37)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-02-03T18:27:05.229Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.ts
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Import from `trigger.dev/core` using subpaths only, never import from root
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx
📚 Learning: 2025-09-02T11:18:06.602Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/services/gitHubSession.server.ts:31-36
Timestamp: 2025-09-02T11:18:06.602Z
Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsxapps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/components/integrations/VercelOnboardingModal.tsxapps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.tsapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-06T19:53:38.843Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts:233-237
Timestamp: 2026-02-06T19:53:38.843Z
Learning: When constructing Vercel dashboard URLs from deployment IDs, always strip the dpl_ prefix from the ID. Implement this by transforming the ID with .replace(/^dpl_/, "") before concatenating into the URL: https://vercel.com/${teamSlug}/${projectName}/${cleanedDeploymentId}. Consider centralizing this logic in a small helper (e.g., getVercelDeploymentId(id) or a URL builder) and add tests to verify both prefixed and non-prefixed inputs.
Applied to files:
apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Keep the `.claude/skills/trigger-dev-tasks/` skill in sync with the latest rules version when SDK features change
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:48:39.285Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: internal-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql:14-18
Timestamp: 2026-02-03T18:48:39.285Z
Learning: When adding indexes to existing tables in Prisma migrations, use CONCURRENTLY in a separate migration file to avoid table locks. Indexes on newly created tables (CREATE TABLE) can be created in the same migration file without CONCURRENTLY.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
🧬 Code graph analysis (4)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (3)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (2)
loader(146-201)VercelOnboardingModal(927-927)apps/webapp/app/utils/pathBuilder.ts (1)
vercelResourcePath(170-176)apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (1)
VercelOnboardingModal(92-1085)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (3)
apps/webapp/app/models/message.server.ts (1)
redirectWithErrorMessage(201-218)apps/webapp/app/utils/pathBuilder.ts (1)
newProjectPath(137-141)apps/webapp/app/v3/vercel/vercelOAuthState.server.ts (1)
generateVercelOAuthState(15-23)
apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.ts (3)
apps/webapp/app/services/vercelIntegration.server.ts (1)
VercelIntegrationService(39-613)apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (2)
SyncEnvVarsMapping(49-49)EnvSlug(5-5)apps/webapp/app/v3/environmentVariables/repository.ts (1)
EnvironmentVariableUpdater(19-19)
apps/webapp/app/services/vercelIntegration.server.ts (1)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (9)
VercelProjectIntegrationData(61-61)VercelProjectIntegrationDataSchema(51-59)createDefaultVercelIntegrationData(63-82)VercelIntegrationConfig(37-37)SyncEnvVarsMapping(49-49)TriggerEnvironmentType(39-39)TriggerEnvironmentType(40-40)envTypeToSlug(133-144)EnvSlug(5-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: typecheck / typecheck
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: sdk-compat / Deno Runtime
🔇 Additional comments (8)
apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx (2)
139-157: OAuth state generation and redirect look correct.The state JWT is properly constructed with the required fields (
organizationId,projectId,environmentSlug, slugs for routing), andorigin: "marketplace"is appropriately hardcoded for this code path. TheURLSearchParamsconstruction cleanly handles the optionalnextparameter. The redirect to/vercel/connectcorrectly delegates the remainder of the OAuth flow.
35-35: Import looks good.
generateVercelOAuthStateis correctly imported and used in the Vercel integration branch of the action.apps/webapp/app/presenters/v3/EnvironmentVariablesPresenter.server.ts (2)
78-113: BatchedupdatedByUserlookup is efficient.
Nice use of a de‑duplicated ID set to avoid per‑value queries.
141-213: Vercel integration fields are surfaced cleanly in the payload.
The mapping and defaults read well.apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx (3)
341-361: Good guard against repeated onboarding loads.
ThevercelFetcher.data === undefinedcheck prevents reload loops.
546-635: Vercel panel + modal wiring looks consistent.
Conditional rendering and data reload hook-up read cleanly.
100-104: No issue found.isVercelSupportedis a static boolean property, not a method. The code correctly assigns its boolean value tovercelIntegrationEnabled.Likely an incorrect or invalid review comment.
apps/webapp/app/components/integrations/VercelOnboardingModal.tsx (1)
51-64: Good allowlist insafeRedirectUrl.
Same‑origin +vercel.comis a solid guard.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx:
- Around line 190-194: The computed local variable authInvalid (from
onboardingData?.authInvalid || result.authInvalid || false) is redundantly
re-checking result.authInvalid when building the typedjson return; update the
returned object in the block that calls typedjson to use the local authInvalid
variable directly (authInvalid: authInvalid) or simply include authInvalid once,
removing the redundant "|| result.authInvalid" so the return uses the
already-computed value from the authInvalid variable (refer to the authInvalid
const and the typedjson({...}) return).
In `@apps/webapp/app/services/vercelIntegration.server.ts`:
- Around line 200-218: The current update spreads ...(parsedData.success ?
parsedData.data : {}) which, when VercelProjectIntegrationDataSchema.safeParse
fails, writes an incomplete integrationData and corrupts future reads; change
the logic in the block that calls VercelProjectIntegrationDataSchema.safeParse
(using variables parsedData and existing.integrationData) to abort the update
when parsedData.success is false—log an error (or throw) and skip/return instead
of performing tx.organizationProjectIntegration.update; alternatively, if you
prefer automatic recovery, merge parsedData.data with explicit defaults for
required fields (e.g., config and syncEnvVarsMapping) before calling
tx.organizationProjectIntegration.update so integrationData always contains
required schema fields.
🧹 Nitpick comments (8)
apps/webapp/app/services/vercelIntegration.server.ts (4)
461-474: Clarify the divergence betweensyncEnvVarsMappingusage for pull vs. persistence.Line 461 defines a
syncEnvVarsMappingfrom params (used at line 495 forpullEnvVarsFromVercel), but line 472 deliberately ignores it for persistence. The inline comment helps, but the same variable name used for two different intents is confusing. Consider renaming line 461's variable (e.g.,pullSyncMapping) to make the separation explicit.♻️ Suggested rename for clarity
- const syncEnvVarsMapping = params.syncEnvVarsMapping ?? { "dev":{}, "stg":{}, "prod":{}, "preview":{} }; + // Used only for the initial pull from Vercel, not persisted as integration data + const pullSyncMapping = params.syncEnvVarsMapping ?? { "dev":{}, "stg":{}, "prod":{}, "preview":{} };Then at line 495:
- syncEnvVarsMapping, + syncEnvVarsMapping: pullSyncMapping,
302-348: Read-then-write without transaction in config updates.
updateVercelIntegrationConfig,updateSyncEnvVarsMapping,updateSyncEnvVarForEnvironment, andremoveSyncEnvVarForEnvironmentall fetch the existing record, mutate in memory, then write back. Concurrent calls can cause lost updates. Since these are settings updates (low contention), the risk is minor, but wrapping in a transaction (or using a JSON merge approach) would be more robust.
524-638:#syncTriggerVersionToVercelProductionis long with many early-return branches.This private method spans ~115 lines with 6 early-return points and intermingles data fetching, API calls, and error handling. Consider extracting the "resolve Vercel project ID" and "check existing env var" steps into helper methods to improve readability.
609-622: Remove unnecessaryas anycast ontargetfield or document the SDK compatibility issue.The Vercel SDK's
createProjectEnvexpectstargetto be an array of'production' | 'preview' | 'development'. Thetarget: ["production"] as anycast is unnecessary and masks what should be a properly typed value. If a real type mismatch exists with your SDK version, add a comment referencing the specific version or issue so maintainers know when it's safe to remove.apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (3)
572-590: Unstable references inuseEffectdependency array cause unnecessary re-runs.
originalAtomicBuilds,originalPullEnvVars,originalDiscoverEnvVars, andoriginalStagingEnvare derived with?? []/?? nullon every render. When the source value is nullish,?? []creates a new array reference each time, causing the effect to fire on every render even when nothing changed. Wrap withuseMemoor move these to a ref.♻️ Proposed fix using useMemo
+import { useMemo } from "react"; - const originalAtomicBuilds = connectedProject.integrationData.config.atomicBuilds ?? []; - const originalPullEnvVars = connectedProject.integrationData.config.pullEnvVarsBeforeBuild ?? []; - const originalDiscoverEnvVars = connectedProject.integrationData.config.discoverEnvVars ?? []; - const originalStagingEnv = connectedProject.integrationData.config.vercelStagingEnvironment ?? null; + const originalAtomicBuilds = useMemo( + () => connectedProject.integrationData.config.atomicBuilds ?? [], + [connectedProject.integrationData.config.atomicBuilds] + ); + const originalPullEnvVars = useMemo( + () => connectedProject.integrationData.config.pullEnvVarsBeforeBuild ?? [], + [connectedProject.integrationData.config.pullEnvVarsBeforeBuild] + ); + const originalDiscoverEnvVars = useMemo( + () => connectedProject.integrationData.config.discoverEnvVars ?? [], + [connectedProject.integrationData.config.discoverEnvVars] + ); + const originalStagingEnv = useMemo( + () => connectedProject.integrationData.config.vercelStagingEnvironment ?? null, + [connectedProject.integrationData.config.vercelStagingEnvironment] + );
560-560:useActionData() as anyloses type safety.This discards all type information from the action response. Consider typing it against the Conform submission type or using a discriminated union of the possible action responses.
924-926: Import placed at the bottom of the file.The
VercelOnboardingModalimport at line 924 is separated from the rest of the imports (lines 1–64). Moving it to the import block at the top improves readability and is more conventional.apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (1)
149-160: Consolidate duplicate environment type to slug mapping to reduce drift risk.Both
envTypeToSlug(lines 149–160) andenvSluginapps/webapp/app/models/api-key.server.ts(lines 89–103) perform identical mappings. Although the input types differ (TriggerEnvironmentTypevsRuntimeEnvironment["type"]), the underlying string values and outputs are identical. Consider exportingenvSlugfrom api-key.server.ts and reusing it in vercelProjectIntegrationSchema.ts, or creating a shared utility to maintain a single source of truth and prevent mapping drift.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
🧠 Learnings (29)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:27:05.229Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:48:39.285Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: internal-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql:14-18
Timestamp: 2026-02-03T18:48:39.285Z
Learning: When adding indexes to existing tables in Prisma migrations, use CONCURRENTLY in a separate migration file to avoid table locks. Indexes on newly created tables (CREATE TABLE) can be created in the same migration file without CONCURRENTLY.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Access environment variables via `env` export from `apps/webapp/app/env.server.ts`, never use `process.env` directly
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Keep the `.claude/skills/trigger-dev-tasks/` skill in sync with the latest rules version when SDK features change
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-02T11:18:06.602Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/services/gitHubSession.server.ts:31-36
Timestamp: 2025-09-02T11:18:06.602Z
Learning: In the GitHub App installation flow in apps/webapp/app/services/gitHubSession.server.ts, the redirectTo parameter stored in httpOnly session cookies is considered acceptable without additional validation by the maintainer, as the httpOnly cookie provides sufficient security for this use case.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
📚 Learning: 2025-09-02T11:27:36.336Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2463
File: apps/webapp/app/routes/_app.github.callback/route.tsx:33-44
Timestamp: 2025-09-02T11:27:36.336Z
Learning: In the GitHub App installation callback flow in apps/webapp/app/routes/_app.github.callback/route.tsx, the install session cookie is not cleared after use due to interface limitations with redirectWithSuccessMessage/redirectWithErrorMessage not supporting custom headers. The maintainer accepts this design as the 1-hour cookie expiration provides sufficient protection against replay attacks.
Applied to files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
🧬 Code graph analysis (3)
apps/webapp/app/services/vercelIntegration.server.ts (1)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (9)
VercelProjectIntegrationData(77-77)VercelProjectIntegrationDataSchema(67-75)createDefaultVercelIntegrationData(79-98)VercelIntegrationConfig(53-53)SyncEnvVarsMapping(65-65)TriggerEnvironmentType(55-55)TriggerEnvironmentType(56-56)envTypeToSlug(149-160)EnvSlug(5-5)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (2)
apps/coordinator/src/util.ts (1)
safeJsonParse(21-31)apps/webapp/app/models/api-key.server.ts (1)
envSlug(89-104)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (8)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (4)
envSlugArrayField(31-40)EnvSlug(5-5)getAvailableEnvSlugs(120-129)getAvailableEnvSlugsForBuildSettings(131-136)apps/webapp/app/presenters/OrganizationsPresenter.server.ts (1)
userId(127-184)apps/webapp/app/utils/pathBuilder.ts (4)
EnvironmentParamSchema(26-28)v3ProjectSettingsPath(474-480)vercelAppInstallPath(162-164)vercelResourcePath(170-176)apps/webapp/app/models/runtimeEnvironment.server.ts (1)
findEnvironmentBySlug(116-145)apps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts (1)
VercelSettingsPresenter(61-585)apps/webapp/app/services/vercelIntegration.server.ts (1)
VercelIntegrationService(39-655)apps/webapp/app/v3/vercel/vercelUrls.server.ts (1)
sanitizeVercelNextUrl(5-26)apps/webapp/app/models/vercelIntegration.server.ts (1)
VercelIntegrationRepository(241-1659)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: typecheck / typecheck
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (4)
1-12: Clean utility setup with neverthrow.The
safeJsonParsewrapper usingResult.fromThrowableand theEnvSlugSchemadefinition are well-structured. Using Zod'sz.enumforEnvSlugis consistent with the project's coding guidelines.
14-40: Good defensive parsing for form field transforms.Both
jsonArrayFieldandenvSlugArrayFieldgracefully handle invalid input by returningnull, andenvSlugArrayFieldfilters invalid elements rather than rejecting the whole array — well-documented and appropriate for form input.
42-77: Schemas are well-defined and use Zod correctly.
VercelIntegrationConfigSchema,TriggerEnvironmentType,SyncEnvVarsMappingSchema, andVercelProjectIntegrationDataSchemaall use types (not interfaces) and Zod for validation, consistent with the coding guidelines.
188-203: This behavior is intentional and correctly implements a default-allow policy.The function returns
truefor missing environment mappings (undefined slugs) as a deliberate design choice: unless an environment setting explicitly disables syncing (value set tofalse), the variable should be synced. For non-existent environments like"stg"or"preview"when those features aren't enabled, returningtruedefaults to syncing rather than silently skipping them. This is the correct semantic—the function answers "should we sync this var to any environment?" and the answer is "yes, unless all mapped environments have explicitly disabled it."apps/webapp/app/services/vercelIntegration.server.ts (2)
39-44: Good: Testable service pattern with injectable PrismaClient.The constructor follows the recommended pattern from the coding guidelines — accepting a
PrismaClientparameter with a default, making it testable without global state. As per coding guidelines: "Separate testable services from configuration files; follow the pattern ofrealtimeClient.server.ts."
181-253: Good: Serializable transaction addresses the race condition.The
$transactionwithisolationLevel: "Serializable"properly prevents duplicate project integrations from concurrent calls. External API calls (syncApiKeysToVercel,disableAutoAssignCustomDomains) are correctly placed outside the transaction to avoid holding locks during network I/O.apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx (4)
95-116: Good:envSlugArrayFieldreplaces unsafeas EnvSlug[]casts.The form schemas now use
envSlugArrayFieldforatomicBuilds,pullEnvVarsBeforeBuild, anddiscoverEnvVars, which validates individual elements againstEnvSlugSchema. This properly addresses the previous review concern about unvalidated casts.
147-202:loaderis properly authenticated and handles errors well.
requireUserIdis called, params are validated withEnvironmentParamSchema.parse, and the presenter result is checked withisErr(). The previous review concern aboutfromPromise/ResultAsyncmisuse has been resolved.
296-304: Good:sanitizeVercelNextUrlproperly restricts redirect targets.The previous review concern about an open redirect via the
nextparameter has been addressed by usingsanitizeVercelNextUrl, which restricts to same-origin orvercel.comHTTPS URLs.
835-839: Error detection relies onfetcher.data === undefinedafter idle.When the loader throws a
Response(e.g., the 500 at line 173), Remix may not populatefetcher.data, sofetcher.data === undefinedafter idle is the only signal. ThehasFetchedguard prevents false positives on initial mount, so this approach works for thrown responses. However, a brief race during the loading→idle transition could still flash the error banner momentarily if Remix settlesstateto"idle"beforedatais hydrated. In practice this is unlikely with Remix's synchronous state updates, but worth noting.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
.../app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
Outdated
Show resolved
Hide resolved
…rays and enhance Vercel integration with transaction handling to prevent race conditions
0128f16 to
d23d10d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/webapp/app/services/vercelIntegration.server.ts`:
- Around line 461-497: The persisted syncEnvVarsMapping
(existing.parsedIntegrationData.syncEnvVarsMapping) is intentionally preserved
but the pull call uses the local syncEnvVarsMapping variable derived from
params, causing a mismatch when params.syncEnvVarsMapping is omitted; update the
code so the value passed to VercelIntegrationRepository.pullEnvVarsFromVercel is
the existing parsed mapping when params.syncEnvVarsMapping is null/undefined
(i.e., use params.syncEnvVarsMapping ??
existing.parsedIntegrationData.syncEnvVarsMapping) instead of the current
syncEnvVarsMapping placeholder, ensuring the pull and the persisted
updatedData.syncEnvVarsMapping agree.
In `@apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts`:
- Around line 188-203: The function shouldSyncEnvVarForAnyEnvironment currently
iterates ALL_ENV_SLUGS (including stg/preview) which treats missing-slug entries
as "sync all" even when those environments don't exist; change the signature to
accept a parameter like availableSlugs: string[] (or Set<string>) and iterate
availableSlugs instead of ALL_ENV_SLUGS, keeping the existing logic for checking
mapping[slug] and envSettings[envVarName] !== false so only real/available
environments influence the result; update call sites of
shouldSyncEnvVarForAnyEnvironment to pass the actual environment slugs for the
org.
🧹 Nitpick comments (5)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (3)
7-7: DeriveALL_ENV_SLUGSfrom the schema to avoid duplication.
EnvSlugSchema.optionsalready provides the array of valid values. Hardcoding a second copy is a DRY violation that can silently drift if a slug is added to the schema but not the array (or vice-versa).Suggested change
-export const ALL_ENV_SLUGS: EnvSlug[] = ["dev", "stg", "prod", "preview"]; +export const ALL_ENV_SLUGS: readonly EnvSlug[] = EnvSlugSchema.options;
138-225: Three near-identicalis*EnabledForEnvironmenthelpers could share a single implementation.
isDiscoverEnvVarsEnabledForEnvironment,isPullEnvVarsEnabledForEnvironment, andisAtomicBuildsEnabledForEnvironmentall follow the exact same pattern: null/empty guard →envTypeToSlug→includes. A single shared helper reduces boilerplate:Example consolidation
function isFeatureEnabledForEnvironment( enabledSlugs: EnvSlug[] | null | undefined, environmentType: TriggerEnvironmentType ): boolean { if (!enabledSlugs || enabledSlugs.length === 0) return false; return enabledSlugs.includes(envTypeToSlug(environmentType)); } // Then alias or inline: export const isDiscoverEnvVarsEnabledForEnvironment = isFeatureEnabledForEnvironment; export const isPullEnvVarsEnabledForEnvironment = isFeatureEnabledForEnvironment; export const isAtomicBuildsEnabledForEnvironment = isFeatureEnabledForEnvironment;
149-160:envTypeToSlugduplicatesenvSlugfromapi-key.server.ts.Both functions implement identical switch logic to map environment types to slug strings (DEVELOPMENT→"dev", PRODUCTION→"prod", STAGING→"stg", PREVIEW→"preview"). Extract to a shared utility function to maintain consistency and reduce maintenance burden.
apps/webapp/app/services/vercelIntegration.server.ts (2)
302-348: Non-transactional read-then-write on JSON column across multiple update methods.
updateVercelIntegrationConfig,updateSyncEnvVarsMapping,updateSyncEnvVarForEnvironment, andremoveSyncEnvVarForEnvironment(lines 302–444) all read the fullintegrationDataJSON, mutate in JS, then write back. Concurrent calls to any of these methods for the sameprojectIdwill race on the JSON blob — the last write silently overwrites the other. If these are exclusively UI-driven single-user operations the risk is low, but if any are called from webhooks or background jobs, consider wrapping them in a serializable transaction (as you already do forselectVercelProject).
127-152: Consider addinginstalledByto the type annotation.
installedByUserIdis optional in the params but maps toinstalledByin the Prisma create. The naming difference is minor but could confuse readers. Otherwise, the creation logic withcreateDefaultVercelIntegrationDatais clean.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsxapps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Always import tasks from@trigger.dev/sdk, never use@trigger.dev/sdk/v3or deprecatedclient.defineJobpattern
Every Trigger.dev task must be exported and have a uniqueidproperty with no timeouts in the run function
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Import from
@trigger.dev/coreusing subpaths only, never import from root
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
apps/webapp/app/services/**/*.server.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Separate testable services from configuration files; follow the pattern of
realtimeClient.server.ts(testable service) andrealtimeClientGlobal.server.ts(configuration) in the webapp
Files:
apps/webapp/app/services/vercelIntegration.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webappAccess environment variables via
envexport fromapps/webapp/app/env.server.ts, never useprocess.envdirectly
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
🧠 Learnings (15)
📓 Common learnings
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:27:05.229Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/BranchesPresenter.server.ts:45-45
Timestamp: 2026-02-03T18:27:05.229Z
Learning: In the Vercel integration feature, the GitHub app is responsible for builds and provides git metadata (using source: "trigger_github_app"). The Vercel integration is only for linking deployments between platforms, not for triggering builds or providing git metadata.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).
Applied to files:
apps/webapp/app/services/vercelIntegration.server.tsapps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-02-03T18:48:39.285Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: internal-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql:14-18
Timestamp: 2026-02-03T18:48:39.285Z
Learning: When adding indexes to existing tables in Prisma migrations, use CONCURRENTLY in a separate migration file to avoid table locks. Indexes on newly created tables (CREATE TABLE) can be created in the same migration file without CONCURRENTLY.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-09-03T14:34:41.781Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx:357-371
Timestamp: 2025-09-03T14:34:41.781Z
Learning: When using Zod's safeParse, the .data property is undefined when parsing fails, but TypeScript may still complain about accessing .data without checking .success first. The suggested approach of checking .success before accessing .data improves type safety and code clarity.
Applied to files:
apps/webapp/app/services/vercelIntegration.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to {packages/core,apps/webapp}/**/*.{ts,tsx} : Use zod for validation in packages/core and apps/webapp
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp
Applied to files:
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
🧬 Code graph analysis (2)
apps/webapp/app/services/vercelIntegration.server.ts (3)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (8)
VercelProjectIntegrationData(77-77)VercelProjectIntegrationDataSchema(67-75)createDefaultVercelIntegrationData(79-98)VercelIntegrationConfig(53-53)TriggerEnvironmentType(55-55)TriggerEnvironmentType(56-56)envTypeToSlug(149-160)EnvSlug(5-5)apps/webapp/app/models/vercelIntegration.server.ts (2)
VercelIntegrationRepository(241-1659)client(499-542)apps/webapp/app/v3/models/workerDeployment.server.ts (1)
findCurrentWorkerDeployment(73-161)
apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts (2)
apps/coordinator/src/util.ts (1)
safeJsonParse(21-31)apps/webapp/app/models/api-key.server.ts (1)
envSlug(89-104)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: typecheck / typecheck
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
apps/webapp/app/services/vercelIntegration.server.ts (6)
1-44: Well-structured service foundation.Clean separation of concerns with injectable
PrismaClient, proper use oftypeoverinterface, Zod-based validation imports, and no directprocess.envaccess. The testable service pattern with constructor injection is consistent with the codebase conventions.
46-82: LGTM — safe parsing with proper soft-delete filtering.Good defensive pattern: logs the parse error with the integration ID for debugging, then returns
nullrather than propagating corrupted data.
181-253: Good: serializable transaction prevents duplicate project integrations.The
$transactionwithisolationLevel: "Serializable"properly addresses the read-then-write race condition for concurrentselectVercelProjectcalls.
640-654: LGTM — clean soft-delete.The disconnect correctly soft-deletes via
deletedAtand reuses the existing read method that already filters out deleted records.
600-606: This is working as designed. The early return is intentional—TRIGGER_VERSIONis meant to be set once during initial Vercel integration setup, not updated on each deployment. Users manage version updates through their deployment pipeline (e.g., CLI → GitHub Actions → Vercel with-e TRIGGER_VERSION=$VERSION), not through the webapp's Vercel integration. The codebase also explicitly excludesTRIGGER_VERSIONfrom general environment variable sync operations, reinforcing that it's managed separately.Likely an incorrect or invalid review comment.
609-622: No changes needed —upsert: "true"is correct per Vercel SDK specs.The
@vercel/sdklibrary expectsupsertas a query parameter typed as a string (e.g.,"true"), not a boolean. The code at line 613 is correct. Similarly,target: ["production"]at line 617 is the correct type (Array<string>); theas anycast is unnecessary but harmless and consistent with other usages in the codebase.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const syncEnvVarsMapping = params.syncEnvVarsMapping ?? { "dev":{}, "stg":{}, "prod":{}, "preview":{} }; | ||
| const updatedData: VercelProjectIntegrationData = { | ||
| ...existing.parsedIntegrationData, | ||
| config: { | ||
| ...existing.parsedIntegrationData.config, | ||
| pullEnvVarsBeforeBuild: params.pullEnvVarsBeforeBuild ?? null, | ||
| atomicBuilds: params.atomicBuilds ?? null, | ||
| discoverEnvVars: params.discoverEnvVars ?? null, | ||
| vercelStagingEnvironment: params.vercelStagingEnvironment ?? null, | ||
| }, | ||
| //This is intentionally not updated here, in case of resetting the onboarding it should not override the existing mapping with an empty one | ||
| syncEnvVarsMapping: existing.parsedIntegrationData.syncEnvVarsMapping, | ||
| onboardingCompleted: true, | ||
| }; | ||
|
|
||
| const updated = await this.#prismaClient.organizationProjectIntegration.update({ | ||
| where: { id: existing.id }, | ||
| data: { | ||
| integrationData: updatedData, | ||
| }, | ||
| }); | ||
|
|
||
| const orgIntegration = await VercelIntegrationRepository.findVercelOrgIntegrationForProject( | ||
| projectId | ||
| ); | ||
|
|
||
| if (orgIntegration) { | ||
| const teamId = await VercelIntegrationRepository.getTeamIdFromIntegration(orgIntegration); | ||
|
|
||
| const pullResult = await VercelIntegrationRepository.pullEnvVarsFromVercel({ | ||
| projectId, | ||
| vercelProjectId: updatedData.vercelProjectId, | ||
| teamId, | ||
| vercelStagingEnvironment: params.vercelStagingEnvironment, | ||
| syncEnvVarsMapping, | ||
| orgIntegration, | ||
| }); |
There was a problem hiding this comment.
Inconsistent mapping used for pull vs. persist.
Line 472 intentionally preserves the existing syncEnvVarsMapping in the persisted data (per the comment). But line 495 passes the params mapping (or the empty default from line 461) to pullEnvVarsFromVercel. If the user omits syncEnvVarsMapping, the pull runs with an empty mapping while the DB retains the old one — these should probably agree. Consider using the existing mapping for the pull as well when params don't provide one:
🐛 Proposed fix
- const syncEnvVarsMapping = params.syncEnvVarsMapping ?? { "dev":{}, "stg":{}, "prod":{}, "preview":{} };
+ const syncEnvVarsMapping = params.syncEnvVarsMapping ?? existing.parsedIntegrationData.syncEnvVarsMapping ?? { "dev":{}, "stg":{}, "prod":{}, "preview":{} };🤖 Prompt for AI Agents
In `@apps/webapp/app/services/vercelIntegration.server.ts` around lines 461 - 497,
The persisted syncEnvVarsMapping
(existing.parsedIntegrationData.syncEnvVarsMapping) is intentionally preserved
but the pull call uses the local syncEnvVarsMapping variable derived from
params, causing a mismatch when params.syncEnvVarsMapping is omitted; update the
code so the value passed to VercelIntegrationRepository.pullEnvVarsFromVercel is
the existing parsed mapping when params.syncEnvVarsMapping is null/undefined
(i.e., use params.syncEnvVarsMapping ??
existing.parsedIntegrationData.syncEnvVarsMapping) instead of the current
syncEnvVarsMapping placeholder, ensuring the pull and the persisted
updatedData.syncEnvVarsMapping agree.
| export function shouldSyncEnvVarForAnyEnvironment( | ||
| mapping: SyncEnvVarsMapping, | ||
| envVarName: string | ||
| ): boolean { | ||
| for (const slug of ALL_ENV_SLUGS) { | ||
| const envSettings = mapping[slug]; | ||
| if (!envSettings) { | ||
| return true; | ||
| } | ||
| if (envSettings[envVarName] !== false) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } |
There was a problem hiding this comment.
shouldSyncEnvVarForAnyEnvironment ignores whether environments actually exist.
This iterates ALL_ENV_SLUGS including "stg" and "preview" regardless of whether the org has staging/preview environments. If a variable is explicitly disabled for all real environments (dev, prod) but the mapping has no entry for the non-existent "stg" slug, this function returns true because a missing slug means "sync all" — even though that environment doesn't exist.
Consider accepting the available slugs as a parameter:
Suggested change
export function shouldSyncEnvVarForAnyEnvironment(
mapping: SyncEnvVarsMapping,
- envVarName: string
+ envVarName: string,
+ availableEnvSlugs: EnvSlug[] = ALL_ENV_SLUGS
): boolean {
- for (const slug of ALL_ENV_SLUGS) {
+ for (const slug of availableEnvSlugs) {
const envSettings = mapping[slug];
if (!envSettings) {
return true;
}
if (envSettings[envVarName] !== false) {
return true;
}
}
return false;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function shouldSyncEnvVarForAnyEnvironment( | |
| mapping: SyncEnvVarsMapping, | |
| envVarName: string | |
| ): boolean { | |
| for (const slug of ALL_ENV_SLUGS) { | |
| const envSettings = mapping[slug]; | |
| if (!envSettings) { | |
| return true; | |
| } | |
| if (envSettings[envVarName] !== false) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| export function shouldSyncEnvVarForAnyEnvironment( | |
| mapping: SyncEnvVarsMapping, | |
| envVarName: string, | |
| availableEnvSlugs: EnvSlug[] = ALL_ENV_SLUGS | |
| ): boolean { | |
| for (const slug of availableEnvSlugs) { | |
| const envSettings = mapping[slug]; | |
| if (!envSettings) { | |
| return true; | |
| } | |
| if (envSettings[envVarName] !== false) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } |
🤖 Prompt for AI Agents
In `@apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts` around lines 188
- 203, The function shouldSyncEnvVarForAnyEnvironment currently iterates
ALL_ENV_SLUGS (including stg/preview) which treats missing-slug entries as "sync
all" even when those environments don't exist; change the signature to accept a
parameter like availableSlugs: string[] (or Set<string>) and iterate
availableSlugs instead of ALL_ENV_SLUGS, keeping the existing logic for checking
mapping[slug] and envSettings[envVarName] !== false so only real/available
environments influence the result; update call sites of
shouldSyncEnvVarForAnyEnvironment to pass the actual environment slugs for the
org.
Vercel integration
Desc + Vid coming soon
For human reviewer:
referralSource)