Skip to content

Project transfer page redesign#1309

Open
Developing-Gamer wants to merge 5 commits into
devfrom
Project-transfer-page-redesign
Open

Project transfer page redesign#1309
Developing-Gamer wants to merge 5 commits into
devfrom
Project-transfer-page-redesign

Conversation

@Developing-Gamer
Copy link
Copy Markdown
Contributor

@Developing-Gamer Developing-Gamer commented Apr 5, 2026

Summary by CodeRabbit

  • New Features

    • Reusable transfer confirmation UI with clear loading, success, and error states.
    • Neon-specific transfer flow added, guiding sign-in, account switching, or accepting transfers.
    • Custom integration transfer flow with streamlined confirm/check behavior.
    • Improved transfer sign-up redirect so users return to the correct page after auth.
  • Bug Fixes

    • Consistent messaging for missing/invalid/expired transfer codes.
    • Safer widget “Reload” handling when reset may be unavailable.

Summary

Redesigns the custom integration project-transfer confirmation page (/integrations/custom/projects/transfer/confirm) onto the new design-components system (DesignCard + DesignAlert + DesignButton + DesignInput). The presentational shell is extracted into a reusable ProjectTransferConfirmView so the route file only handles state + API calls. The legacy Neon transfer page is split out unchanged into its own client component to keep the existing Neon × Stack co-branded UI intact.


Screenshots — before and after

Captured against http://localhost:8101 at 1280×900. Dev-only overlays (outdated-version banner, console toast, DEV badge) are hidden via injected CSS for clarity.

Custom integration — missing transfer code

Visiting /integrations/custom/projects/transfer/confirm with no ?code=… query param.

Before (dev) After (this PR)

Before was a raw "Error: No transfer code provided." line. After is a dedicated DesignAlert with an explanation and recovery instructions.

Custom integration — invalid / expired code (check endpoint fails)

Before (dev) After (this PR)

Before showed the raw backend error string (Request validation failed on POST …). After uses a DesignCard with the ArrowsLeftRightIcon, a friendlier "This transfer can't continue" copy in an inline DesignAlert, the Stack Auth logomark in the actions slot, and an explicit Close button to dismiss.

Neon integration — legacy UI preserved

The Neon page (/integrations/neon/projects/transfer/confirm) was deliberately not redesigned — it still uses the Neon × Stack co-branded card so partner-facing copy/branding stay identical. It's now its own client component (neon-transfer-confirm-page.tsx) instead of sharing the redesigned one.

Before (dev) After (this PR)

Same shell on both sides — copy was tightened slightly ("Return to your Neon dashboard and start the transfer again") and the raw API error string is gone.


What changed

  • New apps/dashboard/src/components/project-transfer-confirm-view.tsx — purely presentational ProjectTransferConfirmView. Owns the design-components shell, the loading spinner, the signed-in vs signed-out branches of the success state (with DesignInput + "Use a different account" button), and the error / missing-code alerts.
  • New apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx — extraction of the legacy Neon UI (Neon logo, Stack logo, "Project transfer" header, Card / CardContent / CardFooter). Behaviour and copy match the previous transfer-confirm-page exactly when type === "neon".
  • Rewritten apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx — now hard-coded to the custom integration (no more type prop), defers UI to ProjectTransferConfirmView, and exports a TransferConfirmMissingCodeView used by the route when code is absent from the URL.
  • Route plumbing
    • app/(main)/integrations/custom/projects/transfer/confirm/page.tsx — renders the redesigned flow, falls back to TransferConfirmMissingCodeView when code is missing.
    • app/(main)/integrations/neon/projects/transfer/confirm/page.tsx — points at the new dedicated Neon client component.
  • New apps/dashboard/src/lib/stack-app-internals.ts — consolidates the symbol-keyed getStackAppInternals(app) helper (and stackAppInternalsSymbol) into one module with a JSDoc explainer + runtime type guard, replacing scattered as any casts.
  • New apps/dashboard/src/lib/transfer-utils.tsbuildTransferSignUpUrl() helper so the route file + the view stay in sync on the /handler/signup?after_auth_return_to=… query construction.

Bot review follow-ups addressed in this PR

  • Fail-loud assertions for unset handlers in the success state of ProjectTransferConfirmView (StackAssertionError instead of silent no-op).
  • SSR safety: moved every window.location read into client-only handlers / useEffects — the page was previously evaluating it at module load.
  • Friendly error fallback when the backend /check endpoint throws — replaces the raw KnownError<…> message with "This transfer link is invalid, has expired, or has already been used. Open the original link from the partner or integrations dashboard, or start the transfer again."
  • runAsynchronouslyWithAlert around every async onClick (Transfer, Sign in, Switch account, Close) so unhandled rejections surface to the user.
  • JSX entity bug fix: &apos; was a string-attribute literal, not a JSX expression — converted to a JSX expression so it renders as '.
  • window.close() removal in error state — replaced with a Close button that resets local state, so users on a fresh tab (no opener) aren't stuck.
  • getStackAppInternals consolidated — previously three independent copies (here + two in projects/page-client.tsx). Now one helper with a runtime type guard instead of as any, plus a comment explaining the symbol-keyed SDK escape hatch.
  • Widget-playground reset: the original change here turned out to duplicate a deliberate prior fix on dev (N2D4, e68015909d "Fix lint"). Reverted in fe92689eb so we don't fight that fix.

