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

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Nov 6, 2025

Summary by CodeRabbit

  • New Features

    • Centralized request instrumentation and middleware logging for improved observability.
    • New standardized error codes for clearer API error responses.
  • Refactor

    • Reorganized validation, permission and limit helpers into clearer modules.
    • Date-range validation standardized and exposed via a strict assertion helper.
  • Bug Fixes

    • Analytics preset upgrade check corrected to use the new validation result.
    • Removed stray debug logging from API routes.
  • Chores

    • Telemetry dependencies updated and tests adapted/mocked for the new stack.

@vercel
Copy link
Contributor

vercel bot commented Nov 6, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 11, 2025 8:48pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 6, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 57 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7f114db and 8ea307b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1 hunks)
  • apps/web/lib/axiom/axiom.ts (1 hunks)
  • apps/web/lib/axiom/server.ts (1 hunks)
  • apps/web/lib/exceeded-limit-error.ts (1 hunks)
  • apps/web/ui/partners/confirm-payouts-sheet.tsx (1 hunks)

Walkthrough

Replace external next-axiom usage with an internal Axiom server wrapper and client, add centralized Axiom logger and route wrappers, extract/shared error codes and exceeded-limit messaging, change date-range validation to non-throwing results plus an asserting wrapper, and update many import paths, typings, middleware, and tests.

Changes

