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 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 (728B)
20
+ - 📦 tiny package size (8KB dist size)
21
21
  - 🔥 no dependencies
22
- - [tinyhttp](https://github.com/talentlessguy/tinyhttp) and Express support
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
- # yarn
31
- yarn add milliparsec
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/talentlessguy/milliparsec/CI?style=for-the-badge&color=25608B&label=&logo=github
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
- /// <reference types="node" />
2
- /// <reference types="node" />
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
- export { custom, json, raw, text, urlencoded };
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
- // Main function
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, undefined, next);
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
- export { custom, hasBody, json, p, raw, text, urlencoded };
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": "2.3.0",
3
+ "version": "4.0.0",
4
4
  "description": "tiniest body parser in the universe",
5
- "repository": "https://github.com/talentlessguy/parsec.git",
6
- "author": "talentlessguy <pilll.PL22@gmail.com>",
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": ">=12.4"
20
+ "node": ">=20"
18
21
  },
19
22
  "exports": "./dist/index.js",
20
23
  "devDependencies": {
21
- "@rollup/plugin-typescript": "^11.1.0",
22
- "@tinyhttp/app": "^2.0.31",
23
- "@types/node": "^18.16.3",
24
- "c8": "7.13.0",
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.0.4",
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": "rollup -c"
42
+ "build": "tsc -p tsconfig.build.json"
40
43
  }
41
44
  }