Skip to content

feat(core): standardize API and client options and return types#143

Merged
halvaradop merged 7 commits intomasterfrom
feat/standarize-return-types
Apr 17, 2026
Merged

feat(core): standardize API and client options and return types#143
halvaradop merged 7 commits intomasterfrom
feat/standarize-return-types

Conversation

@halvaradop
Copy link
Copy Markdown
Member

@halvaradop halvaradop commented Apr 15, 2026

Description

This pull request standardizes the API and client types across all packages, improving type safety and consistency when consuming authentication flows. The changes primarily target the api object returned by createAuth and the client functions from createAuthClient.

The update introduces dedicated types that override default core types, ensuring more precise inference and better developer experience across integrations (e.g., Next.js, React Router).

Additionally, API responses are now strongly typed and include helper utilities to simplify response handling.


Key Changes

  • Added success boolean field to all API responses to indicate operation result
  • Added toResponse helper to transform API results into Response objects
  • Removed authenticated and updated flags in favor of unified success field
  • Introduced dedicated types in integration packages (e.g., @aura-stack/next, @aura-stack/react-router)
  • Improved type inference for API and client responses

Usage

Server-Side Rendering (SSR)

import { createAuth } from "@aura-stack/auth"

export const auth = createAuth({
  oauth: [],
})

const { success, toResponse } = await auth.api.signIn("github")

Client-Side Rendering (CSR)

import { createAuthClient } from "@aura-stack/auth/client"

export const authClient = createAuthClient({
  baseURL: "http://localhost:3000",
})

const data = await authClient.signIn("github")

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 15, 2026

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

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment Apr 17, 2026 0:05am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Warning

Rate limit exceeded

@halvaradop has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 9 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 9 seconds.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b83425f2-5572-4093-bf14-cca2b92da498

📥 Commits

Reviewing files that changed from the base of the PR and between 129de52 and abad508.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • deno.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • package.json
  • packages/core/package.json
  • packages/core/src/@types/session.ts
  • packages/core/src/api/createApi.ts
  • packages/core/src/api/credentials.ts
  • packages/core/src/api/signIn.ts
  • packages/core/src/api/signOut.ts
  • packages/core/src/client/client.ts
  • packages/core/src/shared/utils.ts
  • packages/core/test/actions/signIn/signIn.test.ts
  • packages/core/test/api/signIn.test.ts
  • packages/core/test/client/client.test.ts
  • packages/elysia/package.json
  • packages/elysia/test/index.test.ts
  • packages/express/package.json
  • packages/express/test/index.test.ts
  • packages/hono/package.json
  • packages/hono/test/index.test.ts
  • packages/jose/package.json
  • packages/rate-limiter/package.json
  • packages/react/package.json
  • pnpm-workspace.yaml
📝 Walkthrough

Walkthrough

Refactors auth API and types to return standardized objects with { success: boolean; ... }, headers, and a toResponse() method; replaces authenticated with success; moves DeepPartial to utility types; updates implementations and integrations to consume the new return shapes.

Changes

