mhtml-stream 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/mhtml.esm.min.js +1 -1
- package/dist/utils.js +3 -1
- package/package.json +21 -66
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { argv } from "bun";
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { parseMhtml } from ".";
|
|
6
|
+
async function* readFile(name) {
|
|
7
|
+
const file = Bun.file(name);
|
|
8
|
+
yield await file.bytes();
|
|
9
|
+
}
|
|
10
|
+
async function saveOutput(mkdir, path, content) {
|
|
11
|
+
await mkdir;
|
|
12
|
+
await Bun.write(path, content);
|
|
13
|
+
}
|
|
14
|
+
void (async () => {
|
|
15
|
+
const args = await yargs(argv.slice(2))
|
|
16
|
+
.scriptName("mhtml_stream")
|
|
17
|
+
.usage("$0 <mhtml>", "parse mhtml and extract component files")
|
|
18
|
+
.positional("mhtml", {
|
|
19
|
+
describe: "the mhtml file to parse",
|
|
20
|
+
type: "string",
|
|
21
|
+
})
|
|
22
|
+
.option("output", {
|
|
23
|
+
alias: "o",
|
|
24
|
+
describe: "output directory",
|
|
25
|
+
type: "string",
|
|
26
|
+
})
|
|
27
|
+
.help()
|
|
28
|
+
.alias("h", "help")
|
|
29
|
+
.demandOption("mhtml")
|
|
30
|
+
.strict().argv;
|
|
31
|
+
const makeOut = args.output ? mkdir(args.output, { recursive: true }) : null;
|
|
32
|
+
const proms = [];
|
|
33
|
+
for await (const file of parseMhtml(readFile(args.mhtml))) {
|
|
34
|
+
for (const [key, val] of file.headers) {
|
|
35
|
+
console.log(key, ":", val);
|
|
36
|
+
}
|
|
37
|
+
const loc = file.headers.get("Content-Location");
|
|
38
|
+
if (loc && args.output && makeOut) {
|
|
39
|
+
const path = `${args.output}/${loc.replaceAll(/[:/]/g, "_")}`;
|
|
40
|
+
proms.push(saveOutput(makeOut, path, file.content));
|
|
41
|
+
}
|
|
42
|
+
await Promise.all(proms);
|
|
43
|
+
console.log("[done]");
|
|
44
|
+
}
|
|
45
|
+
})();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -74,7 +74,7 @@ async function parseHeaders(iter) {
|
|
|
74
74
|
const line = decoder.decode(value);
|
|
75
75
|
if (/^\s/.test(line)) {
|
|
76
76
|
// header folded
|
|
77
|
-
val += decodeLine(line.
|
|
77
|
+
val += decodeLine(line.substring(1));
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
80
|
if (key) {
|
|
@@ -113,7 +113,7 @@ function getBoundary(headers) {
|
|
|
113
113
|
if (contentType === null) {
|
|
114
114
|
throw new Error(`first headers didn't contain a content type: ${JSON.stringify(Object.fromEntries(headers))}`);
|
|
115
115
|
}
|
|
116
|
-
let bound
|
|
116
|
+
let bound;
|
|
117
117
|
let multipart = false;
|
|
118
118
|
for (const field of contentType.split(/;\s*/)) {
|
|
119
119
|
if (field.startsWith("multipart/")) {
|
package/dist/mhtml.esm.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var f=function(
|
|
1
|
+
var D=L;var g=[],Y=[],f=typeof Uint8Array<"u"?Uint8Array:Array,P="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(Z=0,S=P.length;Z<S;++Z)g[Z]=P[Z],Y[P.charCodeAt(Z)]=Z;var Z,S;Y[45]=62;Y[95]=63;function m(G){var K=G.length;if(K%4>0)throw Error("Invalid string. Length must be a multiple of 4");var J=G.indexOf("=");if(J===-1)J=K;var M=J===K?0:4-J%4;return[J,M]}function v(G,K,J){return(K+J)*3/4-J}function L(G){var K,J=m(G),M=J[0],R=J[1],U=new f(v(G,M,R)),V=0,W=R>0?M-4:M,X;for(X=0;X<W;X+=4)K=Y[G.charCodeAt(X)]<<18|Y[G.charCodeAt(X+1)]<<12|Y[G.charCodeAt(X+2)]<<6|Y[G.charCodeAt(X+3)],U[V++]=K>>16&255,U[V++]=K>>8&255,U[V++]=K&255;if(R===2)K=Y[G.charCodeAt(X)]<<2|Y[G.charCodeAt(X+1)]>>4,U[V++]=K&255;if(R===1)K=Y[G.charCodeAt(X)]<<10|Y[G.charCodeAt(X+1)]<<4|Y[G.charCodeAt(X+2)]>>2,U[V++]=K>>8&255,U[V++]=K&255;return U}class _{#G=new Map;[Symbol.iterator](){return this.entries()}append(G,K){let J=this.#G.get(G);if(J===void 0)this.#G.set(G,[K]);else J.push(K)}*entries(G=", "){for(let[K,J]of this.#G.entries())yield[K,J.join(G)]}*entriesAll(){for(let[G,K]of this.#G.entries())for(let J of K)yield[G,J]}get(G,K=", "){let J=this.#G.get(G);if(J===void 0)return null;else return J.join(K)}getAll(G){return this.#G.get(G)??[]}has(G){return this.#G.has(G)}keys(){return this.#G.keys()}*values(G=", "){for(let K of this.#G.values())yield K.join(G)}*valuesAll(){for(let G of this.#G.values())yield*G}}function j(G,K){if(G.length!==K.length)return!1;else if(G.byteOffset%4===K.byteOffset%4){let J=(4-G.byteOffset%4)%4,M=Math.floor((G.byteLength-J)/4),R=J+M*4,U=new Uint32Array(G.buffer,G.byteOffset+J,M),V=new Uint32Array(K.buffer,K.byteOffset+J,M);for(let W=0;W<J;++W)if(G[W]!==K[W])return!1;for(let W=0;W<M;++W)if(U[W]!==V[W])return!1;for(let W=R;W<G.length;++W)if(G[W]!==K[W])return!1;return!0}else{for(let J of G.keys())if(G[J]!==K[J])return!1;return!0}}function h(G,K){return G.findIndex((J,M)=>{if(J!==K[0]||M+K.length>G.length)return!1;else{for(let R=1;R<K.length;++R)if(G[M+R]!==K[R])return!1;return!0}})}async function*Q(G,K){let J=new Uint8Array(0);for await(let M of G){J=J.length?E([J,M]):M;let R;while((R=h(J,K))!==-1)yield J.subarray(0,R),J=J.subarray(R+K.length)}yield J}function E(G){let K=G.reduce((R,U)=>R+U.length,0),J=new Uint8Array(K),M=0;for(let R of G)J.set(R,M),M+=R.length;return J}async function O(G){let K=[];for await(let J of G)K.push(J);return E(K)}var k=new Uint8Array([10]);async function*T(G,K=k){for await(let J of G){let M=new Uint8Array(J.length+K.length),R=!1,U=0;for(let V=0;V<J.length;++V){let W=J[V];if(W>=128)throw Error(`got non-ascii character when decoding quoted printable: ${W}`);if(W!==61)M[U++]=W;else{let X=J[++V];if(X===void 0)R=!0;else{let z=J[++V];if(z===void 0)throw Error("quoted printable escape (=) was not followed by two bytes");let $=parseInt(String.fromCharCode(X),16);$*=16,$+=parseInt(String.fromCharCode(z),16),M[U++]=$}}}if(!R)M.set(K,U),U+=K.length;yield M.subarray(0,U)}}var y=new TextDecoder;async function*C(G){for await(let K of G)yield D(y.decode(K))}async function*q(G){for await(let K of G)yield K}function A(){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 ignore anyway, set binary to `decode8bit`")}var I=new TextEncoder,B=new TextDecoder;function b(G){let K=new Uint8Array(G.length),J=0;for(let M=0;M<G.length;++M){let R=G.charCodeAt(M),U;if(R>=128)throw Error(`got non-ascii character when decoding q-quoted word: "${G}"`);else if(R===95)U=32;else if(R===61)U=parseInt(G[++M],16),U*=16,U+=parseInt(G[++M],16);else U=R;K[J++]=U}return K.subarray(0,J)}var p=/^=\?([^?\s]+)\?([BQ])\?([^?\s]+)\?=$/;function H(G){let K=p.exec(G);if(K){let[,J,M,R]=K,U;if(M==="Q")U=b(R);else U=D(R);return new TextDecoder(J).decode(U)}else return G}async function c(G){let K=new _,J="",M="";for(;;){let{done:R,value:U}=await G.next();if(R)throw Error("didn't find an empty line to signify the end of header parsing");let V=B.decode(U);if(/^\s/.test(V))M+=H(V.substring(1));else{if(J)K.append(J,M);if(V){let W=V.indexOf(": ");if(W===-1)throw Error(`header line didn't have key-value delimiter: "${V}"`);J=V.slice(0,W),M=H(V.slice(W+2))}else return K}}}var u=new Map([["7bit",q],["base64",C],["quoted-printable",T],["8bit",q],["binary",A]]);function s(G){let K=G.get("Content-Type");if(K===null)throw Error(`first headers didn't contain a content type: ${JSON.stringify(Object.fromEntries(G))}`);let J,M=!1;for(let V of K.split(/;\s*/))if(V.startsWith("multipart/"))M=!0;else if(V.startsWith("boundary=")){if(J=V.slice(9),J.startsWith('"')&&J.endsWith('"'))J=J.slice(1,-1)}if(!M||J===void 0)throw Error("first content type header didn't contain 'multipart/...' and a boundary string");let R=I.encode(`--${J}`),U=I.encode(`--${J}--`);return[R,U]}async function*t(G,{decoderOverrides:K=new Map}={}){let J=new Map([...u.entries(),...K.entries()]),M=new Uint8Array([13,10]),R=Q(G,M),U=null,V=!0;while(V){let W=await c(R),[X,z]=U??(U=s(W)),$=W.get("Content-Transfer-Encoding")??"7bit",F=J.get($);if(F===void 0)throw Error(`unhandled encoding type: ${$}`);let w=await O(F({[Symbol.asyncIterator](){return{async next(){let{done:x,value:N}=await R.next();if(x)throw Error(`stream didn't end with the appropriate termination boundary: ${B.decode(z)}`);else if(j(N,X))return{done:!0,value:void 0};else if(j(N,z))return V=!1,{done:!0,value:void 0};else return{value:N}}}}}));yield{headers:W,content:w}}}export{t as parseMhtml};
|
package/dist/utils.js
CHANGED
|
@@ -77,7 +77,9 @@ export function indexOf(haystack, needle) {
|
|
|
77
77
|
export async function* splitStream(iter, split) {
|
|
78
78
|
let current = new Uint8Array(0);
|
|
79
79
|
for await (const chunk of iter) {
|
|
80
|
-
current = current.length
|
|
80
|
+
current = current.length
|
|
81
|
+
? concat([current, chunk])
|
|
82
|
+
: chunk;
|
|
81
83
|
let nextInd;
|
|
82
84
|
while ((nextInd = indexOf(current, split)) !== -1) {
|
|
83
85
|
yield current.subarray(0, nextInd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mhtml-stream",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Zero dependency stream MHTML parser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mhtml",
|
|
@@ -8,88 +8,43 @@
|
|
|
8
8
|
],
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/erikbrinkman/mhtml-stream.git"
|
|
11
|
+
"url": "git+https://github.com/erikbrinkman/mhtml-stream.git"
|
|
12
12
|
},
|
|
13
13
|
"author": "Erik Brinkman <erik.brinkman@gmail.com>",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
16
17
|
"types": "dist/index.d.ts",
|
|
17
18
|
"module": "dist/mhtml.esm.min.js",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/mhtml.esm.min.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
18
25
|
"files": [
|
|
19
26
|
"/dist/**/*.js",
|
|
20
27
|
"/dist/**/*.d.ts"
|
|
21
28
|
],
|
|
22
29
|
"scripts": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
30
|
+
"cli": "bun src/cli.ts",
|
|
31
|
+
"fmt": "biome format --write",
|
|
32
|
+
"lint": "tsc && biome check && typedoc --emit none",
|
|
25
33
|
"doc": "typedoc",
|
|
26
34
|
"export": "tsc -p tsconfig.build.json && bun build src/index.ts --minify --outfile dist/mhtml.esm.min.js",
|
|
27
|
-
"prepack": "bun lint && bun test --coverage && bun export"
|
|
35
|
+
"prepack": "bun lint && bun test --coverage && bun export && publint"
|
|
28
36
|
},
|
|
29
37
|
"dependencies": {
|
|
30
38
|
"base64-js": "^1.5.1"
|
|
31
39
|
},
|
|
32
40
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@types/
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"typedoc": "^0.25.8",
|
|
42
|
-
"typescript": "^5.3.3",
|
|
43
|
-
"typescript-eslint": "^7.7.0"
|
|
44
|
-
},
|
|
45
|
-
"eslintConfig": {
|
|
46
|
-
"rules": {
|
|
47
|
-
"@typescript-eslint/no-inferrable-types": "off",
|
|
48
|
-
"@typescript-eslint/no-non-null-assertion": "off",
|
|
49
|
-
"@typescript-eslint/no-unused-vars": [
|
|
50
|
-
"error",
|
|
51
|
-
{
|
|
52
|
-
"varsIgnorePattern": "^_+$"
|
|
53
|
-
}
|
|
54
|
-
],
|
|
55
|
-
"prefer-const": [
|
|
56
|
-
"error",
|
|
57
|
-
{
|
|
58
|
-
"destructuring": "all"
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"no-warning-comments": [
|
|
62
|
-
"error",
|
|
63
|
-
{
|
|
64
|
-
"terms": [
|
|
65
|
-
"fixme"
|
|
66
|
-
],
|
|
67
|
-
"location": "anywhere"
|
|
68
|
-
}
|
|
69
|
-
],
|
|
70
|
-
"spellcheck/spell-checker": [
|
|
71
|
-
"error",
|
|
72
|
-
{
|
|
73
|
-
"identifiers": false,
|
|
74
|
-
"skipIfMatch": [
|
|
75
|
-
"[^\\s]{20,}"
|
|
76
|
-
],
|
|
77
|
-
"skipWords": [
|
|
78
|
-
"0x7fffffff",
|
|
79
|
-
"Chr",
|
|
80
|
-
"Dvalue",
|
|
81
|
-
"Hola",
|
|
82
|
-
"Mhtml",
|
|
83
|
-
"Today’s",
|
|
84
|
-
"Uint",
|
|
85
|
-
"Yorker",
|
|
86
|
-
"ascii",
|
|
87
|
-
"delim",
|
|
88
|
-
"multipart",
|
|
89
|
-
"versa"
|
|
90
|
-
]
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
}
|
|
41
|
+
"@biomejs/biome": "^2.4.15",
|
|
42
|
+
"@types/base64-js": "^1.5.0",
|
|
43
|
+
"@types/bun": "^1.3.14",
|
|
44
|
+
"@types/yargs": "^17.0.35",
|
|
45
|
+
"publint": "^0.3.21",
|
|
46
|
+
"typedoc": "^0.28.19",
|
|
47
|
+
"typescript": "~6.0.3",
|
|
48
|
+
"yargs": "^18.0.0"
|
|
94
49
|
}
|
|
95
50
|
}
|