Notes for reviewers

  • Start with components/project-transfer-confirm-view.tsx. Everything reviewer-interesting is in the props shape (ProjectTransferConfirmUiState union, onPrimary / onCancel / onSwitchAccount callbacks). The route file just wires those to the getStackAppInternals(app).sendRequest(...) calls.
  • The Neon page was intentionally not migrated. Partner-facing co-branding (Neon logo × Stack logo, "Neon would like to transfer…" copy) is unchanged — flag it if you think it should be brought onto design-components too, but the goal of this PR was only the custom flow.
  • API surface is unchanged — same /integrations/custom/projects/transfer/confirm/check and /integrations/custom/projects/transfer/confirm endpoints, same request bodies, same redirect to /projects/{project_id} on success.
  • Success state isn't in the screenshots because reproducing it locally needs a real transfer code (the /check endpoint validates the code against the DB). It uses the same DesignCard shell with either a DesignInput showing the receiving account + a "Use a different account" outline button (signed-in branch), or a DesignAlert variant="info" prompting sign-in (signed-out branch). Worth manually testing on a real transfer before merging.

Test plan

  • Visit /integrations/custom/projects/transfer/confirm with no code → renders the "transfer link is incomplete" alert (screenshots above)
  • Visit /integrations/custom/projects/transfer/confirm?code=invalid → renders the redesigned card with the friendly error inside a DesignAlert variant="error" and a working Close button
  • Trigger a real custom-integration transfer end to end → loading spinner, success state, "Accept transfer" works while signed in, "Sign in" deep-links to /handler/signup?after_auth_return_to=… while signed out
  • Visit /integrations/neon/projects/transfer/confirm?code=… → unchanged legacy Neon × Stack co-branded card
  • Light + dark mode visual sanity (screenshots above are the canonical reference)

…firmation components

- Introduced a new `neon-transfer-confirm-page.tsx` for handling project transfer confirmations specific to Neon, maintaining legacy UI and behavior.
- Refactored existing `transfer-confirm-page.tsx` to support a new `TransferConfirmMissingCodeView` for handling cases where the transfer code is missing.
- Updated integration pages for both Neon and custom transfers to utilize the new components, enhancing code organization and reusability.
- Added a new `project-transfer-confirm-view.tsx` component to standardize the UI for project transfer confirmations across different integrations.

These changes improve the user experience during project transfers and streamline the integration process for different services.
@Developing-Gamer Developing-Gamer requested a review from N2D4 April 5, 2026 02:05
@Developing-Gamer Developing-Gamer self-assigned this Apr 5, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 5, 2026

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

Project Deployment Actions Updated (UTC)
3 Building Building Preview, Comment May 14, 2026 9:08pm
3-1776188452445-5Jus Canceled Canceled May 14, 2026 9:08pm
stack-auth-hosted-components Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-auth-mcp Error Error May 14, 2026 9:08pm
stack-backend Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-dashboard Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-demo Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-docs Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-preview-backend Ready Ready Preview, Comment May 14, 2026 9:08pm
stack-preview-dashboard Ready Ready Preview, Comment May 14, 2026 9:08pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a8d23c7d-5f25-4762-b70c-9428cbca6c9c

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0bd39 and fe92689.