Cohort / File(s) Summary
Core types
packages/core/src/@types/session.ts, packages/core/src/@types/utility.ts, packages/core/src/@types/*
Replaced authenticated/old session return shapes with GetStatelessSessionReturn, GetSessionAPIReturn, and standardized AuthActionAPIReturn variants; moved DeepPartial to utility.ts; added AuthResponse and docstrings.
API surface
packages/core/src/api/getSession.ts, packages/core/src/api/signIn.ts, packages/core/src/api/signOut.ts, packages/core/src/api/updateSession.ts, packages/core/src/api/credentials.ts, packages/core/src/api/createApi.ts
Changed API functions to return { success: boolean, ... , toResponse(): Response } shapes, include headers, and unify success/failure semantics; adjusted signatures and internal header/cookie handling to delegate response construction to toResponse().
Action handlers
packages/core/src/actions/.../*.ts (session.ts, signIn.ts, signInCredentials.ts, signOut.ts, updateSession.ts)
Simplified endpoints to call underlying API functions and return their toResponse() directly, removing manual Response construction and cookie/header wiring in handlers.
Session strategies & utils
packages/core/src/session/stateless.ts, packages/core/src/shared/utils.ts
Aligned stateless getSession typing to GetStatelessSessionReturn; added toUnionHeaders util to merge header sets without overwriting existing keys.
Client libraries
packages/core/src/client/client.ts, packages/next/src/lib/api.ts, packages/react-router/src/lib/api.ts, packages/elysia/src/lib/with-auth.ts
Updated clients and integrations to expect { success } semantics, use API *APIReturn types, apply cookies from returned headers, and conditionally redirect based on new success/redirectURL/signInURL fields.
Type exports & integrations
packages/react-router/src/@types/*, packages/next/src/@types/*, packages/*/@types/*
Added and adjusted package-specific type aliases and conditional return types (Next/React Router) to reflect new API return shapes and redirect semantics.
Tests & config
packages/core/test/**, packages/core/vitest.config.ts
Updated tests to assert { success: boolean } and presence of toResponse() where appropriate; adjusted vitest secrets and test inclusion.
Minor / formatting
packages/core/src/oauth/index.ts, assorted JSDoc additions in packages/core/src/@types/*
Formatting/signature cosmetic changes and added JSDoc comments across many type files (no structural type changes).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API as Auth API
  participant Strategy as SessionStrategy
  participant Headers as HeadersStore

  Client->>API: call getSession(ctx/options)
  API->>Strategy: getSession(request.headers)
  Strategy-->>API: { session, headers }
  API->>Headers: toUnionHeaders(headers, secureApiHeaders)
  API-->>Client: { success: true|false, session|null, headers, toResponse() }
  Client->>Client: call toResponse() or apply cookies/redirect based on result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, feature

Poem

🐰 I nibbled code and shaped a way,
success now hops where old flags lay,
toResponse() carries cookies and cheer,
headers in paw, the responses clear,
a tiny rabbit says: "Auth is gay!" 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: standardizing API and client options and return types across the codebase, which aligns with the extensive type refactoring evident throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/standarize-return-types

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

@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)
packages/core/src/@types/session.ts (2)

238-265: Inconsistent success/failure indicator: AuthActionReturn uses ok while SessionReturn uses success.

AuthActionReturn (lines 238-249) uses ok: true/false, but SessionReturn (lines 263-265) and SignInReturn (line 281) use success: true/false. For API consistency, consider aligning on a single property name across all return types.

♻️ Proposed alignment
 export type AuthActionReturn =
     | {
-          ok: true
+          success: true
           headers: Headers
           redirectURL?: string
           toResponse: () => Response
       }
     | {
-          ok: false
+          success: false
           headers: Headers
           toResponse: () => Response
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/session.ts around lines 238 - 265, The return types
are inconsistent: AuthActionReturn uses ok while SessionReturn and SignInReturn
use success; pick one property name (recommend changing AuthActionReturn to
success) and update all related types and any callers to use that single boolean
key consistently. Specifically, change AuthActionReturn's discriminant from
ok:boolean to success:boolean (and update any code referencing
AuthActionReturn.ok), or alternatively rename SessionReturn/SignInReturn to ok
if you prefer that convention—ensure AuthActionReturn, SessionReturn,
SignInReturn (and all uses of those types/functions) are updated together so the
discriminant name is uniform across the codebase.

279-284: SignInReturn.signInURL type may not accurately reflect all cases.

The interface declares signInURL: string, but when redirect === true, the implementation in signIn.ts line 75 sets signInURL: authorization (a string) on the object while toResponse() returns signInURL: null in the JSON body. Consider whether the type should reflect this nuance, or if the current typing is sufficient since it describes the object property (not the JSON response).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/session.ts around lines 279 - 284, The SignInReturn
interface's signInURL currently typed as string doesn't reflect that
toResponse() may return null for signInURL when redirect is true; update the
type to capture this nuance by making signInURL conditional on the generic
Redirect (e.g. change signInURL to a conditional type such as Redirect extends
true ? string : string | null or, if simpler, string | null) and ensure the
symbols mentioned (SignInReturn, signInURL, toResponse, and the signIn.ts
implementation) remain consistent with that updated typing so callers and the
JSON response types align.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/api/getSession.ts`:
- Around line 15-20: The unauthorized SessionReturn currently sets headers: new
Headers() while its toResponse() uses the existing headers variable that
contains the expired cookie values, causing inconsistency; update the
unauthorized object so its headers property uses the same headers instance (the
headers variable used in toResponse) instead of new Headers(), or construct a
newHeaders variable for both success and unauthorized and reuse it so
unauthorized.headers and unauthorized.toResponse() are consistent (refer to the
unauthorized object, the headers variable, toResponse(), and the newHeaders
usage in the success path).

In `@packages/core/src/api/signIn.ts`:
- Around line 41-55: When redirect === false the toResponse closure references
headersList which is declared only in the redirect path, causing a
ReferenceError; update the non-redirect branch (the block that calls
createSignInURL and returns the object with toResponse) to define or reuse the
same headersList used in the redirect path before building the response (or
construct headers inline), ensuring the returned toResponse() uses a defined
headersList; modify the signIn flow around createSignInURL/toResponse so both
branches share a single headersList variable or factory and keep the
Response.json call consistent.

In `@packages/next/src/lib/api.ts`:
- Line 34: Remove the debug console.log that prints the full session payload in
the server path: locate the console.log("getSession - Retrieved session:",
session) in the getSession function inside packages/next/src/lib/api.ts and
delete it (or replace it with a non-sensitive log such as a single boolean or
masked userId) so session details are not written to server logs; if needed, use
a proper logger at debug level gated by NODE_ENV or logger.debug with redaction
instead of console.log.

---

Nitpick comments:
In `@packages/core/src/`@types/session.ts:
- Around line 238-265: The return types are inconsistent: AuthActionReturn uses
ok while SessionReturn and SignInReturn use success; pick one property name
(recommend changing AuthActionReturn to success) and update all related types
and any callers to use that single boolean key consistently. Specifically,
change AuthActionReturn's discriminant from ok:boolean to success:boolean (and
update any code referencing AuthActionReturn.ok), or alternatively rename
SessionReturn/SignInReturn to ok if you prefer that convention—ensure
AuthActionReturn, SessionReturn, SignInReturn (and all uses of those
types/functions) are updated together so the discriminant name is uniform across
the codebase.
- Around line 279-284: The SignInReturn interface's signInURL currently typed as
string doesn't reflect that toResponse() may return null for signInURL when
redirect is true; update the type to capture this nuance by making signInURL
conditional on the generic Redirect (e.g. change signInURL to a conditional type
such as Redirect extends true ? string : string | null or, if simpler, string |
null) and ensure the symbols mentioned (SignInReturn, signInURL, toResponse, and
the signIn.ts implementation) remain consistent with that updated typing so
callers and the JSON response types align.
🪄 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: a48a23c9-c31d-4eea-82dd-f80abcc0eb3d

📥 Commits

Reviewing files that changed from the base of the PR and between 23749ef and 5f026f5.

📒 Files selected for processing (16)
  • packages/core/src/@types/session.ts
  • packages/core/src/@types/utility.ts
  • packages/core/src/actions/session/session.ts
  • packages/core/src/actions/signIn/signIn.ts
  • packages/core/src/api/createApi.ts
  • packages/core/src/api/getSession.ts
  • packages/core/src/api/signIn.ts
  • packages/core/src/session/stateless.ts
  • packages/core/test/actions/session/session.test.ts
  • packages/core/test/api/getSession.test.ts
  • packages/core/test/api/signIn.test.ts
  • packages/core/test/session/stateless.test.ts
  • packages/core/test/types.test-d.ts
  • packages/elysia/src/lib/with-auth.ts
  • packages/next/src/lib/api.ts
  • packages/react-router/src/lib/api.ts

Comment thread packages/core/src/api/getSession.ts Outdated
Comment thread packages/core/src/api/signIn.ts
Comment thread packages/next/src/lib/api.ts Outdated
Copy link
Copy Markdown

@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: 6

Caution

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

⚠️ Outside diff range comments (2)
packages/core/test/actions/signIn/signIn.test.ts (1)

20-28: ⚠️ Potential issue | 🟠 Major

Fix test expectations to match the API contract: signInURL should contain the authorization URL, not null.

The API implementation at packages/core/src/api/signIn.ts:72-74 returns signInURL: authorization in the 302 response body. The type definition at packages/core/src/@types/session.ts:299-300 explicitly documents this: "When redirect is true, the JSON body still carries the authorization URL for consistency." The test expectations at both packages/core/test/actions/signIn/signIn.test.ts:26 and packages/core/test/api/signIn.test.ts:103 incorrectly expect signInURL: null. Either the tests need to be updated to expect the authorization string, or the implementation must be changed to match the test expectations—but the current state violates the documented API contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/test/actions/signIn/signIn.test.ts` around lines 20 - 28, The
tests are asserting signInURL is null but the API
(packages/core/src/api/signIn.ts) returns signInURL: authorization when
redirect=true; update the expectations in the tests
(packages/core/test/actions/signIn/signIn.test.ts and
packages/core/test/api/signIn.test.ts) to assert that response.json().signInURL
is the authorization URL string (non-null) instead of null, e.g., verify
signInURL is a non-empty string or equals the expected authorization value
returned by the signIn handler.
packages/core/src/client/client.ts (1)

79-161: ⚠️ Potential issue | 🟡 Minor

Casting response.json() to *APIReturn misrepresents the wire body.

The server's toResponse() serializes only the body fields ({ success, redirect?, signInURL | redirectURL | session }), not the full *APIReturn (which also contains headers: Headers and toResponse: () => AuthResponse<...>). Casting the client-side response.json() result to SignInAPIReturn / SignOutAPIReturn / SignInCredentialsAPIReturn / UpdateSessionAPIReturn implies these fields exist on the parsed JSON — they never will, and any code doing json.toResponse() or json.headers would fail at runtime.

Define "body" types in @/@types/session.ts (the generic parameters you already pass into AuthActionAPIReturn) and use those on the client:

🛠️ Suggested approach
// In session.ts
export type SignInAPIBody =
    | { success: true; redirect: true; signInURL: string }
    | { success: true; redirect: false; signInURL: string }

export type SignOutAPIBody =
    | { success: true; redirectURL: string }
    | { success: false; redirectURL: null }

// ...etc, then:
// client.ts
const json = (await response.json()) as SignInAPIBody

Applies to lines 79, 108, 138, and 157.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/client/client.ts` around lines 79 - 161, The client is
casting response.json() to the full *APIReturn types which include
headers/toResponse that the wire JSON never contains; instead define distinct
"body" types (e.g. SignInAPIBody, SignOutAPIBody, SignInCredentialsAPIBody,
UpdateSessionAPIBody) in `@/`@types/session.ts matching the serialized shape and
update the casts inside signIn, signOut, updateSession, and signInCredentials to
use those body types (and adjust any callers expecting full APIReturns
accordingly) so you only treat the actual JSON body fields returned by the
server.
🧹 Nitpick comments (6)
packages/core/src/shared/utils.ts (1)

121-128: toUnionHeaders will drop additional Set-Cookie values.

Headers#forEach combines multiple same-named headers into a single comma-joined string for most names, but Set-Cookie is special — iterating/getting it can collapse or split cookies depending on the runtime, and init.set(key, value) unconditionally replaces any prior value. If headers ever contains multiple Set-Cookie entries and init has none, only one merged value will be written, silently dropping cookies.

Current callers (getSession, updateSession) pass secureApiHeaders (security/cache headers), so today this is safe, but it's an easy footgun if reused. Consider either documenting the “no multi-valued headers” contract, or using init.append(key, value) for set-cookie specifically:

Suggested hardening
 export const toUnionHeaders = (init: Headers, headers: HeadersInit): Headers => {
     new Headers(headers).forEach((value, key) => {
         if (!init.has(key)) {
-            init.set(key, value)
+            if (key.toLowerCase() === "set-cookie") {
+                init.append(key, value)
+            } else {
+                init.set(key, value)
+            }
         }
     })
     return init
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/utils.ts` around lines 121 - 128, toUnionHeaders can
drop multiple Set-Cookie values because Headers.forEach may merge them and
init.set(...) replaces prior values; update the function so that when the header
name lowercased equals "set-cookie" you call init.append(key, value) (not
init.set) to preserve multiple cookies, while keeping the existing behavior for
other headers (only set if !init.has(key)). Modify the toUnionHeaders
implementation accordingly, referencing the toUnionHeaders function and the
Headers.forEach iteration.
packages/core/src/api/updateSession.ts (1)

13-20: Optional: drop the as cast by branching on session.

The as UpdateSessionAPIReturn<DefaultUser> suggests TypeScript can't prove the returned object satisfies the union. If UpdateSessionAPIReturn is a {success: true; session: NonNull} | {success: false; session: null} union, splitting the return resolves this safely and keeps the type inference honest.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/api/updateSession.ts` around lines 13 - 20, The return
currently uses a type cast because TypeScript can't prove the union shape;
instead branch on session and return the appropriate typed variant so no `as` is
needed: if `session` is truthy return an object matching the success-true arm
(success: true, session: the non-null session value, headers: newHeaders,
toResponse returning status 200), otherwise return the success-false arm
(success: false, session: null, headers: newHeaders, toResponse returning status
401). Update the code around the existing `session`, `newHeaders`, and
`toResponse` logic so each branch returns the correctly shaped
`UpdateSessionAPIReturn<DefaultUser>` without casting.
packages/core/src/@types/utility.ts (1)

42-44: Minor: DeepPartial also descends into arrays/functions.

T[P] extends object is true for arrays ([]) and functions, so DeepPartial will recurse into them and produce an array type with optional indices / a function-shape with optional own-keys. If this utility is only used for plain config objects it's fine, but a more conventional form excludes arrays/functions:

♻️ Safer shape
-export type DeepPartial<T> = {
-    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
-}
+export type DeepPartial<T> = T extends (...args: any[]) => any
+    ? T
+    : T extends Array<infer U>
+      ? Array<DeepPartial<U>>
+      : T extends object
+        ? { [P in keyof T]?: DeepPartial<T[P]> }
+        : T
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/utility.ts around lines 42 - 44, DeepPartial
currently recurses into arrays and functions because it uses "T[P] extends
object"; update the DeepPartial conditional so arrays and function types are
handled explicitly: when T[P] is an array, map to an array of DeepPartial of the
element type; when T[P] is a function, leave it as-is (do not recurse);
otherwise, recurse into plain objects. Locate the DeepPartial type and adjust
the conditional checks to first test for array (infer element type) and for
Function before applying the object recursion.
packages/core/src/client/client.ts (1)

146-149: signInCredentials is typed with OAuth SignInOptions.

SignInOptions is the OAuth client options type. While the shape (redirect?, redirectTo?) coincidentally works for credentials today, coupling credentials sign-in to an OAuth-named option type makes future divergence painful (e.g. if credentials grows a rememberMe or OAuth grows scope at the client layer). Consider introducing a dedicated SignInCredentialsOptions and referencing it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/client/client.ts` around lines 146 - 149, The
signInCredentials function is using the OAuth-specific SignInOptions type;
create a dedicated type (e.g., SignInCredentialsOptions) that models only the
options needed by credentials sign-in (e.g., redirect?, redirectTo?, rememberMe?
if applicable), replace SignInOptions with SignInCredentialsOptions in the
signInCredentials declaration and any internal/consumer references, and export
the new type where appropriate so future divergence between OAuth and
credentials options is decoupled (update related overloads/uses that currently
assume SignInOptions).
packages/react-router/src/@types/index.ts (1)

34-47: Optional: narrow the JSON variants to the redirect: false union member.

SignInAPIReturn is itself a union of redirect: true | false variants. When Options extends { redirect: false }, only the redirect: false variant can be produced at runtime, so consumers currently have to discriminate a redirect they already pinned. Narrowing via Extract yields better inference:

♻️ Suggested change
 export type ReactRouterSignInReturn<Options extends ReactRouterSignInAPIOptions> = Options extends {
     redirect: false
 }
-    ? SignInAPIReturn
+    ? Extract<SignInAPIReturn, { redirect: false }>
     : Response

 export type ReactRouterSignInCredentialsReturn<Options extends ReactRouterSignInCredentialsAPIOptions> = Options extends {
     redirect: false
 }
-    ? SignInCredentialsAPIReturn
+    ? SignInCredentialsAPIReturn // already discriminated by success; keep as-is
     : Response
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-router/src/`@types/index.ts around lines 34 - 47,
ReactRouterSignInReturn and ReactRouterSignInCredentialsReturn currently return
SignInAPIReturn / SignInCredentialsAPIReturn even when Options extends {
redirect: false }, which keeps the broader union; change their return types to
extract only the redirect:false variant (use Extract<SignInAPIReturn, {
redirect: false }> and Extract<SignInCredentialsAPIReturn, { redirect: false }>)
so consumers get the narrowed JSON shape when Options pins redirect: false.
packages/core/src/@types/session.ts (1)

286-343: Align client return shapes: SignInReturn/SignOutReturn should be discriminated like SignInCredentialsReturn.

SignInCredentialsReturn is already a proper discriminated union:

{ success: true; redirectURL: string } | { success: false; redirectURL: null }

but SignInReturn and SignOutReturn use success: boolean, which blocks narrowing. Consumers who write if (result.success) can't narrow signInURL/redirectURL to non-null in the success branch. The client also populates these differently on error (e.g. client.ts line 86 returns { success: false, ... signInURL: "/" }), which a discriminated type would have caught.

♻️ Suggested change
 export type SignInReturn<Redirect extends boolean = boolean> = Redirect extends true
     ? void
-    : { success: boolean; redirect: false; signInURL: string | null }
+    : { success: true; redirect: false; signInURL: string } | { success: false; redirect: false; signInURL: null }

 export type SignOutReturn<Redirect extends boolean = boolean> = Redirect extends true
     ? void
-    : { success: boolean; redirect: false; redirectURL: string }
+    : { success: true; redirect: false; redirectURL: string } | { success: false; redirect: false; redirectURL: null }

This also cleans up the odd error-path return of signInURL: "/" in client.ts (line 86) — the type forces it to be null.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/session.ts around lines 286 - 343, Update the
client return types SignInReturn and SignOutReturn to be discriminated unions
like SignInCredentialsReturn (i.e. { success: true; signInURL: string } | {
success: false; signInURL: null } for SignInReturn, and { success: true;
redirectURL: string } | { success: false; redirectURL: null } for SignOutReturn)
so consumers can narrow on success; then fix the client sign-in/sign-out
implementation (the client code that returns the failure shape) to return null
for signInURL/redirectURL on failure instead of string placeholders. Ensure you
change the generic Redirect handling consistently with the existing Redirect
extends true -> void behavior and update any call sites to match the new
discriminated shapes.
🤖 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/react-router/app/lib/auth.ts`:
- Line 12: Replace the hardcoded baseURL value with an environment-driven value:
read from process.env.BASE_URL (with a sensible localhost fallback for
development) and use that when building the auth client/origin instead of the
literal "http://localhost:5174"; update the object/property named baseURL in the
auth setup (the baseURL property in apps/react-router/app/lib/auth.ts) to source
from the env var so builds work across dev/staging/prod.

In `@apps/react-router/app/routes/index.tsx`:
- Around line 18-29: The action handler currently returns the raw results `out`
and `signIn` from `api.signOut` and `api.signIn`, which drops response
headers/cookies; instead call and return their `toResponse()` so the generated
Response (with Set-Cookie headers) is sent to the client. Locate the branches
using `action === "signOut"` and `action === "signIn"` and replace returning
`out` / `signIn` with `out.toResponse()` / `signIn.toResponse()` respectively.

In `@packages/core/src/api/createApi.ts`:
- Around line 31-36: The current call sites in createApi (the signOut and
updateSession wrappers) allow callers to override skipCSRFCheck because they
spread ...options after setting skipCSRFCheck: true; change the argument order
so the forced value cannot be overridden (e.g., pass { ctx, ...options,
skipCSRFCheck: true } for signOut and updateSession<DefaultUser>) to guarantee
skipCSRFCheck is always true, or alternatively remove skipCSRFCheck from the
public SignOutAPIOptions/UpdateSessionAPIOptions types if it should not be
caller-controlled.

In `@packages/core/src/api/credentials.ts`:
- Around line 50-55: The returned invalidCredentials object creates a headers
instance (invalidCredentials.headers) from new Headers(secureApiHeaders) but
toResponse() uses the shared secureApiHeaders, causing inconsistency; update
toResponse inside the SignInCredentialsAPIReturn construction so it reuses the
same Headers instance (invalidCredentials.headers) when calling Response.json
and setting the response headers, ensuring both the headers property and
toResponse() return the identical Headers object (reference the
invalidCredentials object, its headers property, the toResponse() function, and
the secureApiHeaders source to locate and change the code).

In `@packages/core/src/api/signOut.ts`:
- Around line 22-32: The current code always sets a Location header and includes
redirectURL even when the caller didn't pass redirectTo, but then returns status
202; change this so the Location header and redirectURL are only present when
redirectTo is truthy: compute redirectToURL via createRedirectTo as before, but
only call HeadersBuilder(...).setHeader("Location", redirectToURL) when
redirectTo is provided (leave headers unchanged otherwise), and in toResponse
return status 302 and include Location/redirectURL when redirectTo is truthy,
otherwise return a 200 (or 204) response without a Location header and without
redirectURL in the JSON body; update the references around headersList,
redirectToURL, redirectTo, and toResponse accordingly.

In `@packages/next/src/`@types/api.ts:
- Around line 24-26: The generic constraint on NextSignInCredentials is wrong:
it currently limits Options to SignInAPIOptions (OAuth) but the
signInCredentials helper supplies SignInCredentialsAPIOptions; update the type
parameter so NextSignInCredentials<Options extends SignInCredentialsAPIOptions>
uses SignInCredentialsAPIOptions instead of SignInAPIOptions to match usage in
signInCredentials (refer to NextSignInCredentials and signInCredentials symbols)
and ensure the conditional type remains unchanged.

---

Outside diff comments:
In `@packages/core/src/client/client.ts`:
- Around line 79-161: The client is casting response.json() to the full
*APIReturn types which include headers/toResponse that the wire JSON never
contains; instead define distinct "body" types (e.g. SignInAPIBody,
SignOutAPIBody, SignInCredentialsAPIBody, UpdateSessionAPIBody) in
`@/`@types/session.ts matching the serialized shape and update the casts inside
signIn, signOut, updateSession, and signInCredentials to use those body types
(and adjust any callers expecting full APIReturns accordingly) so you only treat
the actual JSON body fields returned by the server.

In `@packages/core/test/actions/signIn/signIn.test.ts`:
- Around line 20-28: The tests are asserting signInURL is null but the API
(packages/core/src/api/signIn.ts) returns signInURL: authorization when
redirect=true; update the expectations in the tests
(packages/core/test/actions/signIn/signIn.test.ts and
packages/core/test/api/signIn.test.ts) to assert that response.json().signInURL
is the authorization URL string (non-null) instead of null, e.g., verify
signInURL is a non-empty string or equals the expected authorization value
returned by the signIn handler.

---

Nitpick comments:
In `@packages/core/src/`@types/session.ts:
- Around line 286-343: Update the client return types SignInReturn and
SignOutReturn to be discriminated unions like SignInCredentialsReturn (i.e. {
success: true; signInURL: string } | { success: false; signInURL: null } for
SignInReturn, and { success: true; redirectURL: string } | { success: false;
redirectURL: null } for SignOutReturn) so consumers can narrow on success; then
fix the client sign-in/sign-out implementation (the client code that returns the
failure shape) to return null for signInURL/redirectURL on failure instead of
string placeholders. Ensure you change the generic Redirect handling
consistently with the existing Redirect extends true -> void behavior and update
any call sites to match the new discriminated shapes.

In `@packages/core/src/`@types/utility.ts:
- Around line 42-44: DeepPartial currently recurses into arrays and functions
because it uses "T[P] extends object"; update the DeepPartial conditional so
arrays and function types are handled explicitly: when T[P] is an array, map to
an array of DeepPartial of the element type; when T[P] is a function, leave it
as-is (do not recurse); otherwise, recurse into plain objects. Locate the
DeepPartial type and adjust the conditional checks to first test for array
(infer element type) and for Function before applying the object recursion.

In `@packages/core/src/api/updateSession.ts`:
- Around line 13-20: The return currently uses a type cast because TypeScript
can't prove the union shape; instead branch on session and return the
appropriate typed variant so no `as` is needed: if `session` is truthy return an
object matching the success-true arm (success: true, session: the non-null
session value, headers: newHeaders, toResponse returning status 200), otherwise
return the success-false arm (success: false, session: null, headers:
newHeaders, toResponse returning status 401). Update the code around the
existing `session`, `newHeaders`, and `toResponse` logic so each branch returns
the correctly shaped `UpdateSessionAPIReturn<DefaultUser>` without casting.

In `@packages/core/src/client/client.ts`:
- Around line 146-149: The signInCredentials function is using the
OAuth-specific SignInOptions type; create a dedicated type (e.g.,
SignInCredentialsOptions) that models only the options needed by credentials
sign-in (e.g., redirect?, redirectTo?, rememberMe? if applicable), replace
SignInOptions with SignInCredentialsOptions in the signInCredentials declaration
and any internal/consumer references, and export the new type where appropriate
so future divergence between OAuth and credentials options is decoupled (update
related overloads/uses that currently assume SignInOptions).

In `@packages/core/src/shared/utils.ts`:
- Around line 121-128: toUnionHeaders can drop multiple Set-Cookie values
because Headers.forEach may merge them and init.set(...) replaces prior values;
update the function so that when the header name lowercased equals "set-cookie"
you call init.append(key, value) (not init.set) to preserve multiple cookies,
while keeping the existing behavior for other headers (only set if
!init.has(key)). Modify the toUnionHeaders implementation accordingly,
referencing the toUnionHeaders function and the Headers.forEach iteration.

In `@packages/react-router/src/`@types/index.ts:
- Around line 34-47: ReactRouterSignInReturn and
ReactRouterSignInCredentialsReturn currently return SignInAPIReturn /
SignInCredentialsAPIReturn even when Options extends { redirect: false }, which
keeps the broader union; change their return types to extract only the
redirect:false variant (use Extract<SignInAPIReturn, { redirect: false }> and
Extract<SignInCredentialsAPIReturn, { redirect: false }>) so consumers get the
narrowed JSON shape when Options pins redirect: false.
🪄 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: 8a61f252-6da6-44b9-810f-f0fe279394f3

📥 Commits

Reviewing files that changed from the base of the PR and between 5f026f5 and 129de52.

📒 Files selected for processing (38)
  • apps/react-router/app/lib/auth.ts
  • apps/react-router/app/routes/index.tsx
  • packages/core/src/@types/config.ts
  • packages/core/src/@types/errors.ts
  • packages/core/src/@types/index.ts
  • packages/core/src/@types/oauth.ts
  • packages/core/src/@types/router.d.ts
  • packages/core/src/@types/session.ts
  • packages/core/src/@types/utility.ts
  • packages/core/src/actions/signIn/signIn.ts
  • packages/core/src/actions/signInCredentials/signInCredentials.ts
  • packages/core/src/actions/signOut/signOut.ts
  • packages/core/src/actions/updateSession/updateSession.ts
  • packages/core/src/api/createApi.ts
  • packages/core/src/api/credentials.ts
  • packages/core/src/api/getSession.ts
  • packages/core/src/api/signIn.ts
  • packages/core/src/api/signOut.ts
  • packages/core/src/api/updateSession.ts
  • packages/core/src/client/client.ts
  • packages/core/src/oauth/index.ts
  • packages/core/src/shared/utils.ts
  • packages/core/test/actions/signIn/signIn.test.ts
  • packages/core/test/actions/signOut/signOut.test.ts
  • packages/core/test/actions/updateSession/updateSession.test.ts
  • packages/core/test/api/updateSession.test.ts
  • packages/core/test/types.test-d.ts
  • packages/core/vitest.config.ts
  • packages/next/src/@types/api.ts
  • packages/next/src/@types/core.ts
  • packages/next/src/@types/index.ts
  • packages/next/src/lib/api.ts
  • packages/react-router/src/@types/core.ts
  • packages/react-router/src/@types/index.ts
  • packages/react-router/src/lib/api.ts
  • packages/react/src/@types/core.ts
  • packages/react/src/@types/index.ts
  • packages/react/src/@types/types.ts
✅ Files skipped from review due to trivial changes (12)
  • packages/react/src/@types/index.ts
  • packages/core/src/@types/errors.ts
  • packages/core/src/@types/router.d.ts
  • packages/react-router/src/@types/core.ts
  • packages/next/src/@types/core.ts
  • packages/react/src/@types/core.ts
  • packages/core/src/@types/index.ts
  • packages/next/src/@types/index.ts
  • packages/core/src/oauth/index.ts
  • packages/react/src/@types/types.ts
  • packages/core/test/types.test-d.ts
  • packages/core/src/@types/config.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/api/getSession.ts
  • packages/next/src/lib/api.ts
  • packages/react-router/src/lib/api.ts

Comment thread apps/react-router/app/lib/auth.ts
Comment thread apps/react-router/app/routes/index.tsx
Comment thread packages/core/src/api/createApi.ts
Comment thread packages/core/src/api/credentials.ts
Comment thread packages/core/src/api/signOut.ts Outdated
Comment thread packages/next/src/@types/api.ts
@halvaradop halvaradop merged commit e69e82b into master Apr 17, 2026
7 checks passed
@halvaradop halvaradop deleted the feat/standarize-return-types branch April 17, 2026 00:05
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