Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Jan 2, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Standardized and tightened input validation for emails, URLs, booleans and pagination defaults to reduce validation surprises.
    • Stronger typing for record/metadata fields to prevent malformed data.
  • Chores

    • Upgraded core libraries and AI SDK packages; unified metadata annotations for tooling consistency.
    • Cleanup and consistency updates across schema composition and imports to improve reliability.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Jan 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
dub Ready Ready Preview Jan 7, 2026 9:38pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Important

Review skipped

Too many files!

130 files out of 280 files are above the max files limit of 150.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Standardized Zod usage and metadata across the codebase: removed the local Zod wrapper, switched many imports to the official "zod" package, replaced .openapi(...) with .meta(...), migrated numerous schema compositions from .merge(...) to .extend(...).shape (or .extend(...)), updated validator shorthands, and added related dependency upgrades.

Changes

Cohort / File(s) Summary
Zod core removal & deps
apps/web/lib/zod/index.ts, apps/web/package.json
Removed local Zod wrapper; upgraded/added dependencies (zod, zod-openapi, zod-error, ai, @ai-sdk/*, etc.).
Global schema updates
apps/web/lib/zod/schemas/*, apps/web/lib/zod/schemas/...
Replaced .openapi(...) with .meta(...); switched many .merge(...).extend(...).shape or .extend(...); tightened record types (string keys), adjusted default booleans and error option names.
API route schema tweaks
apps/web/app/(ee)/api/**, apps/web/app/api/**
Replaced merge→extend when composing query/response schemas; switched to z.email()/z.url() shorthands; tightened record shapes; reordered default/transform in some queries.
Import replacements in actions/utilities
apps/web/lib/actions/**, apps/web/lib/ai/*, apps/web/lib/tinybird/*, apps/web/lib/webhook/*, apps/web/lib/firstpromoter/*
Replaced project-local Zod imports with import { z } from "zod"; moved ai imports to @ai-sdk/*; minor schema adjustments (extend vs merge, validator shorthands).
OAuth provider typing
apps/web/lib/integrations/oauth-provider.ts, apps/web/lib/integrations/*/oauth.ts, apps/web/lib/paypal/oauth.ts
Made OAuthProviderConfig/OAuthProvider generic over token schema type; subclass constructors tightened to OAuthProviderConfig<typeof ...Schema> (type-only changes).
OpenAPI / error metadata
apps/web/lib/openapi/**, apps/web/lib/api/errors.ts
Replaced openapi metadata attachments with meta; updated zod imports and error-schema metadata usage.
Tiny imports & hooks
apps/web/ui/**, apps/web/lib/swr/*.ts
Switched ai imports to @ai-sdk/*; replaced local zod alias imports with external zod; one file added // @ts-nocheck``.
Scripts & migrations
apps/web/scripts/**
Updated zod imports and a few call signatures/filters; minor migration call-site hardenings.
Minor UI / formatting
assorted UI & packages (app.dub.co/..., packages/*, apps/web/ui/*)
Non-functional formatting, class ordering, import consolidation, and small text/JSX reflows.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • steven-tey

Poem

🐰 Hop, hop, the schemas gleam anew,
meta and zod now join the crew.
Merges slimmed to extends with care,
OAuth types sharpened in the air.
I nibble docs and dance — hooray for you!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Zod v4' directly corresponds to the main objective of the pull request: upgrading the Zod dependency from v3 to v4 and refactoring code throughout the codebase to be compatible with the new version.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security
Copy link

socket-security bot commented Jan 2, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​ai-sdk/​rsc@​2.0.3991007398100
Updated@​ai-sdk/​react@​1.0.8 ⏵ 3.0.3991007398100
Updatedzod-error@​1.5.0 ⏵ 2.0.010010010085 +5100
Updated@​ai-sdk/​anthropic@​1.0.7 ⏵ 3.0.110010085 +298100
Updated@​chronark/​zod-bird@​0.3.9 ⏵ 1.0.091100100 +185 +3100
Updatedzod-openapi@​4.2.4 ⏵ 5.4.69910010094 +4100
Addedzod@​4.3.510010010096100
Updatedai@​4.0.30 ⏵ 6.0.399 +1100 +1100100100

View full report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

♻️ Duplicate comments (10)
apps/web/lib/zod/schemas/leads.ts (1)

151-151: Verify .meta() method availability.

Same concern as Line 22 - please ensure the .meta() method is available in the zod-openapi package version being used.

apps/web/lib/actions/partners/revoke-program-invite.ts (1)

10-10: Verify the import syntax for Zod v4.

Same concern as in check-account-exists.ts: ensure that import z from "zod" works correctly with Zod v4.2.1. The standard pattern is import { z } from "zod".

apps/web/lib/actions/verify-workspace-setup.ts (1)

5-5: Verify the import syntax for Zod v4.

Same import syntax concern: ensure import z from "zod" works with Zod v4.2.1. Standard pattern is import { z } from "zod".

apps/web/lib/zod/schemas/customers.ts (1)

1-1: Verify the import syntax for Zod v4.

Same import syntax concern: standard Zod pattern is import { z } from "zod" rather than a default import. This is especially important for this schema file as it's likely imported by many other modules.

apps/web/lib/actions/partners/update-partner-profile.ts (1)

24-24: Verify the import syntax for Zod v4.

Same import syntax concern: ensure import z from "zod" is valid for Zod v4.2.1. The standard pattern is import { z } from "zod".

apps/web/lib/zod/schemas/auth.ts (1)

1-1: Verify the import syntax.

Same concern as in apps/web/lib/openapi/folders/list-folders.ts - the import uses default import syntax which may not be correct for Zod v4.

apps/web/lib/openapi/links/get-links.ts (1)

3-3: Verify the import syntax.

Same import syntax concern as flagged in apps/web/lib/openapi/folders/list-folders.ts.

apps/web/lib/actions/send-test-webhook.ts (1)

4-4: Verify the import syntax.

Same import syntax concern as flagged in other files.

apps/web/lib/openapi/links/bulk-create-links.ts (1)

7-7: Verify consistent import pattern.

Same as bulk-delete-links.ts - ensure zod imports are consistent across the codebase.

apps/web/lib/openapi/analytics/index.ts (1)

4-4: Verify the import syntax for Zod.

Same issue as in apps/web/lib/zod/schemas/folders.ts - the import uses default import syntax instead of the conventional named import pattern. Please see the verification comment in that file.

🟠 Major comments (5)
apps/web/app/(ee)/api/programs/[programId]/applications/export/route.ts-19-20 (1)

19-20: Empty string input will produce invalid column IDs.

In Zod 4, .default() only replaces undefined values, not empty strings. When the columns query parameter is an empty string (e.g., ?columns=), the current implementation will:

  1. Skip the .default() (since the value is "", not undefined)
  2. Transform "".split(",")[""]
  3. Pass an array with one empty string element to downstream logic

This causes issues at:

  • Line 59: columnOrderMap[""] is undefined, assigning priority 999
  • Line 64: schemaFields[columnIdToLabel[""]] creates a property with key "undefined"
  • Lines 72, 76: result[columnIdToLabel[""]] sets an undefined property on the result object

This breaks CSV generation with malformed data.

🔎 Proposed fix to handle empty strings
 const applicationsExportQuerySchema = z.object({
   columns: z
     .string()
     .default(exportApplicationsColumnsDefault.join(","))
-    .transform((v) => v?.split(",")),
+    .transform((v) => (v === "" ? exportApplicationsColumnsDefault.join(",") : v).split(",")),
 });

Alternatively, to maintain the previous optional behavior with Zod 4:

 const applicationsExportQuerySchema = z.object({
   columns: z
     .string()
+    .optional()
     .default(exportApplicationsColumnsDefault.join(","))
-    .transform((v) => v?.split(",")),
+    .transform((v) => v.split(",")),
 });
apps/web/scripts/framer/1-process-framer-combined.ts-5-5 (1)

5-5: Fix the Zod import syntax.

The import uses default import syntax (import z from "zod"), but Zod v4 exports z as a named export. The correct import pattern is import { z } from "zod".

Suggested fix
-import z from "zod";
+import { z } from "zod";
apps/web/lib/swr/use-customers-count.ts-4-4 (1)

4-4: Fix incorrect Zod import syntax.

Change import z from "zod" to a named import. Per project guidelines, all standard Zod imports should use import { z } from "zod". This pattern affects 89+ files across the codebase and should be fixed systematically.

🔎 Fix the import statement
-import z from "zod";
+import { z } from "zod";
apps/web/lib/zod/schemas/analytics.ts-21-29 (1)

21-29: Use the error parameter in z.enum() to customize error messages for Zod v4.

The current pattern of chaining .refine() after .enum() is not the recommended approach in Zod v4 for customizing enum error messages. Instead, pass an error option directly to the enum constructor:

const analyticsEvents = z.enum([...EVENT_TYPES, "composite"], {
  error: () => "Invalid event type. Valid event types are: clicks, leads, sales",
}).default("clicks").describe(
  "The type of event to retrieve analytics for. Defaults to `clicks`.",
);

Alternatively, use superRefine() if you need per-value logic. The .refine() after .enum() is redundant since the enum already validates the value.

apps/web/lib/zod/schemas/oauth.ts-102-104 (1)

102-104: Fix error callback return format for Zod v4 compatibility.

The error callback in discriminatedUnion is returning an object with a message property, which is the v3 errorMap format. In Zod v4, the error callback should return a string directly, not an object.

Fix
 export const tokenGrantSchema = z.discriminatedUnion(
   "grant_type",
   [authCodeExchangeSchema, refreshTokenSchema],
   {
-    error: () => ({
-      message: "grant_type must be 'authorization_code' or 'refresh_token'",
-    }),
+    error: () => "grant_type must be 'authorization_code' or 'refresh_token'",
   },
 );
🟡 Minor comments (10)
apps/web/lib/openapi/tags/delete-tag.ts-2-2 (1)

2-2: Use named import syntax for Zod.

According to codebase conventions, standard zod imports should use named import syntax instead of default import: import { z } from "zod"; rather than import z from "zod";. While both syntaxes work, the named import is the preferred style.

🔎 Fix the import syntax
-import z from "zod";
+import { z } from "zod";
apps/web/lib/swr/use-workspace-preferences.ts-1-1 (1)

1-1: Remove @ts-nocheck and fix the underlying Zod import issue.

The import at line 12 (../zod/schemas/workspace-preferences) is valid and properly structured. However, the @ts-nocheck directive is likely suppressing type errors that originate from a Zod v4 compatibility issue in the related update-workspace-preferences.ts file.

The actual issue: update-workspace-preferences.ts (line 4) uses import z from "zod", which is an incorrect default import. Zod v4 only exports named exports, so this should be import { z } from "zod". This cascades into type errors when updateWorkspacePreferences is imported and used in this file.

Fix the zod import in update-workspace-preferences.ts to resolve the root cause instead of suppressing errors with @ts-nocheck.

apps/web/lib/ai/generate-filters.ts-9-9 (1)

9-9: Use named import for Zod.

Change the default import to the named import pattern used throughout this codebase and per the project's standards:

Suggested fix
-import z from "zod";
+import { z } from "zod";

This import is inconsistent with the rest of the codebase (e.g., generate-csv-mapping.ts line 6) and the project guideline for standard Zod usage.

apps/web/lib/actions/update-workspace-preferences.ts-4-4 (1)

4-4: Align with project import convention for Zod.

This file uses a default import pattern for Zod, but the project standardly uses named imports. Change to import { z } from "zod"; to match project convention.

🔎 Proposed fix
-import z from "zod";
+import { z } from "zod";
apps/web/lib/openapi/customers/get-customers.ts-2-2 (1)

2-2: Use named import syntax for zod.

Change import z from "zod"; to import { z } from "zod";. The zod package exports z as a named export, and this is the standard pattern used throughout the codebase.

Proposed fix
-import z from "zod";
+import { z } from "zod";
apps/web/lib/zod/schemas/misc.ts-35-42 (1)

35-42: Use the error parameter consistently across all validators for Zod v4 compatibility.

The code uses the new error parameter for z.coerce.number() (lines 35, 44), which correctly accepts string values in Zod v4. However, it mixes in the deprecated message parameter for .positive() (lines 36, 45) and .max() (line 47). For consistency with the v4 unified error handling approach, these should also use error:

Suggested fix
page: z.coerce
  .number({ error: "Page must be a number." })
  .positive({ error: "Page must be greater than 0." })
  .optional()
  .default(1)
  .describe("The page number for pagination.")
  .meta({
    example: 1,
  }),
pageSize: z.coerce
  .number({ error: "Page size must be a number." })
  .positive({ error: "Page size must be greater than 0." })
  .max(pageSize, {
    error: `Max page size is ${pageSize}.`,
  })
  .optional()
  .default(pageSize)
  .describe("The number of items per page.")
  .meta({
    example: 50,
  }),
apps/web/lib/actions/check-account-exists.ts-6-6 (1)

6-6: Change the import syntax to use a named import instead of default import.

The Zod library exports z as a named export, not a default export. Line 6 should be import { z } from "zod" instead of import z from "zod". Additionally, since this file uses only standard Zod validation (no OpenAPI-specific features), it should import directly from the zod package rather than @/lib/zod.

apps/web/lib/openapi/links/bulk-delete-links.ts-2-2 (1)

2-2: Change import to use named import syntax.

Line 2 should use import { z } from "zod" instead of the default import import z from "zod". The zod package exports a named export, not a default export.

Recommended change:
-import z from "zod";
+import { z } from "zod";
apps/web/lib/webhook/schemas.ts-31-31 (1)

31-31: Consider using z.unknown() instead of z.any() for metadata value validation to maintain type safety.

While the change to z.record(z.string(), z.any()) correctly implements Zod v4's requirement to explicitly specify both key and value schemas, using z.any() for the value type bypasses all type checking. Since z.unknown() is also valid in Zod v4 and requires consumers to validate data before use, it would provide better type safety for the metadata field without sacrificing Zod v4 compatibility.

Applies to lines 31 and 41.

apps/web/lib/zod/schemas/folders.ts-7-7 (1)

7-7: Change the import to use named export syntax.

The codebase predominantly uses import { z } from "zod" (found in 600+ files), with only a small subset using the default import pattern. All sibling files in apps/web/lib/zod/schemas/ consistently use the named import. Zod exports z as a named export, making import { z } from "zod" the correct syntax.

🧹 Nitpick comments (6)
apps/web/lib/swr/use-links-count.ts (1)

4-4: Prefer named import for consistency with Zod conventions.

The default import works but the idiomatic Zod pattern is to use a named import.

🔎 Suggested change
-import z from "zod";
+import { z } from "zod";
apps/web/scripts/migrations/backfill-link-commissions.ts (1)

34-37: Consider removing the type assertion for consistency.

While the explicit undefined values for optional parameters are acceptable, the type assertion on line 37 (as SaleEvent[]) is inconsistent with the safer approach taken in backfill-missing-sales.ts (lines 49-72), where the type assertion was removed and defensive runtime checks were added instead.

For consistency and type safety, consider either:

  1. Removing the type assertion and relying on TypeScript's inference, or
  2. Adding defensive runtime checks similar to those in backfill-missing-sales.ts (lines 69-72)
Option 1: Remove type assertion
-  })) as SaleEvent[];
+  }));

Note: This assumes the return type of getEvents is correctly inferred when event: "sales" is passed.

apps/web/lib/zod/schemas/links.ts (1)

3-3: Use named import for Zod.

The import should use the named export syntax for consistency with Zod's documentation and ecosystem standards.

🔎 Proposed fix
-import z from "zod";
+import { z } from "zod";

Based on learnings, the standard pattern is import { z } from "zod".

apps/web/lib/tinybird/get-import-error-logs.ts (1)

1-1: Import path updated correctly.

The change from the local Zod wrapper to the external "zod" package is correct. This file doesn't use OpenAPI extensions, so direct import from "zod" is appropriate per project conventions.

Optional: Consider named import syntax.

The codebase uses import z from "zod" (default import). While this works, the more conventional pattern is import { z } from "zod" (named import). If this is intentional across the project, no action needed.

apps/web/lib/zod/schemas/sales.ts (1)

1-1: Consider using named import for consistency.

This file uses the default import import z from "zod", while other files in this PR (e.g., oauth.ts, oauth-provider.ts) use the named import import { z } from "zod". Both work in Zod v4, but using a consistent import style across the codebase improves maintainability.

🔎 Suggested fix
-import z from "zod";
+import { z } from "zod";
apps/web/lib/zod/schemas/analytics-response.ts (1)

3-3: Consider using named import for consistency.

Same as sales.ts - this file uses the default import while other files use named imports. Consider aligning with the named import style import { z } from "zod" for consistency.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13a8e41 and 29b3d9d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (122)
  • apps/web/app/(ee)/api/admin/commissions/route.ts
  • apps/web/app/(ee)/api/admin/payouts/route.ts
  • apps/web/app/(ee)/api/campaigns/[campaignId]/preview/route.ts
  • apps/web/app/(ee)/api/cron/send-batch-email/route.ts
  • apps/web/app/(ee)/api/embed/referrals/leaderboard/route.ts
  • apps/web/app/(ee)/api/programs/[programId]/applications/export/route.ts
  • apps/web/app/(ee)/api/track/click/route.ts
  • apps/web/app/api/ai/completion/route.ts
  • apps/web/app/api/workspaces/[idOrSlug]/users/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/confirm-create-bounty-modal.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-stats.tsx
  • apps/web/lib/actions/add-edit-integration.ts
  • apps/web/lib/actions/check-account-exists.ts
  • apps/web/lib/actions/create-user-account.ts
  • apps/web/lib/actions/enable-disable-webhook.ts
  • apps/web/lib/actions/generate-client-secret.ts
  • apps/web/lib/actions/get-integration-install-url.ts
  • apps/web/lib/actions/partners/create-program-application.ts
  • apps/web/lib/actions/partners/delete-program-invite.ts
  • apps/web/lib/actions/partners/resend-program-invite.ts
  • apps/web/lib/actions/partners/revoke-program-invite.ts
  • apps/web/lib/actions/partners/update-online-presence.ts
  • apps/web/lib/actions/partners/update-partner-notification-preference.ts
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/actions/send-otp.ts
  • apps/web/lib/actions/send-test-webhook.ts
  • apps/web/lib/actions/submit-oauth-app-for-review.ts
  • apps/web/lib/actions/update-workspace-notification-preference.ts
  • apps/web/lib/actions/update-workspace-preferences.ts
  • apps/web/lib/actions/update-workspace-store.ts
  • apps/web/lib/actions/verify-workspace-setup.ts
  • apps/web/lib/ai/generate-csv-mapping.ts
  • apps/web/lib/ai/generate-filters.ts
  • apps/web/lib/analytics/get-analytics.ts
  • apps/web/lib/analytics/get-events.ts
  • apps/web/lib/analytics/types.ts
  • apps/web/lib/api/errors.ts
  • apps/web/lib/api/sales/construct-reward-amount.ts
  • apps/web/lib/firstpromoter/schemas.ts
  • apps/web/lib/integrations/hubspot/oauth.ts
  • apps/web/lib/integrations/oauth-provider.ts
  • apps/web/lib/integrations/slack/oauth.ts
  • apps/web/lib/openapi/analytics/index.ts
  • apps/web/lib/openapi/customers/get-customers.ts
  • apps/web/lib/openapi/domains/check-domain-status.ts
  • apps/web/lib/openapi/domains/list-domains.ts
  • apps/web/lib/openapi/events/index.ts
  • apps/web/lib/openapi/folders/delete-folder.ts
  • apps/web/lib/openapi/folders/list-folders.ts
  • apps/web/lib/openapi/folders/update-folder.ts
  • apps/web/lib/openapi/index.ts
  • apps/web/lib/openapi/links/bulk-create-links.ts
  • apps/web/lib/openapi/links/bulk-delete-links.ts
  • apps/web/lib/openapi/links/bulk-update-links.ts
  • apps/web/lib/openapi/links/delete-link.ts
  • apps/web/lib/openapi/links/get-links-count.ts
  • apps/web/lib/openapi/links/get-links.ts
  • apps/web/lib/openapi/links/update-link.ts
  • apps/web/lib/openapi/partners/list-partners.ts
  • apps/web/lib/openapi/partners/retrieve-analytics.ts
  • apps/web/lib/openapi/qr/index.ts
  • apps/web/lib/openapi/tags/delete-tag.ts
  • apps/web/lib/openapi/tags/get-tags.ts
  • apps/web/lib/openapi/tags/update-tag.ts
  • apps/web/lib/openapi/workspaces/get-workspace-info.ts
  • apps/web/lib/openapi/workspaces/index.ts
  • apps/web/lib/openapi/workspaces/update-workspace.ts
  • apps/web/lib/partners/cutoff-period.ts
  • apps/web/lib/paypal/oauth.ts
  • apps/web/lib/swr/use-customers-count.ts
  • apps/web/lib/swr/use-links-count.ts
  • apps/web/lib/swr/use-workspace-preferences.ts
  • apps/web/lib/tinybird/get-click-event.ts
  • apps/web/lib/tinybird/get-import-error-logs.ts
  • apps/web/lib/tinybird/get-lead-event.ts
  • apps/web/lib/tinybird/get-lead-events.ts
  • apps/web/lib/tinybird/get-webhook-events.ts
  • apps/web/lib/tinybird/record-sale.ts
  • apps/web/lib/webhook/qstash.ts
  • apps/web/lib/webhook/schemas.ts
  • apps/web/lib/webhook/types.ts
  • apps/web/lib/zod/index.ts
  • apps/web/lib/zod/schemas/analytics-response.ts
  • apps/web/lib/zod/schemas/analytics.ts
  • apps/web/lib/zod/schemas/auth.ts
  • apps/web/lib/zod/schemas/bounties.ts
  • apps/web/lib/zod/schemas/clicks.ts
  • apps/web/lib/zod/schemas/commissions.ts
  • apps/web/lib/zod/schemas/customers.ts
  • apps/web/lib/zod/schemas/deep-links.ts
  • apps/web/lib/zod/schemas/deprecated.ts
  • apps/web/lib/zod/schemas/domains.ts
  • apps/web/lib/zod/schemas/email-domains.ts
  • apps/web/lib/zod/schemas/folders.ts
  • apps/web/lib/zod/schemas/invoices.ts
  • apps/web/lib/zod/schemas/leads.ts
  • apps/web/lib/zod/schemas/links.ts
  • apps/web/lib/zod/schemas/misc.ts
  • apps/web/lib/zod/schemas/oauth.ts
  • apps/web/lib/zod/schemas/opens.ts
  • apps/web/lib/zod/schemas/partners.ts
  • apps/web/lib/zod/schemas/payouts.ts
  • apps/web/lib/zod/schemas/qr.ts
  • apps/web/lib/zod/schemas/sales.ts
  • apps/web/lib/zod/schemas/tags.ts
  • apps/web/lib/zod/schemas/token.ts
  • apps/web/lib/zod/schemas/usage.ts
  • apps/web/lib/zod/schemas/utils.ts
  • apps/web/lib/zod/schemas/utm.ts
  • apps/web/lib/zod/schemas/workspaces.ts
  • apps/web/package.json
  • apps/web/scripts/framer/1-process-framer-combined.ts
  • apps/web/scripts/migrations/backfill-customers.ts
  • apps/web/scripts/migrations/backfill-link-commissions.ts
  • apps/web/scripts/migrations/backfill-missing-sales.ts
  • apps/web/ui/analytics/use-analytics-filters.tsx
  • apps/web/ui/links/link-builder/tag-select.tsx
  • apps/web/ui/links/short-link-input.tsx
  • apps/web/ui/modals/import-csv-modal/field-mapping.tsx
  • apps/web/ui/modals/link-builder/og-modal.tsx
💤 Files with no reviewable changes (2)
  • apps/web/lib/openapi/workspaces/index.ts
  • apps/web/lib/zod/index.ts
🧰 Additional context used
🧠 Learnings (40)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.
📚 Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/confirm-create-bounty-modal.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-stats.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/page-client.tsx
📚 Learning: 2025-09-12T17:31:10.548Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2833
File: apps/web/lib/actions/partners/approve-bounty-submission.ts:53-61
Timestamp: 2025-09-12T17:31:10.548Z
Learning: In approve-bounty-submission.ts, the logic `bounty.rewardAmount ?? rewardAmount` is intentional. Bounties with preset reward amounts should use those fixed amounts, and the rewardAmount override parameter is only used when bounty.rewardAmount is null/undefined (for custom reward bounties). This follows the design pattern where bounties are either "flat rate" (fixed amount) or "custom" (variable amount set during approval).

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/confirm-create-bounty-modal.tsx
  • apps/web/lib/api/sales/construct-reward-amount.ts
  • apps/web/lib/zod/schemas/bounties.ts
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/confirm-create-bounty-modal.tsx
  • apps/web/lib/api/sales/construct-reward-amount.ts
📚 Learning: 2025-07-30T15:25:13.936Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
📚 Learning: 2025-08-26T14:32:33.851Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/actions/partners/create-bounty-submission.ts:105-112
Timestamp: 2025-08-26T14:32:33.851Z
Learning: Non-performance bounties are required to have submissionRequirements. In create-bounty-submission.ts, it's appropriate to let the parsing fail if submissionRequirements is null for non-performance bounties, as this indicates a data integrity issue that should be caught.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/add-edit-bounty-sheet.tsx
  • apps/web/lib/zod/schemas/bounties.ts
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.

Applied to files:

  • apps/web/lib/actions/partners/delete-program-invite.ts
  • apps/web/lib/tinybird/get-click-event.ts
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/zod/schemas/invoices.ts
  • apps/web/scripts/framer/1-process-framer-combined.ts
  • apps/web/lib/openapi/links/bulk-create-links.ts
  • apps/web/lib/analytics/get-events.ts
  • apps/web/lib/tinybird/get-lead-events.ts
  • apps/web/lib/tinybird/get-import-error-logs.ts
  • apps/web/lib/openapi/folders/update-folder.ts
  • apps/web/lib/actions/update-workspace-preferences.ts
  • apps/web/lib/actions/add-edit-integration.ts
  • apps/web/lib/openapi/qr/index.ts
  • apps/web/lib/openapi/folders/delete-folder.ts
  • apps/web/lib/actions/create-user-account.ts
  • apps/web/lib/openapi/domains/check-domain-status.ts
  • apps/web/lib/zod/schemas/opens.ts
  • apps/web/lib/actions/partners/create-program-application.ts
  • apps/web/lib/openapi/analytics/index.ts
  • apps/web/lib/zod/schemas/clicks.ts
  • apps/web/lib/openapi/domains/list-domains.ts
  • apps/web/lib/actions/generate-client-secret.ts
  • apps/web/lib/integrations/oauth-provider.ts
  • apps/web/lib/api/errors.ts
  • apps/web/lib/zod/schemas/deep-links.ts
  • apps/web/lib/openapi/links/get-links-count.ts
  • apps/web/lib/actions/partners/revoke-program-invite.ts
  • apps/web/lib/zod/schemas/oauth.ts
  • apps/web/lib/zod/schemas/customers.ts
  • apps/web/lib/partners/cutoff-period.ts
  • apps/web/lib/zod/schemas/utm.ts
  • apps/web/lib/openapi/links/bulk-delete-links.ts
  • apps/web/lib/actions/get-integration-install-url.ts
  • apps/web/lib/openapi/workspaces/update-workspace.ts
  • apps/web/lib/webhook/qstash.ts
  • apps/web/lib/openapi/links/update-link.ts
  • apps/web/lib/openapi/tags/get-tags.ts
  • apps/web/lib/openapi/customers/get-customers.ts
  • apps/web/lib/actions/update-workspace-notification-preference.ts
  • apps/web/lib/actions/send-test-webhook.ts
  • apps/web/lib/actions/partners/resend-program-invite.ts
  • apps/web/lib/openapi/tags/update-tag.ts
  • apps/web/lib/actions/submit-oauth-app-for-review.ts
  • apps/web/lib/swr/use-links-count.ts
  • apps/web/lib/zod/schemas/email-domains.ts
  • apps/web/lib/openapi/links/delete-link.ts
  • apps/web/lib/zod/schemas/workspaces.ts
  • apps/web/lib/actions/verify-workspace-setup.ts
  • apps/web/lib/actions/update-workspace-store.ts
  • apps/web/lib/webhook/schemas.ts
  • apps/web/package.json
  • apps/web/lib/zod/schemas/qr.ts
  • apps/web/lib/swr/use-customers-count.ts
  • apps/web/lib/actions/check-account-exists.ts
  • apps/web/lib/zod/schemas/token.ts
  • apps/web/lib/openapi/tags/delete-tag.ts
  • apps/web/lib/zod/schemas/partners.ts
  • apps/web/lib/analytics/get-analytics.ts
  • apps/web/lib/actions/partners/update-partner-notification-preference.ts
  • apps/web/lib/tinybird/get-lead-event.ts
  • apps/web/lib/openapi/workspaces/get-workspace-info.ts
  • apps/web/lib/actions/enable-disable-webhook.ts
  • apps/web/lib/zod/schemas/sales.ts
  • apps/web/app/(ee)/api/embed/referrals/leaderboard/route.ts
  • apps/web/lib/openapi/partners/list-partners.ts
  • apps/web/lib/zod/schemas/misc.ts
  • apps/web/lib/tinybird/get-webhook-events.ts
  • apps/web/lib/webhook/types.ts
  • apps/web/lib/openapi/links/bulk-update-links.ts
  • apps/web/lib/zod/schemas/folders.ts
  • apps/web/lib/openapi/folders/list-folders.ts
  • apps/web/lib/openapi/events/index.ts
  • apps/web/lib/zod/schemas/auth.ts
  • apps/web/lib/openapi/partners/retrieve-analytics.ts
  • apps/web/lib/actions/send-otp.ts
  • apps/web/lib/zod/schemas/usage.ts
  • apps/web/lib/tinybird/record-sale.ts
  • apps/web/lib/zod/schemas/leads.ts
  • apps/web/lib/zod/schemas/utils.ts
  • apps/web/lib/zod/schemas/domains.ts
  • apps/web/lib/openapi/links/get-links.ts
  • apps/web/scripts/migrations/backfill-customers.ts
  • apps/web/lib/openapi/index.ts
  • apps/web/lib/zod/schemas/deprecated.ts
  • apps/web/lib/analytics/types.ts
  • apps/web/lib/zod/schemas/tags.ts
  • apps/web/lib/zod/schemas/links.ts
  • apps/web/lib/zod/schemas/analytics-response.ts
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/lib/actions/partners/delete-program-invite.ts
  • apps/web/lib/openapi/links/bulk-create-links.ts
  • apps/web/lib/analytics/get-events.ts
  • apps/web/scripts/migrations/backfill-link-commissions.ts
  • apps/web/lib/openapi/links/bulk-delete-links.ts
  • apps/web/lib/openapi/links/bulk-update-links.ts
  • apps/web/lib/openapi/links/get-links.ts
📚 Learning: 2025-05-29T09:47:33.583Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2433
File: apps/web/app/api/workspaces/[idOrSlug]/billing/payment-methods/route.ts:74-76
Timestamp: 2025-05-29T09:47:33.583Z
Learning: When Zod enum validation is used (e.g., `z.enum(PAYMENT_METHOD_TYPES)`), it ensures the validated value is one of the valid enum values, making subsequent type casts to the corresponding TypeScript types safe without additional validation.

Applied to files:

  • apps/web/lib/zod/schemas/invoices.ts
  • apps/web/lib/zod/schemas/tags.ts
📚 Learning: 2025-09-17T02:53:28.359Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2839
File: apps/web/lib/integrations/hubspot/schema.ts:5-12
Timestamp: 2025-09-17T02:53:28.359Z
Learning: HubSpot's OAuth token response returns `scopes` as an array of strings, not as a space-delimited string. The schema `scopes: z.array(z.string())` in hubSpotAuthTokenSchema is correct for HubSpot's actual API response format.

Applied to files:

  • apps/web/lib/integrations/hubspot/oauth.ts
  • apps/web/lib/zod/schemas/oauth.ts
  • apps/web/lib/zod/schemas/token.ts
📚 Learning: 2025-09-28T14:32:34.468Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2898
File: apps/web/lib/integrations/hubspot/oauth.ts:24-41
Timestamp: 2025-09-28T14:32:34.468Z
Learning: In the HubSpot OAuth implementation, the refreshToken() method uses the full hubSpotAuthTokenSchema to validate the refresh response, meaning the refresh endpoint returns all required fields including hub_id and scopes, so no need to merge with existing token data.

Applied to files:

  • apps/web/lib/integrations/hubspot/oauth.ts
📚 Learning: 2025-09-28T14:32:34.468Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2898
File: apps/web/lib/integrations/hubspot/oauth.ts:24-41
Timestamp: 2025-09-28T14:32:34.468Z
Learning: In the HubSpot OAuth implementation, the refresh token endpoint returns all required fields including hub_id and scopes, not just the minimal access_token/refresh_token/expires_in. The refreshToken() method successfully uses the full hubSpotAuthTokenSchema to validate the refresh response.

Applied to files:

  • apps/web/lib/integrations/hubspot/oauth.ts
📚 Learning: 2025-11-24T16:36:36.196Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-11-24T16:36:36.196Z
Learning: Applies to packages/hubspot-app/app/*-hsmeta.json : If the `config.distribution` field is set to `marketplace` in the app component, the only valid `config.auth.type` value is `oauth`

Applied to files:

  • apps/web/lib/integrations/hubspot/oauth.ts
📚 Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/confirm-create-bounty-modal.tsx
📚 Learning: 2025-10-02T22:46:22.739Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2924
File: apps/web/lib/api/conversions/track-lead.ts:7-7
Timestamp: 2025-10-02T22:46:22.739Z
Learning: In apps/web/lib/api/conversions/track-lead.ts, lead events are cached in Redis for 5 minutes (keys: `leadCache:${customer.id}` and `leadCache:${customer.id}:${stringifiedEventName}`) to provide immediate data availability while Tinybird ingestion happens asynchronously. This caching pattern allows for async-only recording without breaking "wait" mode semantics.

Applied to files:

  • apps/web/lib/tinybird/get-lead-events.ts
  • apps/web/lib/tinybird/get-lead-event.ts
  • apps/web/lib/zod/schemas/leads.ts
📚 Learning: 2025-06-18T20:26:25.177Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2538
File: apps/web/ui/partners/overview/blocks/commissions-block.tsx:16-27
Timestamp: 2025-06-18T20:26:25.177Z
Learning: In the Dub codebase, components that use workspace data (workspaceId, defaultProgramId) are wrapped in `WorkspaceAuth` which ensures these values are always available, making non-null assertions safe. This is acknowledged as a common pattern in their codebase, though not ideal.

Applied to files:

  • apps/web/lib/actions/update-workspace-preferences.ts
  • apps/web/lib/zod/schemas/workspaces.ts
📚 Learning: 2025-10-06T15:48:45.956Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: packages/prisma/schema/workspace.prisma:21-36
Timestamp: 2025-10-06T15:48:45.956Z
Learning: In the Dub repository (dubinc/dub), Prisma schema changes are not managed with separate migration files. Do not flag missing Prisma migration files when schema changes are made to files like `packages/prisma/schema/workspace.prisma` or other schema files.

Applied to files:

  • apps/web/lib/actions/add-edit-integration.ts
  • apps/web/lib/analytics/get-analytics.ts
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.

Applied to files:

  • apps/web/lib/actions/partners/revoke-program-invite.ts
  • apps/web/lib/actions/partners/update-partner-notification-preference.ts
  • apps/web/lib/actions/partners/update-online-presence.ts
📚 Learning: 2025-06-19T01:46:45.723Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-06-19T01:46:45.723Z
Learning: PayPal webhook verification in the Dub codebase is handled at the route level in `apps/web/app/(ee)/api/paypal/webhook/route.ts` using the `verifySignature` function. Individual webhook handlers like `payoutsItemFailed` don't need to re-verify signatures since they're only called after successful verification.

Applied to files:

  • apps/web/lib/webhook/qstash.ts
📚 Learning: 2025-09-24T15:47:40.293Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx:152-157
Timestamp: 2025-09-24T15:47:40.293Z
Learning: The Button component from dub/ui automatically adds type="button" when an onClick prop is passed, preventing accidental form submissions without requiring explicit type specification. The implementation uses: type={onClick ? "button" : type}

Applied to files:

  • apps/web/ui/links/link-builder/tag-select.tsx
📚 Learning: 2025-09-24T15:47:40.293Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx:152-157
Timestamp: 2025-09-24T15:47:40.293Z
Learning: The Button component from dub/ui automatically adds type="button" when an onClick prop is passed, preventing accidental form submissions without requiring explicit type specification.

Applied to files:

  • apps/web/ui/links/link-builder/tag-select.tsx
📚 Learning: 2025-12-30T16:17:49.480Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3239
File: apps/web/app/(ee)/api/cron/aggregate-clicks/route.ts:265-273
Timestamp: 2025-12-30T16:17:49.480Z
Learning: In the Dub partner/rewards system, click rewards are always flat amounts specified in `amountInCents`. They never use percentage-based amounts (`amountInPercentage`) or duration fields (`maxDuration`), so when applying modifiers to click rewards (e.g., in resolveClickRewardAmount), only `amountInCents` needs to be transferred from the matched condition.

Applied to files:

  • apps/web/lib/api/sales/construct-reward-amount.ts
📚 Learning: 2025-08-26T14:20:23.943Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/app/api/workspaces/[idOrSlug]/notification-preferences/route.ts:13-14
Timestamp: 2025-08-26T14:20:23.943Z
Learning: The updateNotificationPreference action in apps/web/lib/actions/update-notification-preference.ts already handles all notification preference types dynamically, including newBountySubmitted, through its schema validation using the notificationTypes enum and Prisma's dynamic field update pattern.

Applied to files:

  • apps/web/lib/actions/update-workspace-notification-preference.ts
  • apps/web/lib/zod/schemas/bounties.ts
  • apps/web/lib/actions/partners/update-partner-notification-preference.ts
📚 Learning: 2025-11-17T05:19:11.972Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3113
File: apps/web/app/(ee)/api/cron/payouts/charge-succeeded/send-paypal-payouts.ts:65-75
Timestamp: 2025-11-17T05:19:11.972Z
Learning: In the Dub codebase, `sendBatchEmail` (implemented in packages/email/src/send-via-resend.ts) handles filtering of emails with invalid `to` addresses internally. Call sites can safely use non-null assertions on email addresses because the email sending layer will filter out any entries with null/undefined `to` values before sending. This centralized validation pattern is intentional and removes the need for filtering at individual call sites.

Applied to files:

  • apps/web/lib/actions/partners/resend-program-invite.ts
  • apps/web/app/(ee)/api/cron/send-batch-email/route.ts
  • apps/web/app/(ee)/api/campaigns/[campaignId]/preview/route.ts
📚 Learning: 2025-12-18T16:55:57.098Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3244
File: apps/web/app/(ee)/api/cron/bounties/create-draft-submissions/route.ts:148-174
Timestamp: 2025-12-18T16:55:57.098Z
Learning: In the bounty system (e.g., apps/web/app/(ee)/api/cron/bounties/create-draft-submissions/route.ts), bounties cannot be created with workflow attributes `partnerEnrolledDays` and `partnerJoined`. Bounty workflows only support attributes available from partner link stats (clicks, sales, leads, conversions, saleAmount) and totalCommissions, which is a subset of the general WORKFLOW_ATTRIBUTES schema.

Applied to files:

  • apps/web/lib/zod/schemas/bounties.ts
📚 Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.

Applied to files:

  • apps/web/lib/swr/use-links-count.ts
  • apps/web/lib/swr/use-customers-count.ts
📚 Learning: 2025-07-17T06:41:45.620Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.

Applied to files:

  • apps/web/lib/webhook/schemas.ts
  • apps/web/lib/zod/schemas/analytics.ts
📚 Learning: 2025-11-24T16:36:36.196Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-11-24T16:36:36.196Z
Learning: Applies to packages/hubspot-app/**/*-hsmeta.json : The `type` field in `-hsmeta.json` files defines the type of the component

Applied to files:

  • apps/web/lib/webhook/schemas.ts
📚 Learning: 2025-09-24T16:13:00.387Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: packages/prisma/schema/partner.prisma:151-153
Timestamp: 2025-09-24T16:13:00.387Z
Learning: In the Dub codebase, Prisma schemas use single-column indexes without brackets (e.g., `@index(partnerId)`) and multi-column indexes with brackets (e.g., `@index([programId, partnerId])`). This syntax pattern is consistently used throughout their schema files and works correctly with their Prisma version.

Applied to files:

  • apps/web/lib/analytics/get-analytics.ts
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.

Applied to files:

  • apps/web/lib/actions/partners/update-partner-notification-preference.ts
📚 Learning: 2025-06-16T19:21:23.506Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.

Applied to files:

  • apps/web/lib/zod/schemas/analytics.ts
  • apps/web/ui/analytics/use-analytics-filters.tsx
  • apps/web/lib/analytics/types.ts
📚 Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.

Applied to files:

  • apps/web/ui/links/short-link-input.tsx
  • apps/web/ui/modals/link-builder/og-modal.tsx
📚 Learning: 2025-12-08T09:44:28.429Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3200
File: apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts:55-73
Timestamp: 2025-12-08T09:44:28.429Z
Learning: In apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts, the fraud event creation logic intentionally generates self-referential fraud events (where partnerId equals duplicatePartnerId) for partners with duplicate payout methods. This is by design to create raw events for all partners involved in a duplicate payout method scenario, regardless of whether they reference themselves.

Applied to files:

  • apps/web/lib/zod/schemas/payouts.ts
📚 Learning: 2025-12-15T16:45:51.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3213
File: apps/web/app/(ee)/api/cron/auto-approve-partner/route.ts:122-122
Timestamp: 2025-12-15T16:45:51.667Z
Learning: In cron endpoints under apps/web/app/(ee)/api/cron, continue using handleCronErrorResponse for error handling. Do not detect QStash callbacks or set Upstash-NonRetryable-Error headers in these cron routes, so QStash can retry cron errors via its native retry mechanism. The existing queueFailedRequestForRetry logic should remain limited to specific user-facing API endpoints (e.g., /api/track/lead, /api/track/sale, /api/links) to retry only transient Prisma/database errors. This pattern should apply to all cron endpoints under the cron directory in this codebase.

Applied to files:

  • apps/web/app/(ee)/api/cron/send-batch-email/route.ts
📚 Learning: 2025-08-25T21:03:24.285Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/bounty-card.tsx:1-1
Timestamp: 2025-08-25T21:03:24.285Z
Learning: In Next.js App Router, Server Components that use hooks can work without "use client" directive if they are only imported by Client Components, as they get "promoted" to run on the client side within the Client Component boundary.

Applied to files:

  • apps/web/lib/swr/use-workspace-preferences.ts
📚 Learning: 2025-09-18T16:33:17.719Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2858
File: apps/web/ui/partners/partner-application-tabs.tsx:1-1
Timestamp: 2025-09-18T16:33:17.719Z
Learning: When a React component in Next.js App Router uses non-serializable props (like setState functions), adding "use client" directive can cause serialization warnings. If the component is only imported by Client Components, it's better to omit the "use client" directive to avoid these warnings while still getting client-side execution through promotion.

Applied to files:

  • apps/web/lib/swr/use-workspace-preferences.ts
📚 Learning: 2025-08-18T02:31:22.282Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2756
File: apps/web/ui/webhooks/webhook-header.tsx:20-20
Timestamp: 2025-08-18T02:31:22.282Z
Learning: The Next.js redirect() function can be used in both Server Components and Client Components, as well as Route Handlers and Server Actions. It is not server-only as previously thought.

Applied to files:

  • apps/web/lib/swr/use-workspace-preferences.ts
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-stats.tsx
📚 Learning: 2025-10-31T19:45:25.702Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3044
File: apps/web/lib/analytics/utils/get-interval-data.ts:31-0
Timestamp: 2025-10-31T19:45:25.702Z
Learning: In apps/web/lib/analytics/utils/get-interval-data.ts, the `mtd` and `qtd` interval functions are designed to work in both server and client contexts. When no timezone is provided, they intentionally fall back to `new Date()` (local timezone) because front-end code calls these methods without passing a timezone and should use the browser's local timezone for the user's context.

Applied to files:

  • apps/web/lib/zod/schemas/usage.ts
📚 Learning: 2025-10-29T08:57:50.049Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3025
File: apps/web/lib/zod/schemas/links.ts:251-255
Timestamp: 2025-10-29T08:57:50.049Z
Learning: In the Dub codebase, the `archived` field for links comes from the database as a number (0 or 1), not as a boolean or string, so checking `value === 1` in the parse function is the correct approach.

Applied to files:

  • apps/web/lib/zod/schemas/domains.ts
🧬 Code graph analysis (16)
apps/web/lib/integrations/hubspot/oauth.ts (2)
apps/web/lib/integrations/oauth-provider.ts (1)
  • OAuthProviderConfig (5-18)
apps/web/lib/integrations/hubspot/schema.ts (1)
  • hubSpotAuthTokenSchema (8-15)
apps/web/lib/openapi/links/bulk-create-links.ts (1)
apps/web/lib/zod/schemas/links.ts (2)
  • LinkSchema (600-832)
  • LinkErrorSchema (834-840)
apps/web/lib/openapi/analytics/index.ts (1)
apps/web/lib/zod/schemas/analytics-response.ts (1)
  • analyticsResponse (30-578)
apps/web/lib/integrations/slack/oauth.ts (2)
apps/web/lib/integrations/oauth-provider.ts (1)
  • OAuthProviderConfig (5-18)
apps/web/lib/integrations/slack/schema.ts (1)
  • slackAuthTokenSchema (3-21)
apps/web/app/(ee)/api/admin/commissions/route.ts (1)
apps/web/lib/zod/schemas/analytics.ts (1)
  • analyticsQuerySchema (58-247)
apps/web/lib/paypal/oauth.ts (1)
apps/web/lib/integrations/oauth-provider.ts (1)
  • OAuthProviderConfig (5-18)
apps/web/lib/api/sales/construct-reward-amount.ts (1)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (7-22)
apps/web/scripts/migrations/backfill-missing-sales.ts (1)
apps/web/lib/analytics/get-events.ts (1)
  • getEvents (30-176)
apps/web/lib/zod/schemas/bounties.ts (1)
apps/web/lib/zod/schemas/misc.ts (1)
  • booleanQuerySchema (24-29)
apps/web/lib/zod/schemas/partners.ts (1)
apps/web/lib/zod/schemas/analytics-response.ts (1)
  • analyticsResponse (30-578)
apps/web/lib/zod/schemas/analytics.ts (3)
apps/web/lib/analytics/constants.ts (3)
  • EVENT_TYPES (178-178)
  • VALID_ANALYTICS_ENDPOINTS (64-91)
  • OLD_ANALYTICS_ENDPOINTS (184-203)
apps/web/lib/zod/schemas/misc.ts (1)
  • booleanQuerySchema (24-29)
apps/web/lib/zod/schemas/utm.ts (1)
  • UTMTemplateSchema (51-90)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-stats.tsx (1)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (7-22)
apps/web/lib/zod/schemas/domains.ts (2)
apps/web/lib/zod/schemas/misc.ts (1)
  • booleanQuerySchema (24-29)
apps/web/lib/zod/schemas/utils.ts (1)
  • parseUrlSchemaAllowEmpty (15-35)
apps/web/app/(ee)/api/programs/[programId]/applications/export/route.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
  • exportApplicationsColumnsDefault (101-110)
apps/web/lib/zod/schemas/tags.ts (1)
apps/web/lib/zod/schemas/misc.ts (1)
  • booleanQuerySchema (24-29)
apps/web/lib/zod/schemas/links.ts (2)
apps/web/lib/zod/schemas/misc.ts (1)
  • booleanQuerySchema (24-29)
apps/web/lib/api/error-codes.ts (1)
  • ErrorCode (3-15)
⏰ 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). (1)
  • GitHub Check: build

