@mswjs/interceptors
Advanced tools
Comparing version 0.34.2 to 0.34.3
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkYP3NI6UPjs = require('../../chunk-YP3NI6UP.js'); | ||
var _chunkNBHFK2DJjs = require('../../chunk-NBHFK2DJ.js'); | ||
require('../../chunk-LK6DILFK.js'); | ||
@@ -10,3 +10,3 @@ require('../../chunk-EJYZ4HR3.js'); | ||
exports.XMLHttpRequestInterceptor = _chunkYP3NI6UPjs.XMLHttpRequestInterceptor; | ||
exports.XMLHttpRequestInterceptor = _chunkNBHFK2DJjs.XMLHttpRequestInterceptor; | ||
//# sourceMappingURL=index.js.map |
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkYP3NI6UPjs = require('../chunk-YP3NI6UP.js'); | ||
var _chunkNBHFK2DJjs = require('../chunk-NBHFK2DJ.js'); | ||
require('../chunk-LK6DILFK.js'); | ||
@@ -15,3 +15,3 @@ | ||
new (0, _chunkTPZUQHHYjs.FetchInterceptor)(), | ||
new (0, _chunkYP3NI6UPjs.XMLHttpRequestInterceptor)() | ||
new (0, _chunkNBHFK2DJjs.XMLHttpRequestInterceptor)() | ||
]; | ||
@@ -18,0 +18,0 @@ |
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkCQAXV6QMjs = require('../../chunk-CQAXV6QM.js'); | ||
var _chunkWTJL7BRVjs = require('../../chunk-WTJL7BRV.js'); | ||
require('../../chunk-TQD7SQGP.js'); | ||
@@ -8,3 +8,3 @@ require('../../chunk-YGM3BCJU.js'); | ||
exports.ClientRequestInterceptor = _chunkCQAXV6QMjs.ClientRequestInterceptor; | ||
exports.ClientRequestInterceptor = _chunkWTJL7BRVjs.ClientRequestInterceptor; | ||
//# sourceMappingURL=index.js.map |
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkMFGG7XIPjs = require('../../chunk-MFGG7XIP.js'); | ||
var _chunk6L3UERDRjs = require('../../chunk-6L3UERDR.js'); | ||
require('../../chunk-LK6DILFK.js'); | ||
@@ -10,3 +10,3 @@ require('../../chunk-IDEEMJ3F.js'); | ||
exports.XMLHttpRequestInterceptor = _chunkMFGG7XIPjs.XMLHttpRequestInterceptor; | ||
exports.XMLHttpRequestInterceptor = _chunk6L3UERDRjs.XMLHttpRequestInterceptor; | ||
//# sourceMappingURL=index.js.map |
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkCQAXV6QMjs = require('../chunk-CQAXV6QM.js'); | ||
var _chunkWTJL7BRVjs = require('../chunk-WTJL7BRV.js'); | ||
var _chunkMFGG7XIPjs = require('../chunk-MFGG7XIP.js'); | ||
var _chunk6L3UERDRjs = require('../chunk-6L3UERDR.js'); | ||
require('../chunk-LK6DILFK.js'); | ||
@@ -17,4 +17,4 @@ | ||
var node_default = [ | ||
new (0, _chunkCQAXV6QMjs.ClientRequestInterceptor)(), | ||
new (0, _chunkMFGG7XIPjs.XMLHttpRequestInterceptor)(), | ||
new (0, _chunkWTJL7BRVjs.ClientRequestInterceptor)(), | ||
new (0, _chunk6L3UERDRjs.XMLHttpRequestInterceptor)(), | ||
new (0, _chunkW4AQXISMjs.FetchInterceptor)() | ||
@@ -21,0 +21,0 @@ ]; |
@@ -6,6 +6,6 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
var _chunkCQAXV6QMjs = require('./chunk-CQAXV6QM.js'); | ||
var _chunkWTJL7BRVjs = require('./chunk-WTJL7BRV.js'); | ||
var _chunkMFGG7XIPjs = require('./chunk-MFGG7XIP.js'); | ||
var _chunk6L3UERDRjs = require('./chunk-6L3UERDR.js'); | ||
require('./chunk-LK6DILFK.js'); | ||
@@ -27,4 +27,4 @@ require('./chunk-IDEEMJ3F.js'); | ||
interceptors: [ | ||
new (0, _chunkCQAXV6QMjs.ClientRequestInterceptor)(), | ||
new (0, _chunkMFGG7XIPjs.XMLHttpRequestInterceptor)() | ||
new (0, _chunkWTJL7BRVjs.ClientRequestInterceptor)(), | ||
new (0, _chunk6L3UERDRjs.XMLHttpRequestInterceptor)() | ||
] | ||
@@ -31,0 +31,0 @@ }); |
{ | ||
"name": "@mswjs/interceptors", | ||
"description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.", | ||
"version": "0.34.2", | ||
"version": "0.34.3", | ||
"main": "./lib/node/index.js", | ||
@@ -6,0 +6,0 @@ "module": "./lib/node/index.mjs", |
@@ -189,3 +189,5 @@ import { it, expect } from 'vitest' | ||
// Options must be preserved. | ||
expect(options).toEqual<RequestOptions>({ | ||
// `urlToHttpOptions` from `node:url` generates additional | ||
// ClientRequest options, some of which are not legally allowed. | ||
expect(options).toMatchObject<RequestOptions>({ | ||
agent: false, | ||
@@ -198,4 +200,2 @@ _defaultAgent: httpsGlobalAgent, | ||
}, | ||
host: url.host, | ||
hostname: url.hostname, | ||
@@ -219,3 +219,3 @@ path: url.pathname, | ||
expect(options).toMatchObject({ | ||
host: 'host-from-options.com', | ||
hostname: 'host-from-options.com', | ||
path: '/path-from-url', | ||
@@ -236,4 +236,4 @@ }) | ||
expect(url.href).toBe('http://host-from-options.com:1234/path-from-options') | ||
expect(options).toMatchObject({ | ||
host: 'host-from-options.com:1234', | ||
expect(options).toMatchObject<RequestOptions>({ | ||
hostname: 'host-from-options.com', | ||
path: '/path-from-options', | ||
@@ -252,4 +252,4 @@ port: 1234, | ||
expect(url.href).toBe('http://example.com/path-from-options?foo=bar&baz=xyz') | ||
expect(options).toMatchObject({ | ||
host: 'example.com', | ||
expect(options).toMatchObject<RequestOptions>({ | ||
hostname: 'example.com', | ||
path: '/path-from-options?foo=bar&baz=xyz', | ||
@@ -407,3 +407,2 @@ }) | ||
expect(options.protocol).toEqual(url.protocol) | ||
expect(options.host).toEqual(url.host) | ||
expect(options.hostname).toEqual(url.hostname) | ||
@@ -423,3 +422,2 @@ expect(options.path).toEqual(url.pathname) | ||
expect(options.protocol).toBe('http:') | ||
expect(options.host).toBe('example.com') | ||
expect(options.hostname).toBe('example.com') | ||
@@ -440,3 +438,2 @@ expect(options.path).toBe('/path-from-options') | ||
expect(options.protocol).toBe('http:') | ||
expect(options.host).toBe('example.com') | ||
expect(options.hostname).toBe('example.com') | ||
@@ -448,11 +445,12 @@ expect(options.path).toBe('/path-from-options') | ||
const [url, options] = normalizeClientRequestArgs('http:', [ | ||
new URL('http://example.com/resource?a=b&c=d'), | ||
new URL('http://example.com:8080/resource?a=b&c=d'), | ||
]) | ||
expect(url.href).toBe('http://example.com/resource?a=b&c=d') | ||
expect(url.href).toBe('http://example.com:8080/resource?a=b&c=d') | ||
expect(options.protocol).toBe('http:') | ||
expect(options.host).toBe('example.com') | ||
// expect(options.host).toBe('example.com:8080') | ||
expect(options.hostname).toBe('example.com') | ||
// Query string is a part of the options path. | ||
expect(options.path).toBe('/resource?a=b&c=d') | ||
expect(options.port).toBe(8080) | ||
}) |
@@ -0,1 +1,2 @@ | ||
import { urlToHttpOptions } from 'node:url' | ||
import { | ||
@@ -23,3 +24,2 @@ Agent as HttpAgent, | ||
import { Logger } from '@open-draft/logger' | ||
import { getRequestOptionsByUrl } from '../../../utils/getRequestOptionsByUrl' | ||
import { | ||
@@ -51,3 +51,3 @@ ResolvedRequestOptions, | ||
logger.info('request options not provided, deriving from the url', url) | ||
return getRequestOptionsByUrl(url) | ||
return urlToHttpOptions(url) | ||
} | ||
@@ -57,3 +57,3 @@ | ||
logger.info('has custom RequestOptions!', args[1]) | ||
const requestOptionsFromUrl = getRequestOptionsByUrl(url) | ||
const requestOptionsFromUrl = urlToHttpOptions(url) | ||
@@ -109,3 +109,3 @@ logger.info('derived RequestOptions from the URL:', requestOptionsFromUrl) | ||
options: ResolvedRequestOptions, | ||
callback?: HttpRequestCallback, | ||
callback?: HttpRequestCallback | ||
] | ||
@@ -144,3 +144,3 @@ | ||
const requestOptionsFromUrl = getRequestOptionsByUrl(url) | ||
const requestOptionsFromUrl = urlToHttpOptions(url) | ||
logger.info('request options from url:', requestOptionsFromUrl) | ||
@@ -208,8 +208,8 @@ | ||
: typeof args[1] === 'function' | ||
? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl, args[1]]) | ||
: normalizeClientRequestArgs(defaultProtocol, [ | ||
resolvedUrl, | ||
args[1], | ||
args[2], | ||
]) | ||
? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl, args[1]]) | ||
: normalizeClientRequestArgs(defaultProtocol, [ | ||
resolvedUrl, | ||
args[1], | ||
args[2], | ||
]) | ||
} | ||
@@ -219,3 +219,3 @@ // Handle a given "RequestOptions" object as-is | ||
else if (isObject(args[0])) { | ||
options = { ... args[0] as any } | ||
options = { ...(args[0] as any) } | ||
logger.info('first argument is RequestOptions:', options) | ||
@@ -222,0 +222,0 @@ |
@@ -17,5 +17,7 @@ import { invariant } from 'outvariant' | ||
import { createRequestId } from '../../createRequestId' | ||
import { getBodyByteLength } from './utils/getBodyByteLength' | ||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') | ||
const kIsRequestHandled = Symbol('kIsRequestHandled') | ||
const IS_NODE = isNodeProcess() | ||
const kFetchRequest = Symbol('kFetchRequest') | ||
@@ -44,8 +46,9 @@ /** | ||
} | ||
) => void | ||
) => void; | ||
[kIsRequestHandled]: boolean; | ||
[kFetchRequest]?: Request | ||
private method: string = 'GET' | ||
private url: URL = null as any | ||
private requestHeaders: Headers | ||
private requestBody?: XMLHttpRequestBodyInit | Document | null | ||
private responseBuffer: Uint8Array | ||
@@ -59,2 +62,4 @@ private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>> | ||
constructor(readonly initialRequest: XMLHttpRequest, public logger: Logger) { | ||
this[kIsRequestHandled] = false | ||
this.events = new Map() | ||
@@ -134,7 +139,2 @@ this.uploadEvents = new Map() | ||
if (body != null) { | ||
this.requestBody = | ||
typeof body === 'string' ? encodeBuffer(body) : body | ||
} | ||
this.request.addEventListener('load', () => { | ||
@@ -158,3 +158,3 @@ if (typeof this.onResponse !== 'undefined') { | ||
response: fetchResponse, | ||
isMockedResponse: IS_MOCKED_RESPONSE in this.request, | ||
isMockedResponse: this[kIsRequestHandled], | ||
request: fetchRequest, | ||
@@ -166,4 +166,9 @@ requestId: this.requestId!, | ||
const requestBody = | ||
typeof body === 'string' ? encodeBuffer(body) : body | ||
// Delegate request handling to the consumer. | ||
const fetchRequest = this.toFetchApiRequest() | ||
const fetchRequest = this.toFetchApiRequest(requestBody) | ||
this[kFetchRequest] = fetchRequest | ||
const onceRequestSettled = | ||
@@ -176,6 +181,4 @@ this.onRequest?.call(this, { | ||
onceRequestSettled.finally(() => { | ||
// If the consumer didn't handle the request perform it as-is. | ||
// Note that the request may not yet be DONE and may, in fact, | ||
// be LOADING while the "respondWith" method does its magic. | ||
if (this.request.readyState < this.request.LOADING) { | ||
// If the consumer didn't handle the request (called `.respondWith()`) perform it as-is. | ||
if (!this[kIsRequestHandled]) { | ||
this.logger.info( | ||
@@ -288,12 +291,19 @@ 'request callback settled but request has not been handled (readystate %d), performing as-is...', | ||
/** | ||
* @note Since `XMLHttpRequestController` delegates the handling of the responses | ||
* to the "load" event listener that doesn't distinguish between the mocked and original | ||
* responses, mark the request that had a mocked response with a corresponding symbol. | ||
* | ||
* Mark this request as having a mocked response immediately since | ||
* calculating request/response total body length is asynchronous. | ||
*/ | ||
this[kIsRequestHandled] = true | ||
/** | ||
* Dispatch request upload events for requests with a body. | ||
* @see https://github.com/mswjs/interceptors/issues/573 | ||
*/ | ||
if (this.requestBody != null) { | ||
const totalRequestBodyLength = this.requestHeaders.has('content-length') | ||
? Number(this.requestHeaders.get('content-length')) | ||
: await getXMLHttpRequestBodyInitLength( | ||
this.requestBody, | ||
this.requestHeaders | ||
) | ||
if (this[kFetchRequest]) { | ||
const totalRequestBodyLength = await getBodyByteLength( | ||
this[kFetchRequest].clone() | ||
) | ||
@@ -324,9 +334,2 @@ this.trigger('loadstart', this.request.upload, { | ||
/** | ||
* @note Since `XMLHttpRequestController` delegates the handling of the responses | ||
* to the "load" event listener that doesn't distinguish between the mocked and original | ||
* responses, mark the request that had a mocked response with a corresponding symbol. | ||
*/ | ||
define(this.request, IS_MOCKED_RESPONSE, true) | ||
define(this.request, 'status', response.status) | ||
@@ -404,8 +407,3 @@ define(this.request, 'statusText', response.statusText) | ||
const totalResponseBodyLength = response.headers.has('content-length') | ||
? Number(response.headers.get('Content-Length')) | ||
: /** | ||
* @todo Infer the response body length from the response body. | ||
*/ | ||
undefined | ||
const totalResponseBodyLength = await getBodyByteLength(response.clone()) | ||
@@ -584,2 +582,7 @@ this.logger.info('calculated response body length', totalResponseBodyLength) | ||
public errorWith(error?: Error): void { | ||
/** | ||
* @note Mark this request as handled even if it received a mock error. | ||
* This prevents the controller from trying to perform this request as-is. | ||
*/ | ||
this[kIsRequestHandled] = true | ||
this.logger.info('responding with an error') | ||
@@ -661,5 +664,12 @@ | ||
*/ | ||
private toFetchApiRequest(): Request { | ||
private toFetchApiRequest( | ||
body: XMLHttpRequestBodyInit | Document | null | undefined | ||
): Request { | ||
this.logger.info('converting request to a Fetch API Request...') | ||
// If the `Document` is used as the body of this XMLHttpRequest, | ||
// set its inner text as the Fetch API Request body. | ||
const resolvedBody = | ||
body instanceof Document ? body.documentElement.innerText : body | ||
const fetchRequest = new Request(this.url.href, { | ||
@@ -674,3 +684,3 @@ method: this.method, | ||
? null | ||
: (this.requestBody as BodyInit), | ||
: resolvedBody, | ||
}) | ||
@@ -738,48 +748,1 @@ | ||
} | ||
async function getXMLHttpRequestBodyInitLength( | ||
body: XMLHttpRequestBodyInit | Document, | ||
headers: Headers | ||
): Promise<number> { | ||
if (typeof body === 'object' && 'byteLength' in body) { | ||
return body.byteLength | ||
} | ||
if (body instanceof Blob) { | ||
return body.size | ||
} | ||
if (body instanceof FormData) { | ||
const lines: Array<string> = [] | ||
const contentType = | ||
headers.get('content-type') || 'application/octet-stream' | ||
for (const [name, entry] of body) { | ||
lines.push(`------WebKitFormBoundary1234567890123456`) | ||
lines.push(`content-type: ${contentType}`) | ||
if (typeof entry === 'string') { | ||
lines.push(`content-disposition: form-data; name="${name}"`) | ||
lines.push(``) | ||
lines.push(entry) | ||
} else { | ||
lines.push( | ||
`content-disposition: form-data; name="${name}"; filename="${entry.name}"` | ||
) | ||
lines.push(``) | ||
lines.push(await entry.text()) | ||
} | ||
} | ||
lines.push('------WebKitFormBoundary1234567890123456--') | ||
lines.push(``) | ||
return lines.join('\r\n').length | ||
} | ||
if (body instanceof Document) { | ||
return body.documentElement.innerHTML.length | ||
} | ||
return body.toString().length | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
203
1259076
16779