Cohort / File(s) Summary
Axiom core & server wrapper
apps/web/lib/axiom/server.ts, apps/web/lib/axiom/axiom.ts, apps/web/instrumentation.ts
Add internal Axiom client/logger, export axiomClient, logger, withAxiom, withAxiomBodyLog, and onRequestError; configure transports and onSuccess behavior.
Package deps & test mocks
apps/web/package.json, apps/web/tests/setupTests.ts
Add @axiomhq/* deps, remove next-axiom, bump dev deps; add Vitest mocks for Axiom SDK and helpers.
Middleware & Next config
apps/web/lib/middleware/axiom.ts (removed), apps/web/lib/middleware/index.ts, apps/web/middleware.ts, apps/web/next.config.js
Remove old middleware/re-exports and plugin wrapper; replace with direct logger usage in middleware and export plain Next config.
Route handlers — Axiom wrapper / typing
apps/web/app/(ee)/api/track/*/route.ts, apps/web/app/(ee)/api/hubspot/webhook/route.ts, apps/web/app/(ee)/api/singular/webhook/route.ts, apps/web/app/(ee)/api/stripe/integration/webhook/route.ts, other API routes under app/(ee)/api
Switch withAxiom import to local wrapper and remove AxiomRequest type annotations from handler parameters; behavior preserved.
Auth wrappers & embed flows
apps/web/lib/auth/*.ts (partner.ts, publishable-key.ts, session.ts, workspace.ts), apps/web/lib/embed/referrals/auth.ts
Use internal withAxiom/withAxiomBodyLog, remove AxiomRequest typings, introduce request cloning in workspace flow, and remove per-request req.log.error calls.
Logger usage in actions
apps/web/lib/actions/safe-action.ts
Make handleServerError async; log errors via new logger and flush.
Error codes & central errors refactor
apps/web/lib/api/error-codes.ts, apps/web/lib/api/errors.ts
Add centralized ErrorCode Zod enum; refactor errors.ts to import it, narrow/adjust exports, remove in-file exceededLimit helper, and route errors through logger.
Exceeded-limit util
apps/web/lib/exceeded-limit-error.ts
Add exceededLimitError helper and update callers to import it from new path.
Date-range validation + assert
apps/web/lib/analytics/utils/valid-date-range-for-plan.ts, apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts
Change validDateRangeForPlan to return DateRangeValidationResult (non-throwing) and add assertValidDateRangeForPlan that throws DubApiError when invalid.
Consumers & UI adjustments
multiple analytics/event routes and UI (apps/web/app/**/api/analytics*, apps/web/app/(ee)/api/events/*, apps/web/ui/analytics/toggle.tsx, etc.)
Replace validDateRangeForPlan(..., throwError: true) with assertValidDateRangeForPlan(...), update imports and UI usage to read .valid where applicable.
Import path reorganizations
many files under apps/web/app/**, apps/web/lib/**, apps/web/ui/** (e.g., @/lib/exceeded-limit-error, @/lib/client-access-check, partner-user helpers)
Redirect many imports to new modules without changing runtime logic.
Partner permission reorg
apps/web/lib/auth/partner-users/*, partner action/UI files
Remove old throwIfNoPermission implementation, add throw-if-no-permission module, and update call sites to new helper.
clientAccessCheck extraction
apps/web/lib/client-access-check.ts, ~25+ UI/API files
Add clientAccessCheck util and update many components/routes to import from it.
Small cleanups & tests
apps/web/app/api/links/route.ts, apps/web/lib/zod/schemas/links.ts, apps/web/tests/utils/*, etc.
Remove debug log, update ErrorCode import usage, change test context types TaskContext→TestContext, add test mocks, and small UI logic adjustments for new validation shape.

Sequence Diagram(s)

sequenceDiagram
    participant Req as Incoming Request
    participant MW as Middleware
    participant Logger as Axiom Logger
    participant Wrapper as withAxiom
    participant Route as Route Handler

    Req->>MW: request arrives
    MW->>Logger: logger.info(transformMiddlewareRequest(req))
    MW->>MW: ev.waitUntil(logger.flush())
    MW->>Route: continue

    Route->>Wrapper: wrapped handler invoked
    Wrapper->>Route: handler(req or clonedReq)
    Route->>Route: process request
    alt success
        Wrapper->>Logger: onSuccess logs body/searchParams & level
        Wrapper->>Logger: flush()
    else error
        Wrapper->>Logger: log error and flush
    end
    Wrapper-->>Req: response
Loading
sequenceDiagram
    participant Caller as Caller
    participant Assert as assertValidDateRangeForPlan
    participant Validate as validDateRangeForPlan
    participant Err as DubApiError

    Caller->>Assert: assertValidDateRangeForPlan(params)
    Assert->>Validate: validDateRangeForPlan(params)
    Validate-->>Assert: { valid: false, code, message } or { valid: true }
    alt invalid
        Assert->>Caller: throw DubApiError(message, code)
    else valid
        Assert->>Caller: return true
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Heterogeneous changes: new SDK integration, middleware/config updates, many import-path relocations, API wrapper/typing shifts, new utilities, and test mocks.
  • Areas needing extra attention:
    • apps/web/lib/axiom/server.ts — transport initialization, onSuccess augmentation, and flush behavior
    • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts and all callers (ensure callers correctly use .valid or switch to assert)
    • apps/web/lib/auth/workspace.ts — request cloning and forwarded request correctness
    • apps/web/lib/api/errors.ts — changed exports/visibility and logging side-effects
    • tests/setupTests.ts — mock fidelity for Axiom replacement

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

🐰 I hopped through imports, nudged logs to a den,

Wrapped routes with Axiom and pruned types again.
Dates now say true or tell why they fail,
Limits speak clearly, errors wear a name and tale.
A rabbit’s tidy patch — small hops, big gain!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 'Refactor to @axiomhq/nextjs' directly and clearly summarizes the main change: migrating from the next-axiom library to the new @axiomhq/nextjs package, which is the primary objective evident throughout the changeset.

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.

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: 3

🧹 Nitpick comments (1)
apps/web/app/(ee)/api/track/open/route.ts (1)

35-35: Replace console.log with Axiom logger.

Since the handler is wrapped with withAxiom, use req.log.info() instead of console.log for consistent structured logging.

Apply this diff:

-        console.log(`Checking cache for ${ip}:${dubDomain}:*`);
+        req.log.info(`Checking cache for ${ip}:${dubDomain}:*`);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb20af and 7518cd9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/web/app/(ee)/api/track/click/route.ts (1 hunks)
  • apps/web/app/(ee)/api/track/open/route.ts (1 hunks)
  • apps/web/app/(ee)/api/track/visit/route.ts (1 hunks)
  • apps/web/lib/auth/partner.ts (1 hunks)
  • apps/web/lib/auth/publishable-key.ts (1 hunks)
  • apps/web/lib/auth/session.ts (1 hunks)
  • apps/web/lib/auth/workspace.ts (1 hunks)
  • apps/web/lib/embed/referrals/auth.ts (1 hunks)
  • apps/web/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-21T03:03:39.879Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2737
File: apps/web/lib/api/cors.ts:1-5
Timestamp: 2025-08-21T03:03:39.879Z
Learning: Dub publishable keys are sent via Authorization header using Bearer token format, not via custom X-Dub-Publishable-Key header. The publishable key middleware extracts keys using req.headers.get("Authorization")?.replace("Bearer ", "") and validates they start with "dub_pk_".

Applied to files:

  • apps/web/lib/auth/publishable-key.ts
📚 Learning: 2025-08-21T03:03:39.879Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2737
File: apps/web/lib/api/cors.ts:1-5
Timestamp: 2025-08-21T03:03:39.879Z
Learning: Dub publishable keys are sent via Authorization header using Bearer token format, not via custom X-Dub-Publishable-Key header. The publishable key middleware extracts keys using req.headers.get("authorization")?.replace("Bearer ", "").

Applied to files:

  • apps/web/lib/auth/publishable-key.ts
🧬 Code graph analysis (3)
apps/web/app/(ee)/api/track/visit/route.ts (7)
apps/web/app/(ee)/api/track/click/route.ts (1)
  • POST (50-177)
apps/web/lib/api/utils.ts (1)
  • parseRequestBody (9-20)
apps/web/lib/api/errors.ts (2)
  • DubApiError (75-92)
  • handleAndReturnErrorResponse (175-178)
apps/web/lib/types.ts (1)
  • RedisLinkProps (157-181)
apps/web/lib/api/cors.ts (1)
  • COMMON_CORS_HEADERS (1-5)
apps/web/lib/planetscale/get-workspace-via-edge.ts (1)
  • getWorkspaceViaEdge (5-61)
apps/web/lib/tinybird/record-click.ts (1)
  • recordClick (33-308)
apps/web/app/(ee)/api/track/click/route.ts (7)
apps/web/lib/api/utils.ts (1)
  • parseRequestBody (9-20)
apps/web/lib/types.ts (1)
  • RedisLinkProps (157-181)
apps/web/lib/api/errors.ts (2)
  • DubApiError (75-92)
  • handleAndReturnErrorResponse (175-178)
packages/utils/src/functions/urls.ts (1)
  • isValidUrl (1-8)
apps/web/lib/planetscale/get-workspace-via-edge.ts (1)
  • getWorkspaceViaEdge (5-61)
apps/web/lib/tinybird/record-click.ts (1)
  • recordClick (33-308)
apps/web/lib/api/cors.ts (1)
  • COMMON_CORS_HEADERS (1-5)
apps/web/app/(ee)/api/track/open/route.ts (7)
apps/web/app/(ee)/api/track/click/route.ts (1)
  • POST (50-177)
apps/web/lib/zod/schemas/opens.ts (2)
  • trackOpenRequestSchema (4-26)
  • trackOpenResponseSchema (28-54)
apps/web/lib/api/utils.ts (1)
  • parseRequestBody (9-20)
apps/web/lib/api/cors.ts (1)
  • COMMON_CORS_HEADERS (1-5)
apps/web/lib/types.ts (1)
  • RedisLinkProps (157-181)
apps/web/lib/api/errors.ts (2)
  • DubApiError (75-92)
  • handleAndReturnErrorResponse (175-178)
apps/web/lib/tinybird/record-click.ts (1)
  • recordClick (33-308)
⏰ 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). (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: build
🔇 Additional comments (4)
apps/web/app/(ee)/api/track/open/route.ts (4)

22-27: LGTM! Body logging enabled via Axiom.

The withAxiom wrapper with logRequestDetails: ["body"] successfully enables request body logging, aligning with the PR objectives.


80-100: LGTM! Proper cache-aside pattern implementation.

The clickId assignment and link fetching logic correctly implements the cache-aside pattern with proper fallback to database queries and error handling for missing links.


102-125: LGTM! Proper workspace validation and click tracking.

The workspace validation ensures the link belongs to a workspace before recording the click. The recordClick configuration with skipRatelimit: true, shouldCacheClickId: true, and trigger: "deeplink" is appropriate for tracking deep link open events.


127-140: LGTM! Proper response construction and error handling.

The response is correctly validated against trackOpenResponseSchema and includes proper CORS headers. Error handling uses the standard handleAndReturnErrorResponse utility.

@socket-security
Copy link

socket-security bot commented Nov 6, 2025

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​axiomhq/​js@​1.3.11001008185100
Added@​axiomhq/​logging@​0.1.5921008293100
Addedvite@​7.2.2981008299100
Updatedvite-tsconfig-paths@​4.3.2 ⏵ 5.1.4100 +110010083100
Added@​axiomhq/​nextjs@​0.1.5861008593100
Updatedvitest@​2.1.8 ⏵ 4.0.8100 +3100 +75100 +22100 +2100

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: 3

🧹 Nitpick comments (2)
apps/web/middleware.ts (1)

39-41: Consider the performance impact of per-request logging.

Logging and flushing on every middleware request could introduce latency, especially under high traffic. Consider whether all middleware requests need to be logged, or if this should be sampling-based or limited to specific paths.

apps/web/app/(ee)/api/hubspot/webhook/route.ts (1)

17-17: Consider adding type annotation for better type safety.

While the untyped req parameter works correctly, explicitly typing it as req: Request would improve type safety and developer ergonomics (better IDE autocomplete and compile-time checking). The Stripe webhook endpoint (see apps/web/app/(ee)/api/stripe/integration/webhook/route.ts) uses this pattern.

Apply this diff to add the type annotation:

-export const POST = withAxiom(async (req) => {
+export const POST = withAxiom(async (req: Request) => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7518cd9 and d7d50dc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • apps/web/app/(ee)/api/hubspot/webhook/route.ts (2 hunks)
  • apps/web/app/(ee)/api/singular/webhook/route.ts (2 hunks)
  • apps/web/app/(ee)/api/stripe/integration/webhook/route.ts (1 hunks)
  • apps/web/app/(ee)/api/track/click/route.ts (2 hunks)
  • apps/web/app/(ee)/api/track/open/route.ts (2 hunks)
  • apps/web/app/(ee)/api/track/visit/route.ts (1 hunks)
  • apps/web/instrumentation.ts (1 hunks)
  • apps/web/lib/auth/partner.ts (2 hunks)
  • apps/web/lib/auth/publishable-key.ts (2 hunks)
  • apps/web/lib/auth/session.ts (2 hunks)
  • apps/web/lib/auth/workspace.ts (2 hunks)
  • apps/web/lib/axiom/index.ts (1 hunks)
  • apps/web/lib/axiom/server.ts (1 hunks)
  • apps/web/lib/embed/referrals/auth.ts (2 hunks)
  • apps/web/lib/middleware/axiom.ts (0 hunks)
  • apps/web/lib/middleware/index.ts (0 hunks)
  • apps/web/middleware.ts (2 hunks)
  • apps/web/next.config.js (2 hunks)
  • apps/web/package.json (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/lib/middleware/axiom.ts
  • apps/web/lib/middleware/index.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/web/package.json
  • apps/web/lib/auth/publishable-key.ts
  • apps/web/app/(ee)/api/track/visit/route.ts
  • apps/web/app/(ee)/api/track/open/route.ts
  • apps/web/app/(ee)/api/track/click/route.ts
  • apps/web/lib/embed/referrals/auth.ts
  • apps/web/lib/auth/session.ts
  • apps/web/lib/auth/partner.ts
🧰 Additional context used
🧠 Learnings (1)
📚 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/app/(ee)/api/singular/webhook/route.ts
🧬 Code graph analysis (5)
apps/web/app/(ee)/api/singular/webhook/route.ts (1)
apps/web/lib/axiom/server.ts (1)
  • withAxiom (15-15)
apps/web/instrumentation.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (5-13)
apps/web/middleware.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (5-13)
apps/web/lib/axiom/server.ts (1)
apps/web/lib/axiom/index.ts (1)
  • axiomClient (3-5)
apps/web/app/(ee)/api/hubspot/webhook/route.ts (5)
apps/web/app/(ee)/api/stripe/integration/webhook/route.ts (1)
  • POST (27-110)
apps/web/app/(ee)/api/track/click/route.ts (1)
  • POST (50-171)
apps/web/app/(ee)/api/track/open/route.ts (1)
  • POST (22-140)
apps/web/app/(ee)/api/track/visit/route.ts (1)
  • POST (20-115)
apps/web/lib/axiom/server.ts (1)
  • withAxiom (15-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). (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: build
🔇 Additional comments (7)
apps/web/next.config.js (1)

21-203: LGTM!

The removal of the withAxiom wrapper from the Next.js config export aligns with the migration to a local Axiom wrapper. The configuration remains otherwise unchanged.

apps/web/app/(ee)/api/stripe/integration/webhook/route.ts (1)

1-1: LGTM!

The migration from next-axiom to the local @/lib/axiom/server wrapper is straightforward and maintains the same functionality.

apps/web/instrumentation.ts (1)

1-4: LGTM!

The centralized error handling setup using createOnRequestError with the shared logger provides consistent error logging across the application.

apps/web/app/(ee)/api/singular/webhook/route.ts (1)

3-3: LGTM!

The migration to the local Axiom wrapper is clean and maintains existing functionality.

apps/web/lib/auth/workspace.ts (1)

65-67: Review comment is based on incorrect assumptions—request body logging is not present in the code.

The concern references a PR title mentioning logRequestDetails: 'body', but verification shows:

  1. No logRequestDetails configuration exists anywhere in the codebase
  2. createAxiomRouteHandler is called with only the logger parameter and no config options
  3. Per @axiomhq/nextjs documentation, request body logging requires explicit onSuccess/onError handlers to read and attach the body—it is not automatic
  4. All withAxiom calls across auth routes (workspace, session, partner, publishable-key) pass no configuration

The code is currently secure with respect to request body logging. No action is required.

Likely an incorrect or invalid review comment.

apps/web/lib/axiom/server.ts (1)

15-15: No issues found — request body logging is not supported by createAxiomRouteHandler at the route handler level.

createAxiomRouteHandler in @axiomhq/nextjs v0.1.5 does not expose a documented logRequestDetails option. The package documents createAxiomRouteHandler accepting callbacks (onError/onSuccess) and a store and notes it adds a request_id via the server context formatter, but it does not list a logRequestDetails config for route handlers. The code as written does not introduce request body logging for auth tokens or PII.

apps/web/app/(ee)/api/hubspot/webhook/route.ts (1)

2-2: LGTM! Import change aligns with the migration objective.

The import change from next-axiom to the local Axiom wrapper is correct and consistent with the PR's objective to replace external next-axiom usage with a local implementation.

@steven-tey steven-tey changed the title Upgrade next-axiom + add logRequestDetails: "body" Refactor to @axiomhq/nextjs Nov 6, 2025
@devkiran devkiran marked this pull request as ready for review November 9, 2025 15:09
@devkiran devkiran self-requested a review November 9, 2025 15:09
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: 2

♻️ Duplicate comments (3)
apps/web/lib/axiom/axiom.ts (1)

3-5: Validate environment variable before usage.

The non-null assertion on NEXT_PUBLIC_AXIOM_TOKEN will cause a runtime crash if the variable is undefined.

Apply this diff to add runtime validation:

-export const axiomClient = new Axiom({
-  token: process.env.NEXT_PUBLIC_AXIOM_TOKEN!,
-});
+const token = process.env.NEXT_PUBLIC_AXIOM_TOKEN;
+if (!token) {
+  throw new Error("NEXT_PUBLIC_AXIOM_TOKEN environment variable is required");
+}
+
+export const axiomClient = new Axiom({ token });
apps/web/lib/axiom/server.ts (1)

22-30: Validate environment variable before usage.

The non-null assertion on NEXT_PUBLIC_AXIOM_DATASET (line 26) will cause a runtime crash if the variable is undefined.

Apply this diff to add validation:

+const dataset = process.env.NEXT_PUBLIC_AXIOM_DATASET;
+if (!dataset) {
+  throw new Error("NEXT_PUBLIC_AXIOM_DATASET environment variable is required");
+}
+
 export const logger = new Logger({
   transports: [
     new AxiomJSTransport({
       axiom: axiomClient,
-      dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET!,
+      dataset,
     }),
   ],
   formatters: nextJsFormatters,
 });
apps/web/lib/auth/workspace.ts (1)

65-67: Workspace routes should not log request bodies.

Using withAxiomBodyLog for workspace-scoped routes will log full request bodies (including POST/PATCH/PUT) to Axiom, potentially capturing user PII, workspace settings, API credentials, and other sensitive data. This is a privacy and compliance risk.

Apply this diff to use the non-body-logging wrapper:

-  return withAxiomBodyLog(
+  return withAxiom(
     async (
       req,
       { params: initialParams }: { params: Promise<Record<string, string>> },

Note: A previous review comment indicated this concern was addressed, but the current code still uses the body-logging wrapper. Please verify the intended behavior.

🧹 Nitpick comments (2)
apps/web/tests/setupTests.ts (1)

10-46: Consider adding type safety to the mock constructors.

The mock constructors use _config: any which bypasses TypeScript's type checking. While this is acceptable for test mocks, consider using proper types or unknown to catch potential type mismatches during testing.

Example:

 vi.mock("@axiomhq/js", () => ({
   Axiom: class {
-    constructor(_config: any) {}
+    constructor(_config: unknown) {}
     ingest = vi.fn().mockResolvedValue(undefined);
     query = vi.fn().mockResolvedValue({ matches: [] });
   },
 }));
apps/web/lib/api/errors.ts (1)

3-8: Move "server-only" import to the top of the file.

The "server-only" import should be placed before all other imports to ensure the module is marked as server-only before any code executes. This follows Next.js conventions and prevents potential issues with client-side bundling.

Apply this diff to reorder the imports:

+import "server-only";
 import z from "@/lib/zod";
 import { NextResponse } from "next/server";
-import "server-only";
 import { ZodError } from "zod";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 528b2a9 and eb32792.

📒 Files selected for processing (76)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1 hunks)
  • apps/web/app/(ee)/api/events/export/route.ts (2 hunks)
  • apps/web/app/(ee)/api/events/route.ts (2 hunks)
  • apps/web/app/(ee)/api/groups/route.ts (1 hunks)
  • apps/web/app/(ee)/api/partner-profile/users/route.ts (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-settings-button.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx (1 hunks)
  • apps/web/app/api/analytics/dashboard/route.ts (2 hunks)
  • apps/web/app/api/analytics/export/route.ts (2 hunks)
  • apps/web/app/api/analytics/route.ts (2 hunks)
  • apps/web/app/api/domains/route.ts (1 hunks)
  • apps/web/app/api/folders/route.ts (1 hunks)
  • apps/web/app/api/links/bulk/route.ts (2 hunks)
  • apps/web/app/api/links/route.ts (0 hunks)
  • apps/web/app/api/links/sync/route.ts (1 hunks)
  • apps/web/app/api/tags/route.ts (1 hunks)
  • apps/web/app/api/workspaces/[idOrSlug]/invites/accept/route.ts (1 hunks)
  • apps/web/app/api/workspaces/[idOrSlug]/invites/route.ts (1 hunks)
  • apps/web/app/api/workspaces/[idOrSlug]/users/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/conversion-tracking-toggle.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/hostname-section.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/default/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/tokens/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx (1 hunks)
  • apps/web/lib/actions/partners/confirm-payouts.ts (1 hunks)
  • apps/web/lib/actions/partners/force-withdrawal.ts (1 hunks)
  • apps/web/lib/actions/partners/generate-paypal-oauth-url.ts (1 hunks)
  • apps/web/lib/actions/partners/generate-stripe-account-link.ts (1 hunks)
  • apps/web/lib/actions/partners/retry-failed-paypal-payouts.ts (1 hunks)
  • apps/web/lib/actions/partners/update-partner-payout-settings.ts (1 hunks)
  • apps/web/lib/actions/partners/update-partner-profile.ts (1 hunks)
  • apps/web/lib/actions/safe-action.ts (1 hunks)
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (3 hunks)
  • apps/web/lib/api/error-codes.ts (1 hunks)
  • apps/web/lib/api/errors.ts (3 hunks)
  • apps/web/lib/api/links/usage-checks.ts (1 hunks)
  • apps/web/lib/api/tokens/throw-if-no-access.ts (1 hunks)
  • apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1 hunks)
  • apps/web/lib/auth/partner-users/partner-user-permissions.ts (0 hunks)
  • apps/web/lib/auth/partner-users/throw-if-no-permission.ts (1 hunks)
  • apps/web/lib/auth/partner.ts (2 hunks)
  • apps/web/lib/auth/workspace.ts (4 hunks)
  • apps/web/lib/axiom/axiom.ts (1 hunks)
  • apps/web/lib/axiom/server.ts (1 hunks)
  • apps/web/lib/client-access-check.ts (1 hunks)
  • apps/web/lib/exceeded-limit-error.ts (1 hunks)
  • apps/web/lib/integrations/common/ui/configure-webhook.tsx (1 hunks)
  • apps/web/lib/zod/schemas/links.ts (1 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/tests/setupTests.ts (1 hunks)
  • apps/web/tests/utils/integration-old.ts (2 hunks)
  • apps/web/tests/utils/integration.ts (2 hunks)
  • apps/web/ui/analytics/toggle.tsx (1 hunks)
  • apps/web/ui/domains/domain-card.tsx (1 hunks)
  • apps/web/ui/modals/add-edit-domain-modal.tsx (1 hunks)
  • apps/web/ui/modals/add-edit-token-modal.tsx (1 hunks)
  • apps/web/ui/oauth-apps/add-edit-app-form.tsx (1 hunks)
  • apps/web/ui/oauth-apps/add-edit-integration-form.tsx (1 hunks)
  • apps/web/ui/partners/confirm-payouts-sheet.tsx (1 hunks)
  • apps/web/ui/partners/connect-payout-button.tsx (1 hunks)
  • apps/web/ui/partners/online-presence-form.tsx (1 hunks)
  • apps/web/ui/webhooks/add-edit-webhook-form.tsx (1 hunks)
  • apps/web/ui/webhooks/webhook-header.tsx (1 hunks)
  • apps/web/ui/workspaces/delete-workspace.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/app/api/links/route.ts
  • apps/web/lib/auth/partner-users/partner-user-permissions.ts
✅ Files skipped from review due to trivial changes (5)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/hostname-section.tsx
  • apps/web/ui/partners/confirm-payouts-sheet.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx
  • apps/web/app/api/workspaces/[idOrSlug]/users/route.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/auth/partner.ts
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
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.
📚 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]/settings/members/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/tokens/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/default/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/conversion-tracking-toggle.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-settings-button.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/settings/**/*.{js,jsx,ts,tsx} : Only use components exported by hubspot/ui-extensions in settings components

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-settings-button.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/settings/**/*.{js,jsx,ts,tsx} : Do not use React components from hubspot/ui-extensions/crm in settings components

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx
📚 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/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx
  • apps/web/lib/integrations/common/ui/configure-webhook.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/tokens/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx
  • apps/web/ui/webhooks/webhook-header.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/default/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/conversion-tracking-toggle.tsx
  • apps/web/ui/webhooks/add-edit-webhook-form.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-settings-button.tsx
  • apps/web/ui/workspaces/delete-workspace.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx
📚 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/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx
  • apps/web/lib/integrations/common/ui/configure-webhook.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/tokens/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx
  • apps/web/ui/webhooks/webhook-header.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx
  • apps/web/ui/webhooks/add-edit-webhook-form.tsx
📚 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/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx
  • apps/web/ui/workspaces/delete-workspace.tsx
  • apps/web/ui/domains/domain-card.tsx
📚 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/api/error-codes.ts
  • apps/web/lib/zod/schemas/links.ts
  • apps/web/lib/api/errors.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/api/error-codes.ts
📚 Learning: 2025-06-25T21:20:59.837Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-06-25T21:20:59.837Z
Learning: In the Dub codebase, payout limit validation uses a two-stage pattern: server actions perform quick sanity checks (payoutsUsage > payoutsLimit) for immediate user feedback, while the cron job (/cron/payouts) performs authoritative validation (payoutsUsage + payoutAmount > payoutsLimit) with actual calculated amounts before processing. This design provides fast user feedback while ensuring accurate limit enforcement at transaction time.

Applied to files:

  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts
  • apps/web/lib/actions/partners/confirm-payouts.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/app/api/links/sync/route.ts
  • apps/web/app/api/links/bulk/route.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/update-partner-payout-settings.ts
  • apps/web/lib/actions/partners/update-partner-profile.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/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx
  • apps/web/tests/utils/integration.ts
  • apps/web/tests/utils/integration-old.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/conversion-tracking-toggle.tsx
