@ahoo-wang/fetcher 2.3.4 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/urls.ts","../src/urlTemplateResolver.ts","../src/urlBuilder.ts","../src/fetcherError.ts","../src/timeout.ts","../src/urlResolveInterceptor.ts","../src/fetchRequest.ts","../src/requestBodyInterceptor.ts","../src/fetchInterceptor.ts","../src/orderedCapable.ts","../src/interceptor.ts","../src/validateStatusInterceptor.ts","../src/interceptorManager.ts","../src/resultExtractor.ts","../src/utils.ts","../src/fetchExchange.ts","../src/mergeRequest.ts","../src/fetcher.ts","../src/fetcherRegistrar.ts","../src/fetcherCapable.ts","../src/namedFetcher.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the given URL is an absolute URL\n *\n * @param url - URL string to check\n * @returns boolean - Returns true if it's an absolute URL, false otherwise\n *\n * @example\n * ```typescript\n * isAbsoluteURL('https://api.example.com/users'); // true\n * isAbsoluteURL('/users'); // false\n * isAbsoluteURL('users'); // false\n * ```\n */\nexport function isAbsoluteURL(url: string) {\n return /^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url);\n}\n\n/**\n * Combines a base URL and a relative URL into a complete URL\n *\n * @param baseURL - Base URL\n * @param relativeURL - Relative URL\n * @returns string - Combined complete URL\n *\n * @remarks\n * If the relative URL is already an absolute URL, it will be returned as-is.\n * Otherwise, the base URL and relative URL will be combined with proper path separator handling.\n *\n * @example\n * ```typescript\n * combineURLs('https://api.example.com', '/users'); // https://api.example.com/users\n * combineURLs('https://api.example.com/', 'users'); // https://api.example.com/users\n * combineURLs('https://api.example.com', 'https://other.com/users'); // https://other.com/users\n * ```\n */\nexport function combineURLs(baseURL: string, relativeURL: string) {\n if (isAbsoluteURL(relativeURL)) {\n return relativeURL;\n }\n // If relative URL exists, combine base URL and relative URL, otherwise return base URL\n return relativeURL\n ? baseURL.replace(/\\/?\\/$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Enumeration of URL template path styles.\n *\n * This enum defines the supported URL template path styles for parameter resolution.\n * Each style has its own pattern for defining path parameters in URL templates.\n */\nexport enum UrlTemplateStyle {\n /**\n * URI Template style following RFC 6570 specification.\n * Uses curly braces to define parameters, e.g., /users/{id}/posts/{postId}\n *\n * @see https://www.rfc-editor.org/rfc/rfc6570.html\n *\n * @see {@link UriTemplateResolver}\n */\n UriTemplate,\n\n /**\n * Express.js style for defining route parameters.\n * Uses colons to define parameters, e.g., /users/:id/posts/:postId\n *\n * @see {@link ExpressUrlTemplateResolver}\n */\n Express,\n}\n\n/**\n * Gets the appropriate URL template resolver based on the specified style.\n *\n * This factory function returns a URL template resolver instance based on the requested style.\n * If no style is specified or if the UriTemplate style is requested, it returns the default\n * URI template resolver. If Express style is requested, it returns the Express-style resolver.\n *\n * @param style - The URL template path style to use (optional).\n * If not provided, defaults to UriTemplate style.\n * @returns A UrlTemplateResolver instance corresponding to the requested style\n *\n * @example\n * ```typescript\n * // Get default URI template resolver\n * const resolver = getUrlTemplateResolver();\n *\n * // Get URI template resolver explicitly\n * const uriResolver = getUrlTemplateResolver(UrlTemplatePathStyle.UriTemplate);\n *\n * // Get Express-style resolver\n * const expressResolver = getUrlTemplateResolver(UrlTemplatePathStyle.Express);\n * ```\n */\nexport function getUrlTemplateResolver(\n style?: UrlTemplateStyle,\n): UrlTemplateResolver {\n if (style === UrlTemplateStyle.Express) {\n return expressUrlTemplateResolver;\n }\n return uriTemplateResolver;\n}\n\n/**\n * Interface for resolving URL templates by extracting path parameters and replacing placeholders.\n *\n * This interface provides methods to work with URL templates that contain parameter placeholders.\n * It supports extracting parameter names from templates and replacing placeholders with actual values.\n *\n * @example\n * ```typescript\n * // Example usage of UrlTemplateResolver\n * const resolver: UrlTemplateResolver = new UriTemplateResolver();\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport interface UrlTemplateResolver {\n /**\n * Extracts path parameters from the URL.\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n *\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[];\n\n /**\n * Replaces placeholders in the URL with path parameters.\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns The URL with placeholders replaced by actual values\n *\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // With Express-style resolver\n * const expressResolver: UrlTemplateResolver = new ExpressUrlTemplateResolver();\n * const expressUrl = expressResolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // expressUrl = '/users/123/posts/456'\n * ```\n *\n * @throws Error when required path parameters are missing\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * try {\n * resolver.resolve('/users/{id}', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(urlTemplate: string, pathParams?: Record<string, any> | null): string;\n}\n\n/**\n * Replaces placeholders in a URL template with actual parameter values.\n *\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParamRegex - Regular expression to match parameter placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns The URL with placeholders replaced by actual values\n * @throws Error when required path parameters are missing\n */\nexport function urlTemplateRegexResolve(\n urlTemplate: string,\n pathParamRegex: RegExp,\n pathParams?: Record<string, any> | null,\n) {\n if (!pathParams) return urlTemplate;\n return urlTemplate.replace(pathParamRegex, (_, key) => {\n const value = pathParams[key];\n // If path parameter is undefined, throw an error instead of preserving the placeholder\n if (value === undefined) {\n throw new Error(`Missing required path parameter: ${key}`);\n }\n return encodeURIComponent(value);\n });\n}\n\n/**\n * Extracts parameter names from a URL template using a regular expression.\n *\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParamRegex - Regular expression to match parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n */\nexport function urlTemplateRegexExtract(\n urlTemplate: string,\n pathParamRegex: RegExp,\n): string[] {\n const matches: string[] = [];\n let match;\n while ((match = pathParamRegex.exec(urlTemplate)) !== null) {\n matches.push(match[1]);\n }\n return matches;\n}\n\n/**\n * https://www.rfc-editor.org/rfc/rfc6570.html\n *\n * Implementation of URI Template resolution following RFC 6570 specification.\n * Handles URI templates with parameters enclosed in curly braces like {paramName}.\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport class UriTemplateResolver implements UrlTemplateResolver {\n /**\n * Regular expression pattern to match path parameters in the format {paramName}\n *\n * This regex is used to identify and extract path parameters from URL patterns.\n * It matches any text enclosed in curly braces {} and captures the content inside.\n *\n * Example matches:\n * - {id} -> captures \"id\"\n * - {userId} -> captures \"userId\"\n * - {category-name} -> captures \"category-name\"\n */\n private static PATH_PARAM_REGEX = /{([^}]+)}/g;\n\n /**\n * Extracts path parameters from a URL string.\n *\n * @param urlTemplate - The URL string to extract path parameters from\n * @returns An array of path parameter names without the curly braces, or an empty array if no matches found\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Extract multiple parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Extract parameters from full URLs\n * const urlParams = resolver.extractPathParams('https://api.example.com/{resource}/{id}');\n * // urlParams = ['resource', 'id']\n *\n * // No parameters\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[] {\n return urlTemplateRegexExtract(\n urlTemplate,\n UriTemplateResolver.PATH_PARAM_REGEX,\n );\n }\n\n /**\n * Replaces placeholders in the URL with path parameters.\n *\n * @param urlTemplate - Path string containing placeholders, e.g., \"http://localhost/users/{id}/posts/{postId}\"\n * @param pathParams - Path parameter object used to replace placeholders in the URL\n * @returns Path string with placeholders replaced\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Replace parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // Handle string parameter values\n * const stringUrl = resolver.resolve('/users/{username}', { username: 'john_doe' });\n * // stringUrl = '/users/john_doe'\n *\n * // URL encode parameter values\n * const encodedUrl = resolver.resolve('/search/{query}', { query: 'hello world' });\n * // encodedUrl = '/search/hello%20world'\n * ```\n *\n * @example\n * ```typescript\n * // Missing required parameter throws an error\n * const resolver = uriTemplateResolver;\n * try {\n * resolver.resolve('/users/{id}', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(\n urlTemplate: string,\n pathParams?: Record<string, any> | null,\n ): string {\n return urlTemplateRegexResolve(\n urlTemplate,\n UriTemplateResolver.PATH_PARAM_REGEX,\n pathParams,\n );\n }\n}\n\nexport const uriTemplateResolver = new UriTemplateResolver();\n\n/**\n * Express-style URL template resolver.\n * Handles URI templates with parameters in the format :paramName.\n *\n * @example\n * ```typescript\n * const resolver = expressUrlTemplateResolver;\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/:id/posts/:postId');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport class ExpressUrlTemplateResolver implements UrlTemplateResolver {\n /**\n * Regular expression pattern to match Express-style path parameters in the format :paramName\n */\n private static PATH_PARAM_REGEX = /:([^/]+)/g;\n\n /**\n * Extracts path parameters from an Express-style URL string.\n *\n * @param urlTemplate - The URL string with Express-style parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n *\n * @example\n * ```typescript\n * const resolver = new expressUrlTemplateResolver;\n *\n * // Extract multiple parameters\n * const params = resolver.extractPathParams('/users/:id/posts/:postId');\n * // params = ['id', 'postId']\n *\n * // No parameters\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[] {\n return urlTemplateRegexExtract(\n urlTemplate,\n ExpressUrlTemplateResolver.PATH_PARAM_REGEX,\n );\n }\n\n /**\n * Replaces Express-style placeholders in the URL with path parameters.\n *\n * @param urlTemplate - Path string containing Express-style placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns Path string with placeholders replaced\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const resolver = expressUrlTemplateResolver;\n *\n * // Replace parameters\n * const url = resolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // Handle string parameter values\n * const stringUrl = resolver.resolve('/users/:username', { username: 'john_doe' });\n * // stringUrl = '/users/john_doe'\n * ```\n *\n * @example\n * ```typescript\n * // Missing required parameter throws an error\n * const resolver = expressUrlTemplateResolver;\n * try {\n * resolver.resolve('/users/:id', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(\n urlTemplate: string,\n pathParams?: Record<string, any> | null,\n ): string {\n return urlTemplateRegexResolve(\n urlTemplate,\n ExpressUrlTemplateResolver.PATH_PARAM_REGEX,\n pathParams,\n );\n }\n}\n\nexport const expressUrlTemplateResolver = new ExpressUrlTemplateResolver();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { combineURLs } from './urls';\nimport type { BaseURLCapable, FetchRequest } from './fetchRequest';\nimport {\n getUrlTemplateResolver,\n type UrlTemplateResolver,\n UrlTemplateStyle,\n} from './urlTemplateResolver';\n\n/**\n * Container for URL parameters including path and query parameters.\n *\n * Used to define dynamic parts of a URL including path placeholders and query string parameters.\n */\nexport interface UrlParams {\n /**\n * Path parameter object used to replace placeholders in the URL (e.g., {id}).\n *\n * These parameters are used to substitute named placeholders in the URL path.\n *\n * @example\n * ```typescript\n * // For URL template '/users/{id}/posts/{postId}'\n * const path = { id: 123, postId: 456 };\n * ```\n */\n path?: Record<string, any>;\n\n /**\n * Query parameter object to be added to the URL query string.\n *\n * These parameters are appended to the URL as a query string.\n *\n * @example\n * ```typescript\n * const query = { filter: 'active', page: 1, limit: 10 };\n * // Results in query string: ?filter=active&page=1&limit=10\n * ```\n */\n query?: Record<string, any>;\n}\n\n/**\n * Utility class for constructing complete URLs with path parameters and query parameters.\n *\n * Handles URL composition, path parameter interpolation, and query string generation.\n * Combines a base URL with a path, replaces path placeholders with actual values, and appends\n * query parameters to create a complete URL.\n *\n * @example\n * ```typescript\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n * const url = urlBuilder.build('/users/{id}', {\n * path: { id: 123 },\n * query: { filter: 'active' }\n * });\n * // Result: https://api.example.com/users/123?filter=active\n * ```\n */\nexport class UrlBuilder implements BaseURLCapable {\n /**\n * Base URL that all constructed URLs will be based on.\n *\n * This is typically the root of your API endpoint (e.g., 'https://api.example.com').\n */\n readonly baseURL: string;\n readonly urlTemplateResolver: UrlTemplateResolver;\n\n /**\n * Initializes a new UrlBuilder instance.\n *\n * @param baseURL - Base URL that all constructed URLs will be based on\n * @param urlTemplateStyle - Optional style configuration for URL template resolution.\n * Determines how path parameters are parsed and resolved.\n * Defaults to UriTemplate style if not specified.\n *\n * @example\n * ```typescript\n * // Create a URL builder with default URI template style\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n *\n * // Create a URL builder with Express-style template resolution\n * const expressUrlBuilder = new UrlBuilder('https://api.example.com', UrlTemplateStyle.Express);\n * ```\n */\n constructor(baseURL: string, urlTemplateStyle?: UrlTemplateStyle) {\n this.baseURL = baseURL;\n this.urlTemplateResolver = getUrlTemplateResolver(urlTemplateStyle);\n }\n\n /**\n * Builds a complete URL, including path parameter replacement and query parameter addition.\n *\n * @param url - URL path to build (e.g., '/users/{id}/posts')\n * @param params - URL parameters including path and query parameters\n * @returns Complete URL string with base URL, path parameters interpolated, and query string appended\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n * const url = urlBuilder.build('/users/{id}/posts/{postId}', {\n * path: { id: 123, postId: 456 },\n * query: { filter: 'active', limit: 10 }\n * });\n * // Result: https://api.example.com/users/123/posts/456?filter=active&limit=10\n * ```\n */\n build(url: string, params?: UrlParams): string {\n const path = params?.path;\n const query = params?.query;\n const combinedURL = combineURLs(this.baseURL, url);\n let finalUrl = this.urlTemplateResolver.resolve(combinedURL, path);\n if (query) {\n const queryString = new URLSearchParams(query).toString();\n if (queryString) {\n finalUrl += '?' + queryString;\n }\n }\n return finalUrl;\n }\n\n /**\n * Resolves a complete URL from a FetchRequest.\n *\n * Used internally by the Fetcher to build the final URL for a request\n * by combining the request URL with its URL parameters using this UrlBuilder.\n *\n * @param request - The FetchRequest containing URL and URL parameters\n * @returns Complete resolved URL string\n */\n resolveRequestUrl(request: FetchRequest): string {\n return this.build(request.url, request.urlParams);\n }\n}\n\n/**\n * Interface for objects that have a UrlBuilder capability.\n *\n * Indicates that an object has a UrlBuilder instance for URL construction.\n */\nexport interface UrlBuilderCapable {\n /**\n * The UrlBuilder instance.\n */\n urlBuilder: UrlBuilder;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Base error class for all Fetcher-related errors.\n *\n * This class extends the native Error class and provides a foundation for\n * all custom errors thrown by the Fetcher library. It includes support for\n * error chaining through the cause property.\n *\n * @example\n * ```typescript\n * try {\n * await fetcher.get('/api/users');\n * } catch (error) {\n * if (error instanceof FetcherError) {\n * console.log('Fetcher error:', error.message);\n * if (error.cause) {\n * console.log('Caused by:', error.cause);\n * }\n * }\n * }\n * ```\n */\nexport class FetcherError extends Error {\n /**\n * Creates a new FetcherError instance.\n *\n * @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.\n * @param cause - Optional underlying error that caused this error.\n */\n constructor(\n errorMsg?: string,\n public readonly cause?: Error | any,\n ) {\n const errorMessage =\n errorMsg || cause?.message || 'An error occurred in the fetcher';\n super(errorMessage);\n this.name = 'FetcherError';\n\n // Copy stack trace from cause if available\n if (cause?.stack) {\n this.stack = cause.stack;\n }\n\n // Set prototype for instanceof checks to work correctly\n Object.setPrototypeOf(this, FetcherError.prototype);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type FetchRequest } from './fetchRequest';\nimport { FetcherError } from './fetcherError';\n\n/**\n * Exception class thrown when an HTTP request times out.\n *\n * This error is thrown by the timeoutFetch function when a request exceeds its timeout limit.\n *\n * @example\n * ```typescript\n * try {\n * const response = await timeoutFetch('https://api.example.com/users', {}, 1000);\n * } catch (error) {\n * if (error instanceof FetchTimeoutError) {\n * console.log(`Request timed out after ${error.timeout}ms`);\n * }\n * }\n * ```\n */\nexport class FetchTimeoutError extends FetcherError {\n /**\n * The request options that timed out.\n */\n request: FetchRequest;\n\n /**\n * Creates a new FetchTimeoutError instance.\n *\n * @param request - The request options that timed out\n */\n constructor(request: FetchRequest) {\n const method = request.method || 'GET';\n const message = `Request timeout of ${request.timeout}ms exceeded for ${method} ${request.url}`;\n super(message);\n this.name = 'FetchTimeoutError';\n this.request = request;\n // Fix prototype chain\n Object.setPrototypeOf(this, FetchTimeoutError.prototype);\n }\n}\n\n/**\n * Interface that defines timeout capability for HTTP requests.\n *\n * Objects implementing this interface can specify timeout values for HTTP requests.\n */\nexport interface TimeoutCapable {\n /**\n * Request timeout in milliseconds.\n *\n * When the value is 0, it indicates no timeout should be set.\n * The default value is undefined.\n */\n timeout?: number;\n}\n\n/**\n * Resolves request timeout settings, prioritizing request-level timeout settings.\n *\n * @param requestTimeout - Request-level timeout setting\n * @param optionsTimeout - Configuration-level timeout setting\n * @returns Resolved timeout setting\n *\n * @remarks\n * If requestTimeout is defined, it takes precedence over optionsTimeout.\n * Otherwise, optionsTimeout is returned. If both are undefined, undefined is returned.\n */\nexport function resolveTimeout(\n requestTimeout?: number,\n optionsTimeout?: number,\n): number | undefined {\n if (typeof requestTimeout !== 'undefined') {\n return requestTimeout;\n }\n return optionsTimeout;\n}\n\n/**\n * Executes an HTTP request with optional timeout support.\n *\n * This function provides a wrapper around the native fetch API with added timeout functionality.\n * - If a timeout is specified, it will create an AbortController to cancel the request if it exceeds the timeout.\n * - If the request already has a signal, it will delegate to the native fetch API directly to avoid conflicts.\n * - If the request has an abortController, it will be used instead of creating a new one.\n *\n * @param request - The request configuration including URL, method, headers, body, and optional timeout\n * @returns Promise that resolves to the Response object\n * @throws FetchTimeoutError if the request times out\n * @throws TypeError for network errors\n *\n * @example\n * ```typescript\n * // With timeout\n * try {\n * const response = await timeoutFetch('https://api.example.com/users', { method: 'GET' }, 5000);\n * console.log('Request completed successfully');\n * } catch (error) {\n * if (error instanceof FetchTimeoutError) {\n * console.log(`Request timed out after ${error.timeout}ms`);\n * }\n * }\n *\n * // Without timeout (delegates to regular fetch)\n * const response = await timeoutFetch('https://api.example.com/users', { method: 'GET' });\n * ```\n */\nexport async function timeoutFetch(request: FetchRequest): Promise<Response> {\n const url = request.url;\n const timeout = request.timeout;\n const requestInit = request as RequestInit;\n\n // If the request already has a signal, delegate to native fetch to avoid conflicts\n if (request.signal) {\n return fetch(url, requestInit);\n }\n\n // Extract timeout from request\n if (!timeout) {\n // When no timeout is set, but an abortController is provided, use its signal\n if (request.abortController) {\n requestInit.signal = request.abortController.signal;\n }\n return fetch(url, requestInit);\n }\n\n // Create AbortController for fetch request cancellation\n const controller = request.abortController ?? new AbortController();\n request.abortController = controller;\n requestInit.signal = controller.signal;\n\n // Timer resource management\n let timerId: ReturnType<typeof setTimeout> | null = null;\n // Create timeout Promise that rejects after specified time\n const timeoutPromise = new Promise<Response>((_, reject) => {\n timerId = setTimeout(() => {\n // Clean up timer resources and handle timeout error\n if (timerId) {\n clearTimeout(timerId);\n }\n const error = new FetchTimeoutError(request);\n controller.abort(error);\n reject(error);\n }, timeout);\n });\n\n try {\n // Race between fetch request and timeout Promise\n return await Promise.race([fetch(url, requestInit), timeoutPromise]);\n } finally {\n // Clean up timer resources\n if (timerId) {\n clearTimeout(timerId);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * The name of the UrlResolveInterceptor.\n */\nexport const URL_RESOLVE_INTERCEPTOR_NAME = 'UrlResolveInterceptor';\n\n/**\n * The order of the UrlResolveInterceptor.\n * Set to Number.MIN_SAFE_INTEGER + 1000 to ensure it runs earliest among request interceptors.\n */\nexport const URL_RESOLVE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * Interceptor responsible for resolving the final URL for a request.\n *\n * This interceptor combines the base URL, path parameters, and query parameters\n * to create the final URL for a request. It should be executed earliest in\n * the interceptor chain to ensure the URL is properly resolved before other interceptors\n * process the request.\n *\n * @remarks\n * This interceptor runs at the very beginning of the request interceptor chain to ensure\n * URL resolution happens before any other request processing. The order is set to\n * URL_RESOLVE_INTERCEPTOR_ORDER to ensure it executes before all other request interceptors,\n * establishing the foundation for subsequent processing.\n *\n * @example\n * // With baseURL: 'https://api.example.com'\n * // Request URL: '/users/{id}'\n * // Path params: { id: 123 }\n * // Query params: { filter: 'active' }\n * // Final URL: 'https://api.example.com/users/123?filter=active'\n */\nexport class UrlResolveInterceptor implements RequestInterceptor {\n /**\n * The name of this interceptor.\n */\n readonly name = URL_RESOLVE_INTERCEPTOR_NAME;\n\n /**\n * The order of this interceptor (executed earliest).\n *\n * This interceptor should run at the very beginning of the request interceptor chain to ensure\n * URL resolution happens before any other request processing. The order is set to\n * URL_RESOLVE_INTERCEPTOR_ORDER to ensure it executes before all other request interceptors,\n * establishing the foundation for subsequent processing.\n */\n readonly order = URL_RESOLVE_INTERCEPTOR_ORDER;\n\n /**\n * Resolves the final URL by combining the base URL, path parameters, and query parameters.\n *\n * @param exchange - The fetch exchange containing the request information\n */\n intercept(exchange: FetchExchange) {\n const request = exchange.request;\n request.url = exchange.fetcher.urlBuilder.resolveRequestUrl(request);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type TimeoutCapable } from './timeout';\nimport { type UrlParams } from './urlBuilder';\n\n/**\n * Interface for objects that can have a base URL\n *\n * This interface defines a baseURL property that can be used to set a base URL\n * for HTTP requests. When the baseURL is empty, it means no base URL is set.\n */\nexport interface BaseURLCapable {\n /**\n * The base URL for requests\n * When empty, indicates no base URL is set. Default is undefined.\n */\n baseURL: string;\n}\n\n/**\n * HTTP method enumeration constants\n *\n * Defines the standard HTTP methods that can be used for requests.\n * Each method is represented as a string literal type.\n */\nexport enum HttpMethod {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n DELETE = 'DELETE',\n PATCH = 'PATCH',\n HEAD = 'HEAD',\n OPTIONS = 'OPTIONS',\n TRACE = 'TRACE',\n}\n\nexport const CONTENT_TYPE_HEADER = 'Content-Type';\n\nexport class ContentTypeValues {\n static readonly APPLICATION_JSON = 'application/json';\n static readonly TEXT_EVENT_STREAM = 'text/event-stream';\n}\n\n/**\n * Request headers interface\n *\n * Defines common HTTP headers that can be sent with requests.\n * Allows for additional custom headers through index signature.\n */\nexport interface RequestHeaders {\n [CONTENT_TYPE_HEADER]?: string;\n Accept?: string;\n Authorization?: string;\n\n [key: string]: string | undefined;\n}\n\n/**\n * Interface for objects that can have request headers\n *\n * This interface defines an optional headers property for HTTP requests.\n */\nexport interface RequestHeadersCapable {\n /**\n * Request headers\n */\n headers?: RequestHeaders;\n}\n\n/**\n * Fetcher request configuration interface\n *\n * This interface defines all the configuration options available for making HTTP requests\n * with the Fetcher client. It extends the standard RequestInit interface while adding\n * Fetcher-specific features like path parameters, query parameters, and timeout control.\n *\n * @example\n * ```typescript\n * const request: FetchRequestInit = {\n * method: 'GET',\n * urlParams: {\n * path: { id: 123 },\n * query: { include: 'profile' }\n * },\n * headers: { 'Authorization': 'Bearer token' },\n * timeout: 5000\n * };\n *\n * const response = await fetcher.fetch('/users/{id}', request);\n * ```\n */\nexport interface FetchRequestInit\n extends TimeoutCapable,\n RequestHeadersCapable,\n Omit<RequestInit, 'body' | 'headers'> {\n urlParams?: UrlParams;\n\n /**\n * Request body\n *\n * The body of the request. Can be a string, Blob, ArrayBuffer, FormData,\n * URLSearchParams, or a plain object. Plain objects are automatically\n * converted to JSON and the appropriate Content-Type header is set.\n *\n * @example\n * ```typescript\n * // Plain object (automatically converted to JSON)\n * const request = {\n * method: 'POST',\n * body: { name: 'John', email: 'john@example.com' }\n * };\n *\n * // FormData\n * const formData = new FormData();\n * formData.append('name', 'John');\n * const request = {\n * method: 'POST',\n * body: formData\n * };\n * ```\n */\n body?: BodyInit | Record<string, any> | string | null;\n\n /**\n * AbortController for this request.\n * Used to cancel the request if needed.\n *\n * In timeout scenarios, if this property is provided, it will be used instead of creating a new AbortController.\n * This allows the caller to provide a custom AbortController for more advanced cancellation scenarios.\n *\n * @example\n * ```typescript\n * // Provide a custom AbortController\n * const controller = new AbortController();\n * const request: FetchRequest = {\n * url: 'https://api.example.com/data',\n * method: 'GET',\n * abortController: controller\n * };\n *\n * // Later, cancel the request\n * controller.abort();\n * ```\n */\n abortController?: AbortController;\n}\n\n/**\n * Fetcher request interface\n *\n * Extends FetchRequestInit with a required URL property.\n * Represents a complete request configuration ready to be executed.\n */\nexport interface FetchRequest extends FetchRequestInit {\n /**\n * The URL for this request\n */\n url: string;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { CONTENT_TYPE_HEADER, ContentTypeValues } from './fetchRequest';\nimport { URL_RESOLVE_INTERCEPTOR_ORDER } from './urlResolveInterceptor';\n\n/**\n * The name of the RequestBodyInterceptor.\n */\nexport const REQUEST_BODY_INTERCEPTOR_NAME = 'RequestBodyInterceptor';\n\n/**\n * The order of the RequestBodyInterceptor.\n * Set to URL_RESOLVE_INTERCEPTOR_ORDER + 1000 to ensure it runs early among request interceptors.\n */\nexport const REQUEST_BODY_INTERCEPTOR_ORDER =\n URL_RESOLVE_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor responsible for converting plain objects to JSON strings for HTTP request bodies.\n *\n * This interceptor ensures that object request bodies are properly serialized and that\n * the appropriate Content-Type header is set. It runs early in the request processing chain\n * to ensure request bodies are properly formatted before other interceptors process them.\n *\n * @remarks\n * This interceptor runs after URL resolution (UrlResolveInterceptor) but before\n * the actual HTTP request is made (FetchInterceptor). The order is set to\n * REQUEST_BODY_INTERCEPTOR_ORDER to ensure it executes in the correct position\n * in the interceptor chain, allowing for other interceptors to run between URL resolution\n * and request body processing. This positioning ensures that URL parameters are resolved\n * first, then request bodies are properly formatted, and finally the HTTP request is executed.\n */\nexport class RequestBodyInterceptor implements RequestInterceptor {\n /**\n * Interceptor name, used for identification and management.\n */\n readonly name = REQUEST_BODY_INTERCEPTOR_NAME;\n\n /**\n * Interceptor execution order, set to run after UrlResolveInterceptor but before FetchInterceptor.\n *\n * This interceptor should run after URL resolution (UrlResolveInterceptor) but before\n * the actual HTTP request is made (FetchInterceptor). The order is set to\n * REQUEST_BODY_INTERCEPTOR_ORDER to ensure it executes in the correct position\n * in the interceptor chain, allowing for other interceptors to run between URL resolution\n * and request body processing. This positioning ensures that URL parameters are resolved\n * first, then request bodies are properly formatted, and finally the HTTP request is executed.\n */\n readonly order = REQUEST_BODY_INTERCEPTOR_ORDER;\n\n /**\n * Checks if the provided body is of a supported complex type that doesn't require JSON serialization.\n *\n * @param body - The request body to check\n * @returns True if the body is an ArrayBuffer, TypedArray, DataView or ReadableStream, false otherwise\n */\n private isSupportedComplexBodyType(body: any): boolean {\n return (\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body) ||\n body instanceof ReadableStream\n );\n }\n\n /**\n * Checks if the provided body is of a type that automatically appends Content-Type header.\n *\n * @param body - The request body to check\n * @returns True if the body is a Blob, File, FormData or URLSearchParams, false otherwise\n */\n private isAutoAppendContentType(body: any): boolean {\n return (\n body instanceof Blob ||\n body instanceof File ||\n body instanceof FormData ||\n body instanceof URLSearchParams\n );\n }\n\n /**\n * Attempts to convert request body to a valid fetch API body type.\n *\n * This method follows a specific processing order to handle different types of request bodies:\n * 1. Check if the body is null or undefined and return early if so\n * 2. Check if the body is a non-object type and return early if so\n * 3. Check if the body is a type that automatically appends Content-Type header\n * 4. Check if the body is a supported complex type that doesn't require JSON serialization\n * 5. For plain objects, convert to JSON string and set Content-Type header to application/json\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#setting_a_body}\n *\n * Supported types:\n * - a string\n * - ArrayBuffer\n * - TypedArray\n * - DataView\n * - Blob\n * - File\n * - URLSearchParams\n * - FormData\n * - ReadableStream\n *\n * For unsupported object types (like plain objects), they will be automatically\n * converted to JSON strings.\n *\n * @param exchange - The exchange object containing the request to process\n *\n * @example\n * // Plain object body will be converted to JSON\n * const fetcher = new Fetcher();\n * const exchange = new FetchExchange(\n * fetcher,\n * {\n * body: { name: 'John', age: 30 }\n * }\n * );\n * interceptor.intercept(exchange);\n * // exchange.request.body will be '{\"name\":\"John\",\"age\":30}'\n * // exchange.request.headers will include 'Content-Type: application/json'\n */\n intercept(exchange: FetchExchange) {\n const request = exchange.request;\n // If there's no request body, return unchanged\n if (request.body === undefined || request.body === null) {\n return;\n }\n\n // If request body is not an object, return unchanged\n if (typeof request.body !== 'object') {\n return;\n }\n const headers = exchange.ensureRequestHeaders();\n if (this.isAutoAppendContentType(request.body)) {\n if (headers[CONTENT_TYPE_HEADER]) {\n delete headers[CONTENT_TYPE_HEADER];\n }\n return;\n }\n // Check if it's a supported type\n if (this.isSupportedComplexBodyType(request.body)) {\n return;\n }\n\n // For plain objects, convert to JSON string\n // Also ensure Content-Type header is set to application/json\n exchange.request.body = JSON.stringify(request.body);\n\n if (!headers[CONTENT_TYPE_HEADER]) {\n headers[CONTENT_TYPE_HEADER] = ContentTypeValues.APPLICATION_JSON;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { timeoutFetch } from './timeout';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * The name of the FetchInterceptor.\n */\nexport const FETCH_INTERCEPTOR_NAME = 'FetchInterceptor';\n\n/**\n * The order of the FetchInterceptor.\n * Set to Number.MAX_SAFE_INTEGER - 1000 to ensure it runs latest among request interceptors.\n */\nexport const FETCH_INTERCEPTOR_ORDER = Number.MAX_SAFE_INTEGER - 1000;\n\n/**\n * Interceptor implementation responsible for executing actual HTTP requests.\n *\n * This is an interceptor implementation responsible for executing actual HTTP requests\n * and handling timeout control. It is the latest interceptor in the Fetcher request\n * processing chain, ensuring that the actual network request is executed after all\n * previous interceptors have completed processing.\n *\n * @remarks\n * This interceptor runs at the very end of the request interceptor chain to ensure\n * that the actual HTTP request is executed after all other request processing is complete.\n * The order is set to FETCH_INTERCEPTOR_ORDER to ensure it executes after all other\n * request interceptors, completing the request processing pipeline before the network\n * request is made. This positioning ensures that all request preprocessing is\n * completed before the actual network request is made.\n *\n * @example\n * // Usually not created manually as Fetcher uses it automatically\n * const fetcher = new Fetcher();\n * // FetchInterceptor is automatically registered in fetcher.interceptors.request\n */\nexport class FetchInterceptor implements RequestInterceptor {\n /**\n * Interceptor name, used to identify and manage interceptor instances.\n *\n * Each interceptor must have a unique name for identification and manipulation\n * within the interceptor manager.\n */\n readonly name = FETCH_INTERCEPTOR_NAME;\n\n /**\n * Interceptor execution order, set to near maximum safe integer to ensure latest execution.\n *\n * Since this is the interceptor that actually executes HTTP requests, it should\n * execute after all other request interceptors, so its order is set to\n * FETCH_INTERCEPTOR_ORDER. This ensures that all request preprocessing is\n * completed before the actual network request is made, while still allowing\n * other interceptors to run after it if needed. The positioning at the end\n * of the request chain ensures that all transformations and validations are\n * completed before the network request is executed.\n */\n readonly order = FETCH_INTERCEPTOR_ORDER;\n\n /**\n * Intercept and process HTTP requests.\n *\n * Executes the actual HTTP request and applies timeout control. This is the final\n * step in the request processing chain, responsible for calling the timeoutFetch\n * function to send the network request.\n *\n * @param exchange - Exchange object containing request information\n *\n * @throws {FetchTimeoutError} Throws timeout exception when request times out\n *\n * @example\n * // Usually called internally by Fetcher\n * const fetcher = new Fetcher();\n * const exchange = new FetchExchange(\n * fetcher,\n * {\n * url: 'https://api.example.com/users',\n * method: 'GET',\n * timeout: 5000\n * }\n * );\n * await fetchInterceptor.intercept(exchange);\n * console.log(exchange.response); // HTTP response object\n */\n async intercept(exchange: FetchExchange) {\n exchange.response = await timeoutFetch(exchange.request);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OrderedCapable Interface\n *\n * Interface that provides ordering capability for types that implement it.\n * Implementing types must provide an order property to determine execution order.\n * Lower numerical values have higher priority, and elements with the same value\n * maintain their relative order.\n */\nexport interface OrderedCapable {\n /**\n * Order value\n *\n * Lower numerical values have higher priority. Negative numbers, zero, and\n * positive numbers are all supported.\n * When multiple elements have the same order value, their relative order\n * will remain unchanged (stable sort).\n */\n order: number;\n}\n\n/**\n * Sorts an array of elements that implement the OrderedCapable interface\n *\n * This function creates and returns a new sorted array without modifying the\n * original array. It supports an optional filter function to select elements\n * that should participate in sorting.\n *\n * @template T - Array element type that must implement the OrderedCapable interface\n * @param array - The array to be sorted\n * @param filter - Optional filter function to select elements that should be sorted\n * @returns A new array sorted in ascending order by the order property\n *\n * @example\n * ```typescript\n * const items: OrderedCapable[] = [\n * { order: 10 },\n * { order: 5 },\n * { order: 1 },\n * ];\n *\n * const sortedItems = toSorted(items);\n * // Result: [{ order: 1 }, { order: 5 }, { order: 10 }]\n *\n * // Using filter function\n * const filteredAndSorted = toSorted(items, item => item.order > 3);\n * // Result: [{ order: 5 }, { order: 10 }]\n * ```\n */\nexport function toSorted<T extends OrderedCapable>(\n array: T[],\n filter?: (item: T) => boolean,\n): T[] {\n if (filter) {\n return array.filter(filter).sort((a, b) => a.order - b.order);\n }\n return [...array].sort((a, b) => a.order - b.order);\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type NamedCapable } from './types';\nimport { type OrderedCapable, toSorted } from './orderedCapable';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * Interface for HTTP interceptors in the fetcher pipeline.\n *\n * Interceptors are middleware components that can modify requests, responses, or handle errors\n * at different stages of the HTTP request lifecycle. They follow the Chain of Responsibility\n * pattern, where each interceptor can process the exchange object and pass it to the next.\n *\n * @example\n * // Example of a custom request interceptor\n * const customRequestInterceptor: Interceptor = {\n * name: 'CustomRequestInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * // Modify request headers\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'X-Custom-Header': 'custom-value'\n * };\n * }\n * };\n */\nexport interface Interceptor extends NamedCapable, OrderedCapable {\n /**\n * Unique identifier for the interceptor.\n *\n * Used by InterceptorRegistry to manage interceptors, including adding, removing,\n * and preventing duplicates. Each interceptor must have a unique name.\n */\n readonly name: string;\n\n /**\n * Interceptor method that modifies the request or response.\n *\n * @param exchange - The current exchange object, which contains the request and response.\n * @returns A promise that resolves to the modified exchange object.\n */\n readonly order: number;\n\n /**\n * Process the exchange object in the interceptor pipeline.\n *\n * This method is called by InterceptorRegistry to process the exchange object.\n * Interceptors can modify request, response, or error properties directly.\n *\n * @param exchange - The exchange object containing request, response, and error information\n *\n * @remarks\n * Interceptors should modify the exchange object directly rather than returning it.\n * They can also throw errors or transform errors into responses.\n */\n intercept(exchange: FetchExchange): void | Promise<void>;\n}\n\n/**\n * Interface for request interceptors.\n *\n * Request interceptors are executed before the HTTP request is sent.\n * They can modify the request configuration, add headers, or perform\n * other preprocessing tasks.\n *\n * @example\n * // Example of a request interceptor that adds an authorization header\n * const authInterceptor: RequestInterceptor = {\n * name: 'AuthorizationInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * const token = getAuthToken();\n * if (token) {\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'Authorization': `Bearer ${token}`\n * };\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface RequestInterceptor extends Interceptor {\n}\n\n/**\n * Interface for response interceptors.\n *\n * Response interceptors are executed after the HTTP response is received\n * but before it's processed by the application. They can modify the response,\n * transform data, or handle response-specific logic.\n *\n * @example\n * // Example of a response interceptor that parses JSON data\n * const jsonInterceptor: ResponseInterceptor = {\n * name: 'JsonResponseInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * if (exchange.response && exchange.response.headers.get('content-type')?.includes('application/json')) {\n * const data = await exchange.response.json();\n * // Attach parsed data to a custom property\n * (exchange.response as any).jsonData = data;\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ResponseInterceptor extends Interceptor {\n}\n\n/**\n * Interface for error interceptors.\n *\n * Error interceptors are executed when an HTTP request fails.\n * They can handle errors, transform them, or implement retry logic.\n *\n * @example\n * // Example of an error interceptor that retries failed requests\n * const retryInterceptor: ErrorInterceptor = {\n * name: 'RetryInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * if (exchange.error && isRetryableError(exchange.error)) {\n * // Implement retry logic\n * const retryCount = (exchange.request as any).retryCount || 0;\n * if (retryCount < 3) {\n * (exchange.request as any).retryCount = retryCount + 1;\n * // Retry the request\n * exchange.response = await fetch(exchange.request);\n * // Clear the error since we've recovered\n * exchange.error = undefined;\n * }\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ErrorInterceptor extends Interceptor {\n}\n\n/**\n * Registry for a collection of interceptors of the same type.\n *\n * Handles adding, removing, and executing interceptors in the correct order.\n * Each InterceptorRegistry instance manages one type of interceptor (request, response, or error).\n *\n * @remarks\n * Interceptors are executed in ascending order of their `order` property.\n * Interceptors with the same order value are executed in the order they were added.\n *\n * @example\n * // Create an interceptor registry with initial interceptors\n * const requestRegistry = new InterceptorRegistry([interceptor1, interceptor2]);\n *\n * // Add a new interceptor\n * requestRegistry.use(newInterceptor);\n *\n * // Remove an interceptor by name\n * requestRegistry.eject('InterceptorName');\n *\n * // Process an exchange through all interceptors\n * const result = await requestRegistry.intercept(exchange);\n */\nexport class InterceptorRegistry implements Interceptor {\n /**\n * Gets the name of this interceptor registry.\n *\n * @returns The constructor name of this class\n */\n get name(): string {\n return this.constructor.name;\n }\n\n /**\n * Gets the order of this interceptor registry.\n *\n * @returns Number.MIN_SAFE_INTEGER, indicating this registry should execute early\n */\n get order(): number {\n return Number.MIN_SAFE_INTEGER;\n }\n\n /**\n * Array of interceptors managed by this registry, sorted by their order property.\n */\n private sortedInterceptors: Interceptor[] = [];\n\n /**\n * Initializes a new InterceptorRegistry instance.\n *\n * @param interceptors - Initial array of interceptors to manage\n *\n * @remarks\n * The provided interceptors will be sorted by their order property immediately\n * upon construction.\n */\n constructor(interceptors: Interceptor[] = []) {\n this.sortedInterceptors = toSorted(interceptors);\n }\n\n /**\n * Returns an array of all interceptors in the registry.\n */\n get interceptors(): Interceptor[] {\n return [...this.sortedInterceptors];\n }\n\n /**\n * Adds an interceptor to this registry.\n *\n * @param interceptor - The interceptor to add\n * @returns True if the interceptor was added, false if an interceptor with the\n * same name already exists\n *\n * @remarks\n * Interceptors are uniquely identified by their name property. Attempting to add\n * an interceptor with a name that already exists in the registry will fail.\n *\n * After adding, interceptors are automatically sorted by their order property.\n */\n use(interceptor: Interceptor): boolean {\n if (this.sortedInterceptors.some(item => item.name === interceptor.name)) {\n return false;\n }\n this.sortedInterceptors = toSorted([\n ...this.sortedInterceptors,\n interceptor,\n ]);\n return true;\n }\n\n /**\n * Removes an interceptor by name.\n *\n * @param name - The name of the interceptor to remove\n * @returns True if an interceptor was removed, false if no interceptor with the\n * given name was found\n */\n eject(name: string): boolean {\n const original = this.sortedInterceptors;\n this.sortedInterceptors = toSorted(\n original,\n interceptor => interceptor.name !== name,\n );\n return original.length !== this.sortedInterceptors.length;\n }\n\n /**\n * Removes all interceptors from this registry.\n */\n clear(): void {\n this.sortedInterceptors = [];\n }\n\n /**\n * Executes all managed interceptors on the given exchange object.\n *\n * @param exchange - The exchange object to process\n * @returns A promise that resolves when all interceptors have been executed\n *\n * @remarks\n * Interceptors are executed in order, with each interceptor receiving the result\n * of the previous interceptor. The first interceptor receives the original\n * exchange object.\n *\n * If any interceptor throws an error, the execution chain is broken and the error\n * is propagated to the caller.\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n for (const interceptor of this.sortedInterceptors) {\n // Each interceptor processes the output of the previous interceptor\n await interceptor.intercept(exchange);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type ResponseInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { FetcherError } from './fetcherError';\n\n/**\n * Error thrown when response status validation fails.\n *\n * This error is thrown by ValidateStatusInterceptor when the response status\n * does not pass the validation defined by the validateStatus function.\n */\nexport class HttpStatusValidationError extends FetcherError {\n constructor(public readonly exchange: FetchExchange) {\n super(\n `Request failed with status code ${exchange.response?.status} for ${exchange.request.url}`,\n );\n this.name = 'HttpStatusValidationError';\n Object.setPrototypeOf(this, HttpStatusValidationError.prototype);\n }\n}\n\n/**\n * Defines whether to resolve or reject the promise for a given HTTP response status code.\n * If `validateStatus` returns `true` (or is set to `null` or `undefined`), the promise will be resolved;\n * otherwise, the promise will be rejected.\n *\n * @param status - HTTP response status code\n * @returns true to resolve the promise, false to reject it\n *\n * @example\n * ```typescript\n * // Default behavior (2xx status codes are resolved)\n * const fetcher = new Fetcher();\n *\n * // Custom behavior (only 200 status code is resolved)\n * const fetcher = new Fetcher({\n * validateStatus: (status) => status === 200\n * });\n *\n * // Always resolve (never reject based on status)\n * const fetcher = new Fetcher({\n * validateStatus: (status) => true\n * });\n * ```\n */\ntype ValidateStatus = (status: number) => boolean;\n\nconst DEFAULT_VALIDATE_STATUS: ValidateStatus = (status: number) =>\n status >= 200 && status < 300;\n\n/**\n * The name of the ValidateStatusInterceptor.\n */\nexport const VALIDATE_STATUS_INTERCEPTOR_NAME = 'ValidateStatusInterceptor';\n\n/**\n * The order of the ValidateStatusInterceptor.\n * Set to Number.MAX_SAFE_INTEGER - 1000 to ensure it runs latest among response interceptors,\n * but still allows other interceptors to run after it if needed.\n */\nexport const VALIDATE_STATUS_INTERCEPTOR_ORDER = Number.MAX_SAFE_INTEGER - 1000;\n\n/**\n * Response interceptor that validates HTTP status codes.\n *\n * This interceptor implements behavior similar to axios's validateStatus option.\n * It checks the response status code against a validation function and throws\n * an error if the status is not valid.\n *\n * @remarks\n * This interceptor runs at the very beginning of the response interceptor chain to ensure\n * status validation happens before any other response processing. The order is set to\n * VALIDATE_STATUS_INTERCEPTOR_ORDER to ensure it executes early in the response chain,\n * allowing for other response interceptors to run after it if needed. This positioning\n * ensures that invalid responses are caught and handled early in the response processing\n * pipeline, before other response handlers attempt to process them.\n *\n * @example\n * ```typescript\n * // Default behavior (2xx status codes are valid)\n * const interceptor = new ValidateStatusInterceptor();\n *\n * // Custom validation (only 200 status code is valid)\n * const interceptor = new ValidateStatusInterceptor((status) => status === 200);\n *\n * // Always valid (never throws based on status)\n * const interceptor = new ValidateStatusInterceptor((status) => true);\n * ```\n */\nexport class ValidateStatusInterceptor implements ResponseInterceptor {\n /**\n * Gets the name of this interceptor.\n *\n * @returns The name of this interceptor\n */\n get name(): string {\n return VALIDATE_STATUS_INTERCEPTOR_NAME;\n }\n\n /**\n * Gets the order of this interceptor.\n *\n * @returns VALIDATE_STATUS_INTERCEPTOR_ORDER, indicating this interceptor should execute early\n */\n get order(): number {\n return VALIDATE_STATUS_INTERCEPTOR_ORDER;\n }\n\n /**\n * Creates a new ValidateStatusInterceptor instance.\n *\n * @param validateStatus - Function that determines if a status code is valid\n */\n constructor(\n private readonly validateStatus: ValidateStatus = DEFAULT_VALIDATE_STATUS,\n ) {\n }\n\n /**\n * Validates the response status code.\n *\n * @param exchange - The exchange containing the response to validate\n * @throws HttpStatusValidationError if the status code is not valid\n *\n * @remarks\n * This method runs at the beginning of the response interceptor chain to ensure\n * status validation happens before any other response processing. Invalid responses\n * are caught and converted to HttpStatusValidationError early in the pipeline,\n * preventing other response handlers from attempting to process them. Valid responses\n * proceed through the rest of the response interceptor chain normally.\n */\n intercept(exchange: FetchExchange) {\n // Only validate if there's a response\n if (!exchange.response) {\n return;\n }\n\n const status = exchange.response.status;\n // If status is valid, do nothing\n if (this.validateStatus(status)) {\n return;\n }\n throw new HttpStatusValidationError(exchange);\n }\n}\n","import { UrlResolveInterceptor } from './urlResolveInterceptor';\nimport { RequestBodyInterceptor } from './requestBodyInterceptor';\nimport { FetchInterceptor } from './fetchInterceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { FetcherError } from './fetcherError';\nimport { InterceptorRegistry } from './interceptor';\nimport { ValidateStatusInterceptor } from './validateStatusInterceptor';\n\n/**\n * Custom error class for FetchExchange related errors.\n *\n * This error is thrown when there are issues with the HTTP exchange process,\n * such as when a request fails and no response is generated. It provides\n * comprehensive information about the failed request through the exchange object.\n *\n * @example\n * ```typescript\n * try {\n * await fetcher.get('/api/users');\n * } catch (error) {\n * if (error instanceof ExchangeError) {\n * console.log('Request URL:', error.exchange.request.url);\n * console.log('Request method:', error.exchange.request.method);\n * if (error.exchange.error) {\n * console.log('Underlying error:', error.exchange.error);\n * }\n * }\n * }\n * ```\n */\nexport class ExchangeError extends FetcherError {\n /**\n * Creates a new ExchangeError instance.\n *\n * @param exchange - The FetchExchange object containing request/response/error information.\n * @param errorMsg - An optional error message.\n */\n constructor(\n public readonly exchange: FetchExchange,\n errorMsg?: string,\n ) {\n const errorMessage =\n errorMsg ||\n exchange.error?.message ||\n exchange.response?.statusText ||\n `Request to ${exchange.request.url} failed during exchange`;\n super(errorMessage, exchange.error);\n this.name = 'ExchangeError';\n Object.setPrototypeOf(this, ExchangeError.prototype);\n }\n}\n\n/**\n * Collection of interceptor managers for the Fetcher client.\n *\n * Manages three types of interceptors:\n * 1. Request interceptors - Process requests before sending HTTP requests\n * 2. Response interceptors - Process responses after receiving HTTP responses\n * 3. Error interceptors - Handle errors when they occur during the request process\n *\n * Each type of interceptor is managed by an InterceptorRegistry instance, supporting\n * adding, removing, and executing interceptors.\n *\n * @example\n * // Create a custom interceptor\n * const customRequestInterceptor: Interceptor = {\n * name: 'CustomRequestInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<FetchExchange> {\n * // Modify request headers\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'X-Custom-Header': 'custom-value'\n * };\n * return exchange;\n * }\n * };\n *\n * // Add interceptor to Fetcher\n * const fetcher = new Fetcher();\n * fetcher.interceptors.request.use(customRequestInterceptor);\n *\n * @remarks\n * By default, the request interceptor registry has three built-in interceptors registered:\n * 1. UrlResolveInterceptor - Resolves the final URL with parameters\n * 2. RequestBodyInterceptor - Automatically converts object-type request bodies to JSON strings\n * 3. FetchInterceptor - Executes actual HTTP requests and handles timeouts\n */\nexport class InterceptorManager {\n /**\n * Registry for request-phase interceptors.\n *\n * Executed before HTTP requests are sent. Contains three built-in interceptors by default:\n * UrlResolveInterceptor, RequestBodyInterceptor, and FetchInterceptor.\n *\n * @remarks\n * Request interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority. The default interceptors are:\n * 1. UrlResolveInterceptor (order: Number.MIN_SAFE_INTEGER) - Resolves the final URL\n * 2. RequestBodyInterceptor (order: 0) - Converts object bodies to JSON\n * 3. FetchInterceptor (order: Number.MAX_SAFE_INTEGER) - Executes the actual HTTP request\n */\n readonly request: InterceptorRegistry = new InterceptorRegistry([\n new UrlResolveInterceptor(),\n new RequestBodyInterceptor(),\n new FetchInterceptor(),\n ]);\n\n /**\n * Manager for response-phase interceptors.\n *\n * Executed after HTTP responses are received. Contains ValidateStatusInterceptor by default\n * which validates HTTP status codes and throws errors for invalid statuses.\n *\n * @remarks\n * Response interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority.\n *\n * By default, the response interceptor registry has one built-in interceptor registered:\n * 1. ValidateStatusInterceptor - Validates HTTP status codes and throws HttpStatusValidationError for invalid statuses\n */\n readonly response: InterceptorRegistry = new InterceptorRegistry([\n new ValidateStatusInterceptor(),\n ]);\n\n /**\n * Manager for error-handling phase interceptors.\n *\n * Executed when errors occur during HTTP requests. Empty by default, custom error handling\n * logic can be added as needed.\n *\n * @remarks\n * Error interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority. Error interceptors can transform errors into normal responses,\n * avoiding thrown exceptions.\n */\n readonly error: InterceptorRegistry = new InterceptorRegistry();\n\n /**\n * Processes a FetchExchange through the interceptor pipeline.\n *\n * This method is the core of the Fetcher's interceptor system. It executes the three\n * phases of interceptors in sequence:\n * 1. Request interceptors - Process the request before sending\n * 2. Response interceptors - Process the response after receiving\n * 3. Error interceptors - Handle any errors that occurred during the process\n *\n * The interceptor pipeline follows the Chain of Responsibility pattern, where each\n * interceptor can modify the exchange object and decide whether to continue or\n * terminate the chain.\n *\n * @param fetchExchange - The exchange object containing request, response, and error information\n * @returns Promise that resolves to the processed FetchExchange\n * @throws ExchangeError if an unhandled error occurs during processing\n *\n * @remarks\n * The method handles three distinct phases:\n *\n * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.\n * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.\n *\n * 2. Response Phase: Executes response interceptors which can transform or validate responses.\n * These interceptors only run if the request phase completed without throwing.\n *\n * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors\n * can handle errors by clearing the error property. If error interceptors clear the error,\n * the exchange is returned successfully.\n *\n * Error Handling:\n * - If any interceptor throws an error, the error phase is triggered\n * - Error interceptors can \"fix\" errors by clearing the error property on the exchange\n * - If errors remain after error interceptors run, they are wrapped in ExchangeError\n *\n * Order of Execution:\n * 1. Request interceptors (sorted by order property, ascending)\n * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase\n * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred\n *\n * @example\n * ```typescript\n * // Create a fetcher with custom interceptors\n * const fetcher = new Fetcher();\n *\n * // Add a request interceptor\n * fetcher.interceptors.request.use({\n * name: 'AuthInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'Authorization': 'Bearer ' + getToken()\n * };\n * }\n * });\n *\n * // Add a response interceptor\n * fetcher.interceptors.response.use({\n * name: 'ResponseLogger',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * console.log(`Response status: ${exchange.response?.status}`);\n * }\n * });\n *\n * // Add an error interceptor\n * fetcher.interceptors.error.use({\n * name: 'ErrorLogger',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);\n * // Clear the error to indicate it's been handled\n * exchange.error = undefined;\n * }\n * });\n *\n * // Create and process an exchange\n * const request: FetchRequest = {\n * url: '/api/users',\n * method: HttpMethod.GET\n * };\n * const exchange = new FetchExchange(fetcher, request);\n * const result = await fetcher.exchange(exchange);\n * ```\n */\n async exchange(fetchExchange: FetchExchange): Promise<FetchExchange> {\n try {\n // Apply request interceptors\n await this.request.intercept(fetchExchange);\n // Apply response interceptors\n await this.response.intercept(fetchExchange);\n return fetchExchange;\n } catch (error: any) {\n // Apply error interceptors\n fetchExchange.error = error;\n await this.error.intercept(fetchExchange);\n\n // If error interceptors cleared the error (indicating it's been handled/fixed), return the exchange\n if (!fetchExchange.hasError()) {\n return fetchExchange;\n }\n\n // Otherwise, wrap the error in ExchangeError\n throw new ExchangeError(fetchExchange);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * Function interface for extracting results from a FetchExchange.\n * Defines how to transform a FetchExchange object into a specific result type.\n * @template R - The type of result to extract\n * @param exchange - The FetchExchange object containing request and response information\n * @returns The extracted result of type R\n */\nexport interface ResultExtractor<R> {\n (exchange: FetchExchange): R | Promise<R>;\n}\n\n/**\n * Interface with result extractor capability\n * Defines an optional resultExtractor property\n */\nexport interface ResultExtractorCapable {\n resultExtractor?: ResultExtractor<any>;\n}\n\n/**\n * Returns the original FetchExchange object.\n * @param exchange - The FetchExchange object to return\n * @returns The same FetchExchange object that was passed in\n */\nexport const ExchangeResultExtractor: ResultExtractor<FetchExchange> = (\n exchange: FetchExchange,\n) => {\n return exchange;\n};\n\n/**\n * Extracts the Response object from the exchange.\n * @param exchange - The FetchExchange containing the response\n * @returns The Response object from the exchange\n */\nexport const ResponseResultExtractor: ResultExtractor<Response> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse;\n};\n\n/**\n * Extracts and parses the response body as JSON.\n * @param exchange - The FetchExchange containing the response with JSON data\n * @returns A Promise that resolves to the parsed JSON data\n */\nexport const JsonResultExtractor: ResultExtractor<Promise<any>> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse.json();\n};\n\n/**\n * Extracts the response body as text.\n * @param exchange - The FetchExchange containing the response with text data\n * @returns A Promise that resolves to the response body as a string\n */\nexport const TextResultExtractor: ResultExtractor<Promise<string>> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse.text();\n};\n\nexport const ResultExtractors = {\n Exchange: ExchangeResultExtractor,\n Response: ResponseResultExtractor,\n Json: JsonResultExtractor,\n Text: TextResultExtractor,\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Merges two record objects, giving precedence to the second record for overlapping keys.\n *\n * This utility function is used to merge configuration objects where the second object\n * takes precedence over the first when there are conflicting keys.\n *\n * @template V - The type of values in the records\n * @param first - The first record to merge (lower precedence)\n * @param second - The second record to merge (higher precedence)\n * @returns A new merged record, or undefined if both inputs are undefined\n *\n * @example\n * ```typescript\n * // Merge two objects\n * const defaults = { timeout: 5000, retries: 3 };\n * const overrides = { timeout: 10000 };\n * const result = mergeRecords(defaults, overrides);\n * // Result: { timeout: 10000, retries: 3 }\n *\n * // Handle undefined values\n * const result2 = mergeRecords(undefined, { timeout: 5000 });\n * // Result: { timeout: 5000 }\n *\n * // Return undefined when both are undefined\n * const result3 = mergeRecords(undefined, undefined);\n * // Result: undefined\n * ```\n */\nexport function mergeRecords<V>(\n first?: Record<string, V>,\n second?: Record<string, V>,\n): Record<string, V> | undefined {\n // If both records are undefined, return undefined\n if (first === undefined && second === undefined) {\n return undefined;\n }\n\n // If second record is undefined, return first record\n if (second === undefined) {\n return first;\n }\n\n // If first record is undefined, return second record\n if (first === undefined) {\n return second;\n }\n\n // Merge both records, with second taking precedence\n return { ...first, ...second };\n}\n\n/**\n * Merge a Record object or Map object into a target Map\n * @param record - Source data, can be either Record<string, V> or Map<string, V> type\n * @param map - Target Map object, if not provided a new Map will be created\n * @returns The merged Map object\n */\nexport function mergeRecordToMap<V>(\n record?: Record<string, V> | Map<string, V>,\n map?: Map<string, V>,\n): Map<string, V> {\n map ??= new Map();\n if (!record) {\n return map;\n }\n if (record instanceof Map) {\n for (const [key, value] of record) {\n map.set(key, value);\n }\n return map;\n }\n for (const [key, value] of Object.entries(record)) {\n map.set(key, value);\n }\n return map;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\nimport type { FetchRequest, RequestHeaders } from './fetchRequest';\nimport { ExchangeError } from './interceptorManager';\nimport { type UrlParams } from './urlBuilder';\nimport { type RequiredBy } from './types';\nimport { ResultExtractor, ResultExtractors } from './resultExtractor';\nimport { mergeRecordToMap } from './utils';\n\nexport interface AttributesCapable {\n /**\n * Shared attributes for passing data between interceptors.\n *\n * This property allows interceptors to share arbitrary data with each other.\n * Interceptors can read from and write to this object to pass information\n * along the interceptor chain.\n *\n * @remarks\n * - This property is optional and may be undefined initially\n * - Interceptors should initialize this property if they need to use it\n * - Use string keys to avoid conflicts between different interceptors\n * - Consider namespacing your keys (e.g., 'mylib.retryCount' instead of 'retryCount')\n * - Be mindful of memory usage when storing large objects\n */\n attributes?: Record<string, any> | Map<string, any>;\n}\n\nexport interface FetchExchangeInit extends AttributesCapable {\n /**\n * The Fetcher instance that initiated this exchange.\n */\n fetcher: Fetcher;\n\n /**\n * The request configuration including url, method, headers, body, etc.\n */\n request: FetchRequest;\n resultExtractor?: ResultExtractor<any>;\n\n /**\n * The response object, undefined until the request completes successfully.\n */\n response?: Response;\n\n /**\n * Any error that occurred during the request processing, undefined if no error occurred.\n */\n error?: Error | any;\n}\n\n/**\n * Container for HTTP request/response data that flows through the interceptor chain.\n *\n * Represents the complete exchange object that flows through the interceptor chain.\n * This object contains all the information about a request, response, and any errors\n * that occur during the HTTP request lifecycle. It also provides a mechanism for\n * sharing data between interceptors through the attributes property.\n *\n * FetchExchange instances are unique within a single request scope, meaning each HTTP\n * request creates its own FetchExchange instance that is passed through the interceptor\n * chain for that specific request.\n *\n * @example\n * ```typescript\n * // In a request interceptor\n * const requestInterceptor: Interceptor = {\n * name: 'RequestInterceptor',\n * order: 0,\n * intercept(exchange: FetchExchange) {\n * // Add custom data to share with other interceptors\n * exchange.attributes = exchange.attributes || {};\n * exchange.attributes.startTime = Date.now();\n * exchange.attributes.customHeader = 'my-value';\n * }\n * };\n *\n * // In a response interceptor\n * const responseInterceptor: Interceptor = {\n * name: 'ResponseInterceptor',\n * order: 0,\n * intercept(exchange: FetchExchange) {\n * // Access data shared by previous interceptors\n * if (exchange.attributes && exchange.attributes.startTime) {\n * const startTime = exchange.attributes.startTime;\n * const duration = Date.now() - startTime;\n * console.log(`Request took ${duration}ms`);\n * }\n * }\n * };\n * ```\n */\nexport class FetchExchange\n implements RequiredBy<FetchExchangeInit, 'attributes'> {\n /**\n * The Fetcher instance that initiated this exchange.\n */\n fetcher: Fetcher;\n\n /**\n * The request configuration including url, method, headers, body, etc.\n */\n request: FetchRequest;\n\n /**\n * The result extractor function used to transform the response into the desired format.\n * Defaults to ResultExtractors.Exchange if not provided.\n */\n resultExtractor: ResultExtractor<any>;\n /**\n * The response object, undefined until the request completes successfully.\n */\n private _response?: Response;\n\n /**\n * Any error that occurred during the request processing, undefined if no error occurred.\n */\n error?: Error | any;\n\n /**\n * Cached result of the extracted result to avoid repeated computations.\n * Undefined when not yet computed, null when computation failed.\n */\n private cachedExtractedResult?: any | Promise<any>;\n /**\n * Shared attributes for passing data between interceptors.\n *\n * This property allows interceptors to share arbitrary data with each other.\n * Interceptors can read from and write to this object to pass information\n * along the interceptor chain.\n *\n * @remarks\n * - This property is optional and may be undefined initially\n * - Interceptors should initialize this property if they need to use it\n * - Use string keys to avoid conflicts between different interceptors\n * - Consider namespacing your keys (e.g., 'mylib.retryCount' instead of 'retryCount')\n * - Be mindful of memory usage when storing large objects\n */\n attributes: Map<string, any>;\n\n constructor(exchangeInit: FetchExchangeInit) {\n this.fetcher = exchangeInit.fetcher;\n this.request = exchangeInit.request;\n this.resultExtractor =\n exchangeInit.resultExtractor ?? ResultExtractors.Exchange;\n this.attributes = mergeRecordToMap(exchangeInit.attributes);\n this._response = exchangeInit.response;\n this.error = exchangeInit.error;\n }\n\n /**\n * Ensures that request headers object exists, creating it if necessary.\n *\n * This method checks if the request headers object is present and initializes\n * it as an empty object if it's missing. This guarantees that headers can\n * be safely accessed and modified after calling this method.\n *\n * @returns The request headers object, guaranteed to be non-null\n */\n ensureRequestHeaders(): RequestHeaders {\n if (!this.request.headers) {\n this.request.headers = {};\n }\n return this.request.headers;\n }\n\n /**\n * Ensures that request URL parameters object exists with all required properties,\n * creating them if necessary.\n *\n * This method checks if the request URL parameters object is present and initializes\n * it with empty path and query objects if it's missing. It also ensures that both\n * path and query sub-objects exist. This guarantees that URL parameters can be\n * safely accessed and modified after calling this method.\n *\n * @returns The request URL parameters object with guaranteed non-null path and query properties\n */\n ensureRequestUrlParams(): Required<UrlParams> {\n if (!this.request.urlParams) {\n this.request.urlParams = {\n path: {},\n query: {},\n };\n }\n if (!this.request.urlParams.path) {\n this.request.urlParams.path = {};\n }\n if (!this.request.urlParams.query) {\n this.request.urlParams.query = {};\n }\n return this.request.urlParams as Required<UrlParams>;\n }\n\n /**\n * Checks if the exchange has an error.\n *\n * @returns true if an error is present, false otherwise\n */\n hasError(): boolean {\n return !!this.error;\n }\n\n /**\n * Sets the response object for this exchange.\n * Also invalidates the cached extracted result to ensure data consistency\n * when the response changes.\n *\n * @param response - The Response object to set, or undefined to clear the response\n */\n set response(response: Response | undefined) {\n this._response = response;\n this.cachedExtractedResult = undefined;\n }\n\n /**\n * Gets the response object for this exchange.\n *\n * @returns The response object if available, undefined otherwise\n */\n get response(): Response | undefined {\n return this._response;\n }\n\n /**\n * Checks if the exchange has a response.\n *\n * @returns true if a response is present, false otherwise\n */\n hasResponse(): boolean {\n return !!this.response;\n }\n\n /**\n * Gets the required response object, throwing an error if no response is available.\n *\n * This getter ensures that a response object is available, and throws an ExchangeError\n * with details about the request if no response was received. This is useful for\n * guaranteeing that downstream code always has a valid Response object to work with.\n *\n * @throws {ExchangeError} If no response is available for the current exchange\n * @returns The Response object for this exchange\n */\n get requiredResponse(): Response {\n if (!this.response) {\n throw new ExchangeError(\n this,\n `Request to ${this.request.url} failed with no response`,\n );\n }\n return this.response;\n }\n\n /**\n * Extracts the result by applying the result extractor to the exchange.\n * The result is cached after the first computation to avoid repeated computations.\n *\n * @returns The extracted result\n */\n extractResult<R>(): R | Promise<R> {\n if (this.cachedExtractedResult !== undefined) {\n return this.cachedExtractedResult;\n }\n this.cachedExtractedResult = this.resultExtractor(this);\n return this.cachedExtractedResult;\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type FetchRequestInit } from './fetchRequest';\nimport { type UrlParams } from './urlBuilder';\nimport { mergeRecords } from './utils';\nimport { DEFAULT_REQUEST_OPTIONS, RequestOptions } from './fetcher';\n\n/**\n * Merges two FetcherRequest objects into one.\n *\n * This function combines two FetcherRequest objects, with the second object's properties\n * taking precedence over the first object's properties. Special handling is applied\n * to nested objects like path, query, and headers which are merged recursively.\n * For primitive values, the second object's values override the first's.\n *\n * @param first - The first request object (lower priority)\n * @param second - The second request object (higher priority)\n * @returns A new FetcherRequest object with merged properties\n *\n * @example\n * ```typescript\n * const request1 = {\n * method: 'GET',\n * urlParams: {\n * path: { id: 1 }\n * },\n * headers: { 'Content-Type': 'application/json' }\n * };\n *\n * const request2 = {\n * method: 'POST',\n * urlParams: {\n * query: { filter: 'active' }\n * },\n * headers: { 'Authorization': 'Bearer token' }\n * };\n *\n * const merged = mergeRequest(request1, request2);\n * // Result: {\n * // method: 'POST',\n * // urlParams: {\n * // path: { id: 1 },\n * // query: { filter: 'active' }\n * // },\n * // headers: {\n * // 'Content-Type': 'application/json',\n * // 'Authorization': 'Bearer token'\n * // }\n * // }\n * ```\n */\nexport function mergeRequest(\n first: FetchRequestInit,\n second: FetchRequestInit,\n): FetchRequestInit {\n // If first request is empty, return second request\n if (Object.keys(first).length === 0) {\n return second;\n }\n\n // If second request is empty, return first request\n if (Object.keys(second).length === 0) {\n return first;\n }\n\n // Merge nested objects\n const urlParams: UrlParams = {\n path: mergeRecords(first.urlParams?.path, second.urlParams?.path),\n query: mergeRecords(first.urlParams?.query, second.urlParams?.query),\n };\n\n const headers = {\n ...first.headers,\n ...second.headers,\n };\n\n // For primitive values, second takes precedence\n const method = second.method ?? first.method;\n const body = second.body ?? first.body;\n const timeout = second.timeout ?? first.timeout;\n const signal = second.signal ?? first.signal;\n const abortController = second.abortController ?? first.abortController;\n // Return merged request with second object's top-level properties taking precedence\n return {\n ...first,\n ...second,\n method,\n urlParams,\n headers,\n body,\n timeout,\n signal,\n abortController,\n };\n}\n\n/**\n * Merges two request options objects into one, with the second object taking precedence over the first.\n *\n * @param first - The first request options object (optional)\n * @param second - The second request options object which will override properties from the first (optional)\n * @returns A new RequestOptions object with merged properties\n */\nexport function mergeRequestOptions(\n first?: RequestOptions,\n second?: RequestOptions,\n): RequestOptions {\n if (second && second.resultExtractor && second.attributes) {\n return second;\n }\n // Merge the options, prioritizing second over first, with defaults as fallback\n return {\n resultExtractor:\n second?.resultExtractor ??\n first?.resultExtractor ??\n DEFAULT_REQUEST_OPTIONS.resultExtractor,\n attributes: second?.attributes ?? first?.attributes,\n };\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UrlBuilder, type UrlBuilderCapable } from './urlBuilder';\nimport { resolveTimeout, type TimeoutCapable } from './timeout';\nimport { AttributesCapable, FetchExchange } from './fetchExchange';\nimport {\n BaseURLCapable,\n CONTENT_TYPE_HEADER,\n ContentTypeValues,\n FetchRequest,\n FetchRequestInit,\n RequestHeaders,\n RequestHeadersCapable,\n} from './fetchRequest';\nimport { HttpMethod } from './fetchRequest';\nimport { InterceptorManager } from './interceptorManager';\nimport { UrlTemplateStyle } from './urlTemplateResolver';\nimport { ResultExtractorCapable, ResultExtractors } from './resultExtractor';\nimport { mergeRequestOptions } from './mergeRequest';\n\n/**\n * Configuration options for the Fetcher client.\n *\n * Defines the customizable aspects of a Fetcher instance including base URL,\n * default headers, timeout settings, and interceptors.\n *\n * @example\n * ```typescript\n * const options: FetcherOptions = {\n * urlTemplateStyle: UrlTemplateStyle.UriTemplate,\n * baseURL: 'https://api.example.com',\n * headers: { 'Content-Type': 'application/json' },\n * timeout: 5000,\n * interceptors: new InterceptorManager()\n * };\n * ```\n */\nexport interface FetcherOptions\n extends BaseURLCapable,\n RequestHeadersCapable,\n TimeoutCapable {\n /**\n * The style of URL template to use for URL parameter interpolation.\n * @default UrlTemplateStyle.Path\n */\n urlTemplateStyle?: UrlTemplateStyle;\n\n /**\n * The interceptor manager to use for request/response processing.\n * @default new InterceptorManager()\n */\n interceptors?: InterceptorManager;\n}\n\nconst DEFAULT_HEADERS: RequestHeaders = {\n [CONTENT_TYPE_HEADER]: ContentTypeValues.APPLICATION_JSON,\n};\n\nexport const DEFAULT_OPTIONS: FetcherOptions = {\n baseURL: '',\n headers: DEFAULT_HEADERS,\n};\n\n/**\n * Options for individual requests.\n */\nexport interface RequestOptions\n extends AttributesCapable,\n ResultExtractorCapable {\n}\n\nexport const DEFAULT_REQUEST_OPTIONS: RequestOptions = {\n resultExtractor: ResultExtractors.Exchange,\n};\nexport const DEFAULT_FETCH_OPTIONS: RequestOptions = {\n resultExtractor: ResultExtractors.Response,\n};\n\n/**\n * HTTP client with support for interceptors, URL building, and timeout control.\n *\n * The Fetcher class provides a flexible and extensible HTTP client implementation\n * that follows the interceptor pattern. It supports URL parameter interpolation,\n * request/response transformation, and timeout handling.\n *\n * @example\n * ```typescript\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * const response = await fetcher.fetch('/users/{id}', {\n * urlParams: {\n * path: { id: 123 },\n * query: { filter: 'active' }\n * },\n * timeout: 5000\n * });\n * ```\n */\nexport class Fetcher\n implements UrlBuilderCapable, RequestHeadersCapable, TimeoutCapable {\n readonly urlBuilder: UrlBuilder;\n readonly headers?: RequestHeaders = DEFAULT_HEADERS;\n readonly timeout?: number;\n readonly interceptors: InterceptorManager;\n\n /**\n * Initializes a new Fetcher instance with optional configuration.\n *\n * Creates a Fetcher with default settings that can be overridden through the options parameter.\n * If no interceptors are provided, a default set of interceptors will be used.\n *\n * @param options - Configuration options for the Fetcher instance\n * @param options.baseURL - The base URL to prepend to all requests. Defaults to empty string.\n * @param options.headers - Default headers to include in all requests. Defaults to JSON content type.\n * @param options.timeout - Default timeout for requests in milliseconds. No timeout by default.\n * @param options.urlTemplateStyle - Style for URL template parameter interpolation.\n * @param options.interceptors - Interceptor manager for processing requests and responses.\n */\n constructor(options: FetcherOptions = DEFAULT_OPTIONS) {\n this.urlBuilder = new UrlBuilder(options.baseURL, options.urlTemplateStyle);\n this.headers = options.headers ?? DEFAULT_HEADERS;\n this.timeout = options.timeout;\n this.interceptors = options.interceptors ?? new InterceptorManager();\n }\n\n /**\n * Processes an HTTP request through the Fetcher's internal workflow.\n *\n * This method creates a FetchExchange object and passes it through the interceptor chain.\n * It handles header merging, timeout resolution, and result extractor configuration.\n *\n * @param request - The HTTP request configuration to process\n * @param options - Optional request options including result extractor and attributes\n * @returns Promise that resolves to the processed FetchExchange object\n */\n async exchange(\n request: FetchRequest,\n options?: RequestOptions,\n ): Promise<FetchExchange> {\n // Merge default headers and request-level headers. defensive copy\n const mergedHeaders = {\n ...this.headers,\n ...request.headers,\n };\n // Merge request options\n const fetchRequest: FetchRequest = {\n ...request,\n headers: mergedHeaders,\n timeout: resolveTimeout(request.timeout, this.timeout),\n };\n const { resultExtractor, attributes } = mergeRequestOptions(\n DEFAULT_REQUEST_OPTIONS,\n options,\n );\n const exchange: FetchExchange = new FetchExchange({\n fetcher: this,\n request: fetchRequest,\n resultExtractor,\n attributes,\n });\n return this.interceptors.exchange(exchange);\n }\n\n /**\n * Processes an HTTP request through the Fetcher's internal workflow.\n *\n * This method prepares the request by merging headers and timeout settings,\n * creates a FetchExchange object, and passes it through the exchange method\n * for interceptor processing.\n *\n * @template R - The type of the result to be returned\n * @param request - Complete request configuration object\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ExchangeResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the extracted result based on resultExtractor\n * @throws Error if an unhandled error occurs during request processing\n */\n async request<R = FetchExchange>(\n request: FetchRequest,\n options?: RequestOptions,\n ): Promise<R> {\n const fetchExchange = await this.exchange(request, options);\n return fetchExchange.extractResult();\n }\n\n /**\n * Executes an HTTP request with the specified URL and options.\n *\n * This is the primary method for making HTTP requests. It processes the request\n * through the interceptor chain and returns the resulting Response.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request (relative to baseURL if set)\n * @param request - Request configuration including headers, body, parameters, etc.\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n * @throws FetchError if the request fails and no response is generated\n */\n async fetch<R = Response>(\n url: string,\n request: FetchRequestInit = {},\n options?: RequestOptions,\n ): Promise<R> {\n const mergedRequest: FetchRequest = {\n ...request,\n url,\n };\n return this.request(\n mergedRequest,\n mergeRequestOptions(DEFAULT_FETCH_OPTIONS, options),\n );\n }\n\n /**\n * Internal helper method for making HTTP requests with a specific method.\n *\n * This private method is used by the public HTTP method methods (get, post, etc.)\n * to execute requests with the appropriate HTTP verb.\n *\n * @template R - The type of the result to be returned\n * @param method - The HTTP method to use for the request\n * @param url - The URL path for the request\n * @param request - Additional request options\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n private async methodFetch<R = Response>(\n method: HttpMethod,\n url: string,\n request: FetchRequestInit = {},\n options?: RequestOptions,\n ): Promise<R> {\n const mergedRequest: FetchRequest = {\n ...request,\n url,\n method,\n };\n return this.request(\n mergedRequest,\n mergeRequestOptions(DEFAULT_FETCH_OPTIONS, options),\n );\n }\n\n /**\n * Makes a GET HTTP request.\n *\n * Convenience method for making GET requests. The request body is omitted\n * as GET requests should not contain a body according to HTTP specification.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async get<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.GET, url, request, options);\n }\n\n /**\n * Makes a PUT HTTP request.\n *\n * Convenience method for making PUT requests, commonly used for updating resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async put<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.PUT, url, request, options);\n }\n\n /**\n * Makes a POST HTTP request.\n *\n * Convenience method for making POST requests, commonly used for creating resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async post<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.POST, url, request, options);\n }\n\n /**\n * Makes a PATCH HTTP request.\n *\n * Convenience method for making PATCH requests, commonly used for partial updates.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async patch<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.PATCH, url, request, options);\n }\n\n /**\n * Makes a DELETE HTTP request.\n *\n * Convenience method for making DELETE requests, commonly used for deleting resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async delete<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.DELETE, url, request, options);\n }\n\n /**\n * Makes a HEAD HTTP request.\n *\n * Convenience method for making HEAD requests, which retrieve headers only.\n * The request body is omitted as HEAD requests should not contain a body.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async head<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.HEAD, url, request, options);\n }\n\n /**\n * Makes an OPTIONS HTTP request.\n *\n * Convenience method for making OPTIONS requests, commonly used for CORS preflight.\n * The request body is omitted as OPTIONS requests typically don't contain a body.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async options<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.OPTIONS, url, request, options);\n }\n\n /**\n * Sends an HTTP TRACE request to the specified URL and returns the response.\n *\n * The TRACE method is used to echo the received request for debugging purposes.\n * This method automatically sets the HTTP method to TRACE and omits the request body\n * since TRACE requests must not have a body according to the HTTP specification.\n *\n * @param url - The target URL for the TRACE request. Must be a valid absolute or relative URL.\n * @param request - Request configuration options excluding 'method' and 'body' properties.\n * Defaults to an empty object. Common properties include headers, cache settings, etc.\n * @param options - Optional additional request parameters for extended functionality.\n * May include custom handling logic or metadata for the request pipeline.\n * @returns A Promise resolving to the response object of type R (defaults to Response).\n * The response contains status, headers, and body data from the TRACE request.\n */\n async trace<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.TRACE, url, request, options);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\n\n/**\n * Default fetcher name used when no name is specified\n */\nexport const DEFAULT_FETCHER_NAME = 'default';\n\n/**\n * FetcherRegistrar is a registry for managing multiple Fetcher instances.\n * It allows registering, retrieving, and unregistering Fetcher instances by name.\n * This is useful for applications that need to manage multiple HTTP clients\n * with different configurations.\n *\n * @example\n * // Register a fetcher\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n *\n * // Retrieve a fetcher\n * const apiFetcher = fetcherRegistrar.get('api');\n *\n * // Use the default fetcher\n * const defaultFetcher = fetcherRegistrar.default;\n *\n * // Unregister a fetcher\n * fetcherRegistrar.unregister('api');\n */\nexport class FetcherRegistrar {\n /**\n * Internal map for storing registered fetchers\n * @private\n */\n private registrar: Map<string, Fetcher> = new Map();\n\n /**\n * Register a Fetcher instance with a given name\n *\n * @param name - The name to register the fetcher under\n * @param fetcher - The Fetcher instance to register\n * @example\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n */\n register(name: string, fetcher: Fetcher): void {\n this.registrar.set(name, fetcher);\n }\n\n /**\n * Unregister a Fetcher instance by name\n *\n * @param name - The name of the fetcher to unregister\n * @returns boolean - True if the fetcher was successfully unregistered, false otherwise\n * @example\n * const success = fetcherRegistrar.unregister('api');\n * if (success) {\n * console.log('Fetcher unregistered successfully');\n * }\n */\n unregister(name: string): boolean {\n return this.registrar.delete(name);\n }\n\n /**\n * Get a Fetcher instance by name\n *\n * @param name - The name of the fetcher to retrieve\n * @returns Fetcher | undefined - The Fetcher instance if found, undefined otherwise\n * @example\n * const fetcher = fetcherRegistrar.get('api');\n * if (fetcher) {\n * // Use the fetcher\n * }\n */\n get(name: string): Fetcher | undefined {\n return this.registrar.get(name);\n }\n\n /**\n * Get a Fetcher instance by name, throwing an error if not found\n *\n * @param name - The name of the fetcher to retrieve\n * @returns Fetcher - The Fetcher instance\n * @throws Error - If no fetcher is registered with the given name\n * @example\n * try {\n * const fetcher = fetcherRegistrar.requiredGet('api');\n * // Use the fetcher\n * } catch (error) {\n * console.error('Fetcher not found:', error.message);\n * }\n */\n requiredGet(name: string): Fetcher {\n const fetcher = this.get(name);\n if (!fetcher) {\n throw new Error(`Fetcher ${name} not found`);\n }\n return fetcher;\n }\n\n /**\n * Get the default Fetcher instance\n *\n * @returns Fetcher - The default Fetcher instance\n * @throws Error - If no default fetcher is registered\n * @example\n * const defaultFetcher = fetcherRegistrar.default;\n */\n get default(): Fetcher {\n return this.requiredGet(DEFAULT_FETCHER_NAME);\n }\n\n /**\n * Set the default Fetcher instance\n *\n * @param fetcher - The Fetcher instance to set as default\n * @example\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.default = fetcher;\n */\n set default(fetcher: Fetcher) {\n this.register(DEFAULT_FETCHER_NAME, fetcher);\n }\n\n /**\n * Get a copy of all registered fetchers\n *\n * @returns Map<string, Fetcher> - A copy of the internal registrar map\n * @example\n * const allFetchers = fetcherRegistrar.fetchers;\n * for (const [name, fetcher] of allFetchers) {\n * console.log(`Fetcher ${name}:`, fetcher);\n * }\n */\n get fetchers(): Map<string, Fetcher> {\n return new Map(this.registrar);\n }\n}\n\n/**\n * Global instance of FetcherRegistrar\n * This is the default registrar used throughout the application\n *\n * @example\n * import { fetcherRegistrar } from '@ahoo-wang/fetcher';\n *\n * // Register a fetcher\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n *\n * // Retrieve a fetcher\n * const apiFetcher = fetcherRegistrar.get('api');\n */\nexport const fetcherRegistrar = new FetcherRegistrar();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\nimport { fetcherRegistrar } from './fetcherRegistrar';\n\n/**\n * Interface that defines a capability for objects that can have a fetcher.\n * This interface is typically used to mark components or objects that can perform fetching operations\n * and may need access to fetcher functionality.\n */\nexport interface FetcherCapable {\n /**\n * Optional fetcher property that can be either a string identifier or a Fetcher instance.\n * When present, this property indicates the fetcher associated with the implementing object.\n */\n fetcher?: string | Fetcher;\n}\n\n/**\n * Gets a Fetcher instance based on the provided fetcher parameter.\n *\n * @param fetcher - A string identifier or Fetcher instance to resolve\n * @param defaultFetcher - The default Fetcher to use when fetcher is not provided, defaults to defaultNamedFetcher\n * @returns A Fetcher instance if found, otherwise returns the default Fetcher\n */\nexport function getFetcher(\n fetcher?: string | Fetcher,\n defaultFetcher?: Fetcher,\n): Fetcher {\n // Return default fetcher if no fetcher is provided\n if (!fetcher) {\n return defaultFetcher ?? fetcherRegistrar.default;\n }\n\n // Return the fetcher directly if it's already a Fetcher instance,\n // otherwise resolve it through the fetcher registrar\n return fetcher instanceof Fetcher\n ? fetcher\n : fetcherRegistrar.requiredGet(fetcher);\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type NamedCapable } from './types';\nimport { DEFAULT_OPTIONS, Fetcher, type FetcherOptions } from './fetcher';\nimport { DEFAULT_FETCHER_NAME, fetcherRegistrar } from './fetcherRegistrar';\n\n/**\n * NamedFetcher is an extension of the Fetcher class that automatically registers\n * itself with the global fetcherRegistrar using a provided name.\n * This allows for easy management and retrieval of multiple fetcher instances\n * throughout an application by name.\n *\n * @example\n * // Create a named fetcher that automatically registers itself\n * const apiFetcher = new NamedFetcher('api', {\n * baseURL: 'https://api.example.com',\n * timeout: 5000\n * });\n *\n * // Retrieve the fetcher later by name\n * const sameFetcher = fetcherRegistrar.get('api');\n * console.log(apiFetcher === sameFetcher); // true\n *\n * // Use the fetcher normally\n * const response = await apiFetcher.get('/users');\n */\nexport class NamedFetcher extends Fetcher implements NamedCapable {\n /**\n * The name of this fetcher instance, used for registration and retrieval\n */\n name: string;\n\n /**\n * Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar\n *\n * @param name - The name to register this fetcher under\n * @param options - Fetcher configuration options (same as Fetcher constructor)\n *\n * @example\n * // Create with default options\n * const fetcher1 = new NamedFetcher('default');\n *\n * // Create with custom options\n * const fetcher2 = new NamedFetcher('api', {\n * baseURL: 'https://api.example.com',\n * timeout: 5000,\n * headers: { 'Authorization': 'Bearer token' }\n * });\n */\n constructor(name: string, options: FetcherOptions = DEFAULT_OPTIONS) {\n super(options);\n this.name = name;\n fetcherRegistrar.register(name, this);\n }\n}\n\n/**\n * Default named fetcher instance registered with the name 'default'.\n * This provides a convenient way to use a pre-configured fetcher instance\n * without having to create and register one manually.\n *\n * @example\n * // Use the default fetcher directly\n * import { fetcher } from '@ahoo-wang/fetcher';\n *\n * fetcher.get('/users')\n * .then(response => response.json())\n * .then(data => console.log(data));\n *\n * // Or retrieve it from the registrar\n * import { fetcherRegistrar } from '@ahoo-wang/fetcher';\n *\n * const defaultFetcher = fetcherRegistrar.default;\n * defaultFetcher.get('/users')\n * .then(response => response.json())\n * .then(data => console.log(data));\n */\nexport const fetcher = new NamedFetcher(DEFAULT_FETCHER_NAME);\n"],"names":["isAbsoluteURL","url","combineURLs","baseURL","relativeURL","UrlTemplateStyle","getUrlTemplateResolver","style","expressUrlTemplateResolver","uriTemplateResolver","urlTemplateRegexResolve","urlTemplate","pathParamRegex","pathParams","_","key","value","urlTemplateRegexExtract","matches","match","_UriTemplateResolver","UriTemplateResolver","_ExpressUrlTemplateResolver","ExpressUrlTemplateResolver","UrlBuilder","urlTemplateStyle","params","path","query","combinedURL","finalUrl","queryString","request","FetcherError","errorMsg","cause","errorMessage","FetchTimeoutError","method","message","resolveTimeout","requestTimeout","optionsTimeout","timeoutFetch","timeout","requestInit","controller","timerId","timeoutPromise","reject","error","URL_RESOLVE_INTERCEPTOR_NAME","URL_RESOLVE_INTERCEPTOR_ORDER","UrlResolveInterceptor","exchange","HttpMethod","CONTENT_TYPE_HEADER","_ContentTypeValues","ContentTypeValues","REQUEST_BODY_INTERCEPTOR_NAME","REQUEST_BODY_INTERCEPTOR_ORDER","RequestBodyInterceptor","body","headers","FETCH_INTERCEPTOR_NAME","FETCH_INTERCEPTOR_ORDER","FetchInterceptor","toSorted","array","filter","a","b","InterceptorRegistry","interceptors","interceptor","item","name","original","HttpStatusValidationError","DEFAULT_VALIDATE_STATUS","status","VALIDATE_STATUS_INTERCEPTOR_NAME","VALIDATE_STATUS_INTERCEPTOR_ORDER","ValidateStatusInterceptor","validateStatus","ExchangeError","InterceptorManager","fetchExchange","ExchangeResultExtractor","ResponseResultExtractor","JsonResultExtractor","TextResultExtractor","ResultExtractors","mergeRecords","first","second","mergeRecordToMap","record","map","FetchExchange","exchangeInit","response","mergeRequest","urlParams","signal","abortController","mergeRequestOptions","DEFAULT_REQUEST_OPTIONS","DEFAULT_HEADERS","DEFAULT_OPTIONS","DEFAULT_FETCH_OPTIONS","Fetcher","options","mergedHeaders","fetchRequest","resultExtractor","attributes","mergedRequest","DEFAULT_FETCHER_NAME","FetcherRegistrar","fetcher","fetcherRegistrar","getFetcher","defaultFetcher","NamedFetcher"],"mappings":"AA0BO,SAASA,EAAcC,GAAa;AACzC,SAAO,8BAA8B,KAAKA,CAAG;AAC/C;AAoBO,SAASC,EAAYC,GAAiBC,GAAqB;AAChE,SAAIJ,EAAcI,CAAW,IACpBA,IAGFA,IACHD,EAAQ,QAAQ,UAAU,EAAE,IAAI,MAAMC,EAAY,QAAQ,QAAQ,EAAE,IACpED;AACN;ACrCO,IAAKE,sBAAAA,OASVA,EAAAA,EAAA,cAAA,CAAA,IAAA,eAQAA,EAAAA,EAAA,UAAA,CAAA,IAAA,WAjBUA,IAAAA,KAAA,CAAA,CAAA;AA2CL,SAASC,EACdC,GACqB;AACrB,SAAIA,MAAU,IACLC,IAEFC;AACT;AAiFO,SAASC,EACdC,GACAC,GACAC,GACA;AACA,SAAKA,IACEF,EAAY,QAAQC,GAAgB,CAACE,GAAGC,MAAQ;AACrD,UAAMC,IAAQH,EAAWE,CAAG;AAE5B,QAAIC,MAAU;AACZ,YAAM,IAAI,MAAM,oCAAoCD,CAAG,EAAE;AAE3D,WAAO,mBAAmBC,CAAK;AAAA,EACjC,CAAC,IARuBL;AAS1B;AASO,SAASM,EACdN,GACAC,GACU;AACV,QAAMM,IAAoB,CAAA;AAC1B,MAAIC;AACJ,UAAQA,IAAQP,EAAe,KAAKD,CAAW,OAAO;AACpD,IAAAO,EAAQ,KAAKC,EAAM,CAAC,CAAC;AAEvB,SAAOD;AACT;AAqBO,MAAME,IAAN,MAAMA,EAAmD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqC9D,kBAAkBT,GAA+B;AAC/C,WAAOM;AAAA,MACLN;AAAA,MACAS,EAAoB;AAAA,IAAA;AAAA,EAExB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCA,QACET,GACAE,GACQ;AACR,WAAOH;AAAA,MACLC;AAAA,MACAS,EAAoB;AAAA,MACpBP;AAAA,IAAA;AAAA,EAEJ;AACF;AA9EEO,EAAe,mBAAmB;AAZ7B,IAAMC,IAAND;AA4FA,MAAMX,IAAsB,IAAIY,EAAA,GAmB1BC,IAAN,MAAMA,EAA0D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBrE,kBAAkBX,GAA+B;AAC/C,WAAOM;AAAA,MACLN;AAAA,MACAW,EAA2B;AAAA,IAAA;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCA,QACEX,GACAE,GACQ;AACR,WAAOH;AAAA,MACLC;AAAA,MACAW,EAA2B;AAAA,MAC3BT;AAAA,IAAA;AAAA,EAEJ;AACF;AAtEES,EAAe,mBAAmB;AAJ7B,IAAMC,IAAND;AA4EA,MAAMd,IAA6B,IAAIe,EAAA;AChUvC,MAAMC,EAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BhD,YAAYrB,GAAiBsB,GAAqC;AAChE,SAAK,UAAUtB,GACf,KAAK,sBAAsBG,EAAuBmB,CAAgB;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAMxB,GAAayB,GAA4B;AAC7C,UAAMC,IAAOD,GAAQ,MACfE,IAAQF,GAAQ,OAChBG,IAAc3B,EAAY,KAAK,SAASD,CAAG;AACjD,QAAI6B,IAAW,KAAK,oBAAoB,QAAQD,GAAaF,CAAI;AACjE,QAAIC,GAAO;AACT,YAAMG,IAAc,IAAI,gBAAgBH,CAAK,EAAE,SAAA;AAC/C,MAAIG,MACFD,KAAY,MAAMC;AAAA,IAEtB;AACA,WAAOD;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,kBAAkBE,GAA+B;AAC/C,WAAO,KAAK,MAAMA,EAAQ,KAAKA,EAAQ,SAAS;AAAA,EAClD;AACF;AChHO,MAAMC,UAAqB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtC,YACEC,GACgBC,GAChB;AACA,UAAMC,IACJF,KAAYC,GAAO,WAAW;AAChC,UAAMC,CAAY,GAJF,KAAA,QAAAD,GAKhB,KAAK,OAAO,gBAGRA,GAAO,UACT,KAAK,QAAQA,EAAM,QAIrB,OAAO,eAAe,MAAMF,EAAa,SAAS;AAAA,EACpD;AACF;AC1BO,MAAMI,UAA0BJ,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlD,YAAYD,GAAuB;AACjC,UAAMM,IAASN,EAAQ,UAAU,OAC3BO,IAAU,sBAAsBP,EAAQ,OAAO,mBAAmBM,CAAM,IAAIN,EAAQ,GAAG;AAC7F,UAAMO,CAAO,GACb,KAAK,OAAO,qBACZ,KAAK,UAAUP,GAEf,OAAO,eAAe,MAAMK,EAAkB,SAAS;AAAA,EACzD;AACF;AA4BO,SAASG,EACdC,GACAC,GACoB;AACpB,SAAI,OAAOD,IAAmB,MACrBA,IAEFC;AACT;AA+BA,eAAsBC,EAAaX,GAA0C;AAC3E,QAAM/B,IAAM+B,EAAQ,KACdY,IAAUZ,EAAQ,SAClBa,IAAcb;AAGpB,MAAIA,EAAQ;AACV,WAAO,MAAM/B,GAAK4C,CAAW;AAI/B,MAAI,CAACD;AAEH,WAAIZ,EAAQ,oBACVa,EAAY,SAASb,EAAQ,gBAAgB,SAExC,MAAM/B,GAAK4C,CAAW;AAI/B,QAAMC,IAAad,EAAQ,mBAAmB,IAAI,gBAAA;AAClD,EAAAA,EAAQ,kBAAkBc,GAC1BD,EAAY,SAASC,EAAW;AAGhC,MAAIC,IAAgD;AAEpD,QAAMC,IAAiB,IAAI,QAAkB,CAAClC,GAAGmC,MAAW;AAC1D,IAAAF,IAAU,WAAW,MAAM;AAEzB,MAAIA,KACF,aAAaA,CAAO;AAEtB,YAAMG,IAAQ,IAAIb,EAAkBL,CAAO;AAC3C,MAAAc,EAAW,MAAMI,CAAK,GACtBD,EAAOC,CAAK;AAAA,IACd,GAAGN,CAAO;AAAA,EACZ,CAAC;AAED,MAAI;AAEF,WAAO,MAAM,QAAQ,KAAK,CAAC,MAAM3C,GAAK4C,CAAW,GAAGG,CAAc,CAAC;AAAA,EACrE,UAAA;AAEE,IAAID,KACF,aAAaA,CAAO;AAAA,EAExB;AACF;ACpJO,MAAMI,IAA+B,yBAM/BC,IAAgC,OAAO,mBAAmB;AAuBhE,MAAMC,EAAoD;AAAA,EAA1D,cAAA;AAIL,SAAS,OAAOF,GAUhB,KAAS,QAAQC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjB,UAAUE,GAAyB;AACjC,UAAMtB,IAAUsB,EAAS;AACzB,IAAAtB,EAAQ,MAAMsB,EAAS,QAAQ,WAAW,kBAAkBtB,CAAO;AAAA,EACrE;AACF;ACrCO,IAAKuB,sBAAAA,OACVA,EAAA,MAAM,OACNA,EAAA,OAAO,QACPA,EAAA,MAAM,OACNA,EAAA,SAAS,UACTA,EAAA,QAAQ,SACRA,EAAA,OAAO,QACPA,EAAA,UAAU,WACVA,EAAA,QAAQ,SAREA,IAAAA,KAAA,CAAA,CAAA;AAWL,MAAMC,IAAsB,gBAEtBC,IAAN,MAAMA,EAAkB;AAG/B;AAFEA,EAAgB,mBAAmB,oBACnCA,EAAgB,oBAAoB;AAF/B,IAAMC,IAAND;AC5BA,MAAME,IAAgC,0BAMhCC,IACXR,IAAgC;AAiB3B,MAAMS,EAAqD;AAAA,EAA3D,cAAA;AAIL,SAAS,OAAOF,GAYhB,KAAS,QAAQC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,2BAA2BE,GAAoB;AACrD,WACEA,aAAgB,eAChB,YAAY,OAAOA,CAAI,KACvBA,aAAgB;AAAA,EAEpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,wBAAwBA,GAAoB;AAClD,WACEA,aAAgB,QAChBA,aAAgB,QAChBA,aAAgB,YAChBA,aAAgB;AAAA,EAEpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2CA,UAAUR,GAAyB;AACjC,UAAMtB,IAAUsB,EAAS;AAOzB,QALItB,EAAQ,SAAS,UAAaA,EAAQ,SAAS,QAK/C,OAAOA,EAAQ,QAAS;AAC1B;AAEF,UAAM+B,IAAUT,EAAS,qBAAA;AACzB,QAAI,KAAK,wBAAwBtB,EAAQ,IAAI,GAAG;AAC9C,MAAI+B,EAAQP,CAAmB,KAC7B,OAAOO,EAAQP,CAAmB;AAEpC;AAAA,IACF;AAEA,IAAI,KAAK,2BAA2BxB,EAAQ,IAAI,MAMhDsB,EAAS,QAAQ,OAAO,KAAK,UAAUtB,EAAQ,IAAI,GAE9C+B,EAAQP,CAAmB,MAC9BO,EAAQP,CAAmB,IAAIE,EAAkB;AAAA,EAErD;AACF;AChJO,MAAMM,IAAyB,oBAMzBC,IAA0B,OAAO,mBAAmB;AAuB1D,MAAMC,EAA+C;AAAA,EAArD,cAAA;AAOL,SAAS,OAAOF,GAahB,KAAS,QAAQC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BjB,MAAM,UAAUX,GAAyB;AACvC,IAAAA,EAAS,WAAW,MAAMX,EAAaW,EAAS,OAAO;AAAA,EACzD;AACF;ACtCO,SAASa,EACdC,GACAC,GACK;AACL,SAAIA,IACKD,EAAM,OAAOC,CAAM,EAAE,KAAK,CAACC,GAAGC,MAAMD,EAAE,QAAQC,EAAE,KAAK,IAEvD,CAAC,GAAGH,CAAK,EAAE,KAAK,CAACE,GAAGC,MAAMD,EAAE,QAAQC,EAAE,KAAK;AACpD;AC0GO,MAAMC,EAA2C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCtD,YAAYC,IAA8B,IAAI;AAX9C,SAAQ,qBAAoC,CAAA,GAY1C,KAAK,qBAAqBN,EAASM,CAAY;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA7BA,IAAI,OAAe;AACjB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAgB;AAClB,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAuBA,IAAI,eAA8B;AAChC,WAAO,CAAC,GAAG,KAAK,kBAAkB;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,IAAIC,GAAmC;AACrC,WAAI,KAAK,mBAAmB,KAAK,CAAAC,MAAQA,EAAK,SAASD,EAAY,IAAI,IAC9D,MAET,KAAK,qBAAqBP,EAAS;AAAA,MACjC,GAAG,KAAK;AAAA,MACRO;AAAA,IAAA,CACD,GACM;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAME,GAAuB;AAC3B,UAAMC,IAAW,KAAK;AACtB,gBAAK,qBAAqBV;AAAA,MACxBU;AAAA,MACA,CAAAH,MAAeA,EAAY,SAASE;AAAA,IAAA,GAE/BC,EAAS,WAAW,KAAK,mBAAmB;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,qBAAqB,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,UAAUvB,GAAwC;AACtD,eAAWoB,KAAe,KAAK;AAE7B,YAAMA,EAAY,UAAUpB,CAAQ;AAAA,EAExC;AACF;ACvQO,MAAMwB,UAAkC7C,EAAa;AAAA,EAC1D,YAA4BqB,GAAyB;AACnD;AAAA,MACE,mCAAmCA,EAAS,UAAU,MAAM,QAAQA,EAAS,QAAQ,GAAG;AAAA,IAAA,GAFhE,KAAA,WAAAA,GAI1B,KAAK,OAAO,6BACZ,OAAO,eAAe,MAAMwB,EAA0B,SAAS;AAAA,EACjE;AACF;AA4BA,MAAMC,KAA0C,CAACC,MAC/CA,KAAU,OAAOA,IAAS,KAKfC,KAAmC,6BAOnCC,KAAoC,OAAO,mBAAmB;AA6BpE,MAAMC,GAAyD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBpE,YACmBC,IAAiCL,IAClD;AADiB,SAAA,iBAAAK;AAAA,EAEnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EArBA,IAAI,OAAe;AACjB,WAAOH;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAgB;AAClB,WAAOC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,UAAU5B,GAAyB;AAEjC,QAAI,CAACA,EAAS;AACZ;AAGF,UAAM0B,IAAS1B,EAAS,SAAS;AAEjC,QAAI,MAAK,eAAe0B,CAAM;AAG9B,YAAM,IAAIF,EAA0BxB,CAAQ;AAAA,EAC9C;AACF;AC9HO,MAAM+B,UAAsBpD,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9C,YACkBqB,GAChBpB,GACA;AACA,UAAME,IACJF,KACAoB,EAAS,OAAO,WAChBA,EAAS,UAAU,cACnB,cAAcA,EAAS,QAAQ,GAAG;AACpC,UAAMlB,GAAckB,EAAS,KAAK,GARlB,KAAA,WAAAA,GAShB,KAAK,OAAO,iBACZ,OAAO,eAAe,MAAM+B,EAAc,SAAS;AAAA,EACrD;AACF;AAsCO,MAAMC,GAAmB;AAAA,EAAzB,cAAA;AAcL,SAAS,UAA+B,IAAId,EAAoB;AAAA,MAC9D,IAAInB,EAAA;AAAA,MACJ,IAAIQ,EAAA;AAAA,MACJ,IAAIK,EAAA;AAAA,IAAiB,CACtB,GAeD,KAAS,WAAgC,IAAIM,EAAoB;AAAA,MAC/D,IAAIW,GAAA;AAAA,IAA0B,CAC/B,GAaD,KAAS,QAA6B,IAAIX,EAAA;AAAA,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwF9D,MAAM,SAASe,GAAsD;AACnE,QAAI;AAEF,mBAAM,KAAK,QAAQ,UAAUA,CAAa,GAE1C,MAAM,KAAK,SAAS,UAAUA,CAAa,GACpCA;AAAA,IACT,SAASrC,GAAY;AAMnB,UAJAqC,EAAc,QAAQrC,GACtB,MAAM,KAAK,MAAM,UAAUqC,CAAa,GAGpC,CAACA,EAAc;AACjB,eAAOA;AAIT,YAAM,IAAIF,EAAcE,CAAa;AAAA,IACvC;AAAA,EACF;AACF;AC9MO,MAAMC,KAA0D,CACrElC,MAEOA,GAQImC,KAAqD,CAChEnC,MAEOA,EAAS,kBAQLoC,KAAqD,CAChEpC,MAEOA,EAAS,iBAAiB,KAAA,GAQtBqC,KAAwD,CACnErC,MAEOA,EAAS,iBAAiB,KAAA,GAGtBsC,IAAmB;AAAA,EAC9B,UAAUJ;AAAA,EACV,UAAUC;AAAA,EACV,MAAMC;AAAA,EACN,MAAMC;AACR;AC1CO,SAASE,EACdC,GACAC,GAC+B;AAE/B,MAAI,EAAAD,MAAU,UAAaC,MAAW;AAKtC,WAAIA,MAAW,SACND,IAILA,MAAU,SACLC,IAIF,EAAE,GAAGD,GAAO,GAAGC,EAAA;AACxB;AAQO,SAASC,GACdC,GACAC,GACgB;AAEhB,MADAA,0BAAY,IAAA,GACR,CAACD;AACH,WAAOC;AAET,MAAID,aAAkB,KAAK;AACzB,eAAW,CAAClF,GAAKC,CAAK,KAAKiF;AACzB,MAAAC,EAAI,IAAInF,GAAKC,CAAK;AAEpB,WAAOkF;AAAA,EACT;AACA,aAAW,CAACnF,GAAKC,CAAK,KAAK,OAAO,QAAQiF,CAAM;AAC9C,IAAAC,EAAI,IAAInF,GAAKC,CAAK;AAEpB,SAAOkF;AACT;ACeO,MAAMC,GAC4C;AAAA,EA+CvD,YAAYC,GAAiC;AAC3C,SAAK,UAAUA,EAAa,SAC5B,KAAK,UAAUA,EAAa,SAC5B,KAAK,kBACHA,EAAa,mBAAmBR,EAAiB,UACnD,KAAK,aAAaI,GAAiBI,EAAa,UAAU,GAC1D,KAAK,YAAYA,EAAa,UAC9B,KAAK,QAAQA,EAAa;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,uBAAuC;AACrC,WAAK,KAAK,QAAQ,YAChB,KAAK,QAAQ,UAAU,CAAA,IAElB,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,yBAA8C;AAC5C,WAAK,KAAK,QAAQ,cAChB,KAAK,QAAQ,YAAY;AAAA,MACvB,MAAM,CAAA;AAAA,MACN,OAAO,CAAA;AAAA,IAAC,IAGP,KAAK,QAAQ,UAAU,SAC1B,KAAK,QAAQ,UAAU,OAAO,CAAA,IAE3B,KAAK,QAAQ,UAAU,UAC1B,KAAK,QAAQ,UAAU,QAAQ,CAAA,IAE1B,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAoB;AAClB,WAAO,CAAC,CAAC,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,SAASC,GAAgC;AAC3C,SAAK,YAAYA,GACjB,KAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,WAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAuB;AACrB,WAAO,CAAC,CAAC,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,IAAI,mBAA6B;AAC/B,QAAI,CAAC,KAAK;AACR,YAAM,IAAIhB;AAAA,QACR;AAAA,QACA,cAAc,KAAK,QAAQ,GAAG;AAAA,MAAA;AAGlC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAmC;AACjC,WAAI,KAAK,0BAA0B,SAC1B,KAAK,yBAEd,KAAK,wBAAwB,KAAK,gBAAgB,IAAI,GAC/C,KAAK;AAAA,EACd;AACF;ACtNO,SAASiB,GACdR,GACAC,GACkB;AAElB,MAAI,OAAO,KAAKD,CAAK,EAAE,WAAW;AAChC,WAAOC;AAIT,MAAI,OAAO,KAAKA,CAAM,EAAE,WAAW;AACjC,WAAOD;AAIT,QAAMS,IAAuB;AAAA,IAC3B,MAAMV,EAAaC,EAAM,WAAW,MAAMC,EAAO,WAAW,IAAI;AAAA,IAChE,OAAOF,EAAaC,EAAM,WAAW,OAAOC,EAAO,WAAW,KAAK;AAAA,EAAA,GAG/DhC,IAAU;AAAA,IACd,GAAG+B,EAAM;AAAA,IACT,GAAGC,EAAO;AAAA,EAAA,GAINzD,IAASyD,EAAO,UAAUD,EAAM,QAChChC,IAAOiC,EAAO,QAAQD,EAAM,MAC5BlD,IAAUmD,EAAO,WAAWD,EAAM,SAClCU,IAAST,EAAO,UAAUD,EAAM,QAChCW,IAAkBV,EAAO,mBAAmBD,EAAM;AAExD,SAAO;AAAA,IACL,GAAGA;AAAA,IACH,GAAGC;AAAA,IACH,QAAAzD;AAAA,IACA,WAAAiE;AAAA,IACA,SAAAxC;AAAA,IACA,MAAAD;AAAA,IACA,SAAAlB;AAAA,IACA,QAAA4D;AAAA,IACA,iBAAAC;AAAA,EAAA;AAEJ;AASO,SAASC,EACdZ,GACAC,GACgB;AAChB,SAAIA,KAAUA,EAAO,mBAAmBA,EAAO,aACtCA,IAGF;AAAA,IACL,iBACEA,GAAQ,mBACRD,GAAO,mBACPa,EAAwB;AAAA,IAC1B,YAAYZ,GAAQ,cAAcD,GAAO;AAAA,EAAA;AAE7C;AChEA,MAAMc,IAAkC;AAAA,EACtC,CAACpD,CAAmB,GAAGE,EAAkB;AAC3C,GAEamD,IAAkC;AAAA,EAC7C,SAAS;AAAA,EACT,SAASD;AACX,GAUaD,IAA0C;AAAA,EACrD,iBAAiBf,EAAiB;AACpC,GACakB,IAAwC;AAAA,EACnD,iBAAiBlB,EAAiB;AACpC;AAqBO,MAAMmB,EACyD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBpE,YAAYC,IAA0BH,GAAiB;AAjBvD,SAAS,UAA2BD,GAkBlC,KAAK,aAAa,IAAIpF,EAAWwF,EAAQ,SAASA,EAAQ,gBAAgB,GAC1E,KAAK,UAAUA,EAAQ,WAAWJ,GAClC,KAAK,UAAUI,EAAQ,SACvB,KAAK,eAAeA,EAAQ,gBAAgB,IAAI1B,GAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SACJtD,GACAgF,GACwB;AAExB,UAAMC,IAAgB;AAAA,MACpB,GAAG,KAAK;AAAA,MACR,GAAGjF,EAAQ;AAAA,IAAA,GAGPkF,IAA6B;AAAA,MACjC,GAAGlF;AAAA,MACH,SAASiF;AAAA,MACT,SAASzE,EAAeR,EAAQ,SAAS,KAAK,OAAO;AAAA,IAAA,GAEjD,EAAE,iBAAAmF,GAAiB,YAAAC,EAAA,IAAeV;AAAA,MACtCC;AAAA,MACAK;AAAA,IAAA,GAEI1D,IAA0B,IAAI6C,GAAc;AAAA,MAChD,SAAS;AAAA,MACT,SAASe;AAAA,MACT,iBAAAC;AAAA,MACA,YAAAC;AAAA,IAAA,CACD;AACD,WAAO,KAAK,aAAa,SAAS9D,CAAQ;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,QACJtB,GACAgF,GACY;AAEZ,YADsB,MAAM,KAAK,SAAShF,GAASgF,CAAO,GACrC,cAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,MACJ/G,GACA+B,IAA4B,CAAA,GAC5BgF,GACY;AACZ,UAAMK,IAA8B;AAAA,MAClC,GAAGrF;AAAA,MACH,KAAA/B;AAAA,IAAA;AAEF,WAAO,KAAK;AAAA,MACVoH;AAAA,MACAX,EAAoBI,GAAuBE,CAAO;AAAA,IAAA;AAAA,EAEtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAc,YACZ1E,GACArC,GACA+B,IAA4B,CAAA,GAC5BgF,GACY;AACZ,UAAMK,IAA8B;AAAA,MAClC,GAAGrF;AAAA,MACH,KAAA/B;AAAA,MACA,QAAAqC;AAAA,IAAA;AAEF,WAAO,KAAK;AAAA,MACV+E;AAAA,MACAX,EAAoBI,GAAuBE,CAAO;AAAA,IAAA;AAAA,EAEtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,IACJ/G,GACA+B,IAAqD,CAAA,GACrDgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,KAAKtD,GAAK+B,GAASgF,CAAO;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,IACJ/G,GACA+B,IAA4C,CAAA,GAC5CgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,KAAKtD,GAAK+B,GAASgF,CAAO;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,KACJ/G,GACA+B,IAA4C,CAAA,GAC5CgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,MAAMtD,GAAK+B,GAASgF,CAAO;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,MACJ/G,GACA+B,IAA4C,CAAA,GAC5CgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,OAAOtD,GAAK+B,GAASgF,CAAO;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,OACJ/G,GACA+B,IAA4C,CAAA,GAC5CgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,QAAQtD,GAAK+B,GAASgF,CAAO;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,KACJ/G,GACA+B,IAAqD,CAAA,GACrDgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,MAAMtD,GAAK+B,GAASgF,CAAO;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,QACJ/G,GACA+B,IAAqD,CAAA,GACrDgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,SAAStD,GAAK+B,GAASgF,CAAO;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,MACJ/G,GACA+B,IAAqD,CAAA,GACrDgF,GACY;AACZ,WAAO,KAAK,YAAYzD,EAAW,OAAOtD,GAAK+B,GAASgF,CAAO;AAAA,EACjE;AACF;AC1bO,MAAMM,IAAuB;AAsB7B,MAAMC,GAAiB;AAAA,EAAvB,cAAA;AAKL,SAAQ,gCAAsC,IAAA;AAAA,EAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlD,SAAS3C,GAAc4C,GAAwB;AAC7C,SAAK,UAAU,IAAI5C,GAAM4C,CAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,WAAW5C,GAAuB;AAChC,WAAO,KAAK,UAAU,OAAOA,CAAI;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,IAAIA,GAAmC;AACrC,WAAO,KAAK,UAAU,IAAIA,CAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,YAAYA,GAAuB;AACjC,UAAM4C,IAAU,KAAK,IAAI5C,CAAI;AAC7B,QAAI,CAAC4C;AACH,YAAM,IAAI,MAAM,WAAW5C,CAAI,YAAY;AAE7C,WAAO4C;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAI,UAAmB;AACrB,WAAO,KAAK,YAAYF,CAAoB;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAI,QAAQE,GAAkB;AAC5B,SAAK,SAASF,GAAsBE,CAAO;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,IAAI,WAAiC;AACnC,WAAO,IAAI,IAAI,KAAK,SAAS;AAAA,EAC/B;AACF;AAgBO,MAAMC,IAAmB,IAAIF,GAAA;ACjI7B,SAASG,GACdF,GACAG,GACS;AAET,SAAKH,IAMEA,aAAmBT,IACtBS,IACAC,EAAiB,YAAYD,CAAO,IAP/BG,KAAkBF,EAAiB;AAQ9C;ACbO,MAAMG,WAAqBb,EAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBhE,YAAYnC,GAAcoC,IAA0BH,GAAiB;AACnE,UAAMG,CAAO,GACb,KAAK,OAAOpC,GACZ6C,EAAiB,SAAS7C,GAAM,IAAI;AAAA,EACtC;AACF;AAuBO,MAAM4C,KAAU,IAAII,GAAaN,CAAoB;"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/urls.ts","../src/urlTemplateResolver.ts","../src/urlBuilder.ts","../src/fetcherError.ts","../src/timeout.ts","../src/urlResolveInterceptor.ts","../src/fetchRequest.ts","../src/requestBodyInterceptor.ts","../src/fetchInterceptor.ts","../src/orderedCapable.ts","../src/interceptor.ts","../src/validateStatusInterceptor.ts","../src/interceptorManager.ts","../src/resultExtractor.ts","../src/utils.ts","../src/fetchExchange.ts","../src/mergeRequest.ts","../src/fetcher.ts","../src/fetcherRegistrar.ts","../src/fetcherCapable.ts","../src/namedFetcher.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the given URL is an absolute URL\n *\n * @param url - URL string to check\n * @returns boolean - Returns true if it's an absolute URL, false otherwise\n *\n * @example\n * ```typescript\n * isAbsoluteURL('https://api.example.com/users'); // true\n * isAbsoluteURL('/users'); // false\n * isAbsoluteURL('users'); // false\n * ```\n */\nexport function isAbsoluteURL(url: string) {\n return /^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(url);\n}\n\n/**\n * Combines a base URL and a relative URL into a complete URL\n *\n * @param baseURL - Base URL\n * @param relativeURL - Relative URL\n * @returns string - Combined complete URL\n *\n * @remarks\n * If the relative URL is already an absolute URL, it will be returned as-is.\n * Otherwise, the base URL and relative URL will be combined with proper path separator handling.\n *\n * @example\n * ```typescript\n * combineURLs('https://api.example.com', '/users'); // https://api.example.com/users\n * combineURLs('https://api.example.com/', 'users'); // https://api.example.com/users\n * combineURLs('https://api.example.com', 'https://other.com/users'); // https://other.com/users\n * ```\n */\nexport function combineURLs(baseURL: string, relativeURL: string) {\n if (isAbsoluteURL(relativeURL)) {\n return relativeURL;\n }\n // If relative URL exists, combine base URL and relative URL, otherwise return base URL\n return relativeURL\n ? baseURL.replace(/\\/?\\/$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Enumeration of URL template path styles.\n *\n * This enum defines the supported URL template path styles for parameter resolution.\n * Each style has its own pattern for defining path parameters in URL templates.\n */\nexport enum UrlTemplateStyle {\n /**\n * URI Template style following RFC 6570 specification.\n * Uses curly braces to define parameters, e.g., /users/{id}/posts/{postId}\n *\n * @see https://www.rfc-editor.org/rfc/rfc6570.html\n *\n * @see {@link UriTemplateResolver}\n */\n UriTemplate,\n\n /**\n * Express.js style for defining route parameters.\n * Uses colons to define parameters, e.g., /users/:id/posts/:postId\n *\n * @see {@link ExpressUrlTemplateResolver}\n */\n Express,\n}\n\n/**\n * Gets the appropriate URL template resolver based on the specified style.\n *\n * This factory function returns a URL template resolver instance based on the requested style.\n * If no style is specified or if the UriTemplate style is requested, it returns the default\n * URI template resolver. If Express style is requested, it returns the Express-style resolver.\n *\n * @param style - The URL template path style to use (optional).\n * If not provided, defaults to UriTemplate style.\n * @returns A UrlTemplateResolver instance corresponding to the requested style\n *\n * @example\n * ```typescript\n * // Get default URI template resolver\n * const resolver = getUrlTemplateResolver();\n *\n * // Get URI template resolver explicitly\n * const uriResolver = getUrlTemplateResolver(UrlTemplatePathStyle.UriTemplate);\n *\n * // Get Express-style resolver\n * const expressResolver = getUrlTemplateResolver(UrlTemplatePathStyle.Express);\n * ```\n */\nexport function getUrlTemplateResolver(\n style?: UrlTemplateStyle,\n): UrlTemplateResolver {\n if (style === UrlTemplateStyle.Express) {\n return expressUrlTemplateResolver;\n }\n return uriTemplateResolver;\n}\n\n/**\n * Interface for resolving URL templates by extracting path parameters and replacing placeholders.\n *\n * This interface provides methods to work with URL templates that contain parameter placeholders.\n * It supports extracting parameter names from templates and replacing placeholders with actual values.\n *\n * @example\n * ```typescript\n * // Example usage of UrlTemplateResolver\n * const resolver: UrlTemplateResolver = new UriTemplateResolver();\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport interface UrlTemplateResolver {\n /**\n * Extracts path parameters from the URL.\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n *\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[];\n\n /**\n * Replaces placeholders in the URL with path parameters.\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns The URL with placeholders replaced by actual values\n *\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // With Express-style resolver\n * const expressResolver: UrlTemplateResolver = new ExpressUrlTemplateResolver();\n * const expressUrl = expressResolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // expressUrl = '/users/123/posts/456'\n * ```\n *\n * @throws Error when required path parameters are missing\n * @example\n * ```typescript\n * const resolver: UrlTemplateResolver = uriTemplateResolver;\n * try {\n * resolver.resolve('/users/{id}', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(urlTemplate: string, pathParams?: Record<string, any> | null): string;\n}\n\n/**\n * Replaces placeholders in a URL template with actual parameter values.\n *\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParamRegex - Regular expression to match parameter placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns The URL with placeholders replaced by actual values\n * @throws Error when required path parameters are missing\n */\nexport function urlTemplateRegexResolve(\n urlTemplate: string,\n pathParamRegex: RegExp,\n pathParams?: Record<string, any> | null,\n) {\n if (!pathParams) return urlTemplate;\n return urlTemplate.replace(pathParamRegex, (_, key) => {\n const value = pathParams[key];\n // If path parameter is undefined, throw an error instead of preserving the placeholder\n if (value === undefined) {\n throw new Error(`Missing required path parameter: ${key}`);\n }\n return encodeURIComponent(value);\n });\n}\n\n/**\n * Extracts parameter names from a URL template using a regular expression.\n *\n * @param urlTemplate - The URL template string containing parameter placeholders\n * @param pathParamRegex - Regular expression to match parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n */\nexport function urlTemplateRegexExtract(\n urlTemplate: string,\n pathParamRegex: RegExp,\n): string[] {\n const matches: string[] = [];\n let match;\n while ((match = pathParamRegex.exec(urlTemplate)) !== null) {\n matches.push(match[1]);\n }\n return matches;\n}\n\n/**\n * https://www.rfc-editor.org/rfc/rfc6570.html\n *\n * Implementation of URI Template resolution following RFC 6570 specification.\n * Handles URI templates with parameters enclosed in curly braces like {paramName}.\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport class UriTemplateResolver implements UrlTemplateResolver {\n /**\n * Regular expression pattern to match path parameters in the format {paramName}\n *\n * This regex is used to identify and extract path parameters from URL patterns.\n * It matches any text enclosed in curly braces {} and captures the content inside.\n *\n * Example matches:\n * - {id} -> captures \"id\"\n * - {userId} -> captures \"userId\"\n * - {category-name} -> captures \"category-name\"\n */\n private static PATH_PARAM_REGEX = /{([^}]+)}/g;\n\n /**\n * Extracts path parameters from a URL string.\n *\n * @param urlTemplate - The URL string to extract path parameters from\n * @returns An array of path parameter names without the curly braces, or an empty array if no matches found\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Extract multiple parameters\n * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');\n * // params = ['id', 'postId']\n *\n * // Extract parameters from full URLs\n * const urlParams = resolver.extractPathParams('https://api.example.com/{resource}/{id}');\n * // urlParams = ['resource', 'id']\n *\n * // No parameters\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[] {\n return urlTemplateRegexExtract(\n urlTemplate,\n UriTemplateResolver.PATH_PARAM_REGEX,\n );\n }\n\n /**\n * Replaces placeholders in the URL with path parameters.\n *\n * @param urlTemplate - Path string containing placeholders, e.g., \"http://localhost/users/{id}/posts/{postId}\"\n * @param pathParams - Path parameter object used to replace placeholders in the URL\n * @returns Path string with placeholders replaced\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const resolver = uriTemplateResolver;\n *\n * // Replace parameters\n * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // Handle string parameter values\n * const stringUrl = resolver.resolve('/users/{username}', { username: 'john_doe' });\n * // stringUrl = '/users/john_doe'\n *\n * // URL encode parameter values\n * const encodedUrl = resolver.resolve('/search/{query}', { query: 'hello world' });\n * // encodedUrl = '/search/hello%20world'\n * ```\n *\n * @example\n * ```typescript\n * // Missing required parameter throws an error\n * const resolver = uriTemplateResolver;\n * try {\n * resolver.resolve('/users/{id}', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(\n urlTemplate: string,\n pathParams?: Record<string, any> | null,\n ): string {\n return urlTemplateRegexResolve(\n urlTemplate,\n UriTemplateResolver.PATH_PARAM_REGEX,\n pathParams,\n );\n }\n}\n\nexport const uriTemplateResolver = new UriTemplateResolver();\n\n/**\n * Express-style URL template resolver.\n * Handles URI templates with parameters in the format :paramName.\n *\n * @example\n * ```typescript\n * const resolver = expressUrlTemplateResolver;\n *\n * // Extract path parameters\n * const params = resolver.extractPathParams('/users/:id/posts/:postId');\n * // params = ['id', 'postId']\n *\n * // Resolve URL template with parameters\n * const url = resolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n * ```\n */\nexport class ExpressUrlTemplateResolver implements UrlTemplateResolver {\n /**\n * Regular expression pattern to match Express-style path parameters in the format :paramName\n */\n private static PATH_PARAM_REGEX = /:([^/]+)/g;\n\n /**\n * Extracts path parameters from an Express-style URL string.\n *\n * @param urlTemplate - The URL string with Express-style parameter placeholders\n * @returns An array of parameter names extracted from the URL template\n *\n * @example\n * ```typescript\n * const resolver = new expressUrlTemplateResolver;\n *\n * // Extract multiple parameters\n * const params = resolver.extractPathParams('/users/:id/posts/:postId');\n * // params = ['id', 'postId']\n *\n * // No parameters\n * const noParams = resolver.extractPathParams('/users/profile');\n * // noParams = []\n * ```\n */\n extractPathParams(urlTemplate: string): string[] {\n return urlTemplateRegexExtract(\n urlTemplate,\n ExpressUrlTemplateResolver.PATH_PARAM_REGEX,\n );\n }\n\n /**\n * Replaces Express-style placeholders in the URL with path parameters.\n *\n * @param urlTemplate - Path string containing Express-style placeholders\n * @param pathParams - Object containing parameter values to replace placeholders\n * @returns Path string with placeholders replaced\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const resolver = expressUrlTemplateResolver;\n *\n * // Replace parameters\n * const url = resolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });\n * // url = '/users/123/posts/456'\n *\n * // Handle string parameter values\n * const stringUrl = resolver.resolve('/users/:username', { username: 'john_doe' });\n * // stringUrl = '/users/john_doe'\n * ```\n *\n * @example\n * ```typescript\n * // Missing required parameter throws an error\n * const resolver = expressUrlTemplateResolver;\n * try {\n * resolver.resolve('/users/:id', { name: 'John' });\n * } catch (error) {\n * console.error(error.message); // \"Missing required path parameter: id\"\n * }\n * ```\n */\n resolve(\n urlTemplate: string,\n pathParams?: Record<string, any> | null,\n ): string {\n return urlTemplateRegexResolve(\n urlTemplate,\n ExpressUrlTemplateResolver.PATH_PARAM_REGEX,\n pathParams,\n );\n }\n}\n\nexport const expressUrlTemplateResolver = new ExpressUrlTemplateResolver();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { combineURLs } from './urls';\nimport type { BaseURLCapable, FetchRequest } from './fetchRequest';\nimport {\n getUrlTemplateResolver,\n type UrlTemplateResolver,\n UrlTemplateStyle,\n} from './urlTemplateResolver';\n\n/**\n * Container for URL parameters including path and query parameters.\n *\n * Used to define dynamic parts of a URL including path placeholders and query string parameters.\n */\nexport interface UrlParams {\n /**\n * Path parameter object used to replace placeholders in the URL (e.g., {id}).\n *\n * These parameters are used to substitute named placeholders in the URL path.\n *\n * @example\n * ```typescript\n * // For URL template '/users/{id}/posts/{postId}'\n * const path = { id: 123, postId: 456 };\n * ```\n */\n path?: Record<string, any>;\n\n /**\n * Query parameter object to be added to the URL query string.\n *\n * These parameters are appended to the URL as a query string.\n *\n * @example\n * ```typescript\n * const query = { filter: 'active', page: 1, limit: 10 };\n * // Results in query string: ?filter=active&page=1&limit=10\n * ```\n */\n query?: Record<string, any>;\n}\n\n/**\n * Utility class for constructing complete URLs with path parameters and query parameters.\n *\n * Handles URL composition, path parameter interpolation, and query string generation.\n * Combines a base URL with a path, replaces path placeholders with actual values, and appends\n * query parameters to create a complete URL.\n *\n * @example\n * ```typescript\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n * const url = urlBuilder.build('/users/{id}', {\n * path: { id: 123 },\n * query: { filter: 'active' }\n * });\n * // Result: https://api.example.com/users/123?filter=active\n * ```\n */\nexport class UrlBuilder implements BaseURLCapable {\n /**\n * Base URL that all constructed URLs will be based on.\n *\n * This is typically the root of your API endpoint (e.g., 'https://api.example.com').\n */\n readonly baseURL: string;\n readonly urlTemplateResolver: UrlTemplateResolver;\n\n /**\n * Initializes a new UrlBuilder instance.\n *\n * @param baseURL - Base URL that all constructed URLs will be based on\n * @param urlTemplateStyle - Optional style configuration for URL template resolution.\n * Determines how path parameters are parsed and resolved.\n * Defaults to UriTemplate style if not specified.\n *\n * @example\n * ```typescript\n * // Create a URL builder with default URI template style\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n *\n * // Create a URL builder with Express-style template resolution\n * const expressUrlBuilder = new UrlBuilder('https://api.example.com', UrlTemplateStyle.Express);\n * ```\n */\n constructor(baseURL: string, urlTemplateStyle?: UrlTemplateStyle) {\n this.baseURL = baseURL;\n this.urlTemplateResolver = getUrlTemplateResolver(urlTemplateStyle);\n }\n\n /**\n * Builds a complete URL, including path parameter replacement and query parameter addition.\n *\n * @param url - URL path to build (e.g., '/users/{id}/posts')\n * @param params - URL parameters including path and query parameters\n * @returns Complete URL string with base URL, path parameters interpolated, and query string appended\n * @throws Error when required path parameters are missing\n *\n * @example\n * ```typescript\n * const urlBuilder = new UrlBuilder('https://api.example.com');\n * const url = urlBuilder.build('/users/{id}/posts/{postId}', {\n * path: { id: 123, postId: 456 },\n * query: { filter: 'active', limit: 10 }\n * });\n * // Result: https://api.example.com/users/123/posts/456?filter=active&limit=10\n * ```\n */\n build(url: string, params?: UrlParams): string {\n const path = params?.path;\n const query = params?.query;\n const combinedURL = combineURLs(this.baseURL, url);\n let finalUrl = this.urlTemplateResolver.resolve(combinedURL, path);\n if (query) {\n const queryString = new URLSearchParams(query).toString();\n if (queryString) {\n finalUrl += '?' + queryString;\n }\n }\n return finalUrl;\n }\n\n /**\n * Resolves a complete URL from a FetchRequest.\n *\n * Used internally by the Fetcher to build the final URL for a request\n * by combining the request URL with its URL parameters using this UrlBuilder.\n *\n * @param request - The FetchRequest containing URL and URL parameters\n * @returns Complete resolved URL string\n */\n resolveRequestUrl(request: FetchRequest): string {\n return this.build(request.url, request.urlParams);\n }\n}\n\n/**\n * Interface for objects that have a UrlBuilder capability.\n *\n * Indicates that an object has a UrlBuilder instance for URL construction.\n */\nexport interface UrlBuilderCapable {\n /**\n * The UrlBuilder instance.\n */\n urlBuilder: UrlBuilder;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Base error class for all Fetcher-related errors.\n *\n * This class extends the native Error class and provides a foundation for\n * all custom errors thrown by the Fetcher library. It includes support for\n * error chaining through the cause property.\n *\n * @example\n * ```typescript\n * try {\n * await fetcher.get('/api/users');\n * } catch (error) {\n * if (error instanceof FetcherError) {\n * console.log('Fetcher error:', error.message);\n * if (error.cause) {\n * console.log('Caused by:', error.cause);\n * }\n * }\n * }\n * ```\n */\nexport class FetcherError extends Error {\n /**\n * Creates a new FetcherError instance.\n *\n * @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.\n * @param cause - Optional underlying error that caused this error.\n */\n constructor(\n errorMsg?: string,\n public readonly cause?: Error | any,\n ) {\n const errorMessage =\n errorMsg || cause?.message || 'An error occurred in the fetcher';\n super(errorMessage);\n this.name = 'FetcherError';\n\n // Copy stack trace from cause if available\n if (cause?.stack) {\n this.stack = cause.stack;\n }\n\n // Set prototype for instanceof checks to work correctly\n Object.setPrototypeOf(this, FetcherError.prototype);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type FetchRequest } from './fetchRequest';\nimport { FetcherError } from './fetcherError';\n\n/**\n * Exception class thrown when an HTTP request times out.\n *\n * This error is thrown by the timeoutFetch function when a request exceeds its timeout limit.\n *\n * @example\n * ```typescript\n * try {\n * const response = await timeoutFetch('https://api.example.com/users', {}, 1000);\n * } catch (error) {\n * if (error instanceof FetchTimeoutError) {\n * console.log(`Request timed out after ${error.timeout}ms`);\n * }\n * }\n * ```\n */\nexport class FetchTimeoutError extends FetcherError {\n /**\n * The request options that timed out.\n */\n request: FetchRequest;\n\n /**\n * Creates a new FetchTimeoutError instance.\n *\n * @param request - The request options that timed out\n */\n constructor(request: FetchRequest) {\n const method = request.method || 'GET';\n const message = `Request timeout of ${request.timeout}ms exceeded for ${method} ${request.url}`;\n super(message);\n this.name = 'FetchTimeoutError';\n this.request = request;\n // Fix prototype chain\n Object.setPrototypeOf(this, FetchTimeoutError.prototype);\n }\n}\n\n/**\n * Interface that defines timeout capability for HTTP requests.\n *\n * Objects implementing this interface can specify timeout values for HTTP requests.\n */\nexport interface TimeoutCapable {\n /**\n * Request timeout in milliseconds.\n *\n * When the value is 0, it indicates no timeout should be set.\n * The default value is undefined.\n */\n timeout?: number;\n}\n\n/**\n * Resolves request timeout settings, prioritizing request-level timeout settings.\n *\n * @param requestTimeout - Request-level timeout setting\n * @param optionsTimeout - Configuration-level timeout setting\n * @returns Resolved timeout setting\n *\n * @remarks\n * If requestTimeout is defined, it takes precedence over optionsTimeout.\n * Otherwise, optionsTimeout is returned. If both are undefined, undefined is returned.\n */\nexport function resolveTimeout(\n requestTimeout?: number,\n optionsTimeout?: number,\n): number | undefined {\n if (typeof requestTimeout !== 'undefined') {\n return requestTimeout;\n }\n return optionsTimeout;\n}\n\n/**\n * Executes an HTTP request with optional timeout support.\n *\n * This function provides a wrapper around the native fetch API with added timeout functionality.\n * - If a timeout is specified, it will create an AbortController to cancel the request if it exceeds the timeout.\n * - If the request already has a signal, it will delegate to the native fetch API directly to avoid conflicts.\n * - If the request has an abortController, it will be used instead of creating a new one.\n *\n * @param request - The request configuration including URL, method, headers, body, and optional timeout\n * @returns Promise that resolves to the Response object\n * @throws FetchTimeoutError if the request times out\n * @throws TypeError for network errors\n *\n * @example\n * ```typescript\n * // With timeout\n * try {\n * const response = await timeoutFetch('https://api.example.com/users', { method: 'GET' }, 5000);\n * console.log('Request completed successfully');\n * } catch (error) {\n * if (error instanceof FetchTimeoutError) {\n * console.log(`Request timed out after ${error.timeout}ms`);\n * }\n * }\n *\n * // Without timeout (delegates to regular fetch)\n * const response = await timeoutFetch('https://api.example.com/users', { method: 'GET' });\n * ```\n */\nexport async function timeoutFetch(request: FetchRequest): Promise<Response> {\n const url = request.url;\n const timeout = request.timeout;\n const requestInit = request as RequestInit;\n\n // If the request already has a signal, delegate to native fetch to avoid conflicts\n if (request.signal) {\n return fetch(url, requestInit);\n }\n\n // Extract timeout from request\n if (!timeout) {\n // When no timeout is set, but an abortController is provided, use its signal\n if (request.abortController) {\n requestInit.signal = request.abortController.signal;\n }\n return fetch(url, requestInit);\n }\n\n // Create AbortController for fetch request cancellation\n const controller = request.abortController ?? new AbortController();\n request.abortController = controller;\n requestInit.signal = controller.signal;\n\n // Timer resource management\n let timerId: ReturnType<typeof setTimeout> | null = null;\n // Create timeout Promise that rejects after specified time\n const timeoutPromise = new Promise<Response>((_, reject) => {\n timerId = setTimeout(() => {\n // Clean up timer resources and handle timeout error\n if (timerId) {\n clearTimeout(timerId);\n }\n const error = new FetchTimeoutError(request);\n controller.abort(error);\n reject(error);\n }, timeout);\n });\n\n try {\n // Race between fetch request and timeout Promise\n return await Promise.race([fetch(url, requestInit), timeoutPromise]);\n } finally {\n // Clean up timer resources\n if (timerId) {\n clearTimeout(timerId);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * The name of the UrlResolveInterceptor.\n */\nexport const URL_RESOLVE_INTERCEPTOR_NAME = 'UrlResolveInterceptor';\n\n/**\n * The order of the UrlResolveInterceptor.\n * Set to Number.MIN_SAFE_INTEGER + 1000 to ensure it runs earliest among request interceptors.\n */\nexport const URL_RESOLVE_INTERCEPTOR_ORDER = Number.MIN_SAFE_INTEGER + 1000;\n\n/**\n * Interceptor responsible for resolving the final URL for a request.\n *\n * This interceptor combines the base URL, path parameters, and query parameters\n * to create the final URL for a request. It should be executed earliest in\n * the interceptor chain to ensure the URL is properly resolved before other interceptors\n * process the request.\n *\n * @remarks\n * This interceptor runs at the very beginning of the request interceptor chain to ensure\n * URL resolution happens before any other request processing. The order is set to\n * URL_RESOLVE_INTERCEPTOR_ORDER to ensure it executes before all other request interceptors,\n * establishing the foundation for subsequent processing.\n *\n * @example\n * // With baseURL: 'https://api.example.com'\n * // Request URL: '/users/{id}'\n * // Path params: { id: 123 }\n * // Query params: { filter: 'active' }\n * // Final URL: 'https://api.example.com/users/123?filter=active'\n */\nexport class UrlResolveInterceptor implements RequestInterceptor {\n /**\n * The name of this interceptor.\n */\n readonly name = URL_RESOLVE_INTERCEPTOR_NAME;\n\n /**\n * The order of this interceptor (executed earliest).\n *\n * This interceptor should run at the very beginning of the request interceptor chain to ensure\n * URL resolution happens before any other request processing. The order is set to\n * URL_RESOLVE_INTERCEPTOR_ORDER to ensure it executes before all other request interceptors,\n * establishing the foundation for subsequent processing.\n */\n readonly order = URL_RESOLVE_INTERCEPTOR_ORDER;\n\n /**\n * Resolves the final URL by combining the base URL, path parameters, and query parameters.\n *\n * @param exchange - The fetch exchange containing the request information\n */\n intercept(exchange: FetchExchange) {\n const request = exchange.request;\n request.url = exchange.fetcher.urlBuilder.resolveRequestUrl(request);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type TimeoutCapable } from './timeout';\nimport { type UrlParams } from './urlBuilder';\n\n/**\n * Interface for objects that can have a base URL\n *\n * This interface defines a baseURL property that can be used to set a base URL\n * for HTTP requests. When the baseURL is empty, it means no base URL is set.\n */\nexport interface BaseURLCapable {\n /**\n * The base URL for requests\n * When empty, indicates no base URL is set. Default is undefined.\n */\n baseURL: string;\n}\n\n/**\n * HTTP method enumeration constants\n *\n * Defines the standard HTTP methods that can be used for requests.\n * Each method is represented as a string literal type.\n */\nexport enum HttpMethod {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n DELETE = 'DELETE',\n PATCH = 'PATCH',\n HEAD = 'HEAD',\n OPTIONS = 'OPTIONS',\n TRACE = 'TRACE',\n}\n\nexport const CONTENT_TYPE_HEADER = 'Content-Type';\n\nexport class ContentTypeValues {\n static readonly APPLICATION_JSON = 'application/json';\n static readonly TEXT_EVENT_STREAM = 'text/event-stream';\n}\n\n/**\n * Request headers interface\n *\n * Defines common HTTP headers that can be sent with requests.\n * Allows for additional custom headers through index signature.\n */\nexport interface RequestHeaders {\n [CONTENT_TYPE_HEADER]?: string;\n Accept?: string;\n Authorization?: string;\n\n [key: string]: string | undefined;\n}\n\n/**\n * Interface for objects that can have request headers\n *\n * This interface defines an optional headers property for HTTP requests.\n */\nexport interface RequestHeadersCapable {\n /**\n * Request headers\n */\n headers?: RequestHeaders;\n}\n\nexport type RequestBodyType = BodyInit | Record<string, any> | string | null;\n\n/**\n * Fetcher request configuration interface\n *\n * This interface defines all the configuration options available for making HTTP requests\n * with the Fetcher client. It extends the standard RequestInit interface while adding\n * Fetcher-specific features like path parameters, query parameters, and timeout control.\n *\n * @example\n * ```typescript\n * const request: FetchRequestInit = {\n * method: 'GET',\n * urlParams: {\n * path: { id: 123 },\n * query: { include: 'profile' }\n * },\n * headers: { 'Authorization': 'Bearer token' },\n * timeout: 5000\n * };\n *\n * const response = await fetcher.fetch('/users/{id}', request);\n * ```\n */\nexport interface FetchRequestInit<BODY extends RequestBodyType = RequestBodyType>\n extends TimeoutCapable,\n RequestHeadersCapable,\n Omit<RequestInit, 'body' | 'headers'> {\n urlParams?: UrlParams;\n\n /**\n * Request body\n *\n * The body of the request. Can be a string, Blob, ArrayBuffer, FormData,\n * URLSearchParams, or a plain object. Plain objects are automatically\n * converted to JSON and the appropriate Content-Type header is set.\n *\n * @example\n * ```typescript\n * // Plain object (automatically converted to JSON)\n * const request = {\n * method: 'POST',\n * body: { name: 'John', email: 'john@example.com' }\n * };\n *\n * // FormData\n * const formData = new FormData();\n * formData.append('name', 'John');\n * const request = {\n * method: 'POST',\n * body: formData\n * };\n * ```\n */\n body?: BODY;\n\n /**\n * AbortController for this request.\n * Used to cancel the request if needed.\n *\n * In timeout scenarios, if this property is provided, it will be used instead of creating a new AbortController.\n * This allows the caller to provide a custom AbortController for more advanced cancellation scenarios.\n *\n * @example\n * ```typescript\n * // Provide a custom AbortController\n * const controller = new AbortController();\n * const request: FetchRequest = {\n * url: 'https://api.example.com/data',\n * method: 'GET',\n * abortController: controller\n * };\n *\n * // Later, cancel the request\n * controller.abort();\n * ```\n */\n abortController?: AbortController;\n}\n\n/**\n * Fetcher request interface\n *\n * Extends FetchRequestInit with a required URL property.\n * Represents a complete request configuration ready to be executed.\n */\nexport interface FetchRequest<BODY extends RequestBodyType = RequestBodyType> extends FetchRequestInit<BODY> {\n /**\n * The URL for this request\n */\n url: string;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { CONTENT_TYPE_HEADER, ContentTypeValues } from './fetchRequest';\nimport { URL_RESOLVE_INTERCEPTOR_ORDER } from './urlResolveInterceptor';\n\n/**\n * The name of the RequestBodyInterceptor.\n */\nexport const REQUEST_BODY_INTERCEPTOR_NAME = 'RequestBodyInterceptor';\n\n/**\n * The order of the RequestBodyInterceptor.\n * Set to URL_RESOLVE_INTERCEPTOR_ORDER + 1000 to ensure it runs early among request interceptors.\n */\nexport const REQUEST_BODY_INTERCEPTOR_ORDER =\n URL_RESOLVE_INTERCEPTOR_ORDER + 1000;\n\n/**\n * Interceptor responsible for converting plain objects to JSON strings for HTTP request bodies.\n *\n * This interceptor ensures that object request bodies are properly serialized and that\n * the appropriate Content-Type header is set. It runs early in the request processing chain\n * to ensure request bodies are properly formatted before other interceptors process them.\n *\n * @remarks\n * This interceptor runs after URL resolution (UrlResolveInterceptor) but before\n * the actual HTTP request is made (FetchInterceptor). The order is set to\n * REQUEST_BODY_INTERCEPTOR_ORDER to ensure it executes in the correct position\n * in the interceptor chain, allowing for other interceptors to run between URL resolution\n * and request body processing. This positioning ensures that URL parameters are resolved\n * first, then request bodies are properly formatted, and finally the HTTP request is executed.\n */\nexport class RequestBodyInterceptor implements RequestInterceptor {\n /**\n * Interceptor name, used for identification and management.\n */\n readonly name = REQUEST_BODY_INTERCEPTOR_NAME;\n\n /**\n * Interceptor execution order, set to run after UrlResolveInterceptor but before FetchInterceptor.\n *\n * This interceptor should run after URL resolution (UrlResolveInterceptor) but before\n * the actual HTTP request is made (FetchInterceptor). The order is set to\n * REQUEST_BODY_INTERCEPTOR_ORDER to ensure it executes in the correct position\n * in the interceptor chain, allowing for other interceptors to run between URL resolution\n * and request body processing. This positioning ensures that URL parameters are resolved\n * first, then request bodies are properly formatted, and finally the HTTP request is executed.\n */\n readonly order = REQUEST_BODY_INTERCEPTOR_ORDER;\n\n /**\n * Checks if the provided body is of a supported complex type that doesn't require JSON serialization.\n *\n * @param body - The request body to check\n * @returns True if the body is an ArrayBuffer, TypedArray, DataView or ReadableStream, false otherwise\n */\n private isSupportedComplexBodyType(body: any): boolean {\n return (\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body) ||\n body instanceof ReadableStream\n );\n }\n\n /**\n * Checks if the provided body is of a type that automatically appends Content-Type header.\n *\n * @param body - The request body to check\n * @returns True if the body is a Blob, File, FormData or URLSearchParams, false otherwise\n */\n private isAutoAppendContentType(body: any): boolean {\n return (\n body instanceof Blob ||\n body instanceof File ||\n body instanceof FormData ||\n body instanceof URLSearchParams\n );\n }\n\n /**\n * Attempts to convert request body to a valid fetch API body type.\n *\n * This method follows a specific processing order to handle different types of request bodies:\n * 1. Check if the body is null or undefined and return early if so\n * 2. Check if the body is a non-object type and return early if so\n * 3. Check if the body is a type that automatically appends Content-Type header\n * 4. Check if the body is a supported complex type that doesn't require JSON serialization\n * 5. For plain objects, convert to JSON string and set Content-Type header to application/json\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#setting_a_body}\n *\n * Supported types:\n * - a string\n * - ArrayBuffer\n * - TypedArray\n * - DataView\n * - Blob\n * - File\n * - URLSearchParams\n * - FormData\n * - ReadableStream\n *\n * For unsupported object types (like plain objects), they will be automatically\n * converted to JSON strings.\n *\n * @param exchange - The exchange object containing the request to process\n *\n * @example\n * // Plain object body will be converted to JSON\n * const fetcher = new Fetcher();\n * const exchange = new FetchExchange(\n * fetcher,\n * {\n * body: { name: 'John', age: 30 }\n * }\n * );\n * interceptor.intercept(exchange);\n * // exchange.request.body will be '{\"name\":\"John\",\"age\":30}'\n * // exchange.request.headers will include 'Content-Type: application/json'\n */\n intercept(exchange: FetchExchange) {\n const request = exchange.request;\n // If there's no request body, return unchanged\n if (request.body === undefined || request.body === null) {\n return;\n }\n\n // If request body is not an object, return unchanged\n if (typeof request.body !== 'object') {\n return;\n }\n const headers = exchange.ensureRequestHeaders();\n if (this.isAutoAppendContentType(request.body)) {\n if (headers[CONTENT_TYPE_HEADER]) {\n delete headers[CONTENT_TYPE_HEADER];\n }\n return;\n }\n // Check if it's a supported type\n if (this.isSupportedComplexBodyType(request.body)) {\n return;\n }\n\n // For plain objects, convert to JSON string\n // Also ensure Content-Type header is set to application/json\n exchange.request.body = JSON.stringify(request.body);\n\n if (!headers[CONTENT_TYPE_HEADER]) {\n headers[CONTENT_TYPE_HEADER] = ContentTypeValues.APPLICATION_JSON;\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type RequestInterceptor } from './interceptor';\nimport { timeoutFetch } from './timeout';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * The name of the FetchInterceptor.\n */\nexport const FETCH_INTERCEPTOR_NAME = 'FetchInterceptor';\n\n/**\n * The order of the FetchInterceptor.\n * Set to Number.MAX_SAFE_INTEGER - 1000 to ensure it runs latest among request interceptors.\n */\nexport const FETCH_INTERCEPTOR_ORDER = Number.MAX_SAFE_INTEGER - 1000;\n\n/**\n * Interceptor implementation responsible for executing actual HTTP requests.\n *\n * This is an interceptor implementation responsible for executing actual HTTP requests\n * and handling timeout control. It is the latest interceptor in the Fetcher request\n * processing chain, ensuring that the actual network request is executed after all\n * previous interceptors have completed processing.\n *\n * @remarks\n * This interceptor runs at the very end of the request interceptor chain to ensure\n * that the actual HTTP request is executed after all other request processing is complete.\n * The order is set to FETCH_INTERCEPTOR_ORDER to ensure it executes after all other\n * request interceptors, completing the request processing pipeline before the network\n * request is made. This positioning ensures that all request preprocessing is\n * completed before the actual network request is made.\n *\n * @example\n * // Usually not created manually as Fetcher uses it automatically\n * const fetcher = new Fetcher();\n * // FetchInterceptor is automatically registered in fetcher.interceptors.request\n */\nexport class FetchInterceptor implements RequestInterceptor {\n /**\n * Interceptor name, used to identify and manage interceptor instances.\n *\n * Each interceptor must have a unique name for identification and manipulation\n * within the interceptor manager.\n */\n readonly name = FETCH_INTERCEPTOR_NAME;\n\n /**\n * Interceptor execution order, set to near maximum safe integer to ensure latest execution.\n *\n * Since this is the interceptor that actually executes HTTP requests, it should\n * execute after all other request interceptors, so its order is set to\n * FETCH_INTERCEPTOR_ORDER. This ensures that all request preprocessing is\n * completed before the actual network request is made, while still allowing\n * other interceptors to run after it if needed. The positioning at the end\n * of the request chain ensures that all transformations and validations are\n * completed before the network request is executed.\n */\n readonly order = FETCH_INTERCEPTOR_ORDER;\n\n /**\n * Intercept and process HTTP requests.\n *\n * Executes the actual HTTP request and applies timeout control. This is the final\n * step in the request processing chain, responsible for calling the timeoutFetch\n * function to send the network request.\n *\n * @param exchange - Exchange object containing request information\n *\n * @throws {FetchTimeoutError} Throws timeout exception when request times out\n *\n * @example\n * // Usually called internally by Fetcher\n * const fetcher = new Fetcher();\n * const exchange = new FetchExchange(\n * fetcher,\n * {\n * url: 'https://api.example.com/users',\n * method: 'GET',\n * timeout: 5000\n * }\n * );\n * await fetchInterceptor.intercept(exchange);\n * console.log(exchange.response); // HTTP response object\n */\n async intercept(exchange: FetchExchange) {\n exchange.response = await timeoutFetch(exchange.request);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OrderedCapable Interface\n *\n * Interface that provides ordering capability for types that implement it.\n * Implementing types must provide an order property to determine execution order.\n * Lower numerical values have higher priority, and elements with the same value\n * maintain their relative order.\n */\nexport interface OrderedCapable {\n /**\n * Order value\n *\n * Lower numerical values have higher priority. Negative numbers, zero, and\n * positive numbers are all supported.\n * When multiple elements have the same order value, their relative order\n * will remain unchanged (stable sort).\n */\n order: number;\n}\n\n/**\n * Sorts an array of elements that implement the OrderedCapable interface\n *\n * This function creates and returns a new sorted array without modifying the\n * original array. It supports an optional filter function to select elements\n * that should participate in sorting.\n *\n * @template T - Array element type that must implement the OrderedCapable interface\n * @param array - The array to be sorted\n * @param filter - Optional filter function to select elements that should be sorted\n * @returns A new array sorted in ascending order by the order property\n *\n * @example\n * ```typescript\n * const items: OrderedCapable[] = [\n * { order: 10 },\n * { order: 5 },\n * { order: 1 },\n * ];\n *\n * const sortedItems = toSorted(items);\n * // Result: [{ order: 1 }, { order: 5 }, { order: 10 }]\n *\n * // Using filter function\n * const filteredAndSorted = toSorted(items, item => item.order > 3);\n * // Result: [{ order: 5 }, { order: 10 }]\n * ```\n */\nexport function toSorted<T extends OrderedCapable>(\n array: T[],\n filter?: (item: T) => boolean,\n): T[] {\n if (filter) {\n return array.filter(filter).sort((a, b) => a.order - b.order);\n }\n return [...array].sort((a, b) => a.order - b.order);\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type NamedCapable } from './types';\nimport { type OrderedCapable, toSorted } from './orderedCapable';\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * Interface for HTTP interceptors in the fetcher pipeline.\n *\n * Interceptors are middleware components that can modify requests, responses, or handle errors\n * at different stages of the HTTP request lifecycle. They follow the Chain of Responsibility\n * pattern, where each interceptor can process the exchange object and pass it to the next.\n *\n * @example\n * // Example of a custom request interceptor\n * const customRequestInterceptor: Interceptor = {\n * name: 'CustomRequestInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * // Modify request headers\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'X-Custom-Header': 'custom-value'\n * };\n * }\n * };\n */\nexport interface Interceptor extends NamedCapable, OrderedCapable {\n /**\n * Unique identifier for the interceptor.\n *\n * Used by InterceptorRegistry to manage interceptors, including adding, removing,\n * and preventing duplicates. Each interceptor must have a unique name.\n */\n readonly name: string;\n\n /**\n * Interceptor method that modifies the request or response.\n *\n * @param exchange - The current exchange object, which contains the request and response.\n * @returns A promise that resolves to the modified exchange object.\n */\n readonly order: number;\n\n /**\n * Process the exchange object in the interceptor pipeline.\n *\n * This method is called by InterceptorRegistry to process the exchange object.\n * Interceptors can modify request, response, or error properties directly.\n *\n * @param exchange - The exchange object containing request, response, and error information\n *\n * @remarks\n * Interceptors should modify the exchange object directly rather than returning it.\n * They can also throw errors or transform errors into responses.\n */\n intercept(exchange: FetchExchange): void | Promise<void>;\n}\n\n/**\n * Interface for request interceptors.\n *\n * Request interceptors are executed before the HTTP request is sent.\n * They can modify the request configuration, add headers, or perform\n * other preprocessing tasks.\n *\n * @example\n * // Example of a request interceptor that adds an authorization header\n * const authInterceptor: RequestInterceptor = {\n * name: 'AuthorizationInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * const token = getAuthToken();\n * if (token) {\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'Authorization': `Bearer ${token}`\n * };\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface RequestInterceptor extends Interceptor {\n}\n\n/**\n * Interface for response interceptors.\n *\n * Response interceptors are executed after the HTTP response is received\n * but before it's processed by the application. They can modify the response,\n * transform data, or handle response-specific logic.\n *\n * @example\n * // Example of a response interceptor that parses JSON data\n * const jsonInterceptor: ResponseInterceptor = {\n * name: 'JsonResponseInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * if (exchange.response && exchange.response.headers.get('content-type')?.includes('application/json')) {\n * const data = await exchange.response.json();\n * // Attach parsed data to a custom property\n * (exchange.response as any).jsonData = data;\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ResponseInterceptor extends Interceptor {\n}\n\n/**\n * Interface for error interceptors.\n *\n * Error interceptors are executed when an HTTP request fails.\n * They can handle errors, transform them, or implement retry logic.\n *\n * @example\n * // Example of an error interceptor that retries failed requests\n * const retryInterceptor: ErrorInterceptor = {\n * name: 'RetryInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<void> {\n * if (exchange.error && isRetryableError(exchange.error)) {\n * // Implement retry logic\n * const retryCount = (exchange.request as any).retryCount || 0;\n * if (retryCount < 3) {\n * (exchange.request as any).retryCount = retryCount + 1;\n * // Retry the request\n * exchange.response = await fetch(exchange.request);\n * // Clear the error since we've recovered\n * exchange.error = undefined;\n * }\n * }\n * }\n * };\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ErrorInterceptor extends Interceptor {\n}\n\n/**\n * Registry for a collection of interceptors of the same type.\n *\n * Handles adding, removing, and executing interceptors in the correct order.\n * Each InterceptorRegistry instance manages one type of interceptor (request, response, or error).\n *\n * @remarks\n * Interceptors are executed in ascending order of their `order` property.\n * Interceptors with the same order value are executed in the order they were added.\n *\n * @example\n * // Create an interceptor registry with initial interceptors\n * const requestRegistry = new InterceptorRegistry([interceptor1, interceptor2]);\n *\n * // Add a new interceptor\n * requestRegistry.use(newInterceptor);\n *\n * // Remove an interceptor by name\n * requestRegistry.eject('InterceptorName');\n *\n * // Process an exchange through all interceptors\n * const result = await requestRegistry.intercept(exchange);\n */\nexport class InterceptorRegistry implements Interceptor {\n /**\n * Gets the name of this interceptor registry.\n *\n * @returns The constructor name of this class\n */\n get name(): string {\n return this.constructor.name;\n }\n\n /**\n * Gets the order of this interceptor registry.\n *\n * @returns Number.MIN_SAFE_INTEGER, indicating this registry should execute early\n */\n get order(): number {\n return Number.MIN_SAFE_INTEGER;\n }\n\n /**\n * Array of interceptors managed by this registry, sorted by their order property.\n */\n private sortedInterceptors: Interceptor[] = [];\n\n /**\n * Initializes a new InterceptorRegistry instance.\n *\n * @param interceptors - Initial array of interceptors to manage\n *\n * @remarks\n * The provided interceptors will be sorted by their order property immediately\n * upon construction.\n */\n constructor(interceptors: Interceptor[] = []) {\n this.sortedInterceptors = toSorted(interceptors);\n }\n\n /**\n * Returns an array of all interceptors in the registry.\n */\n get interceptors(): Interceptor[] {\n return [...this.sortedInterceptors];\n }\n\n /**\n * Adds an interceptor to this registry.\n *\n * @param interceptor - The interceptor to add\n * @returns True if the interceptor was added, false if an interceptor with the\n * same name already exists\n *\n * @remarks\n * Interceptors are uniquely identified by their name property. Attempting to add\n * an interceptor with a name that already exists in the registry will fail.\n *\n * After adding, interceptors are automatically sorted by their order property.\n */\n use(interceptor: Interceptor): boolean {\n if (this.sortedInterceptors.some(item => item.name === interceptor.name)) {\n return false;\n }\n this.sortedInterceptors = toSorted([\n ...this.sortedInterceptors,\n interceptor,\n ]);\n return true;\n }\n\n /**\n * Removes an interceptor by name.\n *\n * @param name - The name of the interceptor to remove\n * @returns True if an interceptor was removed, false if no interceptor with the\n * given name was found\n */\n eject(name: string): boolean {\n const original = this.sortedInterceptors;\n this.sortedInterceptors = toSorted(\n original,\n interceptor => interceptor.name !== name,\n );\n return original.length !== this.sortedInterceptors.length;\n }\n\n /**\n * Removes all interceptors from this registry.\n */\n clear(): void {\n this.sortedInterceptors = [];\n }\n\n /**\n * Executes all managed interceptors on the given exchange object.\n *\n * @param exchange - The exchange object to process\n * @returns A promise that resolves when all interceptors have been executed\n *\n * @remarks\n * Interceptors are executed in order, with each interceptor receiving the result\n * of the previous interceptor. The first interceptor receives the original\n * exchange object.\n *\n * If any interceptor throws an error, the execution chain is broken and the error\n * is propagated to the caller.\n */\n async intercept(exchange: FetchExchange): Promise<void> {\n for (const interceptor of this.sortedInterceptors) {\n // Each interceptor processes the output of the previous interceptor\n await interceptor.intercept(exchange);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type ResponseInterceptor } from './interceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { FetcherError } from './fetcherError';\n\n/**\n * Error thrown when response status validation fails.\n *\n * This error is thrown by ValidateStatusInterceptor when the response status\n * does not pass the validation defined by the validateStatus function.\n */\nexport class HttpStatusValidationError extends FetcherError {\n constructor(public readonly exchange: FetchExchange) {\n super(\n `Request failed with status code ${exchange.response?.status} for ${exchange.request.url}`,\n );\n this.name = 'HttpStatusValidationError';\n Object.setPrototypeOf(this, HttpStatusValidationError.prototype);\n }\n}\n\n/**\n * Defines whether to resolve or reject the promise for a given HTTP response status code.\n * If `validateStatus` returns `true` (or is set to `null` or `undefined`), the promise will be resolved;\n * otherwise, the promise will be rejected.\n *\n * @param status - HTTP response status code\n * @returns true to resolve the promise, false to reject it\n *\n * @example\n * ```typescript\n * // Default behavior (2xx status codes are resolved)\n * const fetcher = new Fetcher();\n *\n * // Custom behavior (only 200 status code is resolved)\n * const fetcher = new Fetcher({\n * validateStatus: (status) => status === 200\n * });\n *\n * // Always resolve (never reject based on status)\n * const fetcher = new Fetcher({\n * validateStatus: (status) => true\n * });\n * ```\n */\ntype ValidateStatus = (status: number) => boolean;\n\nconst DEFAULT_VALIDATE_STATUS: ValidateStatus = (status: number) =>\n status >= 200 && status < 300;\n\n/**\n * The name of the ValidateStatusInterceptor.\n */\nexport const VALIDATE_STATUS_INTERCEPTOR_NAME = 'ValidateStatusInterceptor';\n\n/**\n * The order of the ValidateStatusInterceptor.\n * Set to Number.MAX_SAFE_INTEGER - 1000 to ensure it runs latest among response interceptors,\n * but still allows other interceptors to run after it if needed.\n */\nexport const VALIDATE_STATUS_INTERCEPTOR_ORDER = Number.MAX_SAFE_INTEGER - 1000;\n\n/**\n * Response interceptor that validates HTTP status codes.\n *\n * This interceptor implements behavior similar to axios's validateStatus option.\n * It checks the response status code against a validation function and throws\n * an error if the status is not valid.\n *\n * @remarks\n * This interceptor runs at the very beginning of the response interceptor chain to ensure\n * status validation happens before any other response processing. The order is set to\n * VALIDATE_STATUS_INTERCEPTOR_ORDER to ensure it executes early in the response chain,\n * allowing for other response interceptors to run after it if needed. This positioning\n * ensures that invalid responses are caught and handled early in the response processing\n * pipeline, before other response handlers attempt to process them.\n *\n * @example\n * ```typescript\n * // Default behavior (2xx status codes are valid)\n * const interceptor = new ValidateStatusInterceptor();\n *\n * // Custom validation (only 200 status code is valid)\n * const interceptor = new ValidateStatusInterceptor((status) => status === 200);\n *\n * // Always valid (never throws based on status)\n * const interceptor = new ValidateStatusInterceptor((status) => true);\n * ```\n */\nexport class ValidateStatusInterceptor implements ResponseInterceptor {\n /**\n * Gets the name of this interceptor.\n *\n * @returns The name of this interceptor\n */\n get name(): string {\n return VALIDATE_STATUS_INTERCEPTOR_NAME;\n }\n\n /**\n * Gets the order of this interceptor.\n *\n * @returns VALIDATE_STATUS_INTERCEPTOR_ORDER, indicating this interceptor should execute early\n */\n get order(): number {\n return VALIDATE_STATUS_INTERCEPTOR_ORDER;\n }\n\n /**\n * Creates a new ValidateStatusInterceptor instance.\n *\n * @param validateStatus - Function that determines if a status code is valid\n */\n constructor(\n private readonly validateStatus: ValidateStatus = DEFAULT_VALIDATE_STATUS,\n ) {\n }\n\n /**\n * Validates the response status code.\n *\n * @param exchange - The exchange containing the response to validate\n * @throws HttpStatusValidationError if the status code is not valid\n *\n * @remarks\n * This method runs at the beginning of the response interceptor chain to ensure\n * status validation happens before any other response processing. Invalid responses\n * are caught and converted to HttpStatusValidationError early in the pipeline,\n * preventing other response handlers from attempting to process them. Valid responses\n * proceed through the rest of the response interceptor chain normally.\n */\n intercept(exchange: FetchExchange) {\n // Only validate if there's a response\n if (!exchange.response) {\n return;\n }\n\n const status = exchange.response.status;\n // If status is valid, do nothing\n if (this.validateStatus(status)) {\n return;\n }\n throw new HttpStatusValidationError(exchange);\n }\n}\n","import { UrlResolveInterceptor } from './urlResolveInterceptor';\nimport { RequestBodyInterceptor } from './requestBodyInterceptor';\nimport { FetchInterceptor } from './fetchInterceptor';\nimport { FetchExchange } from './fetchExchange';\nimport { FetcherError } from './fetcherError';\nimport { InterceptorRegistry } from './interceptor';\nimport { ValidateStatusInterceptor } from './validateStatusInterceptor';\n\n/**\n * Custom error class for FetchExchange related errors.\n *\n * This error is thrown when there are issues with the HTTP exchange process,\n * such as when a request fails and no response is generated. It provides\n * comprehensive information about the failed request through the exchange object.\n *\n * @example\n * ```typescript\n * try {\n * await fetcher.get('/api/users');\n * } catch (error) {\n * if (error instanceof ExchangeError) {\n * console.log('Request URL:', error.exchange.request.url);\n * console.log('Request method:', error.exchange.request.method);\n * if (error.exchange.error) {\n * console.log('Underlying error:', error.exchange.error);\n * }\n * }\n * }\n * ```\n */\nexport class ExchangeError extends FetcherError {\n /**\n * Creates a new ExchangeError instance.\n *\n * @param exchange - The FetchExchange object containing request/response/error information.\n * @param errorMsg - An optional error message.\n */\n constructor(\n public readonly exchange: FetchExchange,\n errorMsg?: string,\n ) {\n const errorMessage =\n errorMsg ||\n exchange.error?.message ||\n exchange.response?.statusText ||\n `Request to ${exchange.request.url} failed during exchange`;\n super(errorMessage, exchange.error);\n this.name = 'ExchangeError';\n Object.setPrototypeOf(this, ExchangeError.prototype);\n }\n}\n\n/**\n * Collection of interceptor managers for the Fetcher client.\n *\n * Manages three types of interceptors:\n * 1. Request interceptors - Process requests before sending HTTP requests\n * 2. Response interceptors - Process responses after receiving HTTP responses\n * 3. Error interceptors - Handle errors when they occur during the request process\n *\n * Each type of interceptor is managed by an InterceptorRegistry instance, supporting\n * adding, removing, and executing interceptors.\n *\n * @example\n * // Create a custom interceptor\n * const customRequestInterceptor: Interceptor = {\n * name: 'CustomRequestInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange): Promise<FetchExchange> {\n * // Modify request headers\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'X-Custom-Header': 'custom-value'\n * };\n * return exchange;\n * }\n * };\n *\n * // Add interceptor to Fetcher\n * const fetcher = new Fetcher();\n * fetcher.interceptors.request.use(customRequestInterceptor);\n *\n * @remarks\n * By default, the request interceptor registry has three built-in interceptors registered:\n * 1. UrlResolveInterceptor - Resolves the final URL with parameters\n * 2. RequestBodyInterceptor - Automatically converts object-type request bodies to JSON strings\n * 3. FetchInterceptor - Executes actual HTTP requests and handles timeouts\n */\nexport class InterceptorManager {\n /**\n * Registry for request-phase interceptors.\n *\n * Executed before HTTP requests are sent. Contains three built-in interceptors by default:\n * UrlResolveInterceptor, RequestBodyInterceptor, and FetchInterceptor.\n *\n * @remarks\n * Request interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority. The default interceptors are:\n * 1. UrlResolveInterceptor (order: Number.MIN_SAFE_INTEGER) - Resolves the final URL\n * 2. RequestBodyInterceptor (order: 0) - Converts object bodies to JSON\n * 3. FetchInterceptor (order: Number.MAX_SAFE_INTEGER) - Executes the actual HTTP request\n */\n readonly request: InterceptorRegistry = new InterceptorRegistry([\n new UrlResolveInterceptor(),\n new RequestBodyInterceptor(),\n new FetchInterceptor(),\n ]);\n\n /**\n * Manager for response-phase interceptors.\n *\n * Executed after HTTP responses are received. Contains ValidateStatusInterceptor by default\n * which validates HTTP status codes and throws errors for invalid statuses.\n *\n * @remarks\n * Response interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority.\n *\n * By default, the response interceptor registry has one built-in interceptor registered:\n * 1. ValidateStatusInterceptor - Validates HTTP status codes and throws HttpStatusValidationError for invalid statuses\n */\n readonly response: InterceptorRegistry = new InterceptorRegistry([\n new ValidateStatusInterceptor(),\n ]);\n\n /**\n * Manager for error-handling phase interceptors.\n *\n * Executed when errors occur during HTTP requests. Empty by default, custom error handling\n * logic can be added as needed.\n *\n * @remarks\n * Error interceptors are executed in ascending order of their order values, with smaller\n * values having higher priority. Error interceptors can transform errors into normal responses,\n * avoiding thrown exceptions.\n */\n readonly error: InterceptorRegistry = new InterceptorRegistry();\n\n /**\n * Processes a FetchExchange through the interceptor pipeline.\n *\n * This method is the core of the Fetcher's interceptor system. It executes the three\n * phases of interceptors in sequence:\n * 1. Request interceptors - Process the request before sending\n * 2. Response interceptors - Process the response after receiving\n * 3. Error interceptors - Handle any errors that occurred during the process\n *\n * The interceptor pipeline follows the Chain of Responsibility pattern, where each\n * interceptor can modify the exchange object and decide whether to continue or\n * terminate the chain.\n *\n * @param fetchExchange - The exchange object containing request, response, and error information\n * @returns Promise that resolves to the processed FetchExchange\n * @throws ExchangeError if an unhandled error occurs during processing\n *\n * @remarks\n * The method handles three distinct phases:\n *\n * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.\n * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.\n *\n * 2. Response Phase: Executes response interceptors which can transform or validate responses.\n * These interceptors only run if the request phase completed without throwing.\n *\n * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors\n * can handle errors by clearing the error property. If error interceptors clear the error,\n * the exchange is returned successfully.\n *\n * Error Handling:\n * - If any interceptor throws an error, the error phase is triggered\n * - Error interceptors can \"fix\" errors by clearing the error property on the exchange\n * - If errors remain after error interceptors run, they are wrapped in ExchangeError\n *\n * Order of Execution:\n * 1. Request interceptors (sorted by order property, ascending)\n * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase\n * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred\n *\n * @example\n * ```typescript\n * // Create a fetcher with custom interceptors\n * const fetcher = new Fetcher();\n *\n * // Add a request interceptor\n * fetcher.interceptors.request.use({\n * name: 'AuthInterceptor',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * exchange.request.headers = {\n * ...exchange.request.headers,\n * 'Authorization': 'Bearer ' + getToken()\n * };\n * }\n * });\n *\n * // Add a response interceptor\n * fetcher.interceptors.response.use({\n * name: 'ResponseLogger',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * console.log(`Response status: ${exchange.response?.status}`);\n * }\n * });\n *\n * // Add an error interceptor\n * fetcher.interceptors.error.use({\n * name: 'ErrorLogger',\n * order: 100,\n * async intercept(exchange: FetchExchange) {\n * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);\n * // Clear the error to indicate it's been handled\n * exchange.error = undefined;\n * }\n * });\n *\n * // Create and process an exchange\n * const request: FetchRequest = {\n * url: '/api/users',\n * method: HttpMethod.GET\n * };\n * const exchange = new FetchExchange(fetcher, request);\n * const result = await fetcher.exchange(exchange);\n * ```\n */\n async exchange(fetchExchange: FetchExchange): Promise<FetchExchange> {\n try {\n // Apply request interceptors\n await this.request.intercept(fetchExchange);\n // Apply response interceptors\n await this.response.intercept(fetchExchange);\n return fetchExchange;\n } catch (error: any) {\n // Apply error interceptors\n fetchExchange.error = error;\n await this.error.intercept(fetchExchange);\n\n // If error interceptors cleared the error (indicating it's been handled/fixed), return the exchange\n if (!fetchExchange.hasError()) {\n return fetchExchange;\n }\n\n // Otherwise, wrap the error in ExchangeError\n throw new ExchangeError(fetchExchange);\n }\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FetchExchange } from './fetchExchange';\n\n/**\n * Function interface for extracting results from a FetchExchange.\n * Defines how to transform a FetchExchange object into a specific result type.\n * @template R - The type of result to extract\n * @param exchange - The FetchExchange object containing request and response information\n * @returns The extracted result of type R\n */\nexport interface ResultExtractor<R> {\n (exchange: FetchExchange): R | Promise<R>;\n}\n\n/**\n * Interface with result extractor capability\n * Defines an optional resultExtractor property\n */\nexport interface ResultExtractorCapable {\n resultExtractor?: ResultExtractor<any>;\n}\n\n/**\n * Returns the original FetchExchange object.\n * @param exchange - The FetchExchange object to return\n * @returns The same FetchExchange object that was passed in\n */\nexport const ExchangeResultExtractor: ResultExtractor<FetchExchange> = (\n exchange: FetchExchange,\n) => {\n return exchange;\n};\n\n/**\n * Extracts the Response object from the exchange.\n * @param exchange - The FetchExchange containing the response\n * @returns The Response object from the exchange\n */\nexport const ResponseResultExtractor: ResultExtractor<Response> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse;\n};\n\n/**\n * Extracts and parses the response body as JSON.\n * @param exchange - The FetchExchange containing the response with JSON data\n * @returns A Promise that resolves to the parsed JSON data\n */\nexport const JsonResultExtractor: ResultExtractor<Promise<any>> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse.json();\n};\n\n/**\n * Extracts the response body as text.\n * @param exchange - The FetchExchange containing the response with text data\n * @returns A Promise that resolves to the response body as a string\n */\nexport const TextResultExtractor: ResultExtractor<Promise<string>> = (\n exchange: FetchExchange,\n) => {\n return exchange.requiredResponse.text();\n};\n\nexport const ResultExtractors = {\n Exchange: ExchangeResultExtractor,\n Response: ResponseResultExtractor,\n Json: JsonResultExtractor,\n Text: TextResultExtractor,\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Merges two record objects, giving precedence to the second record for overlapping keys.\n *\n * This utility function is used to merge configuration objects where the second object\n * takes precedence over the first when there are conflicting keys.\n *\n * @template V - The type of values in the records\n * @param first - The first record to merge (lower precedence)\n * @param second - The second record to merge (higher precedence)\n * @returns A new merged record, or undefined if both inputs are undefined\n *\n * @example\n * ```typescript\n * // Merge two objects\n * const defaults = { timeout: 5000, retries: 3 };\n * const overrides = { timeout: 10000 };\n * const result = mergeRecords(defaults, overrides);\n * // Result: { timeout: 10000, retries: 3 }\n *\n * // Handle undefined values\n * const result2 = mergeRecords(undefined, { timeout: 5000 });\n * // Result: { timeout: 5000 }\n *\n * // Return undefined when both are undefined\n * const result3 = mergeRecords(undefined, undefined);\n * // Result: undefined\n * ```\n */\nexport function mergeRecords<V>(\n first?: Record<string, V>,\n second?: Record<string, V>,\n): Record<string, V> | undefined {\n // If both records are undefined, return undefined\n if (first === undefined && second === undefined) {\n return undefined;\n }\n\n // If second record is undefined, return first record\n if (second === undefined) {\n return first;\n }\n\n // If first record is undefined, return second record\n if (first === undefined) {\n return second;\n }\n\n // Merge both records, with second taking precedence\n return { ...first, ...second };\n}\n\n/**\n * Merge a Record object or Map object into a target Map\n * @param record - Source data, can be either Record<string, V> or Map<string, V> type\n * @param map - Target Map object, if not provided a new Map will be created\n * @returns The merged Map object\n */\nexport function mergeRecordToMap<V>(\n record?: Record<string, V> | Map<string, V>,\n map?: Map<string, V>,\n): Map<string, V> {\n map ??= new Map();\n if (!record) {\n return map;\n }\n if (record instanceof Map) {\n for (const [key, value] of record) {\n map.set(key, value);\n }\n return map;\n }\n for (const [key, value] of Object.entries(record)) {\n map.set(key, value);\n }\n return map;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\nimport type { FetchRequest, RequestHeaders } from './fetchRequest';\nimport { ExchangeError } from './interceptorManager';\nimport { type UrlParams } from './urlBuilder';\nimport { type RequiredBy } from './types';\nimport { ResultExtractor, ResultExtractors } from './resultExtractor';\nimport { mergeRecordToMap } from './utils';\n\nexport interface AttributesCapable {\n /**\n * Shared attributes for passing data between interceptors.\n *\n * This property allows interceptors to share arbitrary data with each other.\n * Interceptors can read from and write to this object to pass information\n * along the interceptor chain.\n *\n * @remarks\n * - This property is optional and may be undefined initially\n * - Interceptors should initialize this property if they need to use it\n * - Use string keys to avoid conflicts between different interceptors\n * - Consider namespacing your keys (e.g., 'mylib.retryCount' instead of 'retryCount')\n * - Be mindful of memory usage when storing large objects\n */\n attributes?: Record<string, any> | Map<string, any>;\n}\n\nexport interface FetchExchangeInit extends AttributesCapable {\n /**\n * The Fetcher instance that initiated this exchange.\n */\n fetcher: Fetcher;\n\n /**\n * The request configuration including url, method, headers, body, etc.\n */\n request: FetchRequest;\n resultExtractor?: ResultExtractor<any>;\n\n /**\n * The response object, undefined until the request completes successfully.\n */\n response?: Response;\n\n /**\n * Any error that occurred during the request processing, undefined if no error occurred.\n */\n error?: Error | any;\n}\n\n/**\n * Container for HTTP request/response data that flows through the interceptor chain.\n *\n * Represents the complete exchange object that flows through the interceptor chain.\n * This object contains all the information about a request, response, and any errors\n * that occur during the HTTP request lifecycle. It also provides a mechanism for\n * sharing data between interceptors through the attributes property.\n *\n * FetchExchange instances are unique within a single request scope, meaning each HTTP\n * request creates its own FetchExchange instance that is passed through the interceptor\n * chain for that specific request.\n *\n * @example\n * ```typescript\n * // In a request interceptor\n * const requestInterceptor: Interceptor = {\n * name: 'RequestInterceptor',\n * order: 0,\n * intercept(exchange: FetchExchange) {\n * // Add custom data to share with other interceptors\n * exchange.attributes = exchange.attributes || {};\n * exchange.attributes.startTime = Date.now();\n * exchange.attributes.customHeader = 'my-value';\n * }\n * };\n *\n * // In a response interceptor\n * const responseInterceptor: Interceptor = {\n * name: 'ResponseInterceptor',\n * order: 0,\n * intercept(exchange: FetchExchange) {\n * // Access data shared by previous interceptors\n * if (exchange.attributes && exchange.attributes.startTime) {\n * const startTime = exchange.attributes.startTime;\n * const duration = Date.now() - startTime;\n * console.log(`Request took ${duration}ms`);\n * }\n * }\n * };\n * ```\n */\nexport class FetchExchange\n implements RequiredBy<FetchExchangeInit, 'attributes'> {\n /**\n * The Fetcher instance that initiated this exchange.\n */\n fetcher: Fetcher;\n\n /**\n * The request configuration including url, method, headers, body, etc.\n */\n request: FetchRequest;\n\n /**\n * The result extractor function used to transform the response into the desired format.\n * Defaults to ResultExtractors.Exchange if not provided.\n */\n resultExtractor: ResultExtractor<any>;\n /**\n * The response object, undefined until the request completes successfully.\n */\n private _response?: Response;\n\n /**\n * Any error that occurred during the request processing, undefined if no error occurred.\n */\n error?: Error | any;\n\n /**\n * Cached result of the extracted result to avoid repeated computations.\n * Undefined when not yet computed, null when computation failed.\n */\n private cachedExtractedResult?: any | Promise<any>;\n /**\n * Shared attributes for passing data between interceptors.\n *\n * This property allows interceptors to share arbitrary data with each other.\n * Interceptors can read from and write to this object to pass information\n * along the interceptor chain.\n *\n * @remarks\n * - This property is optional and may be undefined initially\n * - Interceptors should initialize this property if they need to use it\n * - Use string keys to avoid conflicts between different interceptors\n * - Consider namespacing your keys (e.g., 'mylib.retryCount' instead of 'retryCount')\n * - Be mindful of memory usage when storing large objects\n */\n attributes: Map<string, any>;\n\n constructor(exchangeInit: FetchExchangeInit) {\n this.fetcher = exchangeInit.fetcher;\n this.request = exchangeInit.request;\n this.resultExtractor =\n exchangeInit.resultExtractor ?? ResultExtractors.Exchange;\n this.attributes = mergeRecordToMap(exchangeInit.attributes);\n this._response = exchangeInit.response;\n this.error = exchangeInit.error;\n }\n\n /**\n * Ensures that request headers object exists, creating it if necessary.\n *\n * This method checks if the request headers object is present and initializes\n * it as an empty object if it's missing. This guarantees that headers can\n * be safely accessed and modified after calling this method.\n *\n * @returns The request headers object, guaranteed to be non-null\n */\n ensureRequestHeaders(): RequestHeaders {\n if (!this.request.headers) {\n this.request.headers = {};\n }\n return this.request.headers;\n }\n\n /**\n * Ensures that request URL parameters object exists with all required properties,\n * creating them if necessary.\n *\n * This method checks if the request URL parameters object is present and initializes\n * it with empty path and query objects if it's missing. It also ensures that both\n * path and query sub-objects exist. This guarantees that URL parameters can be\n * safely accessed and modified after calling this method.\n *\n * @returns The request URL parameters object with guaranteed non-null path and query properties\n */\n ensureRequestUrlParams(): Required<UrlParams> {\n if (!this.request.urlParams) {\n this.request.urlParams = {\n path: {},\n query: {},\n };\n }\n if (!this.request.urlParams.path) {\n this.request.urlParams.path = {};\n }\n if (!this.request.urlParams.query) {\n this.request.urlParams.query = {};\n }\n return this.request.urlParams as Required<UrlParams>;\n }\n\n /**\n * Checks if the exchange has an error.\n *\n * @returns true if an error is present, false otherwise\n */\n hasError(): boolean {\n return !!this.error;\n }\n\n /**\n * Sets the response object for this exchange.\n * Also invalidates the cached extracted result to ensure data consistency\n * when the response changes.\n *\n * @param response - The Response object to set, or undefined to clear the response\n */\n set response(response: Response | undefined) {\n this._response = response;\n this.cachedExtractedResult = undefined;\n }\n\n /**\n * Gets the response object for this exchange.\n *\n * @returns The response object if available, undefined otherwise\n */\n get response(): Response | undefined {\n return this._response;\n }\n\n /**\n * Checks if the exchange has a response.\n *\n * @returns true if a response is present, false otherwise\n */\n hasResponse(): boolean {\n return !!this.response;\n }\n\n /**\n * Gets the required response object, throwing an error if no response is available.\n *\n * This getter ensures that a response object is available, and throws an ExchangeError\n * with details about the request if no response was received. This is useful for\n * guaranteeing that downstream code always has a valid Response object to work with.\n *\n * @throws {ExchangeError} If no response is available for the current exchange\n * @returns The Response object for this exchange\n */\n get requiredResponse(): Response {\n if (!this.response) {\n throw new ExchangeError(\n this,\n `Request to ${this.request.url} failed with no response`,\n );\n }\n return this.response;\n }\n\n /**\n * Extracts the result by applying the result extractor to the exchange.\n * The result is cached after the first computation to avoid repeated computations.\n *\n * @returns The extracted result\n */\n extractResult<R>(): R | Promise<R> {\n if (this.cachedExtractedResult !== undefined) {\n return this.cachedExtractedResult;\n }\n this.cachedExtractedResult = this.resultExtractor(this);\n return this.cachedExtractedResult;\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type FetchRequestInit } from './fetchRequest';\nimport { type UrlParams } from './urlBuilder';\nimport { mergeRecords } from './utils';\nimport { DEFAULT_REQUEST_OPTIONS, RequestOptions } from './fetcher';\n\n/**\n * Merges two FetcherRequest objects into one.\n *\n * This function combines two FetcherRequest objects, with the second object's properties\n * taking precedence over the first object's properties. Special handling is applied\n * to nested objects like path, query, and headers which are merged recursively.\n * For primitive values, the second object's values override the first's.\n *\n * @param first - The first request object (lower priority)\n * @param second - The second request object (higher priority)\n * @returns A new FetcherRequest object with merged properties\n *\n * @example\n * ```typescript\n * const request1 = {\n * method: 'GET',\n * urlParams: {\n * path: { id: 1 }\n * },\n * headers: { 'Content-Type': 'application/json' }\n * };\n *\n * const request2 = {\n * method: 'POST',\n * urlParams: {\n * query: { filter: 'active' }\n * },\n * headers: { 'Authorization': 'Bearer token' }\n * };\n *\n * const merged = mergeRequest(request1, request2);\n * // Result: {\n * // method: 'POST',\n * // urlParams: {\n * // path: { id: 1 },\n * // query: { filter: 'active' }\n * // },\n * // headers: {\n * // 'Content-Type': 'application/json',\n * // 'Authorization': 'Bearer token'\n * // }\n * // }\n * ```\n */\nexport function mergeRequest(\n first: FetchRequestInit,\n second: FetchRequestInit,\n): FetchRequestInit {\n // If first request is empty, return second request\n if (Object.keys(first).length === 0) {\n return second;\n }\n\n // If second request is empty, return first request\n if (Object.keys(second).length === 0) {\n return first;\n }\n\n // Merge nested objects\n const urlParams: UrlParams = {\n path: mergeRecords(first.urlParams?.path, second.urlParams?.path),\n query: mergeRecords(first.urlParams?.query, second.urlParams?.query),\n };\n\n const headers = {\n ...first.headers,\n ...second.headers,\n };\n\n // For primitive values, second takes precedence\n const method = second.method ?? first.method;\n const body = second.body ?? first.body;\n const timeout = second.timeout ?? first.timeout;\n const signal = second.signal ?? first.signal;\n const abortController = second.abortController ?? first.abortController;\n // Return merged request with second object's top-level properties taking precedence\n return {\n ...first,\n ...second,\n method,\n urlParams,\n headers,\n body,\n timeout,\n signal,\n abortController,\n };\n}\n\n/**\n * Merges two request options objects into one, with the second object taking precedence over the first.\n *\n * @param first - The first request options object (optional)\n * @param second - The second request options object which will override properties from the first (optional)\n * @returns A new RequestOptions object with merged properties\n */\nexport function mergeRequestOptions(\n first?: RequestOptions,\n second?: RequestOptions,\n): RequestOptions {\n if (second && second.resultExtractor && second.attributes) {\n return second;\n }\n // Merge the options, prioritizing second over first, with defaults as fallback\n return {\n resultExtractor:\n second?.resultExtractor ??\n first?.resultExtractor ??\n DEFAULT_REQUEST_OPTIONS.resultExtractor,\n attributes: second?.attributes ?? first?.attributes,\n };\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UrlBuilder, type UrlBuilderCapable } from './urlBuilder';\nimport { resolveTimeout, type TimeoutCapable } from './timeout';\nimport { AttributesCapable, FetchExchange } from './fetchExchange';\nimport {\n BaseURLCapable,\n CONTENT_TYPE_HEADER,\n ContentTypeValues,\n FetchRequest,\n FetchRequestInit,\n RequestHeaders,\n RequestHeadersCapable,\n} from './fetchRequest';\nimport { HttpMethod } from './fetchRequest';\nimport { InterceptorManager } from './interceptorManager';\nimport { UrlTemplateStyle } from './urlTemplateResolver';\nimport { ResultExtractorCapable, ResultExtractors } from './resultExtractor';\nimport { mergeRequestOptions } from './mergeRequest';\n\n/**\n * Configuration options for the Fetcher client.\n *\n * Defines the customizable aspects of a Fetcher instance including base URL,\n * default headers, timeout settings, and interceptors.\n *\n * @example\n * ```typescript\n * const options: FetcherOptions = {\n * urlTemplateStyle: UrlTemplateStyle.UriTemplate,\n * baseURL: 'https://api.example.com',\n * headers: { 'Content-Type': 'application/json' },\n * timeout: 5000,\n * interceptors: new InterceptorManager()\n * };\n * ```\n */\nexport interface FetcherOptions\n extends BaseURLCapable,\n RequestHeadersCapable,\n TimeoutCapable {\n /**\n * The style of URL template to use for URL parameter interpolation.\n * @default UrlTemplateStyle.Path\n */\n urlTemplateStyle?: UrlTemplateStyle;\n\n /**\n * The interceptor manager to use for request/response processing.\n * @default new InterceptorManager()\n */\n interceptors?: InterceptorManager;\n}\n\nconst DEFAULT_HEADERS: RequestHeaders = {\n [CONTENT_TYPE_HEADER]: ContentTypeValues.APPLICATION_JSON,\n};\n\nexport const DEFAULT_OPTIONS: FetcherOptions = {\n baseURL: '',\n headers: DEFAULT_HEADERS,\n};\n\n/**\n * Options for individual requests.\n */\nexport interface RequestOptions\n extends AttributesCapable,\n ResultExtractorCapable {\n}\n\nexport const DEFAULT_REQUEST_OPTIONS: RequestOptions = {\n resultExtractor: ResultExtractors.Exchange,\n};\nexport const DEFAULT_FETCH_OPTIONS: RequestOptions = {\n resultExtractor: ResultExtractors.Response,\n};\n\n/**\n * HTTP client with support for interceptors, URL building, and timeout control.\n *\n * The Fetcher class provides a flexible and extensible HTTP client implementation\n * that follows the interceptor pattern. It supports URL parameter interpolation,\n * request/response transformation, and timeout handling.\n *\n * @example\n * ```typescript\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * const response = await fetcher.fetch('/users/{id}', {\n * urlParams: {\n * path: { id: 123 },\n * query: { filter: 'active' }\n * },\n * timeout: 5000\n * });\n * ```\n */\nexport class Fetcher\n implements UrlBuilderCapable, RequestHeadersCapable, TimeoutCapable {\n readonly urlBuilder: UrlBuilder;\n readonly headers?: RequestHeaders = DEFAULT_HEADERS;\n readonly timeout?: number;\n readonly interceptors: InterceptorManager;\n\n /**\n * Initializes a new Fetcher instance with optional configuration.\n *\n * Creates a Fetcher with default settings that can be overridden through the options parameter.\n * If no interceptors are provided, a default set of interceptors will be used.\n *\n * @param options - Configuration options for the Fetcher instance\n * @param options.baseURL - The base URL to prepend to all requests. Defaults to empty string.\n * @param options.headers - Default headers to include in all requests. Defaults to JSON content type.\n * @param options.timeout - Default timeout for requests in milliseconds. No timeout by default.\n * @param options.urlTemplateStyle - Style for URL template parameter interpolation.\n * @param options.interceptors - Interceptor manager for processing requests and responses.\n */\n constructor(options: FetcherOptions = DEFAULT_OPTIONS) {\n this.urlBuilder = new UrlBuilder(options.baseURL, options.urlTemplateStyle);\n this.headers = options.headers ?? DEFAULT_HEADERS;\n this.timeout = options.timeout;\n this.interceptors = options.interceptors ?? new InterceptorManager();\n }\n\n /**\n * Processes an HTTP request through the Fetcher's internal workflow.\n *\n * This method creates a FetchExchange object and passes it through the interceptor chain.\n * It handles header merging, timeout resolution, and result extractor configuration.\n *\n * @param request - The HTTP request configuration to process\n * @param options - Optional request options including result extractor and attributes\n * @returns Promise that resolves to the processed FetchExchange object\n */\n async exchange(\n request: FetchRequest,\n options?: RequestOptions,\n ): Promise<FetchExchange> {\n // Merge default headers and request-level headers. defensive copy\n const mergedHeaders = {\n ...this.headers,\n ...request.headers,\n };\n // Merge request options\n const fetchRequest: FetchRequest = {\n ...request,\n headers: mergedHeaders,\n timeout: resolveTimeout(request.timeout, this.timeout),\n };\n const { resultExtractor, attributes } = mergeRequestOptions(\n DEFAULT_REQUEST_OPTIONS,\n options,\n );\n const exchange: FetchExchange = new FetchExchange({\n fetcher: this,\n request: fetchRequest,\n resultExtractor,\n attributes,\n });\n return this.interceptors.exchange(exchange);\n }\n\n /**\n * Processes an HTTP request through the Fetcher's internal workflow.\n *\n * This method prepares the request by merging headers and timeout settings,\n * creates a FetchExchange object, and passes it through the exchange method\n * for interceptor processing.\n *\n * @template R - The type of the result to be returned\n * @param request - Complete request configuration object\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ExchangeResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the extracted result based on resultExtractor\n * @throws Error if an unhandled error occurs during request processing\n */\n async request<R = FetchExchange>(\n request: FetchRequest,\n options?: RequestOptions,\n ): Promise<R> {\n const fetchExchange = await this.exchange(request, options);\n return fetchExchange.extractResult();\n }\n\n /**\n * Executes an HTTP request with the specified URL and options.\n *\n * This is the primary method for making HTTP requests. It processes the request\n * through the interceptor chain and returns the resulting Response.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request (relative to baseURL if set)\n * @param request - Request configuration including headers, body, parameters, etc.\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n * @throws FetchError if the request fails and no response is generated\n */\n async fetch<R = Response>(\n url: string,\n request: FetchRequestInit = {},\n options?: RequestOptions,\n ): Promise<R> {\n const mergedRequest: FetchRequest = {\n ...request,\n url,\n };\n return this.request(\n mergedRequest,\n mergeRequestOptions(DEFAULT_FETCH_OPTIONS, options),\n );\n }\n\n /**\n * Internal helper method for making HTTP requests with a specific method.\n *\n * This private method is used by the public HTTP method methods (get, post, etc.)\n * to execute requests with the appropriate HTTP verb.\n *\n * @template R - The type of the result to be returned\n * @param method - The HTTP method to use for the request\n * @param url - The URL path for the request\n * @param request - Additional request options\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n private async methodFetch<R = Response>(\n method: HttpMethod,\n url: string,\n request: FetchRequestInit = {},\n options?: RequestOptions,\n ): Promise<R> {\n const mergedRequest: FetchRequest = {\n ...request,\n url,\n method,\n };\n return this.request(\n mergedRequest,\n mergeRequestOptions(DEFAULT_FETCH_OPTIONS, options),\n );\n }\n\n /**\n * Makes a GET HTTP request.\n *\n * Convenience method for making GET requests. The request body is omitted\n * as GET requests should not contain a body according to HTTP specification.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async get<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.GET, url, request, options);\n }\n\n /**\n * Makes a PUT HTTP request.\n *\n * Convenience method for making PUT requests, commonly used for updating resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async put<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.PUT, url, request, options);\n }\n\n /**\n * Makes a POST HTTP request.\n *\n * Convenience method for making POST requests, commonly used for creating resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async post<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.POST, url, request, options);\n }\n\n /**\n * Makes a PATCH HTTP request.\n *\n * Convenience method for making PATCH requests, commonly used for partial updates.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options including body and other parameters\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async patch<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.PATCH, url, request, options);\n }\n\n /**\n * Makes a DELETE HTTP request.\n *\n * Convenience method for making DELETE requests, commonly used for deleting resources.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async delete<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.DELETE, url, request, options);\n }\n\n /**\n * Makes a HEAD HTTP request.\n *\n * Convenience method for making HEAD requests, which retrieve headers only.\n * The request body is omitted as HEAD requests should not contain a body.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async head<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.HEAD, url, request, options);\n }\n\n /**\n * Makes an OPTIONS HTTP request.\n *\n * Convenience method for making OPTIONS requests, commonly used for CORS preflight.\n * The request body is omitted as OPTIONS requests typically don't contain a body.\n *\n * @template R - The type of the result to be returned\n * @param url - The URL path for the request\n * @param request - Request options excluding method and body\n * @param options - Request options including result extractor and attributes\n * @param options.resultExtractor - Function to extract the desired result from the exchange.\n * Defaults to ResponseResultExtractor which returns the entire exchange object.\n * @param options.attributes - Optional shared attributes that can be accessed by interceptors\n * throughout the request lifecycle. These attributes allow passing\n * custom data between different interceptors.\n * @returns Promise that resolves to the HTTP response\n */\n async options<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.OPTIONS, url, request, options);\n }\n\n /**\n * Sends an HTTP TRACE request to the specified URL and returns the response.\n *\n * The TRACE method is used to echo the received request for debugging purposes.\n * This method automatically sets the HTTP method to TRACE and omits the request body\n * since TRACE requests must not have a body according to the HTTP specification.\n *\n * @param url - The target URL for the TRACE request. Must be a valid absolute or relative URL.\n * @param request - Request configuration options excluding 'method' and 'body' properties.\n * Defaults to an empty object. Common properties include headers, cache settings, etc.\n * @param options - Optional additional request parameters for extended functionality.\n * May include custom handling logic or metadata for the request pipeline.\n * @returns A Promise resolving to the response object of type R (defaults to Response).\n * The response contains status, headers, and body data from the TRACE request.\n */\n async trace<R = Response>(\n url: string,\n request: Omit<FetchRequestInit, 'method' | 'body'> = {},\n options?: RequestOptions,\n ): Promise<R> {\n return this.methodFetch(HttpMethod.TRACE, url, request, options);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\n\n/**\n * Default fetcher name used when no name is specified\n */\nexport const DEFAULT_FETCHER_NAME = 'default';\n\n/**\n * FetcherRegistrar is a registry for managing multiple Fetcher instances.\n * It allows registering, retrieving, and unregistering Fetcher instances by name.\n * This is useful for applications that need to manage multiple HTTP clients\n * with different configurations.\n *\n * @example\n * // Register a fetcher\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n *\n * // Retrieve a fetcher\n * const apiFetcher = fetcherRegistrar.get('api');\n *\n * // Use the default fetcher\n * const defaultFetcher = fetcherRegistrar.default;\n *\n * // Unregister a fetcher\n * fetcherRegistrar.unregister('api');\n */\nexport class FetcherRegistrar {\n /**\n * Internal map for storing registered fetchers\n * @private\n */\n private registrar: Map<string, Fetcher> = new Map();\n\n /**\n * Register a Fetcher instance with a given name\n *\n * @param name - The name to register the fetcher under\n * @param fetcher - The Fetcher instance to register\n * @example\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n */\n register(name: string, fetcher: Fetcher): void {\n this.registrar.set(name, fetcher);\n }\n\n /**\n * Unregister a Fetcher instance by name\n *\n * @param name - The name of the fetcher to unregister\n * @returns boolean - True if the fetcher was successfully unregistered, false otherwise\n * @example\n * const success = fetcherRegistrar.unregister('api');\n * if (success) {\n * console.log('Fetcher unregistered successfully');\n * }\n */\n unregister(name: string): boolean {\n return this.registrar.delete(name);\n }\n\n /**\n * Get a Fetcher instance by name\n *\n * @param name - The name of the fetcher to retrieve\n * @returns Fetcher | undefined - The Fetcher instance if found, undefined otherwise\n * @example\n * const fetcher = fetcherRegistrar.get('api');\n * if (fetcher) {\n * // Use the fetcher\n * }\n */\n get(name: string): Fetcher | undefined {\n return this.registrar.get(name);\n }\n\n /**\n * Get a Fetcher instance by name, throwing an error if not found\n *\n * @param name - The name of the fetcher to retrieve\n * @returns Fetcher - The Fetcher instance\n * @throws Error - If no fetcher is registered with the given name\n * @example\n * try {\n * const fetcher = fetcherRegistrar.requiredGet('api');\n * // Use the fetcher\n * } catch (error) {\n * console.error('Fetcher not found:', error.message);\n * }\n */\n requiredGet(name: string): Fetcher {\n const fetcher = this.get(name);\n if (!fetcher) {\n throw new Error(`Fetcher ${name} not found`);\n }\n return fetcher;\n }\n\n /**\n * Get the default Fetcher instance\n *\n * @returns Fetcher - The default Fetcher instance\n * @throws Error - If no default fetcher is registered\n * @example\n * const defaultFetcher = fetcherRegistrar.default;\n */\n get default(): Fetcher {\n return this.requiredGet(DEFAULT_FETCHER_NAME);\n }\n\n /**\n * Set the default Fetcher instance\n *\n * @param fetcher - The Fetcher instance to set as default\n * @example\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.default = fetcher;\n */\n set default(fetcher: Fetcher) {\n this.register(DEFAULT_FETCHER_NAME, fetcher);\n }\n\n /**\n * Get a copy of all registered fetchers\n *\n * @returns Map<string, Fetcher> - A copy of the internal registrar map\n * @example\n * const allFetchers = fetcherRegistrar.fetchers;\n * for (const [name, fetcher] of allFetchers) {\n * console.log(`Fetcher ${name}:`, fetcher);\n * }\n */\n get fetchers(): Map<string, Fetcher> {\n return new Map(this.registrar);\n }\n}\n\n/**\n * Global instance of FetcherRegistrar\n * This is the default registrar used throughout the application\n *\n * @example\n * import { fetcherRegistrar } from '@ahoo-wang/fetcher';\n *\n * // Register a fetcher\n * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });\n * fetcherRegistrar.register('api', fetcher);\n *\n * // Retrieve a fetcher\n * const apiFetcher = fetcherRegistrar.get('api');\n */\nexport const fetcherRegistrar = new FetcherRegistrar();\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fetcher } from './fetcher';\nimport { fetcherRegistrar } from './fetcherRegistrar';\n\n/**\n * Interface that defines a capability for objects that can have a fetcher.\n * This interface is typically used to mark components or objects that can perform fetching operations\n * and may need access to fetcher functionality.\n */\nexport interface FetcherCapable {\n /**\n * Optional fetcher property that can be either a string identifier or a Fetcher instance.\n * When present, this property indicates the fetcher associated with the implementing object.\n */\n fetcher?: string | Fetcher;\n}\n\n/**\n * Gets a Fetcher instance based on the provided fetcher parameter.\n *\n * @param fetcher - A string identifier or Fetcher instance to resolve\n * @param defaultFetcher - The default Fetcher to use when fetcher is not provided, defaults to defaultNamedFetcher\n * @returns A Fetcher instance if found, otherwise returns the default Fetcher\n */\nexport function getFetcher(\n fetcher?: string | Fetcher,\n defaultFetcher?: Fetcher,\n): Fetcher {\n // Return default fetcher if no fetcher is provided\n if (!fetcher) {\n return defaultFetcher ?? fetcherRegistrar.default;\n }\n\n // Return the fetcher directly if it's already a Fetcher instance,\n // otherwise resolve it through the fetcher registrar\n return fetcher instanceof Fetcher\n ? fetcher\n : fetcherRegistrar.requiredGet(fetcher);\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { type NamedCapable } from './types';\nimport { DEFAULT_OPTIONS, Fetcher, type FetcherOptions } from './fetcher';\nimport { DEFAULT_FETCHER_NAME, fetcherRegistrar } from './fetcherRegistrar';\n\n/**\n * NamedFetcher is an extension of the Fetcher class that automatically registers\n * itself with the global fetcherRegistrar using a provided name.\n * This allows for easy management and retrieval of multiple fetcher instances\n * throughout an application by name.\n *\n * @example\n * // Create a named fetcher that automatically registers itself\n * const apiFetcher = new NamedFetcher('api', {\n * baseURL: 'https://api.example.com',\n * timeout: 5000\n * });\n *\n * // Retrieve the fetcher later by name\n * const sameFetcher = fetcherRegistrar.get('api');\n * console.log(apiFetcher === sameFetcher); // true\n *\n * // Use the fetcher normally\n * const response = await apiFetcher.get('/users');\n */\nexport class NamedFetcher extends Fetcher implements NamedCapable {\n /**\n * The name of this fetcher instance, used for registration and retrieval\n */\n name: string;\n\n /**\n * Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar\n *\n * @param name - The name to register this fetcher under\n * @param options - Fetcher configuration options (same as Fetcher constructor)\n *\n * @example\n * // Create with default options\n * const fetcher1 = new NamedFetcher('default');\n *\n * // Create with custom options\n * const fetcher2 = new NamedFetcher('api', {\n * baseURL: 'https://api.example.com',\n * timeout: 5000,\n * headers: { 'Authorization': 'Bearer token' }\n * });\n */\n constructor(name: string, options: FetcherOptions = DEFAULT_OPTIONS) {\n super(options);\n this.name = name;\n fetcherRegistrar.register(name, this);\n }\n}\n\n/**\n * Default named fetcher instance registered with the name 'default'.\n * This provides a convenient way to use a pre-configured fetcher instance\n * without having to create and register one manually.\n *\n * @example\n * // Use the default fetcher directly\n * import { fetcher } from '@ahoo-wang/fetcher';\n *\n * fetcher.get('/users')\n * .then(response => response.json())\n * .then(data => console.log(data));\n *\n * // Or retrieve it from the registrar\n * import { fetcherRegistrar } from '@ahoo-wang/fetcher';\n *\n * const defaultFetcher = fetcherRegistrar.default;\n * defaultFetcher.get('/users')\n * .then(response => response.json())\n * .then(data => console.log(data));\n */\nexport const fetcher = new NamedFetcher(DEFAULT_FETCHER_NAME);\n"],"names":["isAbsoluteURL","url","combineURLs","baseURL","relativeURL","UrlTemplateStyle","getUrlTemplateResolver","style","expressUrlTemplateResolver","uriTemplateResolver","urlTemplateRegexResolve","urlTemplate","pathParamRegex","pathParams","_","key","value","urlTemplateRegexExtract","matches","match","_UriTemplateResolver","UriTemplateResolver","_ExpressUrlTemplateResolver","ExpressUrlTemplateResolver","UrlBuilder","urlTemplateStyle","params","path","query","combinedURL","finalUrl","queryString","request","FetcherError","errorMsg","cause","errorMessage","FetchTimeoutError","method","message","resolveTimeout","requestTimeout","optionsTimeout","timeoutFetch","timeout","requestInit","controller","timerId","timeoutPromise","reject","error","URL_RESOLVE_INTERCEPTOR_NAME","URL_RESOLVE_INTERCEPTOR_ORDER","UrlResolveInterceptor","exchange","HttpMethod","CONTENT_TYPE_HEADER","_ContentTypeValues","ContentTypeValues","REQUEST_BODY_INTERCEPTOR_NAME","REQUEST_BODY_INTERCEPTOR_ORDER","RequestBodyInterceptor","body","headers","FETCH_INTERCEPTOR_NAME","FETCH_INTERCEPTOR_ORDER","FetchInterceptor","toSorted","array","filter","a","b","InterceptorRegistry","interceptors","interceptor","item","name","original","HttpStatusValidationError","DEFAULT_VALIDATE_STATUS","status","VALIDATE_STATUS_INTERCEPTOR_NAME","VALIDATE_STATUS_INTERCEPTOR_ORDER","ValidateStatusInterceptor","validateStatus","ExchangeError","InterceptorManager","fetchExchange","ExchangeResultExtractor","ResponseResultExtractor","JsonResultExtractor","TextResultExtractor","ResultExtractors","mergeRecords","first","second","mergeRecordToMap","record","map","FetchExchange","exchangeInit","response","mergeRequest","urlParams","signal","abortController","mergeRequestOptions","DEFAULT_REQUEST_OPTIONS","DEFAULT_HEADERS","DEFAULT_OPTIONS","DEFAULT_FETCH_OPTIONS","Fetcher","options","mergedHeaders","fetchRequest","resultExtractor","attributes","mergedRequest","DEFAULT_FETCHER_NAME","FetcherRegistrar","fetcher","fetcherRegistrar","getFetcher","defaultFetcher","NamedFetcher"],"mappings":"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"}
|