UNPKG

@zimic/http

Version:

Next-gen TypeScript-first HTTP utilities

525 lines (518 loc) 18.5 kB
'use strict'; // ../zimic-utils/dist/data.mjs async function blobEquals(blob, otherBlob) { if (blob.type !== otherBlob.type || blob.size !== otherBlob.size) { return false; } const reader = blob.stream().getReader(); const otherReader = otherBlob.stream().getReader(); let buffer = new Uint8Array(0); let otherBuffer = new Uint8Array(0); try { while (true) { const bufferReadPromises = []; if (buffer.length === 0) { bufferReadPromises.push( reader.read().then((result) => { if (!result.done) { buffer = result.value; } }) ); } if (otherBuffer.length === 0) { bufferReadPromises.push( otherReader.read().then((result) => { if (!result.done) { otherBuffer = result.value; } }) ); } await Promise.all(bufferReadPromises); const haveStreamsEndedTogether = buffer.length === 0 && otherBuffer.length === 0; if (haveStreamsEndedTogether) { return true; } const hasOneStreamEndedBeforeTheOther = buffer.length === 0 && otherBuffer.length > 0 || buffer.length > 0 && otherBuffer.length === 0; if (hasOneStreamEndedBeforeTheOther) { return false; } const minimumByteLength = Math.min(buffer.length, otherBuffer.length); for (let byteIndex = 0; byteIndex < minimumByteLength; byteIndex++) { if (buffer[byteIndex] !== otherBuffer[byteIndex]) { return false; } } buffer = buffer.slice(minimumByteLength); otherBuffer = otherBuffer.slice(minimumByteLength); } } finally { reader.releaseLock(); otherReader.releaseLock(); } } var blobEquals_default = blobEquals; async function fileEquals(file, otherFile) { return file.name === otherFile.name && await blobEquals_default(file, otherFile); } var fileEquals_default = fileEquals; // src/formData/HttpFormData.ts var HttpFormData = class extends FormData { _schema; set(name, blobOrValue, fileName) { if (fileName === void 0) { super.set(name, blobOrValue); } else { super.set(name, blobOrValue, fileName); } } append(name, blobOrValue, fileName) { if (fileName === void 0) { super.append(name, blobOrValue); } else { super.append(name, blobOrValue, fileName); } } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdataget `formData.get()` API reference} */ get(name) { return super.get(name); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatagetall `formData.getAll()` API reference} */ getAll(name) { return super.getAll(name); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatahas `formData.has()` API reference} */ has(name) { return super.has(name); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatadelete `formData.delete()` API reference} */ delete(name) { super.delete(name); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdataforeach `formData.forEach()` API reference} */ forEach(callback, thisArg) { super.forEach(callback, thisArg); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatakeys `formData.keys()` API reference} */ keys() { return super.keys(); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatavalues `formData.values()` API reference} */ values() { return super.values(); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdataentries `formData.entries()` API reference} */ entries() { return super.entries(); } [Symbol.iterator]() { return super[Symbol.iterator](); } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdataequals `formData.equals()` API reference} */ async equals(otherData) { if (!await this.contains(otherData)) { return false; } for (const fieldName of this.keys()) { if (!super.has.call(otherData, fieldName)) { return false; } } return true; } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatacontains `formData.contains()` API reference} */ async contains(otherData) { for (const [otherFieldName, otherFieldValue] of otherData.entries()) { const fieldValues = super.getAll.call(this, otherFieldName); const haveSameNumberOfFieldValues = fieldValues.length === super.getAll.call(otherData, otherFieldName).length; if (!haveSameNumberOfFieldValues) { return false; } let fieldValueExists = false; for (const fieldValue of fieldValues) { if (fieldValue === otherFieldValue || fieldValue instanceof Blob && otherFieldValue instanceof Blob && await fileEquals_default(fieldValue, otherFieldValue)) { fieldValueExists = true; break; } } if (!fieldValueExists) { return false; } } return true; } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdataassign `formData.assign()` API reference} */ assign(...otherDataArray) { for (const otherData of otherDataArray) { if (this === otherData) { continue; } for (const fieldName of otherData.keys()) { super.delete(fieldName); } for (const [fieldName, fieldValue] of otherData.entries()) { super.append(fieldName, fieldValue); } } } /** @see {@link https://zimic.dev/docs/http/api/http-form-data#formdatatoobject `formData.toObject()` API reference} */ toObject() { const object = {}; for (const [fieldName, fieldValue] of this.entries()) { if (fieldName in object) { const existingFieldValue = object[fieldName]; if (Array.isArray(existingFieldValue)) { existingFieldValue.push(fieldValue); } else { object[fieldName] = [existingFieldValue, fieldValue]; } } else { object[fieldName] = fieldValue; } } return object; } }; var HttpFormData_default = HttpFormData; // src/headers/HttpHeaders.ts function pickPrimitiveProperties(schema) { return Object.entries(schema).reduce((accumulated, [key, value]) => { if (value !== void 0) { accumulated[key] = String(value); } return accumulated; }, {}); } var HttpHeaders = class extends Headers { _schema; constructor(init) { if (init instanceof Headers || Array.isArray(init) || !init) { super(init); } else { super(pickPrimitiveProperties(init)); } } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersset `headers.set()` API reference} */ set(name, value) { super.set(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersappend `headers.append()` API reference} */ append(name, value) { super.append(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersget `headers.get()` API reference} */ get(name) { return super.get(name); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersgetsetcookie `headers.getSetCookie()` API reference} */ getSetCookie() { return super.getSetCookie(); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headershas `headers.has()` API reference} */ has(name) { return super.has(name); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersdelete `headers.delete()` API reference} */ delete(name) { super.delete(name); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersforeach `headers.forEach()` API reference} */ forEach(callback, thisArg) { super.forEach(callback, thisArg); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headerskeys `headers.keys()` API reference} */ keys() { return super.keys(); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersvalues `headers.values()` API reference} */ values() { return super.values(); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersentries `headers.entries()` API reference} */ entries() { return super.entries(); } [Symbol.iterator]() { return super[Symbol.iterator](); } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersequals `headers.equals()` API reference} */ equals(otherHeaders) { if (!this.contains(otherHeaders)) { return false; } for (const headerName of this.keys()) { if (!super.has.call(otherHeaders, headerName)) { return false; } } return true; } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headerscontains `headers.contains()` API reference} */ contains(otherHeaders) { for (const [headerName, otherHeaderValue] of otherHeaders.entries()) { const headerValue = super.get.call(this, headerName); if (headerValue === null) { return false; } const headerValueItems = this.splitHeaderValues(headerValue); const otherHeaderValueItems = this.splitHeaderValues(otherHeaderValue); const haveSameNumberOfHeaderValues = headerValueItems.length === otherHeaderValueItems.length; if (!haveSameNumberOfHeaderValues) { return false; } for (const otherValueItem of otherHeaderValueItems) { if (!headerValueItems.includes(otherValueItem)) { return false; } } } return true; } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headersassign `headers.assign()` API reference} */ assign(...otherHeadersArray) { for (const otherHeaders of otherHeadersArray) { if (this === otherHeaders) { continue; } for (const headerName of otherHeaders.keys()) { super.delete(headerName); } for (const [headerName, headerValue] of otherHeaders.entries()) { super.append(headerName, headerValue); } } } /** @see {@link https://zimic.dev/docs/http/api/http-headers#headerstoobject `headers.toObject()` API reference} */ toObject() { const object = {}; for (const [headerName, headerValue] of this.entries()) { object[headerName] = headerValue; } return object; } splitHeaderValues(value) { return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0); } }; var HttpHeaders_default = HttpHeaders; // src/searchParams/HttpSearchParams.ts function pickPrimitiveProperties2(schema) { const schemaWithPrimitiveProperties = Object.entries(schema).reduce( (accumulated, [key, value]) => { if (value !== void 0 && !Array.isArray(value)) { accumulated[key] = String(value); } return accumulated; }, {} ); return schemaWithPrimitiveProperties; } var HttpSearchParams = class extends URLSearchParams { _schema; constructor(init) { if (init instanceof URLSearchParams || Array.isArray(init) || typeof init === "string" || !init) { super(init); } else { super(pickPrimitiveProperties2(init)); this.populateInitArrayProperties(init); } } populateInitArrayProperties(init) { for (const [paramName, paramValues] of Object.entries(init)) { if (Array.isArray(paramValues)) { for (const paramValue of paramValues) { super.append(paramName, String(paramValue)); } } } } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsset `searchParams.set()` API reference} */ set(name, value) { super.set(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsappend `searchParams.append()` API reference} */ append(name, value) { super.append(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsget `searchParams.get()` API reference} */ get(name) { return super.get(name); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsgetall `searchParams.getAll()` API reference} */ getAll(name) { return super.getAll(name); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamshas `searchParams.has()` API reference} */ has(name, value) { return super.has(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsdelete `searchParams.delete()` API reference} */ delete(name, value) { super.delete(name, value); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsforeach `searchParams.forEach()` API reference} */ forEach(callback, thisArg) { super.forEach(callback, thisArg); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamskeys `searchParams.keys()` API reference} */ keys() { return super.keys(); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsvalues `searchParams.values()` API reference} */ values() { return super.values(); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsentries `searchParams.entries()` API reference} */ entries() { return super.entries(); } [Symbol.iterator]() { return super[Symbol.iterator](); } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsequals `searchParams.equals()` API reference} */ equals(otherParams) { return this.contains(otherParams) && this.size === otherParams.size; } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamscontains `searchParams.contains()` API reference} */ contains(otherParams) { for (const [paramName, otherParamValue] of otherParams.entries()) { const paramValues = super.getAll.call(this, paramName); const haveSameNumberOfParamValues = paramValues.length === super.getAll.call(otherParams, paramName).length; if (!haveSameNumberOfParamValues) { return false; } const paramValueExists = paramValues.includes(otherParamValue); if (!paramValueExists) { return false; } } return true; } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamsassign `searchParams.assign()` API reference} */ assign(...otherParamsArray) { for (const otherParams of otherParamsArray) { if (this === otherParams) { continue; } for (const paramName of otherParams.keys()) { super.delete(paramName); } for (const [paramName, paramValue] of otherParams.entries()) { super.append(paramName, paramValue); } } } /** @see {@link https://zimic.dev/docs/http/api/http-search-params#searchparamstoobject `searchParams.toObject()` API reference} */ toObject() { const object = {}; for (const [paramName, paramValue] of this.entries()) { if (paramName in object) { const existingParamValue = object[paramName]; if (Array.isArray(existingParamValue)) { existingParamValue.push(paramValue); } else { object[paramName] = [existingParamValue, paramValue]; } } else { object[paramName] = paramValue; } } return object; } }; var HttpSearchParams_default = HttpSearchParams; // src/types/schema.ts var HTTP_METHODS = Object.freeze(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]); // src/utils/bodies.ts var InvalidFormDataError = class extends SyntaxError { constructor(value) { super(`Failed to parse value as form data: ${value}`); this.name = "InvalidFormDataError"; } }; var InvalidJSONError = class extends SyntaxError { constructor(value) { super(`Failed to parse value as JSON: ${value}`); this.name = "InvalidJSONError"; } }; async function parseHttpBodyAsText(resource) { const bodyAsText = await resource.text(); return bodyAsText || null; } async function parseHttpBodyAsBlob(resource) { const bodyAsBlob = await resource.blob(); return bodyAsBlob; } async function parseHttpBodyAsFormData(resource) { const resourceClone = resource.clone(); try { const bodyAsRawFormData = await resource.formData(); const bodyAsFormData = new HttpFormData_default(); for (const [key, value] of bodyAsRawFormData) { bodyAsFormData.append(key, value); } return bodyAsFormData; } catch { const bodyAsText = await resourceClone.text(); if (!bodyAsText.trim()) { return null; } throw new InvalidFormDataError(bodyAsText); } } async function parseHttpBodyAsSearchParams(resource) { const bodyAsText = await resource.text(); if (!bodyAsText.trim()) { return null; } const bodyAsSearchParams = new HttpSearchParams_default(bodyAsText); return bodyAsSearchParams; } async function parseHttpBodyAsJSON(resource) { const bodyAsText = await resource.text(); if (!bodyAsText.trim()) { return null; } try { const bodyAsJSON = JSON.parse(bodyAsText); return bodyAsJSON; } catch { throw new InvalidJSONError(bodyAsText); } } async function parseHttpBody(resource) { const contentType = resource.headers.get("content-type"); if (contentType?.startsWith("application/json")) { return parseHttpBodyAsJSON(resource); } if (contentType?.startsWith("multipart/form-data")) { return parseHttpBodyAsFormData(resource); } if (contentType?.startsWith("application/x-www-form-urlencoded")) { return parseHttpBodyAsSearchParams(resource); } if (contentType?.startsWith("text/") || contentType?.startsWith("application/xml")) { return parseHttpBodyAsText(resource); } if (contentType?.startsWith("application/") || contentType?.startsWith("image/") || contentType?.startsWith("audio/") || contentType?.startsWith("font/") || contentType?.startsWith("video/") || contentType?.startsWith("multipart/")) { return parseHttpBodyAsBlob(resource); } const resourceClone = resource.clone(); try { return await parseHttpBodyAsJSON(resource); } catch { return parseHttpBodyAsBlob(resourceClone); } } exports.HTTP_METHODS = HTTP_METHODS; exports.HttpFormData = HttpFormData_default; exports.HttpHeaders = HttpHeaders_default; exports.HttpSearchParams = HttpSearchParams_default; exports.InvalidFormDataError = InvalidFormDataError; exports.InvalidJSONError = InvalidJSONError; exports.parseHttpBody = parseHttpBody; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map