milliparsec 2.3.0 → 4.0.0
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 +18 -10
- package/dist/index.d.ts +4 -5
- package/dist/index.js +39 -7
- package/package.json +16 -13
package/README.md
CHANGED
|
@@ -17,9 +17,10 @@ Check out [deno-libs/parsec](https://github.com/deno-libs/parsec) for Deno port.
|
|
|
17
17
|
|
|
18
18
|
- ⏩ built with `async` / `await`
|
|
19
19
|
- 🛠 JSON / raw / urlencoded data support
|
|
20
|
-
- 📦 tiny package size (
|
|
20
|
+
- 📦 tiny package size (8KB dist size)
|
|
21
21
|
- 🔥 no dependencies
|
|
22
|
-
-
|
|
22
|
+
- ✨ [tinyhttp](https://github.com/tinyhttp/tinyhttp) and Express support
|
|
23
|
+
- ⚡ 30% faster than body-parser
|
|
23
24
|
|
|
24
25
|
## Install
|
|
25
26
|
|
|
@@ -27,11 +28,8 @@ Check out [deno-libs/parsec](https://github.com/deno-libs/parsec) for Deno port.
|
|
|
27
28
|
# pnpm
|
|
28
29
|
pnpm i milliparsec
|
|
29
30
|
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# npm
|
|
34
|
-
npm i milliparsec
|
|
31
|
+
# bun
|
|
32
|
+
bun i milliparsec
|
|
35
33
|
```
|
|
36
34
|
|
|
37
35
|
## Usage
|
|
@@ -41,7 +39,7 @@ npm i milliparsec
|
|
|
41
39
|
Use a middleware inside a server:
|
|
42
40
|
|
|
43
41
|
```js
|
|
44
|
-
import { createServer } from 'http'
|
|
42
|
+
import { createServer } from 'node:http'
|
|
45
43
|
import { json } from 'milliparsec'
|
|
46
44
|
|
|
47
45
|
const server = createServer(async (req: ReqWithBody, res) => {
|
|
@@ -85,12 +83,22 @@ Parses request body using `new URLSearchParams`.
|
|
|
85
83
|
|
|
86
84
|
Parses request body using `JSON.parse`.
|
|
87
85
|
|
|
86
|
+
### `multipart(req, res, cb)`
|
|
87
|
+
|
|
88
|
+
Parses request body using `multipart/form-data` content type and boundary. Supports files as well.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// curl -F "textfield=textfield" -F "someother=textfield with text" localhost:3000
|
|
92
|
+
await multipart()(req, res, (err) => void err && console.log(err))
|
|
93
|
+
res.end(req.body) // { textfield: "textfield", someother: "textfield with text" }
|
|
94
|
+
```
|
|
95
|
+
|
|
88
96
|
### `custom(fn)(req, res, cb)`
|
|
89
97
|
|
|
90
98
|
Custom function for `parsec`.
|
|
91
99
|
|
|
92
100
|
```js
|
|
93
|
-
// curl -d "this text must be uppercased" localhost
|
|
101
|
+
// curl -d "this text must be uppercased" localhost:3000
|
|
94
102
|
await custom(
|
|
95
103
|
req,
|
|
96
104
|
(d) => d.toUpperCase(),
|
|
@@ -108,6 +116,6 @@ The parsec is a unit of length used to measure large distances to astronomical o
|
|
|
108
116
|
[npm-url]: https://www.npmjs.com/package/milliparsec
|
|
109
117
|
[dl-badge-url]: https://img.shields.io/npm/dt/milliparsec?style=for-the-badge&color=25608B
|
|
110
118
|
[github-actions]: https://github.com/talentlessguy/milliparsec/actions
|
|
111
|
-
[gh-actions-img]: https://img.shields.io/github/workflow/status/
|
|
119
|
+
[gh-actions-img]: https://img.shields.io/github/actions/workflow/status/tinyhttp/milliparsec/main.yml?branch=master&style=for-the-badge&color=25608B&label=&logo=github
|
|
112
120
|
[cov-img]: https://img.shields.io/coveralls/github/tinyhttp/milliparsec?style=for-the-badge&color=25608B
|
|
113
121
|
[cov-url]: https://coveralls.io/github/tinyhttp/milliparsec
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { ServerResponse as Response, IncomingMessage } from 'http';
|
|
4
|
-
import { EventEmitter } from 'events';
|
|
1
|
+
import type { EventEmitter } from 'node:events';
|
|
2
|
+
import type { IncomingMessage, ServerResponse as Response } from 'node:http';
|
|
5
3
|
type NextFunction = (err?: any) => void;
|
|
6
4
|
export type ReqWithBody<T = any> = IncomingMessage & {
|
|
7
5
|
body?: T;
|
|
@@ -13,4 +11,5 @@ declare const json: () => (req: ReqWithBody, res: Response, next: NextFunction)
|
|
|
13
11
|
declare const raw: () => (req: ReqWithBody, _res: Response, next: NextFunction) => Promise<void>;
|
|
14
12
|
declare const text: () => (req: ReqWithBody, _res: Response, next: NextFunction) => Promise<void>;
|
|
15
13
|
declare const urlencoded: () => (req: ReqWithBody, res: Response, next: NextFunction) => Promise<void>;
|
|
16
|
-
|
|
14
|
+
declare const multipart: () => (req: ReqWithBody, res: Response, next: NextFunction) => Promise<void>;
|
|
15
|
+
export { custom, json, raw, text, urlencoded, multipart };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const hasBody = (method) => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
|
|
2
|
-
|
|
3
|
-
const p = (fn) => async (req, _res, next) => {
|
|
1
|
+
export const hasBody = (method) => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
|
|
2
|
+
export const p = (fn) => async (req, _res, next) => {
|
|
4
3
|
try {
|
|
5
4
|
let body = '';
|
|
6
5
|
for await (const chunk of req)
|
|
@@ -11,9 +10,8 @@ const p = (fn) => async (req, _res, next) => {
|
|
|
11
10
|
next(e);
|
|
12
11
|
}
|
|
13
12
|
};
|
|
14
|
-
// JSON, raw, FormData
|
|
15
13
|
const custom = (fn) => async (req, _res, next) => {
|
|
16
|
-
req.body = await p(fn)(req,
|
|
14
|
+
req.body = await p(fn)(req, _res, next);
|
|
17
15
|
next();
|
|
18
16
|
};
|
|
19
17
|
const json = () => async (req, res, next) => {
|
|
@@ -51,5 +49,39 @@ const urlencoded = () => async (req, res, next) => {
|
|
|
51
49
|
else
|
|
52
50
|
next();
|
|
53
51
|
};
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const getBoundary = (contentType) => {
|
|
53
|
+
const match = /boundary=(.+);?/.exec(contentType);
|
|
54
|
+
return match ? `--${match[1]}` : null;
|
|
55
|
+
};
|
|
56
|
+
const parseMultipart = (body, boundary) => {
|
|
57
|
+
const parts = body.split(new RegExp(`${boundary}(--)?`)).filter((part) => !!part && /content-disposition/i.test(part));
|
|
58
|
+
const parsedBody = {};
|
|
59
|
+
parts.map((part) => {
|
|
60
|
+
const [headers, ...lines] = part.split('\r\n').filter((part) => !!part);
|
|
61
|
+
const data = lines.join('\r\n').trim();
|
|
62
|
+
const name = /name="(.+?)"/.exec(headers)[1];
|
|
63
|
+
const filename = /filename="(.+?)"/.exec(headers);
|
|
64
|
+
if (filename) {
|
|
65
|
+
const contentTypeMatch = /Content-Type: (.+)/i.exec(data);
|
|
66
|
+
const fileContent = data.slice(contentTypeMatch[0].length + 2);
|
|
67
|
+
return Object.assign(parsedBody, {
|
|
68
|
+
[name]: new File([fileContent], filename[1], { type: contentTypeMatch[1] })
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return Object.assign(parsedBody, { [name]: data });
|
|
72
|
+
});
|
|
73
|
+
return parsedBody;
|
|
74
|
+
};
|
|
75
|
+
const multipart = () => async (req, res, next) => {
|
|
76
|
+
if (hasBody(req.method)) {
|
|
77
|
+
req.body = await p((x) => {
|
|
78
|
+
const boundary = getBoundary(req.headers['content-type']);
|
|
79
|
+
if (boundary)
|
|
80
|
+
return parseMultipart(x, boundary);
|
|
81
|
+
})(req, res, next);
|
|
82
|
+
next();
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
next();
|
|
86
|
+
};
|
|
87
|
+
export { custom, json, raw, text, urlencoded, multipart };
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "milliparsec",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "tiniest body parser in the universe",
|
|
5
|
-
"repository":
|
|
6
|
-
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/tinyhttp/milliparsec"
|
|
8
|
+
},
|
|
9
|
+
"author": "talentlessguy <hi@v1rtl.site>",
|
|
7
10
|
"license": "MIT",
|
|
8
11
|
"types": "./dist/index.d.ts",
|
|
9
12
|
"type": "module",
|
|
@@ -14,28 +17,28 @@
|
|
|
14
17
|
"body-parsing"
|
|
15
18
|
],
|
|
16
19
|
"engines": {
|
|
17
|
-
"node": ">=
|
|
20
|
+
"node": ">=20"
|
|
18
21
|
},
|
|
19
22
|
"exports": "./dist/index.js",
|
|
20
23
|
"devDependencies": {
|
|
21
|
-
"@
|
|
22
|
-
"@
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"rollup": "^3.21.4",
|
|
26
|
-
"supertest-fetch": "^1.5.0",
|
|
27
|
-
"tslib": "^2.5.0",
|
|
24
|
+
"@biomejs/biome": "1.8.3",
|
|
25
|
+
"@types/node": "^20.14.9",
|
|
26
|
+
"c8": "10.1.2",
|
|
27
|
+
"supertest-fetch": "^2.0.0",
|
|
28
28
|
"tsm": "^2.3.0",
|
|
29
|
-
"typescript": "^5.
|
|
29
|
+
"typescript": "^5.5.3",
|
|
30
30
|
"uvu": "^0.5.6"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"provenance": true
|
|
37
|
+
},
|
|
35
38
|
"scripts": {
|
|
36
39
|
"test": "uvu -r tsm",
|
|
37
40
|
"test:coverage": "c8 --include=src pnpm test",
|
|
38
41
|
"test:report": "c8 report --reporter=text-lcov > coverage.lcov",
|
|
39
|
-
"build": "
|
|
42
|
+
"build": "tsc -p tsconfig.build.json"
|
|
40
43
|
}
|
|
41
44
|
}
|