44
55import { ResponseMatcher , HTTPClient , matchStatusCode } from "./http.js" ;
66import { SecurityState , resolveSecurity , resolveGlobalSecurity } from "./security.js" ;
7+ import { retry , RetryConfig } from "./retries.js" ;
78import { pathToFunc } from "./url.js" ;
89import { encodeForm } from "./encodings.js" ;
910import { stringToBase64 } from "./base64.js" ;
@@ -12,6 +13,24 @@ import { SDKHooks } from "../hooks/hooks.js";
1213import { HookContext } from "../hooks/types.js" ;
1314
1415export type RequestOptions = {
16+ /**
17+ * Sets a timeout, in milliseconds, on HTTP requests made by an SDK method. If
18+ * `fetchOptions.signal` is set then it will take precedence over this option.
19+ */
20+ timeoutMs ?: number ;
21+ /**
22+ * Set or override a retry policy on HTTP calls.
23+ */
24+ retries ?: RetryConfig ;
25+ /**
26+ * Specifies the status codes which should be retried using the given retry policy.
27+ */
28+ retryCodes ?: string [ ] ;
29+ /**
30+ * Sets various request options on the `fetch` call made by an SDK method.
31+ *
32+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options|Request }
33+ */
1534 fetchOptions ?: Omit < RequestInit , "method" | "body" > ;
1635} ;
1736
@@ -24,6 +43,7 @@ type RequestConfig = {
2443 headers ?: HeadersInit ;
2544 security ?: SecurityState | null ;
2645 uaHeader ?: string ;
46+ timeoutMs ?: number ;
2747} ;
2848
2949const gt : unknown = typeof globalThis === "undefined" ? null : globalThis ;
@@ -119,10 +139,20 @@ export class ClientSDK {
119139 headers . set ( conf . uaHeader ?? "user-agent" , SDK_METADATA . userAgent ) ;
120140 }
121141
142+ let fetchOptions = options ?. fetchOptions ;
143+ if ( ! fetchOptions ?. signal && conf . timeoutMs && conf . timeoutMs > 0 ) {
144+ const timeoutSignal = AbortSignal . timeout ( conf . timeoutMs ) ;
145+ if ( ! fetchOptions ) {
146+ fetchOptions = { signal : timeoutSignal } ;
147+ } else {
148+ fetchOptions . signal = timeoutSignal ;
149+ }
150+ }
151+
122152 const input = this . hooks$ . beforeCreateRequest ( context , {
123153 url : reqURL ,
124154 options : {
125- ...options ?. fetchOptions ,
155+ ...fetchOptions ,
126156 body : conf . body ?? null ,
127157 headers,
128158 method,
@@ -133,27 +163,40 @@ export class ClientSDK {
133163 }
134164
135165 protected async do$ (
136- req : Request ,
166+ request : Request ,
137167 options : {
138168 context : HookContext ;
139169 errorCodes : number | string | ( number | string ) [ ] ;
170+ retryConfig ?: RetryConfig | undefined ;
171+ retryCodes ?: string [ ] | undefined ;
140172 }
141173 ) : Promise < Response > {
142174 const { context, errorCodes } = options ;
143-
144- let response = await this . client . request ( await this . hooks$ . beforeRequest ( context , req ) ) ;
145-
146- if ( matchStatusCode ( response , errorCodes ) ) {
147- const result = await this . hooks$ . afterError ( context , response , null ) ;
148- if ( result . error ) {
149- throw result . error ;
150- }
151- response = result . response || response ;
152- } else {
153- response = await this . hooks$ . afterSuccess ( context , response ) ;
154- }
155-
156- return response ;
175+ const retryConfig = options . retryConfig || { strategy : "none" } ;
176+ const retryCodes = options . retryCodes || [ ] ;
177+
178+ return retry (
179+ async ( ) => {
180+ const req = request . clone ( ) ;
181+
182+ let response = await this . client . request (
183+ await this . hooks$ . beforeRequest ( context , req )
184+ ) ;
185+
186+ if ( matchStatusCode ( response , errorCodes ) ) {
187+ const result = await this . hooks$ . afterError ( context , response , null ) ;
188+ if ( result . error ) {
189+ throw result . error ;
190+ }
191+ response = result . response || response ;
192+ } else {
193+ response = await this . hooks$ . afterSuccess ( context , response ) ;
194+ }
195+
196+ return response ;
197+ } ,
198+ { config : retryConfig , statusCodes : retryCodes }
199+ ) ;
157200 }
158201
159202 protected matcher < Result > ( ) : ResponseMatcher < Result > {
0 commit comments