@@ -2,49 +2,72 @@ import type { createApiClient } from "./generated";
22
33export const buildApiFetcher : ( config : {
44 apiToken : string ;
5+ onTokenRefresh ?: ( ) => Promise < string > ;
56} ) => Parameters < typeof createApiClient > [ 0 ] = ( config ) => {
6- return {
7- fetch : async ( input ) => {
8- const headers = new Headers ( ) ;
9- headers . set ( "Authorization" , `Bearer ${ config . apiToken } ` ) ;
7+ const makeRequest = async (
8+ input : Parameters < Parameters < typeof createApiClient > [ 0 ] [ "fetch" ] > [ 0 ] ,
9+ token : string ,
10+ ) : Promise < Response > => {
11+ const headers = new Headers ( ) ;
12+ headers . set ( "Authorization" , `Bearer ${ token } ` ) ;
1013
11- if ( input . urlSearchParams ) {
12- input . url . search = input . urlSearchParams . toString ( ) ;
13- }
14+ if ( input . urlSearchParams ) {
15+ input . url . search = input . urlSearchParams . toString ( ) ;
16+ }
1417
15- const body = [ "post" , "put" , "patch" , "delete" ] . includes (
16- input . method . toLowerCase ( ) ,
17- )
18- ? JSON . stringify ( input . parameters ?. body )
19- : undefined ;
18+ const body = [ "post" , "put" , "patch" , "delete" ] . includes (
19+ input . method . toLowerCase ( ) ,
20+ )
21+ ? JSON . stringify ( input . parameters ?. body )
22+ : undefined ;
2023
21- if ( body ) {
22- headers . set ( "Content-Type" , "application/json" ) ;
23- }
24+ if ( body ) {
25+ headers . set ( "Content-Type" , "application/json" ) ;
26+ }
2427
25- if ( input . parameters ?. header ) {
26- for ( const [ key , value ] of Object . entries ( input . parameters . header ) ) {
27- if ( value != null ) {
28- headers . set ( key , String ( value ) ) ;
29- }
28+ if ( input . parameters ?. header ) {
29+ for ( const [ key , value ] of Object . entries ( input . parameters . header ) ) {
30+ if ( value != null ) {
31+ headers . set ( key , String ( value ) ) ;
3032 }
3133 }
34+ }
3235
33- let response : Response ;
34- try {
35- response = await fetch ( input . url , {
36- method : input . method . toUpperCase ( ) ,
37- ...( body && { body } ) ,
38- headers,
39- ...input . overrides ,
40- } ) ;
41- } catch ( err ) {
42- throw new Error (
43- `Network request failed for ${ input . method . toUpperCase ( ) } ${ input . url } : ${
44- err instanceof Error ? err . message : String ( err )
45- } `,
46- { cause : err instanceof Error ? err : undefined } ,
47- ) ;
36+ try {
37+ const response = await fetch ( input . url , {
38+ method : input . method . toUpperCase ( ) ,
39+ ...( body && { body } ) ,
40+ headers,
41+ ...input . overrides ,
42+ } ) ;
43+
44+ return response ;
45+ } catch ( err ) {
46+ throw new Error (
47+ `Network request failed for ${ input . method . toUpperCase ( ) } ${ input . url } : ${
48+ err instanceof Error ? err . message : String ( err )
49+ } `,
50+ { cause : err instanceof Error ? err : undefined } ,
51+ ) ;
52+ }
53+ } ;
54+
55+ return {
56+ fetch : async ( input ) => {
57+ let response = await makeRequest ( input , config . apiToken ) ;
58+
59+ // Handle 401 with automatic token refresh
60+ if ( ! response . ok && response . status === 401 && config . onTokenRefresh ) {
61+ try {
62+ const newToken = await config . onTokenRefresh ( ) ;
63+ response = await makeRequest ( input , newToken ) ;
64+ } catch {
65+ // Token refresh failed - throw the original 401 error
66+ const errorResponse = await response . json ( ) ;
67+ throw new Error (
68+ `Failed request: [${ response . status } ] ${ JSON . stringify ( errorResponse ) } ` ,
69+ ) ;
70+ }
4871 }
4972
5073 if ( ! response . ok ) {
0 commit comments