📚 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/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx
  • apps/web/lib/api/tokens/throw-if-no-access.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/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx
📚 Learning: 2025-09-19T18:46:43.788Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.788Z
Learning: Applies to packages/hubspot-app/app/webhooks/** : Webhooks components are only allowed when config.distribution is private and config.auth.type is static

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/settings/**/*.{js,jsx,ts,tsx} : Do not use window.fetch in settings components; use hubspot.fetch from hubspot/ui-extensions

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.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/app/api/analytics/route.ts
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.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/auth/workspace.ts
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
Repo: dubinc/dub PR: 0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/cards/**/*.{js,jsx,ts,tsx} : Only use components exported by hubspot/ui-extensions within card components

Applied to files:

  • apps/web/ui/domains/domain-card.tsx
📚 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/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx
📚 Learning: 2025-10-06T15:48:14.205Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.

Applied to files:

  • apps/web/app/api/workspaces/[idOrSlug]/invites/accept/route.ts
  • apps/web/app/api/workspaces/[idOrSlug]/invites/route.ts
🧬 Code graph analysis (14)
apps/web/app/api/analytics/route.ts (1)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1)
  • assertValidDateRangeForPlan (4-21)
apps/web/lib/client-access-check.ts (1)
apps/web/lib/api/rbac/permissions.ts (2)
  • PermissionAction (30-30)
  • ROLE_PERMISSIONS (32-157)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (2)
apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (1)
  • validDateRangeForPlan (11-62)
apps/web/lib/api/errors.ts (1)
  • DubApiError (58-75)
apps/web/lib/axiom/server.ts (2)
apps/web/lib/axiom/axiom.ts (1)
  • axiomClient (3-5)
packages/utils/src/functions/urls.ts (1)
  • getSearchParams (40-49)
apps/web/lib/exceeded-limit-error.ts (1)
apps/web/lib/types.ts (1)
  • PlanProps (197-197)
apps/web/app/(ee)/api/events/route.ts (1)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1)
  • assertValidDateRangeForPlan (4-21)
apps/web/app/(ee)/api/events/export/route.ts (1)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1)
  • assertValidDateRangeForPlan (4-21)
apps/web/lib/auth/partner-users/throw-if-no-permission.ts (3)
packages/prisma/client.ts (1)
  • PartnerRole (24-24)
apps/web/lib/auth/partner-users/partner-user-permissions.ts (2)
  • Permission (3-3)
  • hasPermission (28-32)
apps/web/lib/api/errors.ts (1)
  • DubApiError (58-75)
apps/web/lib/actions/safe-action.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (22-30)
apps/web/lib/auth/workspace.ts (1)
apps/web/lib/axiom/server.ts (1)
  • withAxiomBodyLog (32-47)
apps/web/lib/api/errors.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (22-30)
apps/web/tests/setupTests.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (22-30)
apps/web/app/api/analytics/export/route.ts (1)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1)
  • assertValidDateRangeForPlan (4-21)
apps/web/app/api/analytics/dashboard/route.ts (1)
apps/web/lib/api/utils/assert-valid-date-range-for-plan.ts (1)
  • assertValidDateRangeForPlan (4-21)
⏰ 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
🔇 Additional comments (63)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx (1)

2-2: LGTM – Import path refactor looks good.

