UNPKG

@push.rocks/smartrequest

Version:

A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.

111 lines 9.23 kB
const lineBreak = '\r\n'; const defaultContentType = 'application/octet-stream'; const textEncoder = new TextEncoder(); const escapeContentDispositionValue = (value) => { return value .replace(/\\/g, '\\\\') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') .replace(/"/g, '%22'); }; const getFilename = (filename) => { const normalizedFilename = filename.replace(/\\/g, '/'); return normalizedFilename.slice(normalizedFilename.lastIndexOf('/') + 1); }; const createBoundary = () => { const randomBytes = new Uint8Array(12); globalThis.crypto.getRandomValues(randomBytes); const randomHex = Array.from(randomBytes, (byte) => byte.toString(16).padStart(2, '0')).join(''); return `--------------------------${randomHex}`; }; /** * Minimal multipart/form-data encoder for the values accepted by SmartRequest. */ export class MultipartFormData { constructor(boundary = createBoundary()) { this.parts = []; if (boundary.length > 70 || !/^[0-9A-Za-z'()+_,./:=?-]+$/.test(boundary)) { throw new Error('Multipart boundary contains invalid characters.'); } this.boundary = boundary; } append(name, value, options) { let filename; let contentType; if (typeof value !== 'string') { filename = getFilename(options?.filename || 'file'); contentType = options?.contentType; this.validateContentType(contentType); } this.parts.push({ name, value: typeof value === 'string' ? textEncoder.encode(value) : value, filename, contentType, }); } resolveMissingContentTypes(resolver) { for (const part of this.parts) { if (part.filename && !part.contentType) { part.contentType = resolver(part.filename) || defaultContentType; this.validateContentType(part.contentType); } } } getHeaders() { return { 'content-type': `multipart/form-data; boundary=${this.boundary}`, }; } getContentLength() { let length = textEncoder.encode(`--${this.boundary}--${lineBreak}`).byteLength; for (const part of this.parts) { length += this.getPartHeader(part).byteLength + part.value.byteLength + lineBreak.length; } return length; } toArrayBuffer() { const buffer = new ArrayBuffer(this.getContentLength()); const body = new Uint8Array(buffer); let offset = 0; for (const chunk of this.getChunks()) { body.set(chunk, offset); offset += chunk.byteLength; } return buffer; } *getChunks() { const lineBreakBuffer = textEncoder.encode(lineBreak); for (const part of this.parts) { yield this.getPartHeader(part); yield part.value; yield lineBreakBuffer; } yield textEncoder.encode(`--${this.boundary}--${lineBreak}`); } getPartHeader(part) { const dispositionParts = [ 'form-data', `name="${escapeContentDispositionValue(part.name)}"`, ]; const headerLines = [`--${this.boundary}`]; if (part.filename) { dispositionParts.push(`filename="${escapeContentDispositionValue(part.filename)}"`); } headerLines.push(`Content-Disposition: ${dispositionParts.join('; ')}`); if (part.filename) { headerLines.push(`Content-Type: ${part.contentType || defaultContentType}`); } return textEncoder.encode(`${headerLines.join(lineBreak)}${lineBreak}${lineBreak}`); } validateContentType(contentType) { if (contentType && /[\r\n]/.test(contentType)) { throw new Error('Multipart content type must be a single-line value.'); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGlwYXJ0Zm9ybWRhdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jb3JlX2Jhc2UvbXVsdGlwYXJ0Zm9ybWRhdGEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDO0FBQ3pCLE1BQU0sa0JBQWtCLEdBQUcsMEJBQTBCLENBQUM7QUFDdEQsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQWN0QyxNQUFNLDZCQUE2QixHQUFHLENBQUMsS0FBYSxFQUFVLEVBQUU7SUFDOUQsT0FBTyxLQUFLO1NBQ1QsT0FBTyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUM7U0FDdEIsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUM7U0FDckIsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUM7U0FDckIsT0FBTyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztBQUMxQixDQUFDLENBQUM7QUFFRixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQWdCLEVBQVUsRUFBRTtJQUMvQyxNQUFNLGtCQUFrQixHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3hELE9BQU8sa0JBQWtCLENBQUMsS0FBSyxDQUFDLGtCQUFrQixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUMzRSxDQUFDLENBQUM7QUFFRixNQUFNLGNBQWMsR0FBRyxHQUFXLEVBQUU7SUFDbEMsTUFBTSxXQUFXLEdBQUcsSUFBSSxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdkMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDL0MsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNqRCxJQUFJLENBQUMsUUFBUSxD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