Skip to content

fix:(cross-domain): support async storage implementations - #418

Open
Choco-milk-for-u wants to merge 2 commits into
convex:mainfrom
Choco-milk-for-u:crossdomain-async
Open

Choco-milk-for-u wants to merge 2 commits into
convex:mainfrom
Choco-milk-for-u:crossdomain-async

Conversation

@Choco-milk-for-u

Copy link
Copy Markdown

storage methods were expected to be synchronous. This change allows them to return promises as well, so we can pass a storage implementation other than localstorage in crossdomain plugin.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@Choco-milk-for-u is attempting to deploy a commit to the Convex Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8aed95b8-aec1-4ec2-af94-2bc117521bb8

📥 Commits

Reviewing files that changed from the base of the PR and between 1d180bb and eb84ac3.

📒 Files selected for processing (1)
  • src/plugins/cross-domain/client.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/plugins/cross-domain/client.ts

📝 Walkthrough

Walkthrough

The cross-domain client now accepts synchronous or asynchronous storage.getItem implementations. Its getCookie and getSessionData actions return Promises and await storage reads. Cookie retrieval during success handling, session preservation, and initialization was updated accordingly. Tests now await session data access, and the client type assertion expects getCookie to return a Promise. Obsolete fetch option typing and casts were removed.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: cross-domain plugin support for async storage implementations.
Description check ✅ Passed The description matches the changeset and objectives by explaining support for promise-returning storage methods.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
src/plugins/cross-domain/client.types.test.ts (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the second async accessor too.

getSessionData also changed in CrossDomainActions, but this test only protects getCookie. Add an assertion for () => Promise<Record<string, unknown> | null>.

🤖 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 `@src/plugins/cross-domain/client.types.test.ts` at line 18, Add a type
assertion for CrossDomainActions.getSessionData alongside the existing
authClient.getCookie assertion, verifying its type is () =>
Promise<Record<string, unknown> | null> and preserving coverage of both async
accessors.
src/plugins/cross-domain/client.test.ts (1)

162-189: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a runtime test with asynchronous getItem.

The fixture used by getActions() still returns string | null, so these tests do not exercise the new Promise-returning storage path. Add at least one mock whose getItem returns Promise.resolve(...).

🤖 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 `@src/plugins/cross-domain/client.test.ts` around lines 162 - 189, Add a test
case in the getSessionData suite that configures the getActions() storage
fixture with an asynchronous getItem implementation returning
Promise.resolve(...), then verifies the expected parsed session data or null
result. Keep the existing synchronous storage coverage unchanged and ensure the
async mock exercises the Promise-returning path.
🤖 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 `@src/plugins/cross-domain/client.ts`:
- Line 12: Update the examples using the Promise-returning accessors near the
getCookie and corresponding accessor declarations so their containing examples
are async and both accessor calls are awaited. Preserve the existing example
behavior while ensuring the calls are handled asynchronously.
- Around line 168-171: Serialize the cookie read-modify-write sequence around
getSetCookie in the success handlers so concurrent operations cannot read the
same stale cookie and overwrite each other. Apply the same synchronization to
both cookie mutation paths, including the corresponding logic near the second
occurrence, while preserving the existing merge and write behavior.
- Line 76: Update the storage interface alongside getItem so setItem returns
void or Promise<void>, then make the storage.setItem calls in the success/cache
path awaitable and await them before completing onSuccess. Preserve synchronous
storage compatibility while ensuring rejected async writes propagate instead of
being dropped.

---

Nitpick comments:
In `@src/plugins/cross-domain/client.test.ts`:
- Around line 162-189: Add a test case in the getSessionData suite that
configures the getActions() storage fixture with an asynchronous getItem
implementation returning Promise.resolve(...), then verifies the expected parsed
session data or null result. Keep the existing synchronous storage coverage
unchanged and ensure the async mock exercises the Promise-returning path.

In `@src/plugins/cross-domain/client.types.test.ts`:
- Line 18: Add a type assertion for CrossDomainActions.getSessionData alongside
the existing authClient.getCookie assertion, verifying its type is () =>
Promise<Record<string, unknown> | null> and preserving coverage of both async
accessors.
🪄 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: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c18f95f6-8b5f-42f6-9211-f216b066ffc8

📥 Commits

Reviewing files that changed from the base of the PR and between c628916 and 1d180bb.

📒 Files selected for processing (3)
  • src/plugins/cross-domain/client.test.ts
  • src/plugins/cross-domain/client.ts
  • src/plugins/cross-domain/client.types.test.ts

Comment thread src/plugins/cross-domain/client.ts
storage?: {
setItem: (key: string, value: string) => any;
getItem: (key: string) => string | null;
getItem: (key: string) => Promise<string | null> | string | null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
rg -n 'setItem\s*:|setItem\s*\(' src/plugins/cross-domain

Repository: get-convex/better-auth

Length of output: 1100


🏁 Script executed:

#!/usr/bin/env bash
sed -n '60,280p' src/plugins/cross-domain/client.ts

Repository: get-convex/better-auth

Length of output: 6919


🏁 Script executed:

#!/usr/bin/env bash
rg -n 'CrossDomainClientPlugin|storage\?:|setItem:\s*\(|getItem:\s*\(' src/plugins src | sed -n '1,220p'

Repository: get-convex/better-auth

Length of output: 1962


Type setItem as promise-capable and await the cache writes. getItem already accepts async storage, so setItem should return void | Promise<void> too; otherwise the storage.setItem(...) calls in the success/cache path can drop rejected writes and let onSuccess finish before persistence completes.

🤖 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 `@src/plugins/cross-domain/client.ts` at line 76, Update the storage interface
alongside getItem so setItem returns void or Promise<void>, then make the
storage.setItem calls in the success/cache path awaitable and await them before
completing onSuccess. Preserve synchronous storage compatibility while ensuring
rejected async writes propagate instead of being dropped.

Comment on lines +168 to 171
const prevCookie = await storage.getItem(cookieName);
const toSetCookie = getSetCookie(
setCookie || "",
prevCookie ?? undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize cookie read-modify-write operations.

The new await storage.getItem(...) introduces a yield between reading and writing. Concurrent success handlers can therefore merge against the same stale cookie and let the last write discard another response’s session or challenge cookie. Serialize these mutations or use an atomic storage operation.

Also applies to: 210-213

🤖 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 `@src/plugins/cross-domain/client.ts` around lines 168 - 171, Serialize the
cookie read-modify-write sequence around getSetCookie in the success handlers so
concurrent operations cannot read the same stale cookie and overwrite each
other. Apply the same synchronization to both cookie mutation paths, including
the corresponding logic near the second occurrence, while preserving the
existing merge and write behavior.

This branch has not been deployed

No deployments
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.

1 participant