UNPKG

@whatwg-node/node-fetch

Version:

Fetch API implementation for Node

134 lines (133 loc) 4.35 kB
import { Buffer } from 'node:buffer'; import { PonyfillIteratorObject } from './IteratorObject.js'; import { PonyfillReadableStream } from './ReadableStream.js'; export class PonyfillFormData { map = new Map(); append(name, value, fileName) { let values = this.map.get(name); if (!values) { values = []; this.map.set(name, values); } const entry = isBlob(value) ? getNormalizedFile(name, value, fileName) : value; values.push(entry); } delete(name) { this.map.delete(name); } get(name) { const values = this.map.get(name); return values ? values[0] : null; } getAll(name) { return this.map.get(name) || []; } has(name) { return this.map.has(name); } set(name, value, fileName) { const entry = isBlob(value) ? getNormalizedFile(name, value, fileName) : value; this.map.set(name, [entry]); } [Symbol.iterator]() { return this._entries(); } *_entries() { for (const [key, values] of this.map) { for (const value of values) { yield [key, value]; } } } entries() { return new PonyfillIteratorObject(this._entries(), 'FormDataIterator'); } _keys() { return this.map.keys(); } keys() { return new PonyfillIteratorObject(this._keys(), 'FormDataIterator'); } *_values() { for (const values of this.map.values()) { for (const value of values) { yield value; } } } values() { return new PonyfillIteratorObject(this._values(), 'FormDataIterator'); } forEach(callback) { for (const [key, value] of this) { callback(value, key, this); } } } export function getStreamFromFormData(formData, boundary = '---') { const entries = []; let sentInitialHeader = false; return new PonyfillReadableStream({ start: controller => { formData.forEach((value, key) => { if (!sentInitialHeader) { controller.enqueue(Buffer.from(`--${boundary}\r\n`)); sentInitialHeader = true; } entries.push([key, value]); }); if (!sentInitialHeader) { controller.enqueue(Buffer.from(`--${boundary}--\r\n`)); controller.close(); } }, pull: async (controller) => { const entry = entries.shift(); if (entry) { const [key, value] = entry; if (typeof value === 'string') { controller.enqueue(Buffer.from(`Content-Disposition: form-data; name="${key}"\r\n\r\n`)); controller.enqueue(Buffer.from(value)); } else { let filenamePart = ''; if (value.name) { filenamePart = `; filename="${value.name}"`; } controller.enqueue(Buffer.from(`Content-Disposition: form-data; name="${key}"${filenamePart}\r\n`)); controller.enqueue(Buffer.from(`Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`)); const entryStream = value.stream(); for await (const chunk of entryStream) { controller.enqueue(chunk); } } if (entries.length === 0) { controller.enqueue(Buffer.from(`\r\n--${boundary}--\r\n`)); controller.close(); } else { controller.enqueue(Buffer.from(`\r\n--${boundary}\r\n`)); } } else { controller.enqueue(Buffer.from(`\r\n--${boundary}--\r\n`)); controller.close(); } }, }); } function getNormalizedFile(name, blob, fileName) { Object.defineProperty(blob, 'name', { configurable: true, enumerable: true, value: fileName || blob.name || name, }); return blob; } function isBlob(value) { return value?.arrayBuffer != null; }