axios/axios
Input boundaries are the user config object (or URL string), instance defaults, and environment state (env proxy vars, `document.cookie`, platform globals). `mergeConfig` is the single state-materialization point: it emits a **null-prototype** object with `hasOwnProperty` restored as a non-enumerable own slot, skipping `__proto__`/`constructor`/`prototype` keys entirely. `dispatchRequest` re-flattens whatever interceptors handed back via `utils.toSafeFlatObject`, normalizes headers into `AxiosHeaders`, runs `transformRequest`, then hands off to the adapter. Adapters own the outbound byte boundary (header sanitization to byte strings, body size accounting, stream piping) and produce a raw `{data, status, statusText, headers, config, request}`, which flows back through `transformResponse`, `AxiosHeaders.from`, `settle`, and finally the response interceptor chain.
- `mergeConfig` builds its result on `Object.create(null)` and re-attaches `hasOwnProperty` as a non-enumerable own data property with a `__proto__: null` descriptor, so `config.baseURL`/`config.auth` reads can never inherit from a polluted `Object.prototype` while user code calling `config.hasOwnProperty(...)` still works.
- Per-key merge strategy is table-driven: `url`/`method`/`data` take config2 only, ~25 keys (`baseURL`, `timeout`, `httpAgent`, `socketPath`, `allowedSocketPaths`, …) use `defaultToConfig2`, `validateStatus` uses `mergeDirectKeys`, `headers` deep-merges caselessly after `headersToObject`, and everything unlisted falls through to `mergeDeepProperties`.
- The merge loop explicitly `return`s on `__proto__`, `constructor`, and `prototype` before dispatching to a merge function, so those keys can never be materialized onto the merged config.
- Headers enter via three-way flattening in `Axios._request`: `headers.common` merged with `headers[config.method]`, the per-method buckets then deleted from the raw object, and the remainder concatenated through `AxiosHeaders.concat(contextHeaders, headers)`.
- `dispatchRequest` calls `utils.toSafeFlatObject(_config)` first, which flattens the config and its application-defined prototype chain into a null-prototype copy while stopping at the realm's `Object.prototype` boundary — this is the defense against an interceptor returning an ordinary (pollutable) object.
- `transformData` is invoked with `config` as `this`, wraps `context.headers` in `AxiosHeaders`, calls each fn as `fn.call(config, data, headers.normalize(), status)`, and normalizes headers again afterwards — which is how `transformRequest` can mutate content-type in place.
- Default `transformRequest` is a type-dispatch ladder: HTMLForm→FormData, FormData→(JSON if JSON content-type else passthrough), ArrayBuffer/Buffer/Stream/File/Blob/ReadableStream passthrough, ArrayBufferView→`.buffer`, URLSearchParams→string with urlencoded content-type, FileList→`toFormData({'files[]': …})`, plain object→`stringifySafely` with `application/json`.
- Outbound header values are sanitized twice: `AxiosHeaders.normalizeValue` strips C0/DEL via `sanitizeHeaderValue`, and both the Node and XHR adapters additionally pass the whole header set through `toByteStringHeaderObject`, which rewrites into a null-prototype object with non-byte-string chars removed.
- URL construction is a two-stage pipeline: `buildFullPath(baseURL, url, allowAbsoluteUrls, config)` combines/validates (rejecting `http:foo` style missing-`//` URLs with `ERR_INVALID_URL`), then `buildURL(fullPath, params, paramsSerializer)` appends serialized params using a custom encoder that un-escapes `:`/`$`/`,` and maps space to `+`.
- URLs embedded in `ERR_INVALID_URL` messages are redacted before stringification — userinfo, query-parameter values, and fragment contents are replaced with `[REDACTED ****]` while scheme/host/path/param names survive — because `AxiosError.toJSON()` serializes `message` verbatim and `config.redact` cannot reach it.
- Basic-auth credentials flow from either `config.auth` (read with `utils.getSafeProp`) or URL userinfo; in the Node adapter they become the `auth` option and any pre-existing `authorization` header is deleted, while in fetch/XHR they're base64-encoded into an `Authorization: Basic` header after UTF-8→Latin-1 transcoding.
- Response data flows back through `settle(resolve, reject, response)`, which resolves when `validateStatus` is absent/passing and otherwise rejects with `ERR_BAD_REQUEST` for 4xx and `ERR_BAD_RESPONSE` for everything else, carrying `config`, `request`, and `response`.
- `dispatchRequest` temporarily assigns `config.response` before running `transformResponse` (in both the resolve and reject paths) and deletes it in a `finally`, so a JSON-parse failure inside `transformResponse` can attach the response to the thrown `AxiosError` without permanently polluting the config.
- The fetch adapter never trusts a caller-declared `Content-Length` for `maxBodyLength`: it computes real size with `getBodyLength` (blob size, materialized `Request.arrayBuffer().byteLength` for spec forms, `TextEncoder` bytes for strings) and counts unknown-size streams chunk-by-chunk through `trackStream`.