📒 Files selected for processing (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx

📝 Walkthrough

Walkthrough

Splits Neon and Custom transfer-confirm flows into separate client components, introduces a reusable ProjectTransferConfirmView with typed UI state, adds transfer helper utilities (buildTransferSignUpUrl, getStackAppInternals), and small UX/runtime tweaks (missing-code view and nullable widget reset handling).

Changes

Project transfer UI

Layer / File(s) Summary
Presentational view & types
apps/dashboard/src/components/project-transfer-confirm-view.tsx
Adds ProjectTransferConfirmUiState, ProjectTransferConfirmViewProps, and ProjectTransferConfirmView — client component rendering loading/success/error branches, signed-in vs guest UI, footer actions, and runtime assertions for required callbacks/labels.

Neon integration

Layer / File(s) Summary
Neon client component implementation
apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx
New client component (NeonIntegrationProjectTransferConfirmPageClient) that reads code, POSTs /integrations/neon/projects/transfer/confirm/check on mount, manages loading/success/error UI, supports sign-out+redirect, and on primary action POSTs /integrations/neon/projects/transfer/confirm, asserts project_id, and navigates or redirects to signup for guests.
Neon page wiring
apps/dashboard/src/app/(main)/integrations/neon/projects/transfer/confirm/page.tsx
Replaces generic client import with NeonIntegrationProjectTransferConfirmPageClient, reads searchParams.code, returns missing-code error when absent, and renders the Neon-specific client without a type prop.

Custom integration

Layer / File(s) Summary
Custom page wiring & missing-code handling
apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx
Swaps import to CustomIntegrationProjectTransferConfirmPageClient, adds a local MissingCodeView component and returns it when searchParams.code is missing; removes prior type="custom" prop.
Custom confirm controller wiring
apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx
Refactors controller to use ProjectTransferConfirmView, hardcodes /integrations/custom/.../check and /integrations/custom/.../confirm endpoints, uses typed UI state with fixed check-failure message, validates project_id on confirm, wires onPrimary, onSwitchAccount, and onCancel, and changes default export to no longer accept a type prop.

Utilities & minor runtime tweaks

Layer / File(s) Summary
Transfer URL and internals helpers
apps/dashboard/src/lib/transfer-utils.ts
Adds buildTransferSignUpUrl() to build /handler/signup?after_auth_return_to=... from current location, and getStackAppInternals(app) to read symbol-keyed internals.
Widget playground safety
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx
Makes widget error fallback nullable-safe: only render the “Reload widget” button when props.reset exists and call via non-null assertion.

Sequence Diagram

sequenceDiagram
  participant Browser as Browser
  participant Client as Integration Client
  participant AppServer as AppServer (API)
  participant Auth as Auth (session)
  participant Router as Router

  Browser->>Client: load page with ?code=...
  Client->>AppServer: POST /integrations/*/projects/transfer/confirm/check (code)
  AppServer-->>Client: 200 OK / error
  alt check success
    Client->>Auth: read current user session
    Note right of Client: render success UI (signed-in or guest)
    Browser->>Client: user clicks Primary
    alt signed-in
      Client->>AppServer: POST /integrations/*/projects/transfer/confirm
      AppServer-->>Client: { project_id }
      Client->>Router: navigate -> /projects/{project_id}
    else guest
      Client->>Browser: redirect -> /handler/signup?after_auth_return_to=...
    end
  else check error
    Client->>Browser: render error message
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • N2D4

Poem

🐰
I hopped along the transfer trail,
Neon split and custom rail,
A tidy view to show the state,
Helpers guide the signup gate,
Confirmations now set sail.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Project transfer page redesign' clearly summarizes the main change: a comprehensive redesign of the project transfer confirmation page UI to use design-components.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the summary, before/after screenshots, detailed change breakdown, reviewer notes, and test plan—exceeding template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Project-transfer-page-redesign

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx

Parsing error: error TS5012: Cannot read file '/tsconfig.json': ENOENT: no such file or directory, open '/tsconfig.json'.


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.

@Developing-Gamer Developing-Gamer marked this pull request as ready for review April 5, 2026 04:54
Copilot AI review requested due to automatic review settings April 5, 2026 04:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Redesigns the custom integration project transfer confirmation experience in the Dashboard by introducing a new design-components-based view, while keeping the Neon flow on the legacy UI via a dedicated client component.

Changes:

  • Add ProjectTransferConfirmView presentational component (design-components UI) for transfer confirmation states.
  • Update custom integration transfer confirm page to use the new view and custom endpoints; add a richer “missing code” UI.
  • Split Neon transfer confirm page into its own legacy client component and update routing to use it.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/dashboard/src/components/project-transfer-confirm-view.tsx New design-components presentational shell for custom transfer confirm (loading/success/error).
apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx Custom integration client logic updated to render the new view and call custom endpoints.
apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx Server route updated to use redesigned custom client page and improved missing-code UX.
apps/dashboard/src/app/(main)/integrations/neon/projects/transfer/confirm/page.tsx Route updated to use Neon-specific client component.
apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx New Neon-specific legacy client page extracted from the previous shared implementation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx Outdated
Comment thread apps/dashboard/src/components/project-transfer-confirm-view.tsx
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 5, 2026

Greptile Summary

This PR redesigns the custom-integration project-transfer confirmation page onto the new design-components system (DesignCard, DesignAlert, DesignButton, DesignInput) and extracts the legacy Neon UI into its own client component so the two flows are no longer interleaved.

  • New ProjectTransferConfirmView — purely presentational component that owns the loading, success (signed-in / signed-out), and error shells; callbacks are wired back by the route file.
  • Split neon-transfer-confirm-page.tsx — Neon-specific co-branded UI extracted unchanged; the original transfer-confirm-page.tsx is now hard-coded to the custom flow.
  • Improved missing-code view — the bare Error: No transfer code provided. text in the custom page route is replaced with a styled red alert card; the Neon route keeps the old plain-text fallback (intentionally deferred).

Confidence Score: 4/5

Safe to merge with minor UX polish outstanding — no data-loss or auth-boundary changes.

The core logic (API calls, redirect, auth state wiring) is unchanged from the pre-refactor code and the new view component correctly wraps all async handlers with runAsynchronouslyWithAlert. The one notable gap is that the error state in ProjectTransferConfirmView renders no footer buttons, so a user who lands with an invalid code has no UI action to leave.

apps/dashboard/src/components/project-transfer-confirm-view.tsx — the error/loading render paths have no footer actions.

Important Files Changed

Filename Overview
apps/dashboard/src/components/project-transfer-confirm-view.tsx New presentational component for the custom transfer confirmation UI; uses correct runAsynchronouslyWithAlert wrappers, but error/loading states render no footer buttons leaving users with no UI affordance to leave the page.
apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx Rewired to delegate rendering to ProjectTransferConfirmView; logic is clean, error boundary coverage via runAsynchronouslyWithAlert in the view, StackAssertionError guard for missing project_id added.
apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx Extracted legacy Neon UI into its own file; buildSignUpUrl is duplicated verbatim from transfer-confirm-page.tsx; same no-action-buttons gap in error state as the old code.
apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx Server component now renders the styled MissingCodeView for missing code and delegates to the new client component; straightforward and correct.
apps/dashboard/src/app/(main)/integrations/neon/projects/transfer/confirm/page.tsx Minimal change — only the import is updated to point at the new dedicated Neon client component; behavior unchanged.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx One-line defensive fix: props.reset() to props.reset?.() with an eslint-disable comment explaining the Next.js type drift; unrelated to the transfer feature.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant CustomPage as custom/confirm page.tsx (Server)
    participant CustomClient as transfer-confirm-page.tsx (Client)
    participant View as ProjectTransferConfirmView
    participant NeonPage as neon/confirm page.tsx (Server)
    participant NeonClient as neon-transfer-confirm-page.tsx (Client)
    participant API as Stack Auth API

    Browser->>CustomPage: "GET /integrations/custom/.../confirm?code=xxx"
    alt no code param
        CustomPage-->>Browser: MissingCodeView (styled error card)
    else code present
        CustomPage-->>Browser: CustomIntegrationProjectTransferConfirmPageClient
        CustomClient->>View: "render(state=loading)"
        CustomClient->>API: POST /integrations/custom/.../confirm/check
        alt check succeeds
            API-->>CustomClient: 200 OK
            CustomClient->>View: "render(state=success, signedIn=...)"
            alt user clicks Accept
                View->>CustomClient: onPrimary()
                CustomClient->>API: POST /integrations/custom/.../confirm
                API-->>CustomClient: project_id
                CustomClient->>Browser: "router.push(/projects/{id})"
            else user clicks Cancel
                View->>CustomClient: onCancel()
                CustomClient->>Browser: window.close()
            end
        else check fails
            API-->>CustomClient: error
            CustomClient->>View: "render(state=error)"
        end
    end

    Browser->>NeonPage: "GET /integrations/neon/.../confirm?code=xxx"
    alt no code param
        NeonPage-->>Browser: plain text error div
    else code present
        NeonPage-->>Browser: NeonIntegrationProjectTransferConfirmPageClient
        NeonClient->>API: POST /integrations/neon/.../confirm/check
        API-->>NeonClient: result
        NeonClient-->>Browser: Legacy Neon x Stack co-branded card
    end
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/dashboard/src/components/project-transfer-confirm-view.tsx:112-145
**Error state leaves user with no exit action**

When the `/check` call fails the component renders the `DesignAlert` inside the card but the footer (Cancel / Accept buttons) is gated on `state === "success"` and is never shown. A user who arrives with an invalid or expired code sees the error message and then has no UI button to leave — the only escape is the browser's back button. Consider adding a minimal footer (e.g. a single "Close" / "Go back" button) that is also rendered in the error state.

### Issue 2 of 2
apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx:17-22
`buildSignUpUrl` is defined identically in both this file and `transfer-confirm-page.tsx`. Extracting it to a shared module (e.g. `@/lib/transfer-utils`) would remove the duplication and make a future change (e.g. changing the redirect param name) a one-place edit.

```suggestion
// TODO: extract to a shared util (same implementation lives in transfer-confirm-page.tsx)
function buildSignUpUrl(): string {
  const currentUrl = new URL(window.location.href);
  const signUpSearchParams = new URLSearchParams();
  signUpSearchParams.set("after_auth_return_to", currentUrl.pathname + currentUrl.search + currentUrl.hash);
  return `/handler/signup?${signUpSearchParams.toString()}`;
}
```

Reviews (1): Last reviewed commit: "bot comments" | Re-trigger Greptile

Comment thread apps/dashboard/src/components/project-transfer-confirm-view.tsx
Comment thread apps/dashboard/src/components/project-transfer-confirm-view.tsx
Comment thread apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx Outdated
Copy link
Copy Markdown
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx (1)

35-49: ⚠️ Potential issue | 🟠 Major

Guard error types and validate API response shape before routing.

Line 47 silently treats every failure as an Error—if a non-Error value is thrown (e.g., a string or null), accessing .message produces undefined. Lines 82–83 bypass response validation entirely; if the API contract changes or returns malformed data, confirmResJson.project_id becomes undefined, routing the user to /projects/undefined.

Per the coding guidelines ("Fail early, fail loud. Fail fast with an error instead of silently continuing."), guard both the caught error type and the JSON response shape:

Suggested hardening
      } catch (err: any) {
-        setState({ type: "error", message: err.message });
+      } catch (err: unknown) {
+        setState({
+          type: "error",
+          message: err instanceof Error ? err.message : "Failed to verify the transfer link",
+        });
       }
