Buckets:
| /** | |
| * Body.js | |
| * | |
| * Body interface provides common methods for Request and Response | |
| */ | |
| import Stream, {PassThrough} from 'node:stream'; | |
| import {types, deprecate, promisify} from 'node:util'; | |
| import {Buffer} from 'node:buffer'; | |
| import Blob from 'fetch-blob'; | |
| import {FormData, formDataToBlob} from 'formdata-polyfill/esm.min.js'; | |
| import {FetchError} from './errors/fetch-error.js'; | |
| import {FetchBaseError} from './errors/base.js'; | |
| import {isBlob, isURLSearchParameters} from './utils/is.js'; | |
| const pipeline = promisify(Stream.pipeline); | |
| const INTERNALS = Symbol('Body internals'); | |
| /** | |
| * Body mixin | |
| * | |
| * Ref: https://fetch.spec.whatwg.org/#body | |
| * | |
| * @param Stream body Readable stream | |
| * @param Object opts Response options | |
| * @return Void | |
| */ | |
| export default class Body { | |
| constructor(body, { | |
| size = 0 | |
| } = {}) { | |
| let boundary = null; | |
| if (body === null) { | |
| // Body is undefined or null | |
| body = null; | |
| } else if (isURLSearchParameters(body)) { | |
| // Body is a URLSearchParams | |
| body = Buffer.from(body.toString()); | |
| } else if (isBlob(body)) { | |
| // Body is blob | |
| } else if (Buffer.isBuffer(body)) { | |
| // Body is Buffer | |
| } else if (types.isAnyArrayBuffer(body)) { | |
| // Body is ArrayBuffer | |
| body = Buffer.from(body); | |
| } else if (ArrayBuffer.isView(body)) { | |
| // Body is ArrayBufferView | |
| body = Buffer.from(body.buffer, body.byteOffset, body.byteLength); | |
| } else if (body instanceof Stream) { | |
| // Body is stream | |
| } else if (body instanceof FormData) { | |
| // Body is FormData | |
| body = formDataToBlob(body); | |
| boundary = body.type.split('=')[1]; | |
| } else { | |
| // None of the above | |
| // coerce to string then buffer | |
| body = Buffer.from(String(body)); | |
| } | |
| let stream = body; | |
| if (Buffer.isBuffer(body)) { | |
| stream = Stream.Readable.from(body); | |
| } else if (isBlob(body)) { | |
| stream = Stream.Readable.from(body.stream()); | |
| } | |
| this[INTERNALS] = { | |
| body, | |
| stream, | |
| boundary, | |
| disturbed: false, | |
| error: null | |
| }; | |
| this.size = size; | |
| if (body instanceof Stream) { | |
| body.on('error', error_ => { | |
| const error = error_ instanceof FetchBaseError ? | |
| error_ : | |
| new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, 'system', error_); | |
| this[INTERNALS].error = error; | |
| }); | |
| } | |
| } | |
| get body() { | |
| return this[INTERNALS].stream; | |
| } | |
| get bodyUsed() { | |
| return this[INTERNALS].disturbed; | |
| } | |
| /** | |
| * Decode response as ArrayBuffer | |
| * | |
| * @return Promise | |
| */ | |
| async arrayBuffer() { | |
| const {buffer, byteOffset, byteLength} = await consumeBody(this); | |
| return buffer.slice(byteOffset, byteOffset + byteLength); | |
| } | |
| async formData() { | |
| const ct = this.headers.get('content-type'); | |
| if (ct.startsWith('application/x-www-form-urlencoded')) { | |
| const formData = new FormData(); | |
| const parameters = new URLSearchParams(await this.text()); | |
| for (const [name, value] of parameters) { | |
| formData.append(name, value); | |
| } | |
| return formData; | |
| } | |
| const {toFormData} = await import('./utils/multipart-parser.js'); | |
| return toFormData(this.body, ct); | |
| } | |
| /** | |
| * Return raw response as Blob | |
| * | |
| * @return Promise | |
| */ | |
| async blob() { | |
| const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS].body && this[INTERNALS].body.type) || ''; | |
| const buf = await this.arrayBuffer(); | |
| return new Blob([buf], { | |
| type: ct | |
| }); | |
| } | |
| /** | |
| * Decode response as json | |
| * | |
| * @return Promise | |
| */ | |
| async json() { | |
| const text = await this.text(); | |
| return JSON.parse(text); | |
| } | |
| /** | |
| * Decode response as text | |
| * | |
| * @return Promise | |
| */ | |
| async text() { | |
| const buffer = await consumeBody(this); | |
| return new TextDecoder().decode(buffer); | |
| } | |
| /** | |
| * Decode response as buffer (non-spec api) | |
| * | |
| * @return Promise | |
| */ | |
| buffer() { | |
| return consumeBody(this); | |
| } | |
| } | |
| Body.prototype.buffer = deprecate(Body.prototype.buffer, 'Please use \'response.arrayBuffer()\' instead of \'response.buffer()\'', 'node-fetch#buffer'); | |
| // In browsers, all properties are enumerable. | |
| Object.defineProperties(Body.prototype, { | |
| body: {enumerable: true}, | |
| bodyUsed: {enumerable: true}, | |
| arrayBuffer: {enumerable: true}, | |
| blob: {enumerable: true}, | |
| json: {enumerable: true}, | |
| text: {enumerable: true}, | |
| data: {get: deprecate(() => {}, | |
| 'data doesn\'t exist, use json(), text(), arrayBuffer(), or body instead', | |
| 'https://github.com/node-fetch/node-fetch/issues/1000 (response)')} | |
| }); | |
| /** | |
| * Consume and convert an entire Body to a Buffer. | |
| * | |
| * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body | |
| * | |
| * @return Promise | |
| */ | |
| async function consumeBody(data) { | |
| if (data[INTERNALS].disturbed) { | |
| throw new TypeError(`body used already for: ${data.url}`); | |
| } | |
| data[INTERNALS].disturbed = true; | |
| if (data[INTERNALS].error) { | |
| throw data[INTERNALS].error; | |
| } | |
| const {body} = data; | |
| // Body is null | |
| if (body === null) { | |
| return Buffer.alloc(0); | |
| } | |
| /* c8 ignore next 3 */ | |
| if (!(body instanceof Stream)) { | |
| return Buffer.alloc(0); | |
| } | |
| // Body is stream | |
| // get ready to actually consume the body | |
| const accum = []; | |
| let accumBytes = 0; | |
| try { | |
| for await (const chunk of body) { | |
| if (data.size > 0 && accumBytes + chunk.length > data.size) { | |
| const error = new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size'); | |
| body.destroy(error); | |
| throw error; | |
| } | |
| accumBytes += chunk.length; | |
| accum.push(chunk); | |
| } | |
| } catch (error) { | |
| const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, 'system', error); | |
| throw error_; | |
| } | |
| if (body.readableEnded === true || body._readableState.ended === true) { | |
| try { | |
| if (accum.every(c => typeof c === 'string')) { | |
| return Buffer.from(accum.join('')); | |
| } | |
| return Buffer.concat(accum, accumBytes); | |
| } catch (error) { | |
| throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error); | |
| } | |
| } else { | |
| throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`); | |
| } | |
| } | |
| /** | |
| * Clone body given Res/Req instance | |
| * | |
| * @param Mixed instance Response or Request instance | |
| * @param String highWaterMark highWaterMark for both PassThrough body streams | |
| * @return Mixed | |
| */ | |
| export const clone = (instance, highWaterMark) => { | |
| let p1; | |
| let p2; | |
| let {body} = instance[INTERNALS]; | |
| // Don't allow cloning a used body | |
| if (instance.bodyUsed) { | |
| throw new Error('cannot clone body after it is used'); | |
| } | |
| // Check that body is a stream and not form-data object | |
| // note: we can't clone the form-data object without having it as a dependency | |
| if ((body instanceof Stream) && (typeof body.getBoundary !== 'function')) { | |
| // Tee instance body | |
| p1 = new PassThrough({highWaterMark}); | |
| p2 = new PassThrough({highWaterMark}); | |
| body.pipe(p1); | |
| body.pipe(p2); | |
| // Set instance body to teed body and return the other teed body | |
| instance[INTERNALS].stream = p1; | |
| body = p2; | |
| } | |
| return body; | |
| }; | |
| const getNonSpecFormDataBoundary = deprecate( | |
| body => body.getBoundary(), | |
| 'form-data doesn\'t follow the spec and requires special treatment. Use alternative package', | |
| 'https://github.com/node-fetch/node-fetch/issues/1167' | |
| ); | |
| /** | |
| * Performs the operation "extract a `Content-Type` value from |object|" as | |
| * specified in the specification: | |
| * https://fetch.spec.whatwg.org/#concept-bodyinit-extract | |
| * | |
| * This function assumes that instance.body is present. | |
| * | |
| * @param {any} body Any options.body input | |
| * @returns {string | null} | |
| */ | |
| export const extractContentType = (body, request) => { | |
| // Body is null or undefined | |
| if (body === null) { | |
| return null; | |
| } | |
| // Body is string | |
| if (typeof body === 'string') { | |
| return 'text/plain;charset=UTF-8'; | |
| } | |
| // Body is a URLSearchParams | |
| if (isURLSearchParameters(body)) { | |
| return 'application/x-www-form-urlencoded;charset=UTF-8'; | |
| } | |
| // Body is blob | |
| if (isBlob(body)) { | |
| return body.type || null; | |
| } | |
| // Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView) | |
| if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) { | |
| return null; | |
| } | |
| if (body instanceof FormData) { | |
| return `multipart/form-data; boundary=${request[INTERNALS].boundary}`; | |
| } | |
| // Detect form data input from form-data module | |
| if (body && typeof body.getBoundary === 'function') { | |
| return `multipart/form-data;boundary=${getNonSpecFormDataBoundary(body)}`; | |
| } | |
| // Body is stream - can't really do much about this | |
| if (body instanceof Stream) { | |
| return null; | |
| } | |
| // Body constructor defaults other things to string | |
| return 'text/plain;charset=UTF-8'; | |
| }; | |
| /** | |
| * The Fetch Standard treats this as if "total bytes" is a property on the body. | |
| * For us, we have to explicitly get it with a function. | |
| * | |
| * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes | |
| * | |
| * @param {any} obj.body Body object from the Body instance. | |
| * @returns {number | null} | |
| */ | |
| export const getTotalBytes = request => { | |
| const {body} = request[INTERNALS]; | |
| // Body is null or undefined | |
| if (body === null) { | |
| return 0; | |
| } | |
| // Body is Blob | |
| if (isBlob(body)) { | |
| return body.size; | |
| } | |
| // Body is Buffer | |
| if (Buffer.isBuffer(body)) { | |
| return body.length; | |
| } | |
| // Detect form data input from form-data module | |
| if (body && typeof body.getLengthSync === 'function') { | |
| return body.hasKnownLength && body.hasKnownLength() ? body.getLengthSync() : null; | |
| } | |
| // Body is stream | |
| return null; | |
| }; | |
| /** | |
| * Write a Body to a Node.js WritableStream (e.g. http.Request) object. | |
| * | |
| * @param {Stream.Writable} dest The stream to write to. | |
| * @param obj.body Body object from the Body instance. | |
| * @returns {Promise<void>} | |
| */ | |
| export const writeToStream = async (dest, {body}) => { | |
| if (body === null) { | |
| // Body is null | |
| dest.end(); | |
| } else { | |
| // Body is stream | |
| await pipeline(body, dest); | |
| } | |
| }; | |
Xet Storage Details
- Size:
- 9.85 kB
- Xet hash:
- c459be006034aed22a59d7d1b8009f2ab72929684c93b0157d49d67c0894abb1
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.