1- import type { generateRequestInfo } from './request-transform' ;
2- import type { CommonResponse } from './interceptor' ;
3- import type { ClientOptions , Params , FetchPolicy , RequestConfig } from './types' ;
1+ import type { FetchPolicy } from './types' ;
42import type { HydrationStatus } from '../store' ;
53import { ReplaySubject } from 'rxjs' ;
64import { MODE } from '../const' ;
75
86import { createCache , hash } from '../cache' ;
97
10- import { deepMerge } from '../utils' ;
118import { createInterceptor } from './interceptor' ;
9+ import { type CommonExtraParams } from '..' ;
1210
13-
14- const DEFAULT_OPTIONS : ClientOptions = {
15- method : 'GET' ,
16- headers : {
17- 'content-type' : 'application/json' ,
18- } ,
19- timeout : 60 * 1000 ,
20- credentials : 'include' ,
21- } ;
22-
23- // TODO后续再优化下逻辑写法,比如对于method的定义,需要定义好client与options的边界,拆分通用merge和转换成requestInit的部分……
24- function mergeClientOptionsAndParams ( options : ClientOptions , params : Params ) : RequestConfig {
25- const {
26- timeout,
27- headers,
28- method,
29- credentials,
30- baseUrl,
31- } = options ;
32- const url = baseUrl
33- ? new URL ( params . url || '/' , baseUrl ) . href
34- : params . url || '' ;
35-
36- const commonConfig = {
37- timeout : params . timeout || timeout ,
38- headers : deepMerge ( { } , headers , params . headers ) ,
39- credentials,
40- url : url || '' ,
41- variables : params . variables ,
42- } ;
43-
44- if ( 'method' in params ) {
45- return {
46- ...commonConfig ,
47- method : params . method || method ,
48- fetchPolicy : params . fetchPolicy ,
49- } ;
50- }
51-
52- if ( 'query' in params ) {
53- return {
54- ...commonConfig ,
55- query : params . query ,
56- fetchPolicy : params . fetchPolicy ,
57- } ;
58- }
59-
60- if ( 'mutation' in params ) {
61- return {
62- ...commonConfig ,
63- mutation : params . mutation ,
64- }
65- }
66-
67- return commonConfig ;
68- }
69-
70- class TimeoutError extends Error {
71- constructor ( msg : string ) {
72- super ( msg )
73- this . message = msg ;
74- }
11+ export type CustomClient < Params , Response , Variables extends Record < string , any > , ExtraParams extends CommonExtraParams > = {
12+ transformRequestParams ( variables : Variables , extraParams : ExtraParams , mode : typeof MODE ) : Params
13+ hashVariables ?( variables : Variables , hash : ( v : any ) => string ) : string
14+ request ( params : Params , mode : typeof MODE ) : Promise < Response >
7515}
7616
77- export function clientFactory (
78- type : 'GQL' | 'REST' ,
79- createRequestInfo : typeof generateRequestInfo ,
80- options ?: ClientOptions ,
81- ) {
82- const opts = options
83- ? deepMerge ( { } as ClientOptions , DEFAULT_OPTIONS , options )
84- : deepMerge ( { } as ClientOptions , DEFAULT_OPTIONS ) ;
17+ export function createCustomClient < Params , Response , Variables extends Record < string , any > , ExtraParams extends CommonExtraParams > ( type : string , client : CustomClient < Params , Response , Variables , ExtraParams > , options : {
18+ cache ?: ReturnType < typeof createCache >
19+ } ) {
8520 const requestInterceptor = createInterceptor < Params > ( 'request' ) ;
86- const responseInterceptor = createInterceptor < CommonResponse > ( 'response' ) ;
21+ const responseInterceptor = createInterceptor < Response > ( 'response' ) ;
8722
8823 const interceptors = {
8924 request : {
@@ -94,164 +29,70 @@ export function clientFactory(
9429 } ,
9530 } ;
9631
97- function request < T > ( params : Params ) : Promise < T > {
32+ function request < T > ( variables : Variables , extraParams : ExtraParams ) : Promise < T > {
9833 // 处理前置拦截器
9934
10035 const list = [ ...requestInterceptor . list ] ;
10136
102- const config = mergeClientOptionsAndParams ( opts , params ) ;
37+ const params = client . transformRequestParams ( variables , extraParams , MODE )
10338
10439 // 后面再做benchmark看看一个tick会差出来多少性能
105- let promise = Promise . resolve ( config ) ;
40+ let promise = Promise . resolve ( params ) ;
10641
10742 while ( list . length ) {
10843 const item = list . shift ( ) ;
10944 promise = promise . then ( item ?. onResolve , item ?. onReject ) ;
11045 }
11146
112- let request : ReturnType < typeof createRequestInfo > ;
113-
11447 return promise . then ( params => {
115- // TODO这里的
116- request = createRequestInfo ( type , params ) ;
117-
118- if ( ! opts . fetch ) {
119- // 避免Node环境下的判断,所以没法简化写=。=,因为window.fetch会触发一次RHS导致报错
120- if ( MODE === 'SPA' ) {
121- // 小程序因为没有window,所以需要这里绕一下
122- if ( typeof window !== 'undefined' && window . fetch ) {
123- opts . fetch = ( resource , options ) => window . fetch ( resource , options ) ;
124- } else {
125- throw new Error ( 'There is no useful "fetch" function' ) ;
126- }
127- } else if ( MODE === 'SSR' ) {
128- if ( globalThis . fetch ) {
129- opts . fetch = ( resource , options ) => globalThis . fetch ( resource , options ) ;
130- } else {
131- throw new Error ( 'There is no useful "fetch" function' ) ;
132- }
133- }
134- }
135-
136- const {
137- url,
138- requestInit,
139- } = request ;
140- const fetchPromise = opts . fetch ! ( url , requestInit ) ;
141-
142- const timeoutPromise = new Promise < DOMException | TimeoutError > ( ( resolve ) => {
143- setTimeout (
144- ( ) => {
145- if ( MODE === 'SSR' ) {
146- resolve ( new TimeoutError ( 'The request has been timeout' ) )
147- } else {
148- resolve ( new DOMException ( 'The request has been timeout' ) )
149- }
150- } ,
151- config . timeout ,
152- ) ;
153- } ) ;
154- return Promise . race ( [ timeoutPromise , fetchPromise ] ) ;
155- } ) . then ( ( res ) => {
156- // 浏览器断网情况下有可能会是null
157- if ( res === null ) {
158- res = MODE === 'SSR'
159- ? new TimeoutError ( 'The request has been timeout' )
160- : new DOMException ( 'The request has been timeout' ) ;
161- }
162-
48+ let promise = client . request ! ( params , MODE ) ;
16349 const list = [ ...responseInterceptor . list ] ;
164-
165- // 用duck type绕过类型判断
166- if ( ! ( 'status' in res ) ) {
167- let promise : Promise < any > = Promise . reject ( {
168- res,
169- request,
170- } ) ;
171- while ( list . length ) {
172- const transform = list . shift ( ) ;
173- promise = promise . then ( transform ?. onResolve , transform ?. onReject ) ;
174- }
175- return promise ;
176- }
177-
178- const receiveType = res . headers . get ( 'Content-Type' )
179- || ( request . requestInit . headers as Record < string , string > ) ?. [ 'Content-Type' ]
180- || ( request . requestInit . headers as Record < string , string > ) ?. [ 'content-type' ]
181- || 'application/json' ;
182-
183- const commonInfo : CommonResponse = {
184- status : res . status ,
185- statusText : res . statusText ,
186- headers : res . headers ,
187- config : request ,
188- data : undefined ,
189- } ;
190-
191- let promise ;
192- if ( receiveType . indexOf ( 'application/json' ) !== - 1 ) {
193- promise = res . ok
194- ? res . json ( ) . then ( data => {
195- commonInfo . data = data ;
196- return commonInfo ;
197- } )
198- : Promise . reject ( {
199- res,
200- request,
201- } )
202- } else {
203- commonInfo . data = res . body ;
204- // 其它类型就把body先扔回去……也许以后有用……
205- promise = res . ok ? Promise . resolve ( commonInfo ) : Promise . reject ( {
206- res,
207- request,
208- } ) ;
209- }
21050 while ( list . length ) {
21151 const transform = list . shift ( ) ;
21252 promise = promise . then ( transform ?. onResolve , transform ?. onReject ) ;
21353 }
214-
215- return promise ;
216- } ) ;
54+ return promise as any ;
55+ } )
21756 }
21857
21958 const cache = options ?. cache || createCache ( ) ;
22059
221- function getDataFromCache < T > ( params : Parameters < typeof request > [ 0 ] ) {
222- const data = cache . get < T > ( `${ hash ( params . url ) } -${ hash ( params . variables || { } ) } ` ) ;
60+ function getDataFromCache < T > ( variables : Parameters < typeof request > [ 0 ] ) {
61+ const key = ( client . hashVariables ?? hash ) ( variables , hash )
62+ const data = cache . get < T > ( key ) ;
22363 return data ;
22464 }
22565
226- function setDataToCache < T > ( params : Parameters < typeof request > [ 0 ] , data : T ) {
227- const key = ` ${ hash ( params . url ) } - ${ hash ( params . variables || { } ) } ` ;
66+ function setDataToCache < T > ( variables : Parameters < typeof request > [ 0 ] , data : T ) {
67+ const key = ( client . hashVariables ?? hash ) ( variables , hash )
22868 cache . put ( key , data ) ;
22969 }
23070
23171 function requestWithCache < T > (
232- params : Parameters < typeof request > [ 0 ] ,
72+ variables : Parameters < typeof request > [ 0 ] ,
73+ extraParams : Parameters < typeof request > [ 1 ] ,
23374 fetchPolicy : FetchPolicy = 'network-first' ,
23475 hydrationStatus : HydrationStatus ,
23576 ) : ReplaySubject < T > {
23677 const subject = new ReplaySubject < T > ( ) ;
23778 // 处于Hydration阶段,一律先从缓存里面拿
23879 if ( hydrationStatus . value !== 2 ) {
239- const data = getDataFromCache < T > ( params ) ;
80+ const data = getDataFromCache < T > ( variables ) ;
24081 if ( data ) {
24182 subject . next ( data ) ;
24283 subject . complete ( ) ;
24384 return subject ;
24485 }
24586 }
246- const data = getDataFromCache < T > ( params ) ;
87+ const data = getDataFromCache < T > ( variables ) ;
24788 switch ( fetchPolicy ) {
24889 case 'cache-and-network' :
24990 if ( data ) {
25091 subject . next ( data ) ;
25192 }
252- request < T > ( params ) . then ( data => {
93+ request < T > ( variables , extraParams ) . then ( data => {
25394 // TODO还差分发network status出去
254- setDataToCache ( params , data ) ;
95+ setDataToCache ( variables , data ) ;
25596 subject . next ( data ) ;
25697 subject . complete ( ) ;
25798 } ) . catch ( e => {
@@ -263,17 +104,17 @@ export function clientFactory(
263104 if ( data ) {
264105 subject . next ( data ) ;
265106 } else {
266- request < T > ( params ) . then ( data => {
107+ request < T > ( variables , extraParams ) . then ( data => {
267108 // TODO还差分发network status出去
268- setDataToCache ( params , data ) ;
109+ setDataToCache ( variables , data ) ;
269110 subject . next ( data ) ;
270111 subject . complete ( ) ;
271112 } ) . catch ( e => subject . error ( e ) ) ;
272113 }
273114 break ;
274115 case 'network-first' :
275- request < T > ( params ) . then ( data => {
276- setDataToCache ( params , data ) ;
116+ request < T > ( variables , extraParams ) . then ( data => {
117+ setDataToCache ( variables , data ) ;
277118 subject . next ( data ) ;
278119 subject . complete ( ) ;
279120 } ) . catch ( e => {
@@ -291,7 +132,7 @@ export function clientFactory(
291132 }
292133 break ;
293134 case 'network-only' :
294- request < T > ( params )
135+ request < T > ( variables , extraParams )
295136 . then ( data => {
296137 subject . next ( data ) ;
297138 subject . complete ( ) ;
@@ -306,11 +147,11 @@ export function clientFactory(
306147
307148 return subject ;
308149 }
309-
310150 return {
311151 interceptors,
312152 query : requestWithCache ,
313153 mutate : request ,
314154 type
315- } ;
155+ }
156+
316157}
0 commit comments