Interceptor pipeline
Interceptors mutate a shared FetchExchange; they return void | Promise<void>, not a replacement exchange. The exchange's request holds its own copies of the merged headers and of urlParams.path/.query, so interceptor writes never reach the caller's request object. RequestInterceptor, ResponseInterceptor, and ErrorInterceptor are structural specializations of Interceptor, whose required fields are name, order, and intercept(exchange).
Registration and ordering
new InterceptorRegistry(interceptors = []) sorts ascending by order. use(interceptor): boolean rejects a duplicate name; eject(name): boolean reports whether removal happened; clear(): void removes everything. The constructor sorts a copy of the provided array and leaves the input array unchanged; it does not de-duplicate its initial entries. interceptors returns an array copy. intercept(exchange): Promise<void> awaits handlers sequentially and stops on rejection. The registry itself implements Interceptor, with constructor-name name and order Number.MIN_SAFE_INTEGER.
OrderedCapable.order is optional; sortOrder(a, b) treats missing values as zero. toSorted(array, filter?) returns a sorted copy, optionally filtered. DEFAULT_INTERCEPTOR_ORDER_STEP = 1000 and BUILT_IN_INTERCEPTOR_ORDER_STEP = 10000 provide spacing.
Built-in phases
| Registry / implementation | Exported order | Effect |
|---|---|---|
request: RequestBodyInterceptor | REQUEST_BODY_INTERCEPTOR_ORDER = MIN_SAFE_INTEGER + 10000 | Normalize body and Content-Type. |
request: UrlResolveInterceptor | URL_RESOLVE_INTERCEPTOR_ORDER = MAX_SAFE_INTEGER - 20000 | Resolve base/path/query, clear urlParams. |
request: FetchInterceptor | FETCH_INTERCEPTOR_ORDER = MAX_SAFE_INTEGER - 10000 | Await timeoutFetch with the configured fetch, assign response. |
response: ValidateStatusInterceptor | VALIDATE_STATUS_INTERCEPTOR_ORDER = MAX_SAFE_INTEGER - 10000 | Reject unaccepted status unless bypassed. |
Each corresponding *_INTERCEPTOR_NAME equals the implementation's class name. A request interceptor with order zero sees a normalized body but an unresolved URL. A response interceptor with order zero runs before default status validation. Removing FetchInterceptor means no built-in HTTP I/O; clear() is not merely removing custom hooks.
Failure and recovery
new InterceptorManager(validateStatus?, fetchImplementation?) constructs these request/response registries and an empty error registry; fetchImplementation is passed to new FetchInterceptor(fetchImplementation?), which sends with the global fetch, read at call time, when it is omitted. exchange(exchange) runs request then response. On rejection it stores the thrown value in exchange.error, runs the error registry, then, if hasError() remains true, rejects with an ExchangeError: an ExchangeError for this same exchange (such as HttpStatusValidationError) is rethrown as is, anything else is wrapped with the original as cause. To recover, set exchange.error to undefined or null.
An error interceptor recovers by supplying any needed response/result state and clearing exchange.error. The response chain is not rerun after recovery, so recovered responses must already meet the application's policy. A throw from an error interceptor (or from a callback it runs) stops the error chain, so later error interceptors do not run; the thrown value becomes exchange.error and the exchange rejects with an ExchangeError whose cause is that value, like any other failure. Error interceptors are not automatic retries, and extractor failures occur outside this manager.
Interceptors added to a shared client remain until ejected. Use distinct stable names and remove request-specific instrumentation when its owner ends; do not accumulate a new interceptor per request.
Complete example
import { Fetcher, setHeader } from '@ahoo-wang/fetcher';
const client = new Fetcher({ baseURL: 'https://api.example.com' });
client.interceptors.request.use({
name: 'trace',
order: 0,
intercept(exchange) {
setHeader(exchange.ensureRequestHeaders(), 'X-Trace-Id', 'demo-trace');
},
});
console.assert(client.interceptors.request.eject('trace'));Public symbols and source
| Symbol | Implementation |
|---|---|
FETCH_INTERCEPTOR_NAME | fetchInterceptor.ts:24 |
FETCH_INTERCEPTOR_ORDER | fetchInterceptor.ts:30 |
FetchInterceptor | fetchInterceptor.ts:54 |
DEFAULT_INTERCEPTOR_ORDER_STEP | interceptor.ts:18 |
BUILT_IN_INTERCEPTOR_ORDER_STEP | interceptor.ts:20 |
Interceptor | interceptor.ts:44 |
RequestInterceptor | interceptor.ts:111 |
ResponseInterceptor | interceptor.ts:135 |
ErrorInterceptor | interceptor.ts:164 |
InterceptorRegistry | interceptor.ts:189 |
InterceptorManager | interceptorManager.ts:49 |
OrderedCapable | orderedCapable.ts:29 |
sortOrder | orderedCapable.ts:53 |
toSorted | orderedCapable.ts:87 |
REQUEST_BODY_INTERCEPTOR_NAME | requestBodyInterceptor.ts:25 |
REQUEST_BODY_INTERCEPTOR_ORDER | requestBodyInterceptor.ts:30 |
RequestBodyInterceptor | requestBodyInterceptor.ts:50 |
URL_RESOLVE_INTERCEPTOR_NAME | urlResolveInterceptor.ts:24 |
URL_RESOLVE_INTERCEPTOR_ORDER | urlResolveInterceptor.ts:29 |
UrlResolveInterceptor | urlResolveInterceptor.ts:53 |
