UNPKG

@softvisio/core

Version:
153 lines (117 loc) 3.67 kB
import Blob from "#lib/blob"; import File from "#lib/file"; import Headers from "#lib/http/headers"; import stream from "#lib/stream"; import StreamCombined from "#lib/stream/combined"; import uuid from "#lib/uuid"; const TYPES = new Set( [ "form-data", "alternative", "mixed", "related" ] ); export default class StreamMultipart extends StreamCombined { #boundary; #type; #length; #lastChunk; constructor ( type ) { super(); if ( !TYPES.has( type ) ) throw new Error( "Type is invalid" ); this.#boundary = "--------------------------" + uuid(); this.#type = `multipart/${ type }; boundary=${ this.boundary }`; } // properties get boundary () { return this.#boundary; } get length () { return this.#length; } get type () { return this.#type; } // public append ( body, { name, type, filename, headers, transform } = {} ) { var length; // ignore undefined body if ( body === undefined ) { return this; } // string if ( typeof body === "string" ) { body = Buffer.from( body ); } // number else if ( typeof body === "number" ) { body = Buffer.from( String( body ) ); } // blob if ( body instanceof Blob ) { type ||= body.type; length = body.size; // file if ( body instanceof File ) filename ||= body.name; body = body.stream(); } // buffer else if ( Buffer.isBuffer( body ) ) { length = body.length; body = stream.Readable.from( body, { "objectMode": false } ); } // multipart stream else if ( body instanceof StreamMultipart ) { length = body.length; type ||= body.type; } // stream.Readable else if ( body instanceof stream.Readable ) { type ||= body.type; length = body.size; } // invalid body else { throw new Error( "Unsupported body type" ); } headers = new Headers( headers ); // add content-type if ( type ) { if ( filename ) { headers.set( "content-type", `${ type }; name="${ filename.replaceAll( '"', "%22" ) }"` ); } else { headers.set( "content-type", type ); } } // add content-disposition if ( name || filename ) headers.setContentDisposition( { name, filename } ); // compose header const header = Buffer.from( `--${ this.boundary }\r\n` + headers.toString() + "\r\n" ); super.append( header ); if ( transform ) { length = null; super.append( stream.pipeline( body, transform, () => {} ) ); } else { super.append( body ); } super.append( isLast => { var chunk = "\r\n"; if ( isLast ) chunk += this.#getLastChunk(); return chunk; } ); // track length if ( this.#length !== null ) { if ( length == null ) { this.#length = null; } else { this.#length ??= this.#getLastChunk().length; this.#length += header.length + length + 2; } } return this; } // private #getLastChunk () { if ( !this.#lastChunk ) { this.#lastChunk = `--${ this.boundary }--\r\n`; } return this.#lastChunk; } }