@@
-          const confirmResJson = await confirmRes.json();
-          router.push(`/projects/${confirmResJson.project_id}`);
+          const confirmResJson: unknown = await confirmRes.json();
+          const projectId =
+            typeof confirmResJson === "object" &&
+            confirmResJson != null &&
+            "project_id" in confirmResJson &&
+            typeof confirmResJson["project_id"] === "string"
+              ? confirmResJson["project_id"]
+              : null;
+          if (projectId == null) {
+            throw new Error("Transfer confirm response missing `project_id`");
+          }
+          router.push(`/projects/${encodeURIComponent(projectId)}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/app/`(main)/integrations/transfer-confirm-page.tsx around
lines 35 - 49, The catch block in the runAsynchronously call and the subsequent
handling of confirmResJson need hardening: when catching errors from (app as
any)[stackAppInternalsSymbol].sendRequest, normalize the error message (e.g.,
use err?.message ?? String(err) or assert instanceof Error) before calling
setState so you never pass undefined, and when handling the response from
sendRequest validate the parsed JSON (confirmResJson) and assert that
confirmResJson.project_id is a non-empty string (otherwise throw a clear Error)
before routing to `/projects/${project_id}`; update the setState error paths to
use the normalized message and throw/handle a descriptive error when the API
response shape is invalid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/dashboard/src/app/`(main)/integrations/neon-transfer-confirm-page.tsx:
- Around line 29-41: The catch block and response handling need explicit
validation: when catching errors from (app as
any)[stackAppInternalsSymbol].sendRequest, check if err is an Error (e.g., err
instanceof Error) and use err.message, otherwise use String(err) or throw a new
Error("Unexpected error from sendRequest"); similarly, after parsing the confirm
response (confirmResJson), validate that confirmResJson and
confirmResJson.project_id exist and are of the expected type before calling
navigate(`/projects/${confirmResJson.project_id}`) — if missing, throw a clear
Error("Missing project_id in confirm response") and setState({type: "error",
message: ...}) so we fail loud; finally, remove or justify the any casts (app as
any and catch err: any) by either adding a brief explanatory comment per
guidelines or converting to proper types so casts are not needed (referencing
stackAppInternalsSymbol, sendRequest, setState, and confirmResJson.project_id).
- Around line 46-49: The code builds signUpUrl during render using
window.location (currentUrl, signUpSearchParams, signUpUrl), which can cause
"window is not defined" on server render; move that construction out of the
render path by either 1) creating a small helper function buildSignUpUrl() that
reads window.location and returns the `/handler/signup?...` string and call it
inside the click handlers that currently use signUpUrl (the handlers referenced
around where signUpUrl is used), or 2) compute and store signUpUrl in a
useEffect into state (useState + useEffect) so it runs only on the client; then
remove the top-level construction of currentUrl/signUpSearchParams/signUpUrl
from the component body. Ensure the handlers use the new helper or state value.

In `@apps/dashboard/src/app/`(main)/integrations/transfer-confirm-page.tsx:
- Around line 71-95: The code reads window.location.href at render and captures
signUpUrl for the onPrimary and onSwitchAccount callbacks, which risks SSR
crashes; move the currentUrl/signUpUrl computation out of render and into a
client-only effect or ref: create a state/ref (e.g., signUpUrlRef or signUpUrl
state) and set it inside a useEffect that runs on mount, or compute signUpUrl
inside the onPrimary/onSwitchAccount handlers themselves before using it; update
references inside onPrimary and onSwitchAccount (and any usage of currentUrl) to
use the ref/state or freshly computed value so no window access happens during
server render.

In `@apps/dashboard/src/components/project-transfer-confirm-view.tsx`:
- Around line 13-21: When rendering ProjectTransferConfirmView, enforce its
success-state contract by removing the fallback "Signed in as
preview@example.com" label and adding runtime validations in the component
(e.g., at the start of ProjectTransferConfirmView render): if state ===
"success" ensure onCancel and onPrimary are non-null and throw descriptive
Errors if missing; additionally if state === "success" && signedIn === true
ensure signedInAsLabel and onSwitchAccount are non-null and throw Errors if
missing. Make these checks reference the props by name (state, signedIn,
signedInAsLabel, onCancel, onPrimary, onSwitchAccount) so callers fail loud
instead of rendering disabled buttons or fake labels.

---

Outside diff comments:
In `@apps/dashboard/src/app/`(main)/integrations/transfer-confirm-page.tsx:
- Around line 35-49: The catch block in the runAsynchronously call and the
subsequent handling of confirmResJson need hardening: when catching errors from
(app as any)[stackAppInternalsSymbol].sendRequest, normalize the error message
(e.g., use err?.message ?? String(err) or assert instanceof Error) before
calling setState so you never pass undefined, and when handling the response
from sendRequest validate the parsed JSON (confirmResJson) and assert that
confirmResJson.project_id is a non-empty string (otherwise throw a clear Error)
before routing to `/projects/${project_id}`; update the setState error paths to
use the normalized message and throw/handle a descriptive error when the API
response shape is invalid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e09c23fe-5da6-46e1-9316-0c489fee819b

📥 Commits

Reviewing files that changed from the base of the PR and between ce49eae and b491e83.

📒 Files selected for processing (5)
  • apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx
  • apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx
  • apps/dashboard/src/app/(main)/integrations/neon/projects/transfer/confirm/page.tsx
  • apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx
  • apps/dashboard/src/components/project-transfer-confirm-view.tsx

Comment thread apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx Outdated
Comment thread apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx Outdated
Comment thread apps/dashboard/src/components/project-transfer-confirm-view.tsx
Comment thread apps/dashboard/src/components/project-transfer-confirm-view.tsx Outdated
@aadesh18
Copy link
Copy Markdown
Collaborator

@greptile review again

Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx (1)

35-37: ⚡ Quick win

Avoid dynamic searchParams in this app route; use a client component instead.

This page can remain static by delegating the transfer code check to a client component that uses useSearchParams. Move the conditional rendering logic to a client component wrapper, keeping only the metadata export in the server component if needed.

As per coding guidelines: "NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component to ensure the page remains static."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dashboard/src/app/`(main)/integrations/custom/projects/transfer/confirm/page.tsx
around lines 35 - 37, The Page server component currently awaits
props.searchParams and reads transferCode (in Page and transferCode variable)
which makes the route dynamic; instead keep Page static and move the conditional
transfer-code check and rendering into a client component that uses
useSearchParams (create a ClientTransferConfirm or similar client wrapper that
calls useSearchParams to get code and handles the conditional
rendering/redirect/UI). Leave any metadata export in the server Page if
required, remove awaiting of props.searchParams and references to transferCode
from Page, and import/render the new client wrapper from Page so the route stays
static.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard/src/app/`(main)/integrations/transfer-confirm-page.tsx:
- Line 31: The code uses an untyped cast (app as any)[stackAppInternalsSymbol]
to call sendRequest, which should be fixed by either adding a brief explanatory
comment above the access that states why the SDK internals are not expressible
in TS and how runtime safety is ensured, or by introducing a small typed
accessor helper (e.g., a function like getStackAppInternals(app): { sendRequest:
(...) => Promise<any> } that encapsulates the cast and returns a typed object)
and replacing direct (app as any)[stackAppInternalsSymbol].sendRequest calls
with getStackAppInternals(app).sendRequest to centralize and document the unsafe
cast (reference symbols: app, stackAppInternalsSymbol, sendRequest,
transfer-confirm-page.tsx).

---

Nitpick comments:
In
`@apps/dashboard/src/app/`(main)/integrations/custom/projects/transfer/confirm/page.tsx:
- Around line 35-37: The Page server component currently awaits
props.searchParams and reads transferCode (in Page and transferCode variable)
which makes the route dynamic; instead keep Page static and move the conditional
transfer-code check and rendering into a client component that uses
useSearchParams (create a ClientTransferConfirm or similar client wrapper that
calls useSearchParams to get code and handles the conditional
rendering/redirect/UI). Leave any metadata export in the server Page if
required, remove awaiting of props.searchParams and references to transferCode
from Page, and import/render the new client wrapper from Page so the route stays
static.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4e4fda67-6678-4532-b54a-4a8231a88cab

📥 Commits

Reviewing files that changed from the base of the PR and between b491e83 and cd28f10.

📒 Files selected for processing (5)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx
  • apps/dashboard/src/app/(main)/integrations/custom/projects/transfer/confirm/page.tsx
  • apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx
  • apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx
  • apps/dashboard/src/components/project-transfer-confirm-view.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/widget-playground/page-client.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx
  • apps/dashboard/src/components/project-transfer-confirm-view.tsx

Comment thread apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx Outdated
Comment on lines +112 to +145
{typeof state !== "string" && (
<DesignAlert
variant="error"
title="This transfer can’t continue"
description={state.message}
glassmorphic
/>
)}

{state === "success" && (
<div className="flex flex-col-reverse gap-2 border-t border-black/[0.08] pt-5 dark:border-white/[0.08] sm:flex-row sm:justify-end">
<DesignButton
variant="outline"
className="transition-colors duration-150 hover:transition-none sm:min-w-[6.5rem]"
onClick={() => {
runAsynchronouslyWithAlert(async () => {
await onCancel?.();
});
}}
>
Cancel
</DesignButton>
<DesignButton
className="sm:min-w-[6.5rem]"
onClick={() => {
runAsynchronouslyWithAlert(async () => {
await onPrimary?.();
});
}}
>
{primaryLabel}
</DesignButton>
</div>
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Error state leaves user with no exit action

When the /check call fails the component renders the DesignAlert inside the card but the footer (Cancel / Accept buttons) is gated on state === "success" and is never shown. A user who arrives with an invalid or expired code sees the error message and then has no UI button to leave — the only escape is the browser's back button. Consider adding a minimal footer (e.g. a single "Close" / "Go back" button) that is also rendered in the error state.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/components/project-transfer-confirm-view.tsx
Line: 112-145

Comment:
**Error state leaves user with no exit action**

When the `/check` call fails the component renders the `DesignAlert` inside the card but the footer (Cancel / Accept buttons) is gated on `state === "success"` and is never shown. A user who arrives with an invalid or expired code sees the error message and then has no UI button to leave — the only escape is the browser's back button. Consider adding a minimal footer (e.g. a single "Close" / "Go back" button) that is also rendered in the error state.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx Outdated
disabled
size="md"
prefixItem={<Logo noLink width={15} height={15} alt="" />}
value={signedInAsLabel}
Copy link
Copy Markdown

@vercel vercel Bot May 13, 2026

Choose a reason for hiding this comment

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

TypeScript type error: signedInAsLabel property is optional but used without null check or assertion, causing potential type mismatch despite runtime validation

Fix on Vercel

Copy link
Copy Markdown
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 (1)
apps/dashboard/src/components/project-transfer-confirm-view.tsx (1)

93-93: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Type-narrow signedInAsLabel to satisfy TypeScript and follow coding guidelines.

Although runtime validation at line 48 ensures signedInAsLabel is non-null when signedIn is true, TypeScript cannot infer this guarantee at the usage site. This creates a type mismatch if DesignInput's value prop requires a non-optional string.

Per coding guidelines, prefer ?? throwErr(...) over non-null assertions for defensive coding with explicit error messages.

🛡️ Suggested fix using guideline-compliant pattern

Extract a validated variable immediately after the runtime checks:

   if (signedIn && (signedInAsLabel == null || onSwitchAccount == null)) {
     throw new StackAssertionError("ProjectTransferConfirmView requires `signedInAsLabel` and `onSwitchAccount` when `signedIn` is true in the success state");
   }
 }

+const validatedSignedInLabel = signedIn 
+  ? signedInAsLabel ?? throwErr("signedInAsLabel must be non-null when signedIn is true", StackAssertionError)
+  : undefined;
+
 const primaryLabel = signedIn ? "Accept transfer" : "Sign in";

Then at line 93:

-                    value={signedInAsLabel}
+                    value={validatedSignedInLabel}

Alternatively, if you prefer inline defensive coding:

-                    value={signedInAsLabel}
+                    value={signedInAsLabel ?? throwErr("signedInAsLabel must be non-null when signedIn is true", StackAssertionError)}

Based on coding guidelines: "Prefer ?? throwErr(...) over non-null assertions, with good error messages explicitly stating the assumption that must've been violated for the error to be thrown."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard/src/components/project-transfer-confirm-view.tsx` at line 93,
TypeScript can't see the earlier runtime check that guarantees signedInAsLabel
when signedIn is true, so replace the direct usage to a narrowed/defensive
value: after the runtime check for signedIn, create a local validated variable
(e.g., const validatedSignedInAs = signedInAsLabel ?? throwErr("expected
signedInAsLabel to be set when signedIn is true")) and use that
validatedSignedInAs as the value prop for DesignInput (instead of
signedInAsLabel); alternatively apply the inline defensive pattern
value={signedInAsLabel ?? throwErr("expected signedInAsLabel to be set when
signedIn is true")}. This satisfies TypeScript and follows the guideline
preferring ?? throwErr(...) over non-null assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard/src/lib/transfer-utils.ts`:
- Around line 3-7: buildTransferSignUpUrl currently builds the signup URL with a
template literal; instead create a URL object for "/handler/signup" using new
URL('/handler/signup', window.location.origin), set its search via the existing
signUpSearchParams (e.g., signupUrl.search = signUpSearchParams.toString() or
use signupUrl.searchParams directly), and return the relative path + search
(e.g., signupUrl.pathname + signupUrl.search) so the function uses the URL API;
update references to currentUrl and signUpSearchParams inside
buildTransferSignUpUrl accordingly.
- Around line 18-20: The getStackAppInternals function currently casts blindly
and may return undefined; change it to validate that app is a non-null object
and that the stackAppInternalsSymbol key exists before returning. Implement a
type-guard style check inside getStackAppInternals: verify typeof app ===
"object" && app !== null and Object.prototype.hasOwnProperty.call(app as object,
stackAppInternalsSymbol), then retrieve the value, assert it matches
StackAppInternals (or narrow its type) and return it; if the checks fail, throw
a clear Error like "Missing StackAppInternals on app" so callers fail fast.
Ensure you reference stackAppInternalsSymbol and getStackAppInternals in your
fix.

---

Duplicate comments:
In `@apps/dashboard/src/components/project-transfer-confirm-view.tsx`:
- Line 93: TypeScript can't see the earlier runtime check that guarantees
signedInAsLabel when signedIn is true, so replace the direct usage to a
narrowed/defensive value: after the runtime check for signedIn, create a local
validated variable (e.g., const validatedSignedInAs = signedInAsLabel ??
throwErr("expected signedInAsLabel to be set when signedIn is true")) and use
that validatedSignedInAs as the value prop for DesignInput (instead of
signedInAsLabel); alternatively apply the inline defensive pattern
value={signedInAsLabel ?? throwErr("expected signedInAsLabel to be set when
signedIn is true")}. This satisfies TypeScript and follows the guideline
preferring ?? throwErr(...) over non-null assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c7d1581-f427-4570-b17b-2c25a885d695

📥 Commits

Reviewing files that changed from the base of the PR and between cd28f10 and 1c0bd39.

📒 Files selected for processing (4)
  • apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx
  • apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx
  • apps/dashboard/src/components/project-transfer-confirm-view.tsx
  • apps/dashboard/src/lib/transfer-utils.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx
  • apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx

Comment on lines +3 to +7
export function buildTransferSignUpUrl(): string {
const currentUrl = new URL(window.location.href);
const signUpSearchParams = new URLSearchParams();
signUpSearchParams.set("after_auth_return_to", currentUrl.pathname + currentUrl.search + currentUrl.hash);
return `/handler/signup?${signUpSearchParams.toString()}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, verify the file exists and read its contents
cat -n apps/dashboard/src/lib/transfer-utils.ts

Repository: hexclave/stack-auth

Length of output: 927


🏁 Script executed:

# Check if there are any server/client-specific markers in the file or directory
head -20 apps/dashboard/src/lib/transfer-utils.ts

Repository: hexclave/stack-auth

Length of output: 787


🏁 Script executed:

# Check the directory structure to understand if this is a shared lib
ls -la apps/dashboard/src/lib/

Repository: hexclave/stack-auth

Length of output: 1897


🏁 Script executed:

# Search for other usages of buildTransferSignUpUrl to understand context
rg "buildTransferSignUpUrl" --type ts --type tsx -B2 -A2

Repository: hexclave/stack-auth

Length of output: 91


🏁 Script executed:

# Search for usages of buildTransferSignUpUrl without using file type
rg "buildTransferSignUpUrl" -B2 -A2

Repository: hexclave/stack-auth

Length of output: 4058


🏁 Script executed:

# Check if this is within a client component or page (check parent directory structure)
find apps/dashboard/src -type f -name "*.tsx" -o -name "*.ts" | head -20

Repository: hexclave/stack-auth

Length of output: 1739


🏁 Script executed:

# Verify the specific context - check if apps/dashboard is client-only
cat apps/dashboard/src/lib/transfer-utils.ts

Repository: hexclave/stack-auth

Length of output: 787


🏁 Script executed:

# Check the project structure to understand if apps/dashboard is exclusively client-side
ls -la apps/dashboard/

Repository: hexclave/stack-auth

Length of output: 1314


Prefer URL API over string interpolation when constructing the signup URL.

The function currently constructs the URL using template literal interpolation (/handler/signup?${...}). Per coding guidelines, use URL APIs for URL construction. While buildTransferSignUpUrl() is currently only called from client components and won't encounter a server context, the guideline recommends using new URL() with proper APIs rather than string concatenation.

 export function buildTransferSignUpUrl(): string {
   const currentUrl = new URL(window.location.href);
   const signUpSearchParams = new URLSearchParams();
   signUpSearchParams.set("after_auth_return_to", currentUrl.pathname + currentUrl.search + currentUrl.hash);
-  return `/handler/signup?${signUpSearchParams.toString()}`;
+  const signUpUrl = new URL("/handler/signup", window.location.origin);
+  signUpUrl.search = signUpSearchParams.toString();
+  return signUpUrl.pathname + signUpUrl.search;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard/src/lib/transfer-utils.ts` around lines 3 - 7,
buildTransferSignUpUrl currently builds the signup URL with a template literal;
instead create a URL object for "/handler/signup" using new
URL('/handler/signup', window.location.origin), set its search via the existing
signUpSearchParams (e.g., signupUrl.search = signUpSearchParams.toString() or
use signupUrl.searchParams directly), and return the relative path + search
(e.g., signupUrl.pathname + signupUrl.search) so the function uses the URL API;
update references to currentUrl and signUpSearchParams inside
buildTransferSignUpUrl accordingly.

Comment on lines +18 to +20
export function getStackAppInternals(app: unknown): StackAppInternals {
return (app as Record<symbol, StackAppInternals>)[stackAppInternalsSymbol];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n apps/dashboard/src/lib/transfer-utils.ts

Repository: hexclave/stack-auth

Length of output: 927


🏁 Script executed:

rg -A 3 "getStackAppInternals" --type ts --type tsx

Repository: hexclave/stack-auth

Length of output: 91


🏁 Script executed:

rg "getStackAppInternals" -A 3

Repository: hexclave/stack-auth

Length of output: 6432


🏁 Script executed:

cat -n apps/dashboard/src/app/\(main\)/\(protected\)/\(outside-dashboard\)/projects/page-client.tsx | sed -n '1,50p'

Repository: hexclave/stack-auth

Length of output: 2720


🏁 Script executed:

cat -n apps/dashboard/src/app/\(main\)/\(protected\)/\(outside-dashboard\)/new-project/page-client.tsx | sed -n '1,60p'

Repository: hexclave/stack-auth

Length of output: 1985


🏁 Script executed:

rg "function getStackAppInternals" apps/dashboard/src/app/\(main\)/\(protected\)/\(outside-dashboard\)/new-project/page-client.tsx -A 10

Repository: hexclave/stack-auth

Length of output: 469


🏁 Script executed:

cat -n apps/dashboard/src/app/\(main\)/integrations/transfer-confirm-page.tsx | head -30

Repository: hexclave/stack-auth

Length of output: 1664


🏁 Script executed:

cat -n apps/dashboard/src/lib/stack-app-internals.ts

Repository: hexclave/stack-auth

Length of output: 186


Don't bypass typing here—validate internals before returning.

This cast can return undefined as StackAppInternals and defer failure to call sites. Validate object/symbol presence before returning, then throw a clear error if assumptions are violated. Other parts of the codebase already implement this pattern correctly with type guards.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard/src/lib/transfer-utils.ts` around lines 18 - 20, The
getStackAppInternals function currently casts blindly and may return undefined;
change it to validate that app is a non-null object and that the
stackAppInternalsSymbol key exists before returning. Implement a type-guard
style check inside getStackAppInternals: verify typeof app === "object" && app
!== null and Object.prototype.hasOwnProperty.call(app as object,
stackAppInternalsSymbol), then retrieve the value, assert it matches
StackAppInternals (or narrow its type) and return it; if the checks fail, throw
a clear Error like "Missing StackAppInternals on app" so callers fail fast.
Ensure you reference stackAppInternalsSymbol and getStackAppInternals in your
fix.

</div>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Page component uses await on props.searchParams making the route dynamic when it should remain static

Fix on Vercel

setState('success');
} catch (err: any) {
setState({ type: 'error', message: err.message });
setState("success");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing explanatory comment for type assertion used to access internal SDK APIs via symbol-keyed property access

Fix on Vercel

</div>
);
}

Copy link
Copy Markdown

@vercel vercel Bot May 13, 2026

Choose a reason for hiding this comment

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

Page component uses await on props.searchParams making the route dynamic when it should remain static

Fix on Vercel

setState('success');
} catch (err: any) {
setState({ type: 'error', message: err.message });
setState("success");
Copy link
Copy Markdown

@vercel vercel Bot May 13, 2026

Choose a reason for hiding this comment

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

Missing explanatory comment for type assertion used to access internal SDK APIs via symbol-keyed property access

Fix on Vercel

dev already had a deliberate fix for the same TS2722 issue in
e680159 (N2D4, "Fix lint"). The bot-comment fix on this branch
diverged from it; restoring dev's `{props.reset && ... !()}` form
so the merge stays clean.
Comment on lines +5 to +7
const signUpSearchParams = new URLSearchParams();
signUpSearchParams.set("after_auth_return_to", currentUrl.pathname + currentUrl.search + currentUrl.hash);
return `/handler/signup?${signUpSearchParams.toString()}`;
Copy link
Copy Markdown

@vercel vercel Bot May 14, 2026

Choose a reason for hiding this comment

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

URL construction using template literal string interpolation instead of URL API with proper search parameter handling

Fix on Vercel

Comment on lines +18 to +19
export function getStackAppInternals(app: unknown): StackAppInternals {
return (app as Record<symbol, StackAppInternals>)[stackAppInternalsSymbol];
Copy link
Copy Markdown

@vercel vercel Bot May 14, 2026

Choose a reason for hiding this comment

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

getStackAppInternals function unsafely casts and accesses a symbol-keyed property without validation, potentially returning undefined

Fix on Vercel

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