UNPKG

@apeleghq/multipart-parser

Version:

TypeScript streaming parser for MIME multipart messages

7 lines (6 loc) 5.04 kB
var I=/^[0-9a-zA-Z'()+_,\-./:=? ]{0,69}[0-9a-zA-Z'()+_,\-./:=?]$/,x=/;\s*boundary=(?:"([0-9a-zA-Z'()+_,\-./:=? ]{0,69}[0-9a-zA-Z'()+_,\-./:=?])"|([0-9a-zA-Z'+_\-.]{0,69}[0-9a-zA-Z'+_\-.]))/;var N=t=>new ReadableStream({pull(r){if(ArrayBuffer.isView(t))r.enqueue(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength));else if(t instanceof ArrayBuffer)r.enqueue(t);else throw new TypeError("Expected ArrayBuffer or an ArrayBuffer view.");r.close()}}),b=N;var L=/;\s*boundary=(?:"([^"]+)"|([^;",]+))/,O="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-.",P=()=>{let t=new Uint8Array(24);return globalThis.crypto.getRandomValues(t),Array.from(t).map(n=>O[n%O.length]).join("")},T={preventClose:!0};async function*U(t,n,r){let o=new TextEncoder,c=o.encode(`\r --${t}`);if(Array.isArray(n)&&n.length<1){await r.abort(Error("At least one part is required"));return}let s=0;for await(let e of n){s++;let a,d;if(!e.body&&e.parts)if(d=e.headers.get("content-type"),!d)a=P(),d=`multipart/mixed; boundary="${a}"`;else if(!d.startsWith("multipart/")||!L.test(d)){await r.abort(Error("Invalid multipart content type: "+d));return}else{let i=d.match(x);(!i||!(a=i[1]||i[2]))&&(a=P(),d=d.replace(L,`; boundary="${a}"`))}await b(c).pipeTo(r,T),yield;{let i=[""];if(d){let f=!1;e.headers.forEach((u,l)=>{l!=="content-type"?i.push(`${l}: ${u}`):(f=!0,i.push(`${l}: ${d}`))}),f||i.push(`content-type: ${d}`)}else e.headers.forEach((f,u)=>{i.push(`${u}: ${f}`)});e.parts||!e.body?i.push(""):i.push("","");let A=o.encode(i.join(`\r `));i.length=0,await b(A).pipeTo(r,T),yield}if(e.body){if(e.body instanceof ArrayBuffer||ArrayBuffer.isView(e.body))await b(e.body).pipeTo(r,T);else if(e.body instanceof Blob)await e.body.stream().pipeTo(r,T);else if(e.body instanceof ReadableStream)await e.body.pipeTo(r,T);else{await r.abort(Error("Invalid body type"));return}yield}else if(e.parts){if(!a){await r.abort(Error("Runtime exception: undefined part boundary"));return}yield*U(a,e.parts,r),yield}}if(!s){await r.abort(Error("At least one part is required"));return}let p=o.encode(`\r --${t}--`);await b(p).pipeTo(r,T)}var _=(t,n)=>{let r=new TransformStream,o=U(t,n,r.writable),c=!1,s=r.readable.getReader();return new ReadableStream({start(e){(async()=>{for(;;)try{let a=await s.read();if(a.done){let d=new Uint8Array([13,10]);e.enqueue(d.buffer.slice(d.byteOffset,d.byteOffset+d.byteLength)),e.close();return}e.enqueue(a.value)}catch(a){e.error(a);return}})().catch(()=>{})},async pull(){if(c)return;(await o.next()).done&&(c=!0,await r.writable.close())}})},k=_;var C=(t,n)=>{e:for(let r=0;r<=t.length-n.length;r++){for(let o=0;o<n.length;o++)if(t[r+o]!==n[o])continue e;return r}return-1},h=C;var v=new TextDecoder,q=new TextEncoder,w=q.encode(`\r `),z=[9,32],H=(t,n)=>{let r=0,o=[];for(;(r=h(t,w))!==-1&&r!==0;){let s=t.indexOf(58);if(s===-1)throw new Error("Invalid header");let p=v.decode(t.subarray(0,s));for(;z.includes(t[r+2]);){let a=h(t.subarray(r+2),w);if(a<1)break;r+=2+a}let e=v.decode(t.subarray(s+1,r)).replace(/\r\n/g,"");o.push([p,e]),t=t.subarray(r+w.length)}return{headers:n?n(o):(()=>{let s=new Headers(o);return s.has("content-transfer-encoding")||s.set("content-transfer-encoding","7bit"),s.has("content-type")||s.set("content-type","text/plain; charset=us-ascii"),s})(),body:r===-1?null:t.subarray(r+w.length)}},S=H;var Z=(t,...n)=>{let r=n.reduce((s,p)=>s+p.byteLength,t.length),o=new(Object(n[0])).constructor(r);o.set(t);let c=t.length;return n.forEach(s=>{o.set(s,c),c+=s.byteLength}),o},$=Z;async function*j(t,n,r){var A;if(!I.test(n))throw new Error("Invalid boundary delimiter");let o=new TextEncoder,c=[9,32],s=new Uint8Array([13,10]),p=o.encode(`\r --${n}`),e=new Uint8Array,a=0,d=!1,i=t.getReader();try{for(;a!==3;){let{done:f,value:u}=await i.read();if(f){if(e.length===0||d)throw new Error("Invalid message");d=!0}else e=$(e,ArrayBuffer.isView(u)?new Uint8Array(u.buffer,u.byteOffset,u.byteLength):new Uint8Array(u));for(;e.length>0;){let l=NaN;if(a===0&&(l=h(e,p.slice(2))-2,l===-3)||(l!==-2&&(l=h(e,p)),l===-1))break;let y=l+p.length,g=f?0:h(e.subarray(y,y+32),s);if(g===-1&&e.length-y<32)break;if(g===-1||!Array.from(e.subarray(y+Math.min(2,g),y+g)).every(B=>c.includes(B)))throw new Error(`Invalid boundary at index ${l}`);if(f||g>=2){if([1,2].includes(a)&&e[y+0]===e[y+1]&&e[y+0]===45)a=3;else if(!c.includes(e[y+0])||!c.includes(e[y+1]))throw new Error(`Invalid boundary at index ${l} (${n}): ${(A=e[y+1])==null?void 0:A.toString(16)}`)}switch(a){case 0:a=1;break;case 1:a=2;case 2:case 3:if(e.subarray(l,y).length>0){let B=e.subarray(0,l),m=S(B,r),E=m.headers.get("content-type"),M;if(m.body&&(E!=null&&E.startsWith("multipart/"))){let R=E.match(x);if(R){let G=R[1]||R[2];M=j(b(m.body),G)}else M=null}yield{headers:m.headers,body:m.body,...M!==void 0&&{parts:M}}}break}if(a===3){e=e.subarray(e.length);break}e=e.subarray(g+y+2)}}}finally{i.releaseLock()}}var D=j;export{x as boundaryMatchRegex,I as boundaryRegex,D as default,k as encodeMultipartMessage,S as parseMessage,D as parseMultipartMessage};