mhtml-stream
Version:
Streaming MHTML parser
2 lines (1 loc) • 6.19 kB
JavaScript
var R=h;var m=[],Z=[],f=typeof Uint8Array<"u"?Uint8Array:Array,q="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for($=0,Q=q.length;$<Q;++$)m[$]=q[$],Z[q.charCodeAt($)]=$;var $,Q;Z[45]=62;Z[95]=63;function L(G){var J=G.length;if(J%4>0)throw Error("Invalid string. Length must be a multiple of 4");var K=G.indexOf("=");if(K===-1)K=J;var M=K===J?0:4-K%4;return[K,M]}function k(G,J,K){return(J+K)*3/4-K}function h(G){var J,K=L(G),M=K[0],U=K[1],W=new f(k(G,M,U)),X=0,V=U>0?M-4:M,Y;for(Y=0;Y<V;Y+=4)J=Z[G.charCodeAt(Y)]<<18|Z[G.charCodeAt(Y+1)]<<12|Z[G.charCodeAt(Y+2)]<<6|Z[G.charCodeAt(Y+3)],W[X++]=J>>16&255,W[X++]=J>>8&255,W[X++]=J&255;if(U===2)J=Z[G.charCodeAt(Y)]<<2|Z[G.charCodeAt(Y+1)]>>4,W[X++]=J&255;if(U===1)J=Z[G.charCodeAt(Y)]<<10|Z[G.charCodeAt(Y+1)]<<4|Z[G.charCodeAt(Y+2)]>>2,W[X++]=J>>8&255,W[X++]=J&255;return W}class E{#G=new Map;[Symbol.iterator](){return this.entries()}append(G,J){let K=this.#G.get(G.toLowerCase());if(K===void 0)this.#G.set(G.toLowerCase(),{key:G,values:[J]});else K.values.push(J)}*entries(G=", "){for(let{key:J,values:K}of this.#G.values())yield[J,K.join(G)]}*entriesAll(){for(let{key:G,values:J}of this.#G.values())for(let K of J)yield[G,K]}get(G,J=", "){let K=this.#G.get(G.toLowerCase());if(K===void 0)return null;else return K.values.join(J)}getAll(G){return this.#G.get(G.toLowerCase())?.values??[]}has(G){return this.#G.has(G.toLowerCase())}*keys(){for(let{key:G}of this.#G.values())yield G}*values(G=", "){for(let{values:J}of this.#G.values())yield J.join(G)}*valuesAll(){for(let{values:G}of this.#G.values())yield*G}}function F(G,J){if(G.length!==J.length)return!1;else if(G.byteOffset%4===J.byteOffset%4){let K=(4-G.byteOffset%4)%4,M=Math.floor((G.byteLength-K)/4),U=K+M*4,W=new Uint32Array(G.buffer,G.byteOffset+K,M),X=new Uint32Array(J.buffer,J.byteOffset+K,M);for(let V=0;V<K;++V)if(G[V]!==J[V])return!1;for(let V=0;V<M;++V)if(W[V]!==X[V])return!1;for(let V=U;V<G.length;++V)if(G[V]!==J[V])return!1;return!0}else{for(let K of G.keys())if(G[K]!==J[K])return!1;return!0}}function y(G,J){return G.findIndex((K,M)=>{if(K!==J[0]||M+J.length>G.length)return!1;else{for(let U=1;U<J.length;++U)if(G[M+U]!==J[U])return!1;return!0}})}async function*T(G,J){let K=new Uint8Array(0);for await(let M of G){K=K.length?j([K,M]):M;let U;while((U=y(K,J))!==-1)yield K.subarray(0,U),K=K.subarray(U+J.length)}yield K}function j(G){let J=G.reduce((U,W)=>U+W.length,0),K=new Uint8Array(J),M=0;for(let U of G)K.set(U,M),M+=U.length;return K}async function C(G){let J=[];for await(let K of G)J.push(K);return j(J)}var D=new Uint8Array([13,10]);function S(G){return G>=48&&G<=57||G>=65&&G<=70||G>=97&&G<=102}async function*A(G,J=D){let K=!1;for await(let M of G){let U=new Uint8Array(M.length+J.length),W=0;if(K)U.set(J,W),W+=J.length,K=!1;let X=!1;for(let V=0;V<M.length;++V){let Y=M[V];if(Y>=128)throw Error(`got non-ascii character when decoding quoted printable: ${Y}`);if(Y!==61)U[W++]=Y;else{let z=M[++V];if(z===void 0)X=!0;else{let P=M[++V];if(P===void 0)throw Error("quoted printable escape (=) was not followed by two bytes");else if(!S(z)||!S(P))throw Error(`quoted printable escape (=) was not followed by two hex digits: "=${String.fromCharCode(z,P)}"`);U[W++]=parseInt(String.fromCharCode(z,P),16)}}}if(!X)K=!0;yield U.subarray(0,W)}}var b=new TextDecoder;async function*I(G){let J="";for await(let K of G){J+=b.decode(K).replace(/\s/g,"");let M=J.length-J.length%4;if(M>0)yield R(J.slice(0,M)),J=J.slice(M)}if(J.length>0)yield R(J)}async function*N(G,J=D){let K=!0;for await(let M of G){if(K)K=!1;else yield J;yield M}}function H(){throw Error("binary transfer-encoding is explicitly not supported and trying to add an implementation will likely result in unexpected results, but if you want to handle it anyway, override `binary` in `decoderOverrides`")}var B=new TextEncoder,x=new TextDecoder;function p(G){let J=new Uint8Array(G.length),K=0;for(let M=0;M<G.length;++M){let U=G.charCodeAt(M),W;if(U>=128)throw Error(`got non-ascii character when decoding q-quoted word: "${G}"`);else if(U===95)W=32;else if(U===61){let X=G.charCodeAt(++M),V=G.charCodeAt(++M);if(!S(X)||!S(V))throw Error(`got invalid hex escape when decoding q-quoted word: "${G}"`);W=parseInt(String.fromCharCode(X,V),16)}else W=U;J[K++]=W}return J.subarray(0,K)}var c=/^=\?([^?\s]+)\?([BQ])\?([^?\s]+)\?=$/;function w(G){let J=c.exec(G);if(J){let[,K,M,U]=J,W;if(M==="Q")W=p(U);else W=R(U);return new TextDecoder(K).decode(W)}else return G}async function u(G){let J=new E,K="",M="";for(;;){let{done:U,value:W}=await G.next();if(U)throw Error("didn't find an empty line to signify the end of header parsing");let X=x.decode(W);if(/^\s/.test(X))M+=w(X.substring(1));else{if(K)J.append(K,M);if(X){let V=X.indexOf(":");if(V===-1)throw Error(`header line didn't have key-value delimiter: "${X}"`);K=X.slice(0,V),M=w(X.slice(V+1).replace(/^\s+/,""))}else return J}}}var a=new Map([["7bit",N],["base64",I],["quoted-printable",A],["8bit",N],["binary",H]]);function s(G){let J=G.get("Content-Type");if(J===null)throw Error(`first headers didn't contain a content type: ${JSON.stringify(Object.fromEntries(G))}`);let K,M=!1;for(let X of J.split(/;\s*/)){let V=X.toLowerCase();if(V.startsWith("multipart/"))M=!0;else if(V.startsWith("boundary=")){if(K=X.slice(9),K.startsWith('"')&&K.endsWith('"'))K=K.slice(1,-1)}}if(!M||K===void 0)throw Error("first content type header didn't contain 'multipart/...' and a boundary string");let U=B.encode(`--${K}`),W=B.encode(`--${K}--`);return[U,W]}async function*e(G,{decoderOverrides:J=new Map}={}){let K=new Map([...a.entries(),...J.entries()]),M=new Uint8Array([13,10]),U=T(G,M),W=null,X=!0;while(X){let V=await u(U),[Y,z]=W??(W=s(V)),P=(V.get("Content-Transfer-Encoding")??"7bit").toLowerCase(),O=K.get(P);if(O===void 0)throw Error(`unhandled encoding type: ${P}`);let g=await C(O({[Symbol.asyncIterator](){return{async next(){let{done:v,value:_}=await U.next();if(v)throw Error(`stream didn't end with the appropriate termination boundary: ${x.decode(z)}`);else if(F(_,Y))return{done:!0,value:void 0};else if(F(_,z))return X=!1,{done:!0,value:void 0};else return{value:_}}}}}));yield{headers:V,content:g}}}export{e as parseMhtml};