This reverts commit 15cd6b4.
@devkiran
Copy link
Collaborator Author

devkiran commented Jan 7, 2026

@coderabbitai do a final review and list out breaking changes, if any

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for query parameter schemas with changed defaults (string to boolean)
echo "=== 1. Query parameter default changes (string → boolean) ==="
rg 'default\(false\)|default\(true\)' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1 | head -50

echo ""
echo "=== 2. Check exported schemas in public APIs ==="
rg 'export const.*Schema.*=.*z\.' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas/*.ts | head -30

echo ""
echo "=== 3. Check for error message changes ==="
rg 'error:.*"' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -B 1 | head -40

echo ""
echo "=== 4. Check discriminated unions that might have changed ==="
rg 'discriminatedUnion' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 3 -B 1 | head -30

echo ""
echo "=== 5. Check API route query schemas ==="
rg 'QuerySchema|querySchema' -g 'route.ts' -g '!node_modules' apps/web/app -A 2 | head -40

Length of output: 12051


🏁 Script executed:

#!/bin/bash
# Check for record type changes and their impact
echo "=== 1. Record type changes in exported schemas ==="
rg 'z\.record\(z\.string\(\), z\.(any|unknown)\(\)\)' -g '*.ts' -g '!node_modules' apps/web/lib -B 2 | head -40

echo ""
echo "=== 2. Check metadata field changes specifically ==="
rg 'metadata.*z\.record' -g '*.ts' -g '!node_modules' apps/web/lib -A 1 -B 1 | head -30

echo ""
echo "=== 3. Check for email validation changes ==="
rg 'z\.email\(\)' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -B 1 | head -30

echo ""
echo "=== 4. Check for URL validation changes ==="
rg 'z\.url\(\)' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -B 1 | head -30

Length of output: 5584


🏁 Script executed:

#!/bin/bash
# Check for specific schemas that changed from merge to extend
echo "=== 1. Pagination query schemas using extend ==="
rg 'getPaginationQuerySchema' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1 | head -50

echo ""
echo "=== 2. Check analytics query schema changes ==="
cat apps/web/lib/zod/schemas/analytics.ts | grep -A 5 -B 5 'analyticsQuerySchema.*=' | head -40

echo ""
echo "=== 3. Check for any .partial() usage that might be affected ==="
rg '\.partial\(\)\.extend' -g '*.ts' -g '!node_modules' apps/web/lib/zod/schemas | head -20

Length of output: 3570


🏁 Script executed:

#!/bin/bash
# Look at specific files mentioned in the AI summary for breaking changes
echo "=== 1. Check domains schema for breaking changes ==="
rg 'archived.*default|includeLink.*default' -g 'domains.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1

echo ""
echo "=== 2. Check links schema for breaking changes ==="
rg 'showArchived.*default|withTags.*default|includeUser.*default|includeWebhooks.*default' -g 'links.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1

echo ""
echo "=== 3. Check tags schema for breaking changes ==="
rg 'includeLinksCount.*default' -g 'tags.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1

echo ""
echo "=== 4. Check bounties schema for breaking changes ==="
rg 'includeSubmissionsCount.*default' -g 'bounties.ts' -g '!node_modules' apps/web/lib/zod/schemas -A 1 -B 1

Length of output: 1534


🏁 Script executed:

#!/bin/bash
# Check WorkspaceSchema and other complex schemas for type changes
echo "=== 1. WorkspaceSchema flags and store changes ==="
cat apps/web/lib/zod/schemas/workspaces.ts | grep -A 3 -B 3 'flags:\|store:'

echo ""
echo "=== 2. AnalyticsFilters type changes ==="
cat apps/web/lib/analytics/types.ts | grep -A 10 'export type AnalyticsFilters'

echo ""
echo "=== 3. Check error handling in API errors ==="
cat apps/web/lib/api/errors.ts | grep -A 5 -B 5 'ErrorCode\|ErrorSchema'

Length of output: 2814


@devkiran I've completed a comprehensive final review. Here are all the breaking changes identified in this Zod v4 migration:

🚨 Critical Breaking Changes (API Contract Changes)

1. Query Parameter Default Values Changed

Boolean query parameters now default to actual booleans instead of strings:

Affected schemas:

  • getDomainsQuerySchemaExtended.includeLink: "false"false
  • getLinkInfoQuerySchemaExtended.includeUser: "false"false
  • getLinkInfoQuerySchemaExtended.includeWebhooks: "false"false
  • getTagsQuerySchemaExtended.includeLinksCount: "false"false
  • getBountiesQuerySchema.includeSubmissionsCount: "false"false
  • getQRCodeQuerySchema.hideLogo: "false"false
  • getQRCodeQuerySchema.includeMargin: "true"true

Impact: Query string parsing behavior may differ. If clients rely on string coercion behavior, this could cause validation failures.


⚠️ Important Breaking Changes (Type-Level Changes)

2. Record Key Types Now Explicit

All z.record() schemas now require explicit string keys:

Affected fields:

  • WorkspaceSchema.flags: z.record(z.boolean())z.record(z.string(), z.boolean())
  • WorkspaceSchema.store: z.record(z.any())z.record(z.string(), z.any())
  • WorkspaceSchemaExtended.users[].workspacePreferences: z.record(z.any())z.record(z.string(), z.any())
  • Webhook schemas metadata: z.record(z.unknown())z.record(z.string(), z.any())
  • Sale schemas metadata: z.record(z.unknown())z.record(z.string(), z.any())
  • FirstPromoter schemas metadata: z.record(z.any())z.record(z.string(), z.any())
  • Campaign bodyJson: Now explicitly z.record(z.string(), z.any())
  • Audit log metadata: Now explicitly z.record(z.string(), z.any())

Impact: TypeScript type inference changes. Non-string keys will now be rejected at validation time.

3. String Format Validators Changed to Top-Level Schemas

Email and URL validations now use dedicated schema types:

Changes:

  • z.string().email()z.email()
  • z.string().url()z.url()

Affected schemas:

  • All email fields across invites, customers, partners, programs, campaigns
  • All URL fields in links, programs, program resources, domains

Impact:

  • Different TypeScript inferred types
  • Potentially different validation error messages
  • May affect schema composition and type guards

4. Error Configuration API Changed

Error handling moved to unified error option:

Before:

z.string({ required_error: "field is required" })
z.string({ invalid_type_error: "must be string" })
z.enum([...], { errorMap: ... })

After:

z.string({ error: "field is required" })
z.enum([...], { error: ... })

Impact: Custom error message format and precedence may have changed.

5. Schema Composition Changed from .merge() to .extend()

Pattern change:

// Before
schema.merge(otherSchema)
schema.merge(z.object({ field: type }))

// After  
schema.extend(otherSchema.shape)
schema.extend({ field: type })

Affected areas:

  • All pagination query schemas (30+ schemas)
  • Partner, customer, commission, payout, domain, link, tag, folder, and analytics schemas
  • Program onboarding, rewards, groups schemas

Impact:

  • Field conflict resolution behavior differs between merge and extend
  • Type inference may produce different results
  • .extend() is more strict about overlapping keys

📋 Important Type Changes

6. AnalyticsFilters Type Expanded

New Override helper type introduced with explicit fields:

// Now includes explicit fields:
- start?: Date | null
- end?: Date | null  
- device?: string | undefined
- browser?: string | undefined
- os?: string | undefined

Impact: Type inference for analytics filters is more precise.

7. Geo Field in LinkSchema

// Before
geo: z.record(z.string(), z.string().url())

// After
geo: z.record(z.string(), z.url())

Impact: Type inference changes for geo targeting URLs.


🔧 Non-Breaking Changes (Internal Only)

8. Metadata API Changed

.openapi().meta() throughout all schemas

Impact: Internal only. Affects OpenAPI generation but not runtime validation. Requires zod-openapi v5.4.5+.

9. Import Path Changes

  • Removed local @/lib/zod wrapper
  • All imports now from "zod" directly
  • AI SDK imports updated: ai/rsc@ai-sdk/rsc, ai/react@ai-sdk/react

Impact: Build-time only. No runtime behavior changes.


📦 Package Version Changes

{
  "zod": "3.22.4" → "4.2.1",
  "zod-error": "1.5.0" → "2.0.0",
  "zod-openapi": "4.2.4" → "5.4.5",
  "@chronark/zod-bird": "0.3.9" → "1.0.0",
  "ai": "4.0.30" → "6.0.3",
  "@ai-sdk/anthropic": "1.0.7" → "3.0.1",
  "@ai-sdk/react": "^3.0.3" (new),
  "@ai-sdk/rsc": "^2.0.3" (new)
}

🎯 Migration Impact Summary

High Impact (Immediate attention needed):

  1. Query parameter defaults (affects API consumers)
  2. Record type keys (affects TypeScript compilation)
  3. Email/URL schema types (affects type inference)

Medium Impact (Review recommended):
4. Error message handling (may need error handling updates)
5. Schema composition (review field conflict resolution)

Low Impact (Transparent to consumers):
6. Metadata API changes
7. Import path changes
8. Internal type refinements


✅ Recommended Actions

  1. Test API endpoints with query parameters that rely on defaults
  2. Verify TypeScript compilation in dependent packages
  3. Review error handling logic that depends on error message formats
  4. Update API documentation to reflect new query parameter types
  5. Test metadata validation for webhook and sale events
  6. Verify OpenAPI schema generation with zod-openapi v5.4.5

🎉 Conclusion

This is a well-executed, comprehensive migration with mostly type-level breaking changes. The most significant runtime breaking change is the query parameter defaults (string → boolean), which may require coordination with API consumers. All other changes improve type safety and align with Zod v4 best practices.

The migration is ready for staging deployment with thorough testing of query parameter behavior and error handling.


🧠 Learnings used
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.

@steven-tey steven-tey merged commit 90bf383 into main Jan 7, 2026
7 checks passed
@steven-tey steven-tey deleted the zod-v4 branch January 7, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants