@zimic/fetch
Version:
Next-gen TypeScript-first fetch API client
282 lines (273 loc) • 10.7 kB
JavaScript
'use strict';
var http = require('@zimic/http');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var FetchResponseError = class extends Error {
constructor(request, response) {
super(`${request.method} ${request.url} failed with status ${response.status}: ${response.statusText}`);
this.request = request;
this.response = response;
this.name = "FetchResponseError";
}
static {
__name(this, "FetchResponseError");
}
toObject(options) {
const includeRequestBody = options?.includeRequestBody ?? false;
const includeResponseBody = options?.includeResponseBody ?? false;
const partialObject = {
name: this.name,
message: this.message
};
if (!includeRequestBody && !includeResponseBody) {
const request = this.convertRequestToObject({ includeBody: false });
const response = this.convertResponseToObject({ includeBody: false });
return { ...partialObject, request, response };
}
return Promise.all([
this.convertRequestToObject({ includeBody: includeRequestBody }),
this.convertResponseToObject({ includeBody: includeResponseBody })
]).then(([request, response]) => ({ ...partialObject, request, response }));
}
convertRequestToObject(options) {
const requestObject = {
url: this.request.url,
path: this.request.path,
method: this.request.method,
headers: http.HttpHeaders.prototype.toObject.call(this.request.headers),
cache: this.request.cache,
destination: this.request.destination,
credentials: this.request.credentials,
integrity: this.request.integrity,
keepalive: this.request.keepalive,
mode: this.request.mode,
redirect: this.request.redirect,
referrer: this.request.referrer,
referrerPolicy: this.request.referrerPolicy
};
if (!options.includeBody) {
return requestObject;
}
const bodyAsTextPromise = this.request.text();
return bodyAsTextPromise.then((bodyAsText) => {
requestObject.body = bodyAsText.length > 0 ? bodyAsText : null;
return requestObject;
});
}
convertResponseToObject(options) {
const responseObject = {
url: this.response.url,
type: this.response.type,
status: this.response.status,
statusText: this.response.statusText,
ok: this.response.ok,
headers: http.HttpHeaders.prototype.toObject.call(this.response.headers),
redirected: this.response.redirected
};
if (!options.includeBody) {
return responseObject;
}
const bodyAsTextPromise = this.response.text();
return bodyAsTextPromise.then((bodyAsText) => {
responseObject.body = bodyAsText.length > 0 ? bodyAsText : null;
return responseObject;
});
}
};
var FetchResponseError_default = FetchResponseError;
// ../zimic-utils/dist/chunk-2D3UJWOA.mjs
var __defProp2 = Object.defineProperty;
var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
// ../zimic-utils/dist/chunk-4RR2YNYT.mjs
var URL_PATH_PARAM_REGEX = /\/:([^/]+)/g;
function createRegExpFromURL(url) {
URL_PATH_PARAM_REGEX.lastIndex = 0;
const urlWithReplacedPathParams = encodeURI(url).replace(/([.()*?+$\\])/g, "\\$1").replace(URL_PATH_PARAM_REGEX, "/(?<$1>[^/]+)").replace(/^(\/)|(\/)$/g, "");
return new RegExp(`^(?:/)?${urlWithReplacedPathParams}(?:/)?$`);
}
__name(createRegExpFromURL, "createRegExpFromURL");
__name2(createRegExpFromURL, "createRegExpFromURL");
var createRegExpFromURL_default = createRegExpFromURL;
// ../zimic-utils/dist/url/excludeURLParams.mjs
function excludeURLParams(url) {
url.hash = "";
url.search = "";
url.username = "";
url.password = "";
return url;
}
__name(excludeURLParams, "excludeURLParams");
__name2(excludeURLParams, "excludeURLParams");
var excludeURLParams_default = excludeURLParams;
// ../zimic-utils/dist/url/joinURL.mjs
function joinURL(...parts) {
return parts.map((part, index) => {
const isFirstPart = index === 0;
const isLastPart = index === parts.length - 1;
let partAsString = part.toString();
if (!isFirstPart) {
partAsString = partAsString.replace(/^\//, "");
}
if (!isLastPart) {
partAsString = partAsString.replace(/\/$/, "");
}
return partAsString;
}).filter((part) => part.length > 0).join("/");
}
__name(joinURL, "joinURL");
__name2(joinURL, "joinURL");
var joinURL_default = joinURL;
// src/client/FetchClient.ts
var FetchClient = class {
static {
__name(this, "FetchClient");
}
fetch;
constructor({ onRequest, onResponse, ...defaults }) {
this.fetch = this.createFetchFunction();
this.fetch.defaults = {
...defaults,
headers: defaults.headers ?? {},
searchParams: defaults.searchParams ?? {}
};
this.fetch.loose = this.fetch;
this.fetch.Request = this.createRequestClass(this.fetch.defaults);
this.fetch.onRequest = onRequest;
this.fetch.onResponse = onResponse;
}
createFetchFunction() {
const fetch = /* @__PURE__ */ __name(async (input, init) => {
const request = await this.createFetchRequest(input, init);
const requestClone = request.clone();
const rawResponse = await globalThis.fetch(
// Optimize type checking by narrowing the type of request
requestClone
);
const response = await this.createFetchResponse(request, rawResponse);
return response;
}, "fetch");
Object.setPrototypeOf(fetch, this);
return fetch;
}
async createFetchRequest(input, init) {
let request = input instanceof Request ? input : new this.fetch.Request(input, init);
if (this.fetch.onRequest) {
const requestAfterInterceptor = await this.fetch.onRequest(
// Optimize type checking by narrowing the type of request
request
);
if (requestAfterInterceptor !== request) {
const isFetchRequest = requestAfterInterceptor instanceof this.fetch.Request;
request = isFetchRequest ? requestAfterInterceptor : new this.fetch.Request(requestAfterInterceptor, init);
}
}
return request;
}
async createFetchResponse(fetchRequest, rawResponse) {
let response = this.defineFetchResponseProperties(fetchRequest, rawResponse);
if (this.fetch.onResponse) {
const responseAfterInterceptor = await this.fetch.onResponse(
// Optimize type checking by narrowing the type of response
response
);
const isFetchResponse = responseAfterInterceptor instanceof Response && "request" in responseAfterInterceptor && responseAfterInterceptor.request instanceof this.fetch.Request;
response = isFetchResponse ? responseAfterInterceptor : this.defineFetchResponseProperties(fetchRequest, responseAfterInterceptor);
}
return response;
}
defineFetchResponseProperties(fetchRequest, response) {
const fetchResponse = response;
Object.defineProperty(fetchResponse, "request", {
value: fetchRequest,
writable: false,
enumerable: true,
configurable: false
});
let responseError;
Object.defineProperty(fetchResponse, "error", {
get() {
if (responseError === void 0) {
responseError = fetchResponse.ok ? null : new FetchResponseError_default(
fetchRequest,
fetchResponse
);
}
return responseError;
},
enumerable: true,
configurable: false
});
return fetchResponse;
}
createRequestClass(defaults) {
class Request2 extends globalThis.Request {
static {
__name(this, "Request");
}
path;
constructor(input, init) {
const initWithDefaults = { ...defaults, ...init };
const headersFromDefaults = new http.HttpHeaders(defaults.headers);
const headersFromInit = new http.HttpHeaders(init.headers);
let url;
const baseURL = new URL(initWithDefaults.baseURL);
if (input instanceof globalThis.Request) {
const request = input;
const headersFromRequest = new http.HttpHeaders(input.headers);
initWithDefaults.headers = {
...headersFromDefaults.toObject(),
...headersFromRequest.toObject(),
...headersFromInit.toObject()
};
super(request, initWithDefaults);
url = new URL(input.url);
} else {
initWithDefaults.headers = {
...headersFromDefaults.toObject(),
...headersFromInit.toObject()
};
url = input instanceof URL ? new URL(input) : new URL(joinURL_default(baseURL, input));
const searchParamsFromDefaults = new http.HttpSearchParams(defaults.searchParams);
const searchParamsFromInit = new http.HttpSearchParams(initWithDefaults.searchParams);
initWithDefaults.searchParams = {
...searchParamsFromDefaults.toObject(),
...searchParamsFromInit.toObject()
};
url.search = new http.HttpSearchParams(initWithDefaults.searchParams).toString();
super(url, initWithDefaults);
}
const baseURLWithoutTrailingSlash = baseURL.toString().replace(/\/$/, "");
this.path = excludeURLParams_default(url).toString().replace(baseURLWithoutTrailingSlash, "");
}
clone() {
const rawClone = super.clone();
return new Request2(
rawClone,
rawClone
);
}
}
return Request2;
}
isRequest(request, method, path) {
return request instanceof Request && request.method === method && "path" in request && typeof request.path === "string" && createRegExpFromURL_default(path).test(request.path);
}
isResponse(response, method, path) {
return response instanceof Response && "request" in response && this.isRequest(response.request, method, path) && "error" in response && (response.error === null || response.error instanceof FetchResponseError_default);
}
isResponseError(error, method, path) {
return error instanceof FetchResponseError_default && this.isRequest(error.request, method, path) && this.isResponse(error.response, method, path);
}
};
var FetchClient_default = FetchClient;
// src/client/factory.ts
function createFetch(options) {
const { fetch } = new FetchClient_default(options);
return fetch;
}
__name(createFetch, "createFetch");
var factory_default = createFetch;
exports.FetchResponseError = FetchResponseError_default;
exports.createFetch = factory_default;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map