The relocation of hasPermission to the partner-users subdirectory is a straightforward organizational change with no impact on functionality.

apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (1)

4-4: LGTM – Import path refactor looks good.

The import path update is consistent with the broader refactoring effort to reorganize partner permissions under the partner-users namespace.

apps/web/ui/partners/connect-payout-button.tsx (1)

5-5: LGTM – Import path refactor looks good.

The relocation maintains the same functionality while improving the module organization.

apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/partner-payout-settings-button.tsx (1)

3-3: LGTM – Import path refactor looks good.

The updated import path aligns with the new module structure for partner permissions.

apps/web/ui/partners/online-presence-form.tsx (1)

5-5: LGTM – Import path refactor looks good.

The import path change is part of the consistent refactoring to organize partner permissions under the partner-users directory structure.

apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx (1)

2-2: Import refactor verified as complete and correct.

The import path @/lib/auth/partner-users/partner-user-permissions correctly resolves to apps/web/lib/auth/partner-users/partner-user-permissions.ts where hasPermission is properly exported. No stale imports from alternative paths remain in the codebase. The refactor is consistent and complete.

apps/web/lib/api/links/usage-checks.ts (1)

1-1: LGTM: Import refactor aligns with modularization.

The import path change to the dedicated @/lib/exceeded-limit-error module is consistent with the PR's broader refactoring effort to centralize error messaging utilities.

apps/web/app/api/workspaces/[idOrSlug]/invites/route.ts (1)

4-4: LGTM: Consistent with modularization effort.

The import path update to @/lib/exceeded-limit-error aligns with the project-wide refactoring to centralize exceeded limit messaging.

apps/web/lib/actions/safe-action.ts (1)

12-14: LGTM: Clean Axiom logger integration.

The addition of logger.error() and logger.flush() properly integrates the new Axiom logging system while maintaining the existing console.error for redundancy.

apps/web/package.json (2)

20-22: LGTM: Axiom dependencies support the migration.

