milliparsec 5.1.0 → 5.1.1
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/README.md +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +85 -56
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +2 -1
- package/package.json +7 -8
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Check out [deno-libs/parsec](https://github.com/deno-libs/parsec) for Deno port.
|
|
|
20
20
|
- 📦 tiny package size (8KB dist size)
|
|
21
21
|
- 🔥 no dependencies
|
|
22
22
|
- ✨ [tinyhttp](https://github.com/tinyhttp/tinyhttp) and Express support
|
|
23
|
-
- ⚡
|
|
23
|
+
- ⚡ ~15% faster than body-parser, ~4x faster than formidable
|
|
24
24
|
|
|
25
25
|
## Install
|
|
26
26
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import type { ServerResponse as Response } from 'node:http';
|
|
3
|
-
import type { LimitErrorFn, NextFunction, ParserOptions, ReqWithBody } from './types.
|
|
4
|
-
export * from './types.
|
|
5
|
-
export declare const p: <T = any>(fn: (body: Buffer) => void, payloadLimit?: number, payloadLimitErrorFn?: LimitErrorFn) => (req: ReqWithBody<T>, _res: Response, next?: (err?: any) => void) => Promise<
|
|
3
|
+
import type { LimitErrorFn, NextFunction, ParserOptions, ReqWithBody } from './types.ts';
|
|
4
|
+
export * from './types.ts';
|
|
5
|
+
export declare const p: <T = any>(fn: (body: Buffer, req: ReqWithBody<T>) => void, payloadLimit?: number, payloadLimitErrorFn?: LimitErrorFn) => (req: ReqWithBody<T>, _res: Response, next?: (err?: any) => void) => Promise<T | undefined>;
|
|
6
6
|
/**
|
|
7
7
|
* Parse payload with a custom function
|
|
8
8
|
* @param fn
|
package/dist/index.js
CHANGED
|
@@ -1,77 +1,102 @@
|
|
|
1
1
|
import { Buffer, File } from 'node:buffer';
|
|
2
|
-
import { checkType, hasBody } from
|
|
3
|
-
export * from
|
|
2
|
+
import { checkType, hasBody } from "./utils.js";
|
|
3
|
+
export * from "./types.js";
|
|
4
4
|
const defaultPayloadLimit = 102400; // 100KiB
|
|
5
5
|
const defaultErrorFn = (payloadLimit) => new Error(`Payload too large. Limit: ${payloadLimit} bytes`);
|
|
6
6
|
// Main function
|
|
7
|
-
export const p = (fn, payloadLimit = defaultPayloadLimit, payloadLimitErrorFn = defaultErrorFn) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export const p = (fn, payloadLimit = defaultPayloadLimit, payloadLimitErrorFn = defaultErrorFn) => (req, _res, next) => new Promise((resolve) => {
|
|
8
|
+
const body = [];
|
|
9
|
+
let totalSize = 0;
|
|
10
|
+
req.on('data', (chunk) => {
|
|
11
|
+
totalSize += chunk.byteLength;
|
|
12
|
+
if (totalSize > payloadLimit) {
|
|
13
|
+
req.removeAllListeners();
|
|
14
|
+
next === null || next === void 0 ? void 0 : next(payloadLimitErrorFn(payloadLimit));
|
|
15
|
+
resolve(undefined);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
14
18
|
body.push(chunk);
|
|
15
19
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
});
|
|
21
|
+
req.on('end', () => {
|
|
22
|
+
try {
|
|
23
|
+
resolve(fn(body.length === 1 ? body[0] : Buffer.concat(body), req));
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
next === null || next === void 0 ? void 0 : next(e);
|
|
27
|
+
resolve(undefined);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
req.on('error', (err) => {
|
|
31
|
+
next === null || next === void 0 ? void 0 : next(err);
|
|
32
|
+
resolve(undefined);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
22
35
|
/**
|
|
23
36
|
* Parse payload with a custom function
|
|
24
37
|
* @param fn
|
|
25
38
|
*/
|
|
26
|
-
const custom = (fn, type) =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
const custom = (fn, type) => {
|
|
40
|
+
const parse = p(fn);
|
|
41
|
+
return async (req, _res, next) => {
|
|
42
|
+
if (hasBody(req.method) && checkType(req, type))
|
|
43
|
+
req.body = await parse(req, _res, next);
|
|
44
|
+
next === null || next === void 0 ? void 0 : next();
|
|
45
|
+
};
|
|
30
46
|
};
|
|
31
47
|
/**
|
|
32
48
|
* Parse JSON payload
|
|
33
49
|
* @param options
|
|
34
50
|
*/
|
|
35
|
-
const json = ({ payloadLimit, payloadLimitErrorFn, type, reviver } = {}) =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
const json = ({ payloadLimit, payloadLimitErrorFn, type, reviver } = {}) => {
|
|
52
|
+
const parse = p((x) => (x.length === 0 ? {} : JSON.parse(x.toString(), reviver)), payloadLimit, payloadLimitErrorFn);
|
|
53
|
+
return async (req, res, next) => {
|
|
54
|
+
if (hasBody(req.method) && checkType(req, type)) {
|
|
55
|
+
req.body = await parse(req, res, next);
|
|
56
|
+
}
|
|
57
|
+
next === null || next === void 0 ? void 0 : next();
|
|
58
|
+
};
|
|
43
59
|
};
|
|
44
60
|
/**
|
|
45
61
|
* Parse raw payload
|
|
46
62
|
* @param options
|
|
47
63
|
*/
|
|
48
|
-
const raw = ({ payloadLimit, payloadLimitErrorFn, type } = {}) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
const raw = ({ payloadLimit, payloadLimitErrorFn, type } = {}) => {
|
|
65
|
+
const parse = p((x) => x, payloadLimit, payloadLimitErrorFn);
|
|
66
|
+
return async (req, _res, next) => {
|
|
67
|
+
if (hasBody(req.method) && checkType(req, type)) {
|
|
68
|
+
req.body = await parse(req, _res, next);
|
|
69
|
+
}
|
|
70
|
+
next === null || next === void 0 ? void 0 : next();
|
|
71
|
+
};
|
|
53
72
|
};
|
|
54
|
-
const td = new TextDecoder();
|
|
55
73
|
/**
|
|
56
74
|
* Stringify request payload
|
|
57
75
|
* @param param0
|
|
58
76
|
* @returns
|
|
59
77
|
*/
|
|
60
|
-
const text = ({ payloadLimit, payloadLimitErrorFn, type } = {}) =>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
const text = ({ payloadLimit, payloadLimitErrorFn, type } = {}) => {
|
|
79
|
+
const parse = p((x) => x.toString(), payloadLimit, payloadLimitErrorFn);
|
|
80
|
+
return async (req, _res, next) => {
|
|
81
|
+
if (hasBody(req.method) && checkType(req, type)) {
|
|
82
|
+
req.body = await parse(req, _res, next);
|
|
83
|
+
}
|
|
84
|
+
next === null || next === void 0 ? void 0 : next();
|
|
85
|
+
};
|
|
65
86
|
};
|
|
87
|
+
const td = new TextDecoder();
|
|
66
88
|
/**
|
|
67
89
|
* Parse urlencoded payload
|
|
68
90
|
* @param options
|
|
69
91
|
*/
|
|
70
|
-
const urlencoded = ({ payloadLimit, payloadLimitErrorFn, type } = {}) =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
92
|
+
const urlencoded = ({ payloadLimit, payloadLimitErrorFn, type } = {}) => {
|
|
93
|
+
const parse = p((x) => Object.fromEntries(new URLSearchParams(x.toString()).entries()), payloadLimit, payloadLimitErrorFn);
|
|
94
|
+
return async (req, _res, next) => {
|
|
95
|
+
if (hasBody(req.method) && checkType(req, type)) {
|
|
96
|
+
req.body = await parse(req, _res, next);
|
|
97
|
+
}
|
|
98
|
+
next === null || next === void 0 ? void 0 : next();
|
|
99
|
+
};
|
|
75
100
|
};
|
|
76
101
|
const getBoundary = (contentType) => {
|
|
77
102
|
const match = /boundary=(.+);?/.exec(contentType);
|
|
@@ -85,6 +110,7 @@ const parseMultipart = (body, boundary, { fileCountLimit, fileSizeLimit = defaul
|
|
|
85
110
|
if (fileCountLimit && parts.length > fileCountLimit)
|
|
86
111
|
throw new Error(`Too many files. Limit: ${fileCountLimit}`);
|
|
87
112
|
parts.forEach((part) => {
|
|
113
|
+
var _a, _b;
|
|
88
114
|
const [headers, ...lines] = part.split('\r\n').filter((part) => !!part);
|
|
89
115
|
const data = lines.join('\r\n').trim();
|
|
90
116
|
if (data.length > fileSizeLimit)
|
|
@@ -96,11 +122,11 @@ const parseMultipart = (body, boundary, { fileCountLimit, fileSizeLimit = defaul
|
|
|
96
122
|
const contentTypeMatch = /Content-Type: (.+)/i.exec(data);
|
|
97
123
|
const fileContent = data.slice(contentTypeMatch[0].length + 2);
|
|
98
124
|
const file = new File([fileContent], filename[1], { type: contentTypeMatch[1] });
|
|
99
|
-
|
|
125
|
+
((_a = parsedBody[name]) !== null && _a !== void 0 ? _a : (parsedBody[name] = [])).push(file);
|
|
100
126
|
return;
|
|
101
127
|
}
|
|
102
|
-
|
|
103
|
-
|
|
128
|
+
;
|
|
129
|
+
((_b = parsedBody[name]) !== null && _b !== void 0 ? _b : (parsedBody[name] = [])).push(data);
|
|
104
130
|
});
|
|
105
131
|
return parsedBody;
|
|
106
132
|
};
|
|
@@ -110,15 +136,18 @@ const parseMultipart = (body, boundary, { fileCountLimit, fileSizeLimit = defaul
|
|
|
110
136
|
* Does not restrict total payload size by default.
|
|
111
137
|
* @param options
|
|
112
138
|
*/
|
|
113
|
-
const multipart = ({ payloadLimit = Number.POSITIVE_INFINITY, payloadLimitErrorFn, type, ...opts } = {}) =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
const multipart = ({ payloadLimit = Number.POSITIVE_INFINITY, payloadLimitErrorFn, type, ...opts } = {}) => {
|
|
140
|
+
const parse = p((x, req) => {
|
|
141
|
+
const boundary = getBoundary(req.headers['content-type']);
|
|
142
|
+
if (boundary)
|
|
143
|
+
return parseMultipart(td.decode(x), boundary, opts);
|
|
144
|
+
return {};
|
|
145
|
+
}, payloadLimit, payloadLimitErrorFn);
|
|
146
|
+
return async (req, res, next) => {
|
|
147
|
+
if (hasBody(req.method) && checkType(req, type)) {
|
|
148
|
+
req.body = await parse(req, res, next);
|
|
149
|
+
}
|
|
150
|
+
next === null || next === void 0 ? void 0 : next();
|
|
151
|
+
};
|
|
123
152
|
};
|
|
124
153
|
export { custom, json, raw, text, urlencoded, multipart };
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
|
-
import type { ParserOptions } from './types.
|
|
2
|
+
import type { ParserOptions } from './types.ts';
|
|
3
3
|
export declare const hasBody: (method: string) => boolean;
|
|
4
4
|
export declare const checkType: (req: IncomingMessage, type: ParserOptions["type"]) => boolean;
|
package/dist/utils.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
const bodyMethods = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
2
|
+
export const hasBody = (method) => bodyMethods.has(method);
|
|
2
3
|
export const checkType = (req, type) => typeof type === 'function' ? type(req) : true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "milliparsec",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "tiniest body parser in the universe",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,20 +22,19 @@
|
|
|
22
22
|
"exports": "./dist/index.js",
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@biomejs/biome": "2.2.2",
|
|
25
|
-
"@tinyhttp/app": "^2.
|
|
26
|
-
"@types/express": "^5.0.
|
|
27
|
-
"@types/node": "^18",
|
|
25
|
+
"@tinyhttp/app": "^2.5.2",
|
|
26
|
+
"@types/express": "^5.0.6",
|
|
27
|
+
"@types/node": "^18.19.130",
|
|
28
28
|
"c8": "10.1.2",
|
|
29
|
-
"express": "^5.1
|
|
29
|
+
"express": "^5.2.1",
|
|
30
30
|
"supertest-fetch": "^2.0.0",
|
|
31
|
-
"
|
|
32
|
-
"typescript": "^5.6.2"
|
|
31
|
+
"typescript": "^5.9.3"
|
|
33
32
|
},
|
|
34
33
|
"files": [
|
|
35
34
|
"dist"
|
|
36
35
|
],
|
|
37
36
|
"scripts": {
|
|
38
|
-
"test": "
|
|
37
|
+
"test": "node --test test.ts",
|
|
39
38
|
"test:coverage": "c8 --include=src pnpm test",
|
|
40
39
|
"test:report": "c8 report --reporter=text-lcov > coverage.lcov",
|
|
41
40
|
"build": "tsc -p tsconfig.build.json",
|