11import type { AuthObject , RequestState } from '@clerk/backend' ;
22import { constants , debugRequestState , loadInterstitialFromLocal } from '@clerk/backend' ;
3- import type { AppLoadContext } from '@remix-run/server-runtime' ;
3+ import type { AppLoadContext , defer } from '@remix-run/server-runtime' ;
44import { json } from '@remix-run/server-runtime' ;
55import cookie from 'cookie' ;
66
@@ -108,9 +108,51 @@ export const injectRequestStateIntoResponse = async (
108108 context : AppLoadContext ,
109109) => {
110110 // eslint-disable-next-line @typescript-eslint/no-unused-vars
111- const { reason, message, isSignedIn, isInterstitial, ...rest } = requestState ;
112111 const clone = response . clone ( ) ;
113112 const data = await clone . json ( ) ;
113+
114+ const { clerkState, headers } = getResponseClerkState ( requestState , context ) ;
115+
116+ // set the correct content-type header in case the user returned a `Response` directly
117+ // without setting the header, instead of using the `json()` helper
118+ clone . headers . set ( constants . Headers . ContentType , constants . ContentTypes . Json ) ;
119+ headers . forEach ( ( value , key ) => {
120+ clone . headers . set ( key , value ) ;
121+ } ) ;
122+
123+ return json ( { ...( data || { } ) , ...clerkState } , clone ) ;
124+ } ;
125+
126+ export function injectRequestStateIntoDeferredData (
127+ data : ReturnType < typeof defer > ,
128+ requestState : RequestState ,
129+ context : AppLoadContext ,
130+ ) {
131+ const { clerkState, headers } = getResponseClerkState ( requestState , context ) ;
132+
133+ // Avoid creating a new object here to retain referential equality.
134+ data . data . clerkState = clerkState . clerkState ;
135+
136+ if ( typeof data . init !== 'undefined' ) {
137+ data . init . headers = new Headers ( data . init . headers ) ;
138+
139+ headers . forEach ( ( value , key ) => {
140+ // @ts -expect-error -- We are ensuring headers is defined above
141+ data . init . headers . set ( key , value ) ;
142+ } ) ;
143+ }
144+
145+ return data ;
146+ }
147+
148+ /**
149+ * Returns the clerk state object and observability headers to be injected into a loader response.
150+ *
151+ * @internal
152+ */
153+ export function getResponseClerkState ( requestState : RequestState , context : AppLoadContext ) {
154+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
155+ const { reason, message, isSignedIn, isInterstitial, ...rest } = requestState ;
114156 const clerkState = wrapWithClerkState ( {
115157 __clerk_ssr_state : rest . toAuth ( ) ,
116158 __frontendApi : requestState . frontendApi ,
@@ -126,15 +168,14 @@ export const injectRequestStateIntoResponse = async (
126168 __clerkJSUrl : getEnvVariable ( 'CLERK_JS' , context ) ,
127169 __clerkJSVersion : getEnvVariable ( 'CLERK_JS_VERSION' , context ) ,
128170 } ) ;
129- // set the correct content-type header in case the user returned a `Response` directly
130- // without setting the header, instead of using the `json()` helper
131- clone . headers . set ( constants . Headers . ContentType , constants . ContentTypes . Json ) ;
132- observabilityHeadersFromRequestState ( requestState ) . forEach ( ( value , key ) => {
133- clone . headers . set ( key , value ) ;
134- } ) ;
135171
136- return json ( { ...( data || { } ) , ...clerkState } , clone ) ;
137- } ;
172+ const headers = observabilityHeadersFromRequestState ( requestState ) ;
173+
174+ return {
175+ clerkState,
176+ headers,
177+ } ;
178+ }
138179
139180/**
140181 * Wraps obscured clerk internals with a readable `clerkState` key.
0 commit comments