The new @axiomhq/* packages align with the PR's migration from next-axiom to a custom Axiom integration.


153-155: Verify compatibility with major version updates before merging.

The identified major version jumps (vite 5→7, vite-tsconfig-paths 4→5, vitest 2→4) are confirmed as real versions in the npm registry. These significant version increments commonly introduce breaking changes. While a vitest configuration exists at apps/web/vitest.config.ts and the test script is properly configured, I cannot execute the test suite in this environment to verify actual compatibility.

Before merging these dependency updates:

  • Run pnpm test locally to confirm the full test suite passes
  • Review the changelogs for vite, vite-tsconfig-paths, and vitest for any migration requirements
  • Check for any deprecation warnings during test execution
apps/web/lib/api/tokens/throw-if-no-access.ts (1)

1-1: LGTM: Server-side enforcement added.

The "server-only" import is good practice to prevent accidental client-side usage of this module, ensuring token validation and permission checks remain server-side only.

apps/web/lib/auth/workspace.ts (1)

70-72: LGTM: Request cloning pattern is correct.

The early cloning strategy is well-documented and correctly implemented. The clone is passed to handlers for body consumption, while the original is preserved for withAxiomBodyLog to read in its onSuccess callback.

apps/web/lib/exceeded-limit-error.ts (1)

1-29: LGTM! Clean utility extraction.

The exceededLimitError function is well-implemented with proper handling for:

  • Monthly limits for specific types (links, AI, payouts)
  • Currency formatting for payouts
  • Correct pluralization logic
apps/web/app/(ee)/api/groups/route.ts (1)

3-8: LGTM! Import refactor is consistent.

The import path update for exceededLimitError is clean and maintains the existing functionality.

apps/web/lib/actions/partners/confirm-payouts.ts (1)

8-8: LGTM! Import path updated correctly.

The import refactor maintains the existing two-stage payout validation pattern.

apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1)

5-5: LGTM! Import path consistently updated.

The refactor maintains the authoritative validation logic in the cron job.

apps/web/ui/oauth-apps/add-edit-integration-form.tsx (1)

5-5: LGTM! Import refactor is clean.

The import path update for clientAccessCheck maintains existing permission validation logic.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/(basic-layout)/page-client.tsx (1)

3-3: LGTM! Import path updated correctly.

The refactor maintains the existing workspace permission validation.

apps/web/app/api/workspaces/[idOrSlug]/invites/accept/route.ts (1)

1-4: LGTM! Import refactor is well-structured.

The separation of exceededLimitError into its own module while keeping DubApiError in the errors module is a clean architectural improvement.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/conversion-tracking-toggle.tsx (1)

3-3: LGTM! Import path consistently updated.

The refactor maintains the existing conversion tracking permission validation.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/publishable-key-form.tsx (1)

3-3: Clean refactor consolidating clientAccessCheck import.

The import path change maintains existing behavior while organizing the utility into a dedicated module.

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

1-1: Clean refactor consolidating ErrorCode import.

The import path change maintains existing behavior while organizing the error code enum into a dedicated module.

apps/web/ui/modals/add-edit-domain-modal.tsx (1)

1-1: Clean refactor consolidating clientAccessCheck import.

The import path change maintains existing behavior while organizing the utility into a dedicated module.

apps/web/app/api/tags/route.ts (1)

2-4: Clean refactor consolidating exceededLimitError import.

The import path change for exceededLimitError maintains existing behavior while organizing the utility into a dedicated module. DubApiError appropriately remains in the errors module.

apps/web/app/api/links/sync/route.ts (1)

4-4: Clean refactor consolidating exceededLimitError import.

The import path change maintains existing behavior while organizing the utility into a dedicated module.

apps/web/ui/workspaces/delete-workspace.tsx (1)

3-3: Clean refactor consolidating clientAccessCheck import.

The import path change maintains existing behavior while organizing the utility into a dedicated module.

apps/web/lib/integrations/common/ui/configure-webhook.tsx (1)

3-3: Clean refactor consolidating clientAccessCheck import.

The import path change maintains existing behavior while organizing the utility into a dedicated module.

apps/web/app/api/domains/route.ts (1)

5-9: Clean refactor consolidating exceededLimitError import.

The import path change for exceededLimitError maintains existing behavior while organizing the utility into a dedicated module. DubApiError appropriately remains in the errors module.

apps/web/ui/oauth-apps/add-edit-app-form.tsx (1)

3-3: LGTM! Clean import refactor.

The clientAccessCheck import has been correctly updated to use the new centralized module path. The usage remains unchanged and consistent with the broader refactoring effort.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/[appId]/page-client.tsx (1)

4-4: LGTM! Consistent import refactor.

The import path update for clientAccessCheck is correct and aligns with the module consolidation effort across the codebase.

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

4-4: LGTM! Import path correctly updated.

The throwIfNoPermission import has been properly relocated to the new dedicated module under partner-users. Usage remains unchanged.

apps/web/app/api/folders/route.ts (1)

2-5: LGTM! Proper module separation.

The exceededLimitError has been correctly moved to its dedicated module while keeping DubApiError in the errors module. This separation is appropriate and maintains the existing functionality.

apps/web/lib/actions/partners/force-withdrawal.ts (1)

3-3: LGTM! Consistent import update.

The throwIfNoPermission import path has been correctly updated to match the new module structure.

apps/web/ui/modals/add-edit-token-modal.tsx (1)

7-7: LGTM! Import refactor correctly applied.

The clientAccessCheck import has been properly updated to the new centralized module path. The usage at line 340-344 remains unchanged.

apps/web/lib/actions/partners/retry-failed-paypal-payouts.ts (1)

3-3: LGTM! Import path updated correctly.

The throwIfNoPermission import has been properly relocated to the new module path, consistent with other partner action files.

apps/web/ui/domains/domain-card.tsx (1)

1-1: LGTM! Import refactor complete.

The clientAccessCheck import has been correctly updated to use the new centralized module. The usage at lines 358-361 remains unchanged.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/create-webhook-button.tsx (1)

3-3: LGTM! Clean import path refactor.

The clientAccessCheck import has been successfully centralized to the new module location. Usage remains unchanged.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/domains/default/page-client.tsx (1)

3-3: LGTM! Import path successfully updated.

The refactor correctly updates the import path to the centralized client-access-check module without affecting functionality.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx (1)

3-3: LGTM! Centralized import applied correctly.

The import path update is consistent with the project-wide refactor. All call sites at lines 244 and 257 continue to work as expected.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/new/page-client.tsx (1)

3-3: LGTM! Import refactor applied correctly.

The centralized import path is properly used without affecting the permission check logic.

apps/web/app/(ee)/api/partner-profile/users/route.ts (1)

4-4: LGTM! Permission utility successfully relocated.

The throwIfNoPermission import has been moved to a dedicated module, improving code organization. Usage at line 175 remains unchanged.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/tokens/page.tsx (1)

4-4: LGTM! Import path refactor completed successfully.

The centralized import is correctly applied. Both call sites (lines 67 and 261) continue to function properly.

apps/web/ui/webhooks/webhook-header.tsx (1)

4-4: LGTM! Consistent refactor applied.

The import path update aligns with the broader consolidation effort. Functionality at line 47 remains intact.

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

3-3: LGTM! Permission check utility properly relocated.

The throwIfNoPermission function has been moved to a dedicated module under the partner-users namespace, improving organization. Usage at line 16 is unaffected.

apps/web/app/api/links/bulk/route.ts (1)

1-1: LGTM! Clean refactor to dedicated module.

The exceededLimitError utility has been properly moved to a dedicated module while maintaining the same usage pattern. This improves code organization and aligns with the PR's objective to consolidate utilities.

Also applies to: 17-17

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/oauth-apps/create-oauth-app-button.tsx (1)

3-3: LGTM! Centralized permission check module.

The import path update consolidates client access checks into a dedicated module, improving code organization without altering functionality.

apps/web/ui/webhooks/add-edit-webhook-form.tsx (1)

3-3: LGTM! Consistent with centralized permission checks.

The import path update aligns with the broader refactor to consolidate access checks into a dedicated module.

apps/web/tests/utils/integration.ts (1)

2-2: LGTM! Proper vitest context type update.

The migration from TaskContext to TestContext is correct for vitest test execution context. The addition of readonly modifier on the field is a good practice that prevents unintended mutations.

Also applies to: 15-15, 21-21

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/webhooks/[webhookId]/page-client.tsx (1)

3-3: LGTM! Centralized permission check module.

The import path update is consistent with the PR-wide consolidation of client access checks.

apps/web/lib/actions/partners/generate-stripe-account-link.ts (1)

3-3: LGTM! Improved module organization for partner permissions.

The import path update moves partner-specific permission utilities into a dedicated partner-users namespace, improving code organization and modularity.

apps/web/app/app.dub.co/(dashboard)/[slug]/settings/analytics/add-hostname-modal.tsx (1)

3-3: LGTM! Centralized permission check module.

The import path update aligns with the systematic refactor to consolidate client access checks across the codebase.

apps/web/lib/actions/partners/generate-paypal-oauth-url.ts (1)

3-3: LGTM! Consistent partner permission module organization.

The import path update is consistent with the reorganization of partner-specific utilities into the partner-users namespace.

apps/web/lib/api/error-codes.ts (1)

1-15: LGTM! Clean separation of error codes.

The extraction of ErrorCode into a dedicated module improves modularity. The use of standard zod import is correct for non-OpenAPI schema usage.

apps/web/app/api/analytics/dashboard/route.ts (1)

71-77: LGTM! Consistent with validation refactor pattern.

The replacement of validDateRangeForPlan with assertValidDateRangeForPlan maintains the same throwing behavior while providing a cleaner API without the throwError flag. This aligns with the broader refactor to separate validation logic from error handling.

apps/web/lib/auth/partner-users/throw-if-no-permission.ts (1)

5-22: LGTM! Clean permission check abstraction.

The function provides a clear, reusable pattern for partner permission checks with appropriate error handling. The early return pattern and default message improve code readability and user experience.

apps/web/app/(ee)/api/events/export/route.ts (1)

83-89: LGTM! Consistent validation refactor.

The migration to assertValidDateRangeForPlan is consistent with the broader refactor pattern across analytics routes. The behavior remains equivalent while providing a cleaner API.

apps/web/app/api/analytics/route.ts (1)

91-97: LGTM! Validation refactor applied consistently.

The change aligns with the project-wide validation refactor pattern, maintaining equivalent behavior with a cleaner API.

apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (2)

3-9: LGTM! Well-designed result type.

The DateRangeValidationResult discriminated union provides a clean, type-safe pattern for validation results. The explicit error codes enable better error handling and testing.


11-62: LGTM! Excellent refactor for separation of concerns.

Converting this function from throwing to returning result objects is a significant improvement:

  • Pure validation logic: No side effects make the function more predictable and testable
  • Flexible usage: Can be used in both throwing (assertValidDateRangeForPlan) and non-throwing contexts
  • Type-safe results: The discriminated union enables exhaustive pattern matching

This refactor aligns with functional programming best practices and improves overall code quality.

apps/web/lib/client-access-check.ts (1)

14-14: No issues found—code is type-safe.

The non-null assertion on line 14 is safe. PermissionAction is derived from PERMISSION_ACTIONS via the type system (typeof PERMISSION_ACTIONS)[number], and verification confirms all 24 possible PermissionAction values are present in ROLE_PERMISSIONS. TypeScript enforces that only valid action values can reach the .find() call, guaranteeing the assertion will always succeed. No defensive checks are needed.

apps/web/lib/api/errors.ts (2)

55-55: LGTM!

Good refactoring to make ErrorResponse internal since it's only used within this module. This reduces the public API surface.


107-107: LGTM!

Good refactoring to make handleApiError private since it's only used internally via handleAndReturnErrorResponse. This improves encapsulation and reduces the public API surface.

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb32792 and d60dc15.

📒 Files selected for processing (2)
  • apps/web/lib/actions/safe-action.ts (1 hunks)
  • apps/web/lib/axiom/server.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/axiom/server.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
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.
🧬 Code graph analysis (1)
apps/web/lib/actions/safe-action.ts (1)
apps/web/lib/axiom/server.ts (1)
  • logger (22-30)
⏰ 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
🔇 Additional comments (1)
apps/web/lib/actions/safe-action.ts (1)

9-9: next-safe-action's handleServerError can be an async function, so the conversion to async is supported in v7.10.1. The implementation change itself is compatible with the library.

However, additional concerns remain from the original review:

  • Accessing e.message before the instanceof Error check: If e is not an Error object, e.message will be undefined, causing potential issues during logging.
  • No error handling for logging operations: If logger.error() or logger.flush() throw exceptions, the error handler will fail without gracefully handling the logging failure.

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: 1

♻️ Duplicate comments (2)
apps/web/lib/axiom/server.ts (2)

30-40: Validate environment variable before usage.

The non-null assertion on NEXT_PUBLIC_AXIOM_DATASET at line 35 will cause a runtime crash if the variable is undefined despite isAxiomEnabled being true (which checks for both env vars).

Apply this diff to add validation:

+const dataset = process.env.NEXT_PUBLIC_AXIOM_DATASET;
+if (isAxiomEnabled && !dataset) {
+  throw new Error("NEXT_PUBLIC_AXIOM_DATASET environment variable is required");
+}
+
 export const logger = new Logger({
   transports: isAxiomEnabled
     ? [
         new AxiomJSTransport({
           axiom: axiomClient,
-          dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET!,
+          dataset: dataset!,
         }),
       ]
     : [new ConsoleTransport()],
   formatters: nextJsFormatters,
 });

42-57: Use withAxiomBodyLog selectively to avoid PII exposure.

withAxiomBodyLog logs the full request body for POST/PATCH/PUT requests (line 48). Request bodies often contain user PII, credentials, or sensitive metadata.

Consider:

  • Using the plain withAxiom wrapper for auth-protected routes that handle user data
  • Reserving withAxiomBodyLog for public or non-sensitive endpoints
  • Documenting which routes use body logging and why
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ea8043 and 5d30843.

📒 Files selected for processing (7)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx (1 hunks)
  • apps/web/lib/actions/partners/confirm-payouts.ts (1 hunks)
  • apps/web/lib/actions/partners/update-partner-profile.ts (1 hunks)
  • apps/web/lib/axiom/server.ts (1 hunks)
  • apps/web/ui/partners/confirm-payouts-sheet.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx
  • apps/web/lib/actions/partners/confirm-payouts.ts
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
📚 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/update-partner-profile.ts
🧬 Code graph analysis (1)
apps/web/lib/axiom/server.ts (2)
apps/web/lib/axiom/axiom.ts (1)
  • axiomClient (3-5)
packages/utils/src/functions/urls.ts (1)
  • getSearchParams (40-49)
⏰ 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
🔇 Additional comments (4)
apps/web/lib/actions/partners/update-partner-profile.ts (1)

4-4: LGTM! Clean import path refactor.

The updated import path for throwIfNoPermission aligns with the centralized authorization utilities reorganization mentioned in the PR objectives. The more specific import path improves clarity.

apps/web/ui/partners/confirm-payouts-sheet.tsx (1)

2-3: LGTM! Clean import path refactor.

The import paths have been correctly updated to use the new centralized modules for clientAccessCheck and exceededLimitError. The usage throughout the file remains unchanged and consistent.

apps/web/lib/axiom/server.ts (2)

18-28: LGTM! Status code mapping is appropriate.

The log level mapping correctly categorizes HTTP status codes: successful responses (1xx-3xx) as info, client errors (4xx) as warnings, and server errors (5xx) as errors.


59-59: LGTM! Clean route handler wrapper.

The withAxiom export provides a safe, generic wrapper for route handlers without the body logging concerns of withAxiomBodyLog.

Comment on lines +47 to +49
if (["POST", "PATCH", "PUT"].includes(data.req.method)) {
report.body = await data.req.json();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for JSON parsing.

The await data.req.json() call at line 48 will throw if the request body is not valid JSON, has already been consumed, or if the stream is locked. Since this is in the onSuccess callback, it could cause the logging to fail silently or propagate an unhandled error.

Apply this diff to add error handling:

 // Add body to report if the method is POST, PATCH, or PUT
 if (["POST", "PATCH", "PUT"].includes(data.req.method)) {
-  report.body = await data.req.json();
+  try {
+    report.body = await data.req.json();
+  } catch (error) {
+    report.body = { error: "Failed to parse request body" };
+  }
 }
📝 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.

Suggested change
if (["POST", "PATCH", "PUT"].includes(data.req.method)) {
report.body = await data.req.json();
}
if (["POST", "PATCH", "PUT"].includes(data.req.method)) {
try {
report.body = await data.req.json();
} catch (error) {
report.body = { error: "Failed to parse request body" };
}
}
🤖 Prompt for AI Agents
In apps/web/lib/axiom/server.ts around lines 47 to 49, the call to await
data.req.json() can throw if the body isn't valid JSON or the stream is
unavailable; wrap the JSON parse in a try/catch, on success assign report.body
the parsed object, on failure attempt a safe fallback (e.g., try to read raw
text or set report.body = undefined) and record the parsing error in your
logger/report so the onSuccess path does not throw; ensure you do not rethrow
the error so logging continues normally.

@steven-tey steven-tey merged commit 34e5775 into main Nov 11, 2025
9 of 10 checks passed
@steven-tey steven-tey deleted the upgrade-axiom branch November 11, 2025 21:23
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.

3 participants