├── .github
├── FUNDING.yml
└── workflows
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── bench
├── body-parser.js
├── file.txt
├── formidable.js
├── index.md
├── milliparsec-multipart.js
├── milliparsec.js
├── package.json
└── pnpm-lock.yaml
├── biome.json
├── logo.png
├── package.json
├── pnpm-lock.yaml
├── src
└── index.ts
├── test.ts
├── tsconfig.build.json
└── tsconfig.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | ko_fi: v1rtl
4 | liberapay: v1rtl
5 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [master]
10 | pull_request:
11 | branches: [master]
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "test"
16 | test:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 | # Use environment for CI
20 | environment: ci
21 |
22 | # Steps represent a sequence of tasks that will be executed as part of the job
23 | steps:
24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
25 | - uses: actions/checkout@v4
26 | - uses: pnpm/action-setup@v4
27 | - uses: actions/setup-node@v4
28 | with:
29 | node-version: 18.13
30 | cache: "pnpm"
31 | - run: pnpm install
32 | - run: pnpm test:coverage
33 | - run: pnpm test:report
34 | - name: Setup Biome
35 | uses: biomejs/setup-biome@v2
36 | with:
37 | version: latest
38 |
39 | - name: Run Biome
40 | run: biome ci .
41 | - name: Coveralls
42 | uses: coverallsapp/github-action@master
43 | with:
44 | github-token: ${{ secrets.GITHUB_TOKEN }}
45 | path-to-lcov: ./coverage.lcov
46 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Package to npm
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | permissions:
9 | contents: read
10 | id-token: write
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: pnpm/action-setup@v4
14 | - uses: actions/setup-node@v4
15 | with:
16 | cache: "pnpm"
17 | node-version: 20
18 | registry-url: "https://registry.npmjs.org"
19 | - run: pnpm install
20 | - run: pnpm build
21 | - name: Setup Biome
22 | uses: biomejs/setup-biome@v2
23 | with:
24 | version: latest
25 |
26 | - name: Run Biome
27 | run: biome ci .
28 | - run: pnpm publish --no-git-checks
29 | env:
30 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Coverage directory used by tools like istanbul
11 | coverage
12 |
13 | # nyc test coverage
14 | .nyc_output
15 |
16 | # node-waf configuration
17 | .lock-wscript
18 |
19 | # Dependency directories
20 | node_modules/
21 | jspm_packages/
22 |
23 | # TypeScript v1 declaration files
24 | typings/
25 |
26 | # Optional npm cache directory
27 | .npm
28 |
29 | # Optional eslint cache
30 | .eslintcache
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 | # Output of 'npm pack'
36 | *.tgz
37 |
38 | # Yarn Integrity file
39 | .yarn-integrity
40 |
41 | # dotenv environment variables file
42 | .env
43 |
44 | dist
45 | coverage
46 | coverage.lcov
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "npm.packageManager": "pnpm",
3 | "editor.formatOnSave": true,
4 | "biome.enabled": true,
5 | "eslint.enable": false,
6 | "prettier.enable": false,
7 | "editor.codeActionsOnSave": {
8 | "source.fixAll": "explicit",
9 | "source.organizeImports.biome": "explicit"
10 | },
11 | "typescript.tsdk": "node_modules/typescript/lib"
12 | }
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 v 1 r t l
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 | [![Version][v-badge-url]][npm-url] [![Coverage][cov-img]][cov-url]
7 | [![Github actions][gh-actions-img]][github-actions]
8 | [![Downloads][dl-badge-url]][npm-url]
9 |
10 |
11 |
12 |
13 | Tiniest body parser in the universe. Built for modern Node.js.
14 |
15 | Check out [deno-libs/parsec](https://github.com/deno-libs/parsec) for Deno port.
16 |
17 | ## Features
18 |
19 | - 🛠 JSON / raw / urlencoded / multipart support
20 | - 📦 tiny package size (8KB dist size)
21 | - 🔥 no dependencies
22 | - ✨ [tinyhttp](https://github.com/tinyhttp/tinyhttp) and Express support
23 | - ⚡ 40% faster than body-parser and 20x faster than formidable
24 |
25 | ## Install
26 |
27 | ```sh
28 | # pnpm
29 | pnpm i milliparsec
30 |
31 | # bun
32 | bun i milliparsec
33 | ```
34 |
35 | ## Usage
36 |
37 | ### Basic example
38 |
39 | Use a middleware inside a server:
40 |
41 | ```js
42 | import { createServer } from 'node:http'
43 | import { json } from 'milliparsec'
44 |
45 | const server = createServer(async (req: ReqWithBody, res) => {
46 | await json()(req, res, (err) => void err && res.end(err))
47 |
48 | res.setHeader('Content-Type', 'application/json')
49 |
50 | res.end(JSON.stringify(req.body))
51 | })
52 | ```
53 |
54 | ### What is "parsec"?
55 |
56 | The parsec is a unit of length used to measure large distances to astronomical
57 | objects outside the Solar System.
58 |
59 | [v-badge-url]: https://img.shields.io/npm/v/milliparsec.svg?style=for-the-badge&color=25608B&logo=npm&label=
60 | [npm-url]: https://www.npmjs.com/package/milliparsec
61 | [dl-badge-url]: https://img.shields.io/npm/dt/milliparsec?style=for-the-badge&color=25608B
62 | [github-actions]: https://github.com/talentlessguy/milliparsec/actions
63 | [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
64 | [cov-img]: https://img.shields.io/coveralls/github/tinyhttp/milliparsec?style=for-the-badge&color=25608B
65 | [cov-url]: https://coveralls.io/github/tinyhttp/milliparsec
66 |
--------------------------------------------------------------------------------
/bench/body-parser.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import { createServer } from 'node:http'
4 | import bodyParser from 'body-parser'
5 |
6 | const mw = bodyParser.json()
7 |
8 | const server = createServer((req, res) => {
9 | mw(req, res, () => {
10 | // @ts-expect-error added by body parser
11 | res.end(JSON.stringify(req.body))
12 | })
13 | })
14 |
15 | server.listen(3002)
16 |
--------------------------------------------------------------------------------
/bench/file.txt:
--------------------------------------------------------------------------------
1 | this is a file that is being sent in a multipart request
--------------------------------------------------------------------------------
/bench/formidable.js:
--------------------------------------------------------------------------------
1 | import { createReadStream } from 'node:fs'
2 | // @ts-check
3 | import { createServer } from 'node:http'
4 | import formidable from 'formidable'
5 |
6 | const form = formidable({})
7 |
8 | const server = createServer((req, res) => {
9 | form.parse(req, (_, fields, files) => {
10 | // @ts-expect-error this is JS
11 | const file = createReadStream(files.file[0].filepath)
12 | file.pipe(res)
13 | })
14 | })
15 |
16 | server.listen(3005)
17 |
--------------------------------------------------------------------------------
/bench/index.md:
--------------------------------------------------------------------------------
1 | # Benchmark
2 |
3 | Below are benchmarks of body-parser vs milliparsec and formidable vs
4 | milliparsec. Please take into account that these benchmarks are not entirely
5 | accurate, since they are taken on a regular desktop computer in usual
6 | conditions.
7 |
8 | ## Environment
9 |
10 | - Node.js 22.3.1
11 | - System: macOS Sequoia 15.3.1 / Darwin 24.3.0 arm64 kernel
12 | - CPU: Apple M2 (8) @ 3.50 GHz
13 | - Machine: MacBook Air (M2, 2022)
14 |
15 | ## JSON parsing
16 |
17 | ### Benchmark command:
18 |
19 | ```sh
20 | autocannon -m POST -b '{"a":1}' -H "Content-Type=application/json" localhost:3002 # or 3003
21 | ```
22 |
23 | ### Results
24 |
25 | body-parser result:
26 |
27 | ```
28 | ┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐
29 | │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
30 | ├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤
31 | │ Latency │ 0 ms │ 0 ms │ 0 ms │ 0 ms │ 0.01 ms │ 0.12 ms │ 22 ms │
32 | └─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘
33 | ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬──────────┬─────────┐
34 | │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
35 | ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
36 | │ Req/Sec │ 54,591 │ 54,591 │ 61,759 │ 63,871 │ 61,436.8 │ 2,478.39 │ 54,589 │
37 | ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
38 | │ Bytes/Sec │ 7.05 MB │ 7.05 MB │ 7.97 MB │ 8.24 MB │ 7.93 MB │ 319 kB │ 7.04 MB │
39 | └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴──────────┴─────────┘
40 |
41 | Req/Bytes counts sampled once per second.
42 | # of samples: 10
43 |
44 | 614k requests in 10.01s, 79.3 MB read
45 | ```
46 |
47 | milliparsec result:
48 |
49 | ```
50 | ┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐
51 | │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
52 | ├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤
53 | │ Latency │ 0 ms │ 0 ms │ 0 ms │ 0 ms │ 0.01 ms │ 0.04 ms │ 11 ms │
54 | └─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘
55 | ┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬──────────┬─────────┐
56 | │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
57 | ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
58 | │ Req/Sec │ 79,999 │ 79,999 │ 88,127 │ 88,767 │ 87,095.28 │ 2,370.01 │ 79,966 │
59 | ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
60 | │ Bytes/Sec │ 9.76 MB │ 9.76 MB │ 10.7 MB │ 10.8 MB │ 10.6 MB │ 289 kB │ 9.76 MB │
61 | └───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴──────────┴─────────┘
62 |
63 | Req/Bytes counts sampled once per second.
64 | # of samples: 11
65 |
66 | 958k requests in 11.01s, 117 MB read
67 |
68 | Req/Bytes counts sampled once per second.
69 | # of samples: 11
70 |
71 | 641k requests in 11.02s, 78.2 MB read
72 | ```
73 |
74 | ### Verdict
75 |
76 | milliparsec, on average, is ~40% faster.
77 |
78 | ## Multipart with files
79 |
80 | ### Benchmark command:
81 |
82 | ```sh
83 | autocannon -m POST --form '{ "file": { "type": "file", "path": "./file.txt" } }' localhost:3004
84 | ```
85 |
86 | ### Results
87 |
88 | formidable result:
89 |
90 | ```
91 | ┌─────────┬──────┬──────┬───────┬───────┬─────────┬─────────┬───────┐
92 | │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
93 | ├─────────┼──────┼──────┼───────┼───────┼─────────┼─────────┼───────┤
94 | │ Latency │ 1 ms │ 5 ms │ 19 ms │ 26 ms │ 6.63 ms │ 5.86 ms │ 54 ms │
95 | └─────────┴──────┴──────┴───────┴───────┴─────────┴─────────┴───────┘
96 | ┌───────────┬────────┬────────┬────────┬────────┬────────┬──────────┬────────┐
97 | │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
98 | ├───────────┼────────┼────────┼────────┼────────┼────────┼──────────┼────────┤
99 | │ Req/Sec │ 530 │ 530 │ 775 │ 4,595 │ 1,404 │ 1,179.32 │ 530 │
100 | ├───────────┼────────┼────────┼────────┼────────┼────────┼──────────┼────────┤
101 | │ Bytes/Sec │ 105 kB │ 105 kB │ 153 kB │ 910 kB │ 278 kB │ 233 kB │ 105 kB │
102 | └───────────┴────────┴────────┴────────┴────────┴────────┴──────────┴────────┘
103 |
104 | Req/Bytes counts sampled once per second.
105 | # of samples: 10
106 |
107 | 14k requests in 10.02s, 2.78 MB read
108 | ```
109 |
110 | milliparsec result:
111 |
112 | ```
113 | ┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐
114 | │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
115 | ├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤
116 | │ Latency │ 0 ms │ 0 ms │ 0 ms │ 0 ms │ 0.02 ms │ 0.19 ms │ 20 ms │
117 | └─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘
118 | ┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬──────────┬─────────┐
119 | │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
120 | ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
121 | │ Req/Sec │ 24,063 │ 24,063 │ 29,727 │ 30,863 │ 29,263.28 │ 1,758.94 │ 24,051 │
122 | ├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤
123 | │ Bytes/Sec │ 4.76 MB │ 4.76 MB │ 5.89 MB │ 6.11 MB │ 5.79 MB │ 348 kB │ 4.76 MB │
124 | └───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴──────────┴─────────┘
125 |
126 | Req/Bytes counts sampled once per second.
127 | # of samples: 11
128 |
129 | 322k requests in 11.01s, 63.7 MB read
130 | ```
131 |
132 | ### Verdict
133 |
134 | milliparsec, on average, is ~20x faster.
135 |
--------------------------------------------------------------------------------
/bench/milliparsec-multipart.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import { createServer } from 'node:http'
4 | import * as bodyParser from '../dist/index.js'
5 | const mw = bodyParser.multipart()
6 |
7 | const server = createServer((req, res) => {
8 | mw(req, res, () => {
9 | /**
10 | * @type {File}
11 | */
12 | // @ts-ignore
13 | const file = req.body.file[0]
14 | const stream = file.stream()
15 |
16 | // Pipe the stream to the response
17 | stream.pipeTo(
18 | new WritableStream({
19 | write(chunk) {
20 | res.write(chunk)
21 | },
22 | close() {
23 | res.end()
24 | },
25 | abort(err) {
26 | console.error('Stream error:', err)
27 | res.writeHead(500)
28 | res.end('Error streaming file')
29 | }
30 | })
31 | )
32 | })
33 | })
34 |
35 | server.listen(3004)
36 |
--------------------------------------------------------------------------------
/bench/milliparsec.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import { createServer } from 'node:http'
4 | import * as bodyParser from '../dist/index.js'
5 |
6 | const mw = bodyParser.json()
7 |
8 | const server = createServer((req, res) => {
9 | mw(req, res, () => {
10 | // @ts-expect-error added by body parser
11 | res.end(JSON.stringify(req.body))
12 | })
13 | })
14 |
15 | server.listen(3003)
16 |
--------------------------------------------------------------------------------
/bench/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bench",
3 | "type": "module",
4 | "version": "1.0.0",
5 | "description": "",
6 | "main": "index.js",
7 | "scripts": {
8 | "bench": "autocannon -m POST -b '{\"a\":1}' -H \"Content-Type=application/json\"",
9 | "bench:multipart": "autocannon -m POST --form '{ \"file\": { \"type\": \"file\", \"path\": \"./file.txt\" } }'"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "@types/body-parser": "^1.19.5",
16 | "@types/formidable": "^3.4.5",
17 | "autocannon": "^8.0.0",
18 | "body-parser": "^1.20.3"
19 | },
20 | "dependencies": {
21 | "formidable": "^3.5.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bench/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | formidable:
12 | specifier: ^3.5.2
13 | version: 3.5.2
14 | devDependencies:
15 | '@types/body-parser':
16 | specifier: ^1.19.5
17 | version: 1.19.5
18 | '@types/formidable':
19 | specifier: ^3.4.5
20 | version: 3.4.5
21 | autocannon:
22 | specifier: ^8.0.0
23 | version: 8.0.0
24 | body-parser:
25 | specifier: ^1.20.3
26 | version: 1.20.3
27 |
28 | packages:
29 |
30 | '@assemblyscript/loader@0.19.23':
31 | resolution: {integrity: sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==}
32 |
33 | '@colors/colors@1.5.0':
34 | resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
35 | engines: {node: '>=0.1.90'}
36 |
37 | '@minimistjs/subarg@1.0.0':
38 | resolution: {integrity: sha512-Q/ONBiM2zNeYUy0mVSO44mWWKYM3UHuEK43PKIOzJCbvUnPoMH1K+gk3cf1kgnCVJFlWmddahQQCmrmBGlk9jQ==}
39 |
40 | '@types/body-parser@1.19.5':
41 | resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
42 |
43 | '@types/connect@3.4.38':
44 | resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
45 |
46 | '@types/formidable@3.4.5':
47 | resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==}
48 |
49 | '@types/node@20.14.9':
50 | resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
51 |
52 | ansi-regex@5.0.1:
53 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
54 | engines: {node: '>=8'}
55 |
56 | ansi-styles@4.3.0:
57 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
58 | engines: {node: '>=8'}
59 |
60 | asap@2.0.6:
61 | resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
62 |
63 | asynckit@0.4.0:
64 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
65 |
66 | autocannon@8.0.0:
67 | resolution: {integrity: sha512-fMMcWc2JPFcUaqHeR6+PbmEpTxCrPZyBUM95oG4w3ngJ8NfBNas/ZXA+pTHXLqJ0UlFVTcy05GC25WxKx/M20A==}
68 | hasBin: true
69 |
70 | base64-js@1.5.1:
71 | resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
72 |
73 | body-parser@1.20.3:
74 | resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
75 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
76 |
77 | buffer@5.7.1:
78 | resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
79 |
80 | bytes@3.1.2:
81 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
82 | engines: {node: '>= 0.8'}
83 |
84 | call-bind@1.0.7:
85 | resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
86 | engines: {node: '>= 0.4'}
87 |
88 | chalk@4.1.2:
89 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
90 | engines: {node: '>=10'}
91 |
92 | char-spinner@1.0.1:
93 | resolution: {integrity: sha512-acv43vqJ0+N0rD+Uw3pDHSxP30FHrywu2NO6/wBaHChJIizpDeBUd6NjqhNhy9LGaEAhZAXn46QzmlAvIWd16g==}
94 |
95 | cli-table3@0.6.5:
96 | resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==}
97 | engines: {node: 10.* || >= 12.*}
98 |
99 | color-convert@2.0.1:
100 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
101 | engines: {node: '>=7.0.0'}
102 |
103 | color-name@1.1.4:
104 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
105 |
106 | color-support@1.1.3:
107 | resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
108 | hasBin: true
109 |
110 | combined-stream@1.0.8:
111 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
112 | engines: {node: '>= 0.8'}
113 |
114 | content-type@1.0.5:
115 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
116 | engines: {node: '>= 0.6'}
117 |
118 | cross-argv@2.0.0:
119 | resolution: {integrity: sha512-YIaY9TR5Nxeb8SMdtrU8asWVM4jqJDNDYlKV21LxtYcfNJhp1kEsgSa6qXwXgzN0WQWGODps0+TlGp2xQSHwOg==}
120 |
121 | debug@2.6.9:
122 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
123 | peerDependencies:
124 | supports-color: '*'
125 | peerDependenciesMeta:
126 | supports-color:
127 | optional: true
128 |
129 | define-data-property@1.1.4:
130 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
131 | engines: {node: '>= 0.4'}
132 |
133 | delayed-stream@1.0.0:
134 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
135 | engines: {node: '>=0.4.0'}
136 |
137 | depd@2.0.0:
138 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
139 | engines: {node: '>= 0.8'}
140 |
141 | destroy@1.2.0:
142 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
143 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
144 |
145 | dezalgo@1.0.4:
146 | resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
147 |
148 | ee-first@1.1.1:
149 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
150 |
151 | emoji-regex@8.0.0:
152 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
153 |
154 | es-define-property@1.0.0:
155 | resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
156 | engines: {node: '>= 0.4'}
157 |
158 | es-errors@1.3.0:
159 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
160 | engines: {node: '>= 0.4'}
161 |
162 | form-data@4.0.0:
163 | resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
164 | engines: {node: '>= 6'}
165 |
166 | formidable@3.5.2:
167 | resolution: {integrity: sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==}
168 |
169 | function-bind@1.1.2:
170 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
171 |
172 | get-intrinsic@1.2.4:
173 | resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
174 | engines: {node: '>= 0.4'}
175 |
176 | gopd@1.0.1:
177 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
178 |
179 | has-async-hooks@1.0.0:
180 | resolution: {integrity: sha512-YF0VPGjkxr7AyyQQNykX8zK4PvtEDsUJAPqwu06UFz1lb6EvI53sPh5H1kWxg8NXI5LsfRCZ8uX9NkYDZBb/mw==}
181 |
182 | has-flag@4.0.0:
183 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
184 | engines: {node: '>=8'}
185 |
186 | has-property-descriptors@1.0.2:
187 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
188 |
189 | has-proto@1.0.3:
190 | resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
191 | engines: {node: '>= 0.4'}
192 |
193 | has-symbols@1.0.3:
194 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
195 | engines: {node: '>= 0.4'}
196 |
197 | hasown@2.0.2:
198 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
199 | engines: {node: '>= 0.4'}
200 |
201 | hdr-histogram-js@3.0.0:
202 | resolution: {integrity: sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==}
203 | engines: {node: '>=14'}
204 |
205 | hdr-histogram-percentiles-obj@3.0.0:
206 | resolution: {integrity: sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==}
207 |
208 | hexoid@2.0.0:
209 | resolution: {integrity: sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==}
210 | engines: {node: '>=8'}
211 |
212 | http-errors@2.0.0:
213 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
214 | engines: {node: '>= 0.8'}
215 |
216 | http-parser-js@0.5.8:
217 | resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==}
218 |
219 | hyperid@3.2.0:
220 | resolution: {integrity: sha512-PdTtDo+Rmza9nEhTunaDSUKwbC69TIzLEpZUwiB6f+0oqmY0UPfhyHCPt6K1NQ4WFv5yJBTG5vELztVWP+nEVQ==}
221 |
222 | iconv-lite@0.4.24:
223 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
224 | engines: {node: '>=0.10.0'}
225 |
226 | ieee754@1.2.1:
227 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
228 |
229 | inherits@2.0.4:
230 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
231 |
232 | is-fullwidth-code-point@3.0.0:
233 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
234 | engines: {node: '>=8'}
235 |
236 | lodash.chunk@4.2.0:
237 | resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==}
238 |
239 | lodash.clonedeep@4.5.0:
240 | resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
241 |
242 | lodash.flatten@4.4.0:
243 | resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
244 |
245 | manage-path@2.0.0:
246 | resolution: {integrity: sha512-NJhyB+PJYTpxhxZJ3lecIGgh4kwIY2RAh44XvAz9UlqthlQwtPBf62uBVR8XaD8CRuSjQ6TnZH2lNJkbLPZM2A==}
247 |
248 | media-typer@0.3.0:
249 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
250 | engines: {node: '>= 0.6'}
251 |
252 | mime-db@1.52.0:
253 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
254 | engines: {node: '>= 0.6'}
255 |
256 | mime-types@2.1.35:
257 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
258 | engines: {node: '>= 0.6'}
259 |
260 | minimist@1.2.8:
261 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
262 |
263 | ms@2.0.0:
264 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
265 |
266 | object-inspect@1.13.2:
267 | resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
268 | engines: {node: '>= 0.4'}
269 |
270 | on-finished@2.4.1:
271 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
272 | engines: {node: '>= 0.8'}
273 |
274 | on-net-listen@1.1.2:
275 | resolution: {integrity: sha512-y1HRYy8s/RlcBvDUwKXSmkODMdx4KSuIvloCnQYJ2LdBBC1asY4HtfhXwe3UWknLakATZDnbzht2Ijw3M1EqFg==}
276 | engines: {node: '>=9.4.0 || ^8.9.4'}
277 |
278 | once@1.4.0:
279 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
280 |
281 | pako@1.0.11:
282 | resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
283 |
284 | pretty-bytes@5.6.0:
285 | resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
286 | engines: {node: '>=6'}
287 |
288 | progress@2.0.3:
289 | resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
290 | engines: {node: '>=0.4.0'}
291 |
292 | qs@6.13.0:
293 | resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
294 | engines: {node: '>=0.6'}
295 |
296 | raw-body@2.5.2:
297 | resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
298 | engines: {node: '>= 0.8'}
299 |
300 | reinterval@1.1.0:
301 | resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==}
302 |
303 | retimer@3.0.0:
304 | resolution: {integrity: sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==}
305 |
306 | safer-buffer@2.1.2:
307 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
308 |
309 | semver@7.6.2:
310 | resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
311 | engines: {node: '>=10'}
312 | hasBin: true
313 |
314 | set-function-length@1.2.2:
315 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
316 | engines: {node: '>= 0.4'}
317 |
318 | setprototypeof@1.2.0:
319 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
320 |
321 | side-channel@1.0.6:
322 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
323 | engines: {node: '>= 0.4'}
324 |
325 | statuses@2.0.1:
326 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
327 | engines: {node: '>= 0.8'}
328 |
329 | string-width@4.2.3:
330 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
331 | engines: {node: '>=8'}
332 |
333 | strip-ansi@6.0.1:
334 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
335 | engines: {node: '>=8'}
336 |
337 | supports-color@7.2.0:
338 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
339 | engines: {node: '>=8'}
340 |
341 | timestring@6.0.0:
342 | resolution: {integrity: sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==}
343 | engines: {node: '>=8'}
344 |
345 | toidentifier@1.0.1:
346 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
347 | engines: {node: '>=0.6'}
348 |
349 | type-is@1.6.18:
350 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
351 | engines: {node: '>= 0.6'}
352 |
353 | undici-types@5.26.5:
354 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
355 |
356 | unpipe@1.0.0:
357 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
358 | engines: {node: '>= 0.8'}
359 |
360 | uuid-parse@1.1.0:
361 | resolution: {integrity: sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==}
362 |
363 | uuid@8.3.2:
364 | resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
365 | hasBin: true
366 |
367 | wrappy@1.0.2:
368 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
369 |
370 | snapshots:
371 |
372 | '@assemblyscript/loader@0.19.23': {}
373 |
374 | '@colors/colors@1.5.0':
375 | optional: true
376 |
377 | '@minimistjs/subarg@1.0.0':
378 | dependencies:
379 | minimist: 1.2.8
380 |
381 | '@types/body-parser@1.19.5':
382 | dependencies:
383 | '@types/connect': 3.4.38
384 | '@types/node': 20.14.9
385 |
386 | '@types/connect@3.4.38':
387 | dependencies:
388 | '@types/node': 20.14.9
389 |
390 | '@types/formidable@3.4.5':
391 | dependencies:
392 | '@types/node': 20.14.9
393 |
394 | '@types/node@20.14.9':
395 | dependencies:
396 | undici-types: 5.26.5
397 |
398 | ansi-regex@5.0.1: {}
399 |
400 | ansi-styles@4.3.0:
401 | dependencies:
402 | color-convert: 2.0.1
403 |
404 | asap@2.0.6: {}
405 |
406 | asynckit@0.4.0: {}
407 |
408 | autocannon@8.0.0:
409 | dependencies:
410 | '@minimistjs/subarg': 1.0.0
411 | chalk: 4.1.2
412 | char-spinner: 1.0.1
413 | cli-table3: 0.6.5
414 | color-support: 1.1.3
415 | cross-argv: 2.0.0
416 | form-data: 4.0.0
417 | has-async-hooks: 1.0.0
418 | hdr-histogram-js: 3.0.0
419 | hdr-histogram-percentiles-obj: 3.0.0
420 | http-parser-js: 0.5.8
421 | hyperid: 3.2.0
422 | lodash.chunk: 4.2.0
423 | lodash.clonedeep: 4.5.0
424 | lodash.flatten: 4.4.0
425 | manage-path: 2.0.0
426 | on-net-listen: 1.1.2
427 | pretty-bytes: 5.6.0
428 | progress: 2.0.3
429 | reinterval: 1.1.0
430 | retimer: 3.0.0
431 | semver: 7.6.2
432 | timestring: 6.0.0
433 |
434 | base64-js@1.5.1: {}
435 |
436 | body-parser@1.20.3:
437 | dependencies:
438 | bytes: 3.1.2
439 | content-type: 1.0.5
440 | debug: 2.6.9
441 | depd: 2.0.0
442 | destroy: 1.2.0
443 | http-errors: 2.0.0
444 | iconv-lite: 0.4.24
445 | on-finished: 2.4.1
446 | qs: 6.13.0
447 | raw-body: 2.5.2
448 | type-is: 1.6.18
449 | unpipe: 1.0.0
450 | transitivePeerDependencies:
451 | - supports-color
452 |
453 | buffer@5.7.1:
454 | dependencies:
455 | base64-js: 1.5.1
456 | ieee754: 1.2.1
457 |
458 | bytes@3.1.2: {}
459 |
460 | call-bind@1.0.7:
461 | dependencies:
462 | es-define-property: 1.0.0
463 | es-errors: 1.3.0
464 | function-bind: 1.1.2
465 | get-intrinsic: 1.2.4
466 | set-function-length: 1.2.2
467 |
468 | chalk@4.1.2:
469 | dependencies:
470 | ansi-styles: 4.3.0
471 | supports-color: 7.2.0
472 |
473 | char-spinner@1.0.1: {}
474 |
475 | cli-table3@0.6.5:
476 | dependencies:
477 | string-width: 4.2.3
478 | optionalDependencies:
479 | '@colors/colors': 1.5.0
480 |
481 | color-convert@2.0.1:
482 | dependencies:
483 | color-name: 1.1.4
484 |
485 | color-name@1.1.4: {}
486 |
487 | color-support@1.1.3: {}
488 |
489 | combined-stream@1.0.8:
490 | dependencies:
491 | delayed-stream: 1.0.0
492 |
493 | content-type@1.0.5: {}
494 |
495 | cross-argv@2.0.0: {}
496 |
497 | debug@2.6.9:
498 | dependencies:
499 | ms: 2.0.0
500 |
501 | define-data-property@1.1.4:
502 | dependencies:
503 | es-define-property: 1.0.0
504 | es-errors: 1.3.0
505 | gopd: 1.0.1
506 |
507 | delayed-stream@1.0.0: {}
508 |
509 | depd@2.0.0: {}
510 |
511 | destroy@1.2.0: {}
512 |
513 | dezalgo@1.0.4:
514 | dependencies:
515 | asap: 2.0.6
516 | wrappy: 1.0.2
517 |
518 | ee-first@1.1.1: {}
519 |
520 | emoji-regex@8.0.0: {}
521 |
522 | es-define-property@1.0.0:
523 | dependencies:
524 | get-intrinsic: 1.2.4
525 |
526 | es-errors@1.3.0: {}
527 |
528 | form-data@4.0.0:
529 | dependencies:
530 | asynckit: 0.4.0
531 | combined-stream: 1.0.8
532 | mime-types: 2.1.35
533 |
534 | formidable@3.5.2:
535 | dependencies:
536 | dezalgo: 1.0.4
537 | hexoid: 2.0.0
538 | once: 1.4.0
539 |
540 | function-bind@1.1.2: {}
541 |
542 | get-intrinsic@1.2.4:
543 | dependencies:
544 | es-errors: 1.3.0
545 | function-bind: 1.1.2
546 | has-proto: 1.0.3
547 | has-symbols: 1.0.3
548 | hasown: 2.0.2
549 |
550 | gopd@1.0.1:
551 | dependencies:
552 | get-intrinsic: 1.2.4
553 |
554 | has-async-hooks@1.0.0: {}
555 |
556 | has-flag@4.0.0: {}
557 |
558 | has-property-descriptors@1.0.2:
559 | dependencies:
560 | es-define-property: 1.0.0
561 |
562 | has-proto@1.0.3: {}
563 |
564 | has-symbols@1.0.3: {}
565 |
566 | hasown@2.0.2:
567 | dependencies:
568 | function-bind: 1.1.2
569 |
570 | hdr-histogram-js@3.0.0:
571 | dependencies:
572 | '@assemblyscript/loader': 0.19.23
573 | base64-js: 1.5.1
574 | pako: 1.0.11
575 |
576 | hdr-histogram-percentiles-obj@3.0.0: {}
577 |
578 | hexoid@2.0.0: {}
579 |
580 | http-errors@2.0.0:
581 | dependencies:
582 | depd: 2.0.0
583 | inherits: 2.0.4
584 | setprototypeof: 1.2.0
585 | statuses: 2.0.1
586 | toidentifier: 1.0.1
587 |
588 | http-parser-js@0.5.8: {}
589 |
590 | hyperid@3.2.0:
591 | dependencies:
592 | buffer: 5.7.1
593 | uuid: 8.3.2
594 | uuid-parse: 1.1.0
595 |
596 | iconv-lite@0.4.24:
597 | dependencies:
598 | safer-buffer: 2.1.2
599 |
600 | ieee754@1.2.1: {}
601 |
602 | inherits@2.0.4: {}
603 |
604 | is-fullwidth-code-point@3.0.0: {}
605 |
606 | lodash.chunk@4.2.0: {}
607 |
608 | lodash.clonedeep@4.5.0: {}
609 |
610 | lodash.flatten@4.4.0: {}
611 |
612 | manage-path@2.0.0: {}
613 |
614 | media-typer@0.3.0: {}
615 |
616 | mime-db@1.52.0: {}
617 |
618 | mime-types@2.1.35:
619 | dependencies:
620 | mime-db: 1.52.0
621 |
622 | minimist@1.2.8: {}
623 |
624 | ms@2.0.0: {}
625 |
626 | object-inspect@1.13.2: {}
627 |
628 | on-finished@2.4.1:
629 | dependencies:
630 | ee-first: 1.1.1
631 |
632 | on-net-listen@1.1.2: {}
633 |
634 | once@1.4.0:
635 | dependencies:
636 | wrappy: 1.0.2
637 |
638 | pako@1.0.11: {}
639 |
640 | pretty-bytes@5.6.0: {}
641 |
642 | progress@2.0.3: {}
643 |
644 | qs@6.13.0:
645 | dependencies:
646 | side-channel: 1.0.6
647 |
648 | raw-body@2.5.2:
649 | dependencies:
650 | bytes: 3.1.2
651 | http-errors: 2.0.0
652 | iconv-lite: 0.4.24
653 | unpipe: 1.0.0
654 |
655 | reinterval@1.1.0: {}
656 |
657 | retimer@3.0.0: {}
658 |
659 | safer-buffer@2.1.2: {}
660 |
661 | semver@7.6.2: {}
662 |
663 | set-function-length@1.2.2:
664 | dependencies:
665 | define-data-property: 1.1.4
666 | es-errors: 1.3.0
667 | function-bind: 1.1.2
668 | get-intrinsic: 1.2.4
669 | gopd: 1.0.1
670 | has-property-descriptors: 1.0.2
671 |
672 | setprototypeof@1.2.0: {}
673 |
674 | side-channel@1.0.6:
675 | dependencies:
676 | call-bind: 1.0.7
677 | es-errors: 1.3.0
678 | get-intrinsic: 1.2.4
679 | object-inspect: 1.13.2
680 |
681 | statuses@2.0.1: {}
682 |
683 | string-width@4.2.3:
684 | dependencies:
685 | emoji-regex: 8.0.0
686 | is-fullwidth-code-point: 3.0.0
687 | strip-ansi: 6.0.1
688 |
689 | strip-ansi@6.0.1:
690 | dependencies:
691 | ansi-regex: 5.0.1
692 |
693 | supports-color@7.2.0:
694 | dependencies:
695 | has-flag: 4.0.0
696 |
697 | timestring@6.0.0: {}
698 |
699 | toidentifier@1.0.1: {}
700 |
701 | type-is@1.6.18:
702 | dependencies:
703 | media-typer: 0.3.0
704 | mime-types: 2.1.35
705 |
706 | undici-types@5.26.5: {}
707 |
708 | unpipe@1.0.0: {}
709 |
710 | uuid-parse@1.1.0: {}
711 |
712 | uuid@8.3.2: {}
713 |
714 | wrappy@1.0.2: {}
715 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
3 | "files": {
4 | "ignore": ["node_modules", "dist", "coverage", ".pnpm-store", "package.json"]
5 | },
6 | "formatter": {
7 | "enabled": true,
8 | "formatWithErrors": false,
9 | "indentStyle": "space",
10 | "indentWidth": 2,
11 | "lineEnding": "lf",
12 | "lineWidth": 120,
13 | "attributePosition": "auto"
14 | },
15 | "organizeImports": { "enabled": true },
16 | "linter": {
17 | "enabled": true,
18 | "rules": {
19 | "recommended": true,
20 | "correctness": {
21 | "noVoidTypeReturn": "off"
22 | },
23 | "style": {
24 | "noParameterAssign": "off",
25 | "noNonNullAssertion": "off"
26 | },
27 | "suspicious": {
28 | "noAssignInExpressions": "off",
29 | "noExplicitAny": "off"
30 | }
31 | }
32 | },
33 | "javascript": {
34 | "formatter": {
35 | "jsxQuoteStyle": "double",
36 | "quoteProperties": "asNeeded",
37 | "trailingCommas": "none",
38 | "semicolons": "asNeeded",
39 | "arrowParentheses": "always",
40 | "bracketSpacing": true,
41 | "bracketSameLine": false,
42 | "quoteStyle": "single",
43 | "attributePosition": "auto"
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinyhttp/milliparsec/61f919d46556852e8a515de08c8b46c917684a03/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "milliparsec",
3 | "version": "5.0.2",
4 | "description": "tiniest body parser in the universe",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tinyhttp/milliparsec"
8 | },
9 | "author": "talentlessguy ",
10 | "license": "MIT",
11 | "types": "./dist/index.d.ts",
12 | "type": "module",
13 | "keywords": [
14 | "body-parser",
15 | "express",
16 | "http",
17 | "body-parsing"
18 | ],
19 | "engines": {
20 | "node": ">=18.13 || >=19.20 || >=20"
21 | },
22 | "exports": "./dist/index.js",
23 | "devDependencies": {
24 | "@biomejs/biome": "1.9.3",
25 | "@tinyhttp/app": "^2.4.0",
26 | "@types/node": "^18.19.76",
27 | "c8": "10.1.2",
28 | "supertest-fetch": "^2.0.0",
29 | "tsx": "^4.19.1",
30 | "typescript": "^5.6.2"
31 | },
32 | "files": [
33 | "dist"
34 | ],
35 | "scripts": {
36 | "test": "tsx --test test.ts",
37 | "test:coverage": "c8 --include=src pnpm test",
38 | "test:report": "c8 report --reporter=text-lcov > coverage.lcov",
39 | "build": "tsc -p tsconfig.build.json",
40 | "prepublishOnly": "pnpm build && pnpm test",
41 | "check": "biome check --write"
42 | },
43 | "packageManager": "pnpm@9.4.0",
44 | "publishConfig": {
45 | "provenance": true
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | devDependencies:
11 | '@biomejs/biome':
12 | specifier: 1.9.3
13 | version: 1.9.3
14 | '@tinyhttp/app':
15 | specifier: ^2.4.0
16 | version: 2.4.0
17 | '@types/node':
18 | specifier: ^18.19.76
19 | version: 18.19.76
20 | c8:
21 | specifier: 10.1.2
22 | version: 10.1.2
23 | supertest-fetch:
24 | specifier: ^2.0.0
25 | version: 2.0.0
26 | tsx:
27 | specifier: ^4.19.1
28 | version: 4.19.1
29 | typescript:
30 | specifier: ^5.6.2
31 | version: 5.6.2
32 |
33 | packages:
34 |
35 | '@bcoe/v8-coverage@0.2.3':
36 | resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
37 |
38 | '@biomejs/biome@1.9.3':
39 | resolution: {integrity: sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==}
40 | engines: {node: '>=14.21.3'}
41 | hasBin: true
42 |
43 | '@biomejs/cli-darwin-arm64@1.9.3':
44 | resolution: {integrity: sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==}
45 | engines: {node: '>=14.21.3'}
46 | cpu: [arm64]
47 | os: [darwin]
48 |
49 | '@biomejs/cli-darwin-x64@1.9.3':
50 | resolution: {integrity: sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==}
51 | engines: {node: '>=14.21.3'}
52 | cpu: [x64]
53 | os: [darwin]
54 |
55 | '@biomejs/cli-linux-arm64-musl@1.9.3':
56 | resolution: {integrity: sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==}
57 | engines: {node: '>=14.21.3'}
58 | cpu: [arm64]
59 | os: [linux]
60 |
61 | '@biomejs/cli-linux-arm64@1.9.3':
62 | resolution: {integrity: sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==}
63 | engines: {node: '>=14.21.3'}
64 | cpu: [arm64]
65 | os: [linux]
66 |
67 | '@biomejs/cli-linux-x64-musl@1.9.3':
68 | resolution: {integrity: sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==}
69 | engines: {node: '>=14.21.3'}
70 | cpu: [x64]
71 | os: [linux]
72 |
73 | '@biomejs/cli-linux-x64@1.9.3':
74 | resolution: {integrity: sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==}
75 | engines: {node: '>=14.21.3'}
76 | cpu: [x64]
77 | os: [linux]
78 |
79 | '@biomejs/cli-win32-arm64@1.9.3':
80 | resolution: {integrity: sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==}
81 | engines: {node: '>=14.21.3'}
82 | cpu: [arm64]
83 | os: [win32]
84 |
85 | '@biomejs/cli-win32-x64@1.9.3':
86 | resolution: {integrity: sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==}
87 | engines: {node: '>=14.21.3'}
88 | cpu: [x64]
89 | os: [win32]
90 |
91 | '@esbuild/aix-ppc64@0.23.1':
92 | resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==}
93 | engines: {node: '>=18'}
94 | cpu: [ppc64]
95 | os: [aix]
96 |
97 | '@esbuild/android-arm64@0.23.1':
98 | resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==}
99 | engines: {node: '>=18'}
100 | cpu: [arm64]
101 | os: [android]
102 |
103 | '@esbuild/android-arm@0.23.1':
104 | resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==}
105 | engines: {node: '>=18'}
106 | cpu: [arm]
107 | os: [android]
108 |
109 | '@esbuild/android-x64@0.23.1':
110 | resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==}
111 | engines: {node: '>=18'}
112 | cpu: [x64]
113 | os: [android]
114 |
115 | '@esbuild/darwin-arm64@0.23.1':
116 | resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==}
117 | engines: {node: '>=18'}
118 | cpu: [arm64]
119 | os: [darwin]
120 |
121 | '@esbuild/darwin-x64@0.23.1':
122 | resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==}
123 | engines: {node: '>=18'}
124 | cpu: [x64]
125 | os: [darwin]
126 |
127 | '@esbuild/freebsd-arm64@0.23.1':
128 | resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==}
129 | engines: {node: '>=18'}
130 | cpu: [arm64]
131 | os: [freebsd]
132 |
133 | '@esbuild/freebsd-x64@0.23.1':
134 | resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==}
135 | engines: {node: '>=18'}
136 | cpu: [x64]
137 | os: [freebsd]
138 |
139 | '@esbuild/linux-arm64@0.23.1':
140 | resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==}
141 | engines: {node: '>=18'}
142 | cpu: [arm64]
143 | os: [linux]
144 |
145 | '@esbuild/linux-arm@0.23.1':
146 | resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==}
147 | engines: {node: '>=18'}
148 | cpu: [arm]
149 | os: [linux]
150 |
151 | '@esbuild/linux-ia32@0.23.1':
152 | resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==}
153 | engines: {node: '>=18'}
154 | cpu: [ia32]
155 | os: [linux]
156 |
157 | '@esbuild/linux-loong64@0.23.1':
158 | resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==}
159 | engines: {node: '>=18'}
160 | cpu: [loong64]
161 | os: [linux]
162 |
163 | '@esbuild/linux-mips64el@0.23.1':
164 | resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==}
165 | engines: {node: '>=18'}
166 | cpu: [mips64el]
167 | os: [linux]
168 |
169 | '@esbuild/linux-ppc64@0.23.1':
170 | resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==}
171 | engines: {node: '>=18'}
172 | cpu: [ppc64]
173 | os: [linux]
174 |
175 | '@esbuild/linux-riscv64@0.23.1':
176 | resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==}
177 | engines: {node: '>=18'}
178 | cpu: [riscv64]
179 | os: [linux]
180 |
181 | '@esbuild/linux-s390x@0.23.1':
182 | resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==}
183 | engines: {node: '>=18'}
184 | cpu: [s390x]
185 | os: [linux]
186 |
187 | '@esbuild/linux-x64@0.23.1':
188 | resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==}
189 | engines: {node: '>=18'}
190 | cpu: [x64]
191 | os: [linux]
192 |
193 | '@esbuild/netbsd-x64@0.23.1':
194 | resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==}
195 | engines: {node: '>=18'}
196 | cpu: [x64]
197 | os: [netbsd]
198 |
199 | '@esbuild/openbsd-arm64@0.23.1':
200 | resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==}
201 | engines: {node: '>=18'}
202 | cpu: [arm64]
203 | os: [openbsd]
204 |
205 | '@esbuild/openbsd-x64@0.23.1':
206 | resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==}
207 | engines: {node: '>=18'}
208 | cpu: [x64]
209 | os: [openbsd]
210 |
211 | '@esbuild/sunos-x64@0.23.1':
212 | resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==}
213 | engines: {node: '>=18'}
214 | cpu: [x64]
215 | os: [sunos]
216 |
217 | '@esbuild/win32-arm64@0.23.1':
218 | resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==}
219 | engines: {node: '>=18'}
220 | cpu: [arm64]
221 | os: [win32]
222 |
223 | '@esbuild/win32-ia32@0.23.1':
224 | resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==}
225 | engines: {node: '>=18'}
226 | cpu: [ia32]
227 | os: [win32]
228 |
229 | '@esbuild/win32-x64@0.23.1':
230 | resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==}
231 | engines: {node: '>=18'}
232 | cpu: [x64]
233 | os: [win32]
234 |
235 | '@isaacs/cliui@8.0.2':
236 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
237 | engines: {node: '>=12'}
238 |
239 | '@istanbuljs/schema@0.1.3':
240 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
241 | engines: {node: '>=8'}
242 |
243 | '@jridgewell/resolve-uri@3.1.2':
244 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
245 | engines: {node: '>=6.0.0'}
246 |
247 | '@jridgewell/sourcemap-codec@1.5.0':
248 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
249 |
250 | '@jridgewell/trace-mapping@0.3.25':
251 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
252 |
253 | '@pkgjs/parseargs@0.11.0':
254 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
255 | engines: {node: '>=14'}
256 |
257 | '@tinyhttp/accepts@2.2.3':
258 | resolution: {integrity: sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==}
259 | engines: {node: '>=12.20.0'}
260 |
261 | '@tinyhttp/app@2.4.0':
262 | resolution: {integrity: sha512-vOPiCemQRJq5twnl06dde6XnWiNbVMdVRFJWW/yC/9G0qgvV2TvzNNTxrdlz6YmyB7vIC7Fg3qS6m6gx8RbBNQ==}
263 | engines: {node: '>=14.21.3'}
264 |
265 | '@tinyhttp/content-disposition@2.2.2':
266 | resolution: {integrity: sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==}
267 | engines: {node: '>=12.20.0'}
268 |
269 | '@tinyhttp/content-type@0.1.4':
270 | resolution: {integrity: sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==}
271 | engines: {node: '>=12.4'}
272 |
273 | '@tinyhttp/cookie-signature@2.1.1':
274 | resolution: {integrity: sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==}
275 | engines: {node: '>=12.20.0'}
276 |
277 | '@tinyhttp/cookie@2.1.1':
278 | resolution: {integrity: sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==}
279 | engines: {node: '>=12.20.0'}
280 |
281 | '@tinyhttp/encode-url@2.1.1':
282 | resolution: {integrity: sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==}
283 | engines: {node: '>=12.20.0'}
284 |
285 | '@tinyhttp/etag@2.1.2':
286 | resolution: {integrity: sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==}
287 | engines: {node: '>=12.20.0'}
288 |
289 | '@tinyhttp/forwarded@2.1.1':
290 | resolution: {integrity: sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ==}
291 | engines: {node: '>=12.20.0'}
292 |
293 | '@tinyhttp/proxy-addr@2.2.0':
294 | resolution: {integrity: sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA==}
295 | engines: {node: '>=12.20.0'}
296 |
297 | '@tinyhttp/req@2.2.4':
298 | resolution: {integrity: sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ==}
299 | engines: {node: '>=12.20.0'}
300 |
301 | '@tinyhttp/res@2.2.4':
302 | resolution: {integrity: sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA==}
303 | engines: {node: '>=12.20.0'}
304 |
305 | '@tinyhttp/router@2.2.3':
306 | resolution: {integrity: sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==}
307 | engines: {node: '>=12.20.0'}
308 |
309 | '@tinyhttp/send@2.2.3':
310 | resolution: {integrity: sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==}
311 | engines: {node: '>=12.20.0'}
312 |
313 | '@tinyhttp/type-is@2.2.4':
314 | resolution: {integrity: sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==}
315 | engines: {node: '>=12.20.0'}
316 |
317 | '@tinyhttp/url@2.1.1':
318 | resolution: {integrity: sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==}
319 | engines: {node: '>=12.20.0'}
320 |
321 | '@tinyhttp/vary@0.1.3':
322 | resolution: {integrity: sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==}
323 | engines: {node: '>=12.20'}
324 |
325 | '@types/istanbul-lib-coverage@2.0.6':
326 | resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
327 |
328 | '@types/node@18.19.76':
329 | resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==}
330 |
331 | ansi-regex@5.0.1:
332 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
333 | engines: {node: '>=8'}
334 |
335 | ansi-regex@6.1.0:
336 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
337 | engines: {node: '>=12'}
338 |
339 | ansi-styles@4.3.0:
340 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
341 | engines: {node: '>=8'}
342 |
343 | ansi-styles@6.2.1:
344 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
345 | engines: {node: '>=12'}
346 |
347 | balanced-match@1.0.2:
348 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
349 |
350 | brace-expansion@2.0.1:
351 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
352 |
353 | c8@10.1.2:
354 | resolution: {integrity: sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==}
355 | engines: {node: '>=18'}
356 | hasBin: true
357 | peerDependencies:
358 | monocart-coverage-reports: ^2
359 | peerDependenciesMeta:
360 | monocart-coverage-reports:
361 | optional: true
362 |
363 | cliui@8.0.1:
364 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
365 | engines: {node: '>=12'}
366 |
367 | color-convert@2.0.1:
368 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
369 | engines: {node: '>=7.0.0'}
370 |
371 | color-name@1.1.4:
372 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
373 |
374 | convert-source-map@2.0.0:
375 | resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
376 |
377 | cross-spawn@7.0.6:
378 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
379 | engines: {node: '>= 8'}
380 |
381 | eastasianwidth@0.2.0:
382 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
383 |
384 | emoji-regex@8.0.0:
385 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
386 |
387 | emoji-regex@9.2.2:
388 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
389 |
390 | es-escape-html@0.1.1:
391 | resolution: {integrity: sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==}
392 | engines: {node: '>=12.x'}
393 |
394 | esbuild@0.23.1:
395 | resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==}
396 | engines: {node: '>=18'}
397 | hasBin: true
398 |
399 | escalade@3.2.0:
400 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
401 | engines: {node: '>=6'}
402 |
403 | find-up@5.0.0:
404 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
405 | engines: {node: '>=10'}
406 |
407 | foreground-child@3.3.0:
408 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
409 | engines: {node: '>=14'}
410 |
411 | fsevents@2.3.3:
412 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
413 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
414 | os: [darwin]
415 |
416 | get-caller-file@2.0.5:
417 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
418 | engines: {node: 6.* || 8.* || >= 10.*}
419 |
420 | get-tsconfig@4.8.1:
421 | resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==}
422 |
423 | glob@10.4.5:
424 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
425 | hasBin: true
426 |
427 | has-flag@4.0.0:
428 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
429 | engines: {node: '>=8'}
430 |
431 | header-range-parser@1.1.3:
432 | resolution: {integrity: sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==}
433 | engines: {node: '>=12.22.0'}
434 |
435 | html-escaper@2.0.2:
436 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
437 |
438 | ipaddr.js@2.2.0:
439 | resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
440 | engines: {node: '>= 10'}
441 |
442 | is-fullwidth-code-point@3.0.0:
443 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
444 | engines: {node: '>=8'}
445 |
446 | isexe@2.0.0:
447 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
448 |
449 | istanbul-lib-coverage@3.2.2:
450 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
451 | engines: {node: '>=8'}
452 |
453 | istanbul-lib-report@3.0.1:
454 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
455 | engines: {node: '>=10'}
456 |
457 | istanbul-reports@3.1.7:
458 | resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
459 | engines: {node: '>=8'}
460 |
461 | jackspeak@3.4.3:
462 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
463 |
464 | locate-path@6.0.0:
465 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
466 | engines: {node: '>=10'}
467 |
468 | lru-cache@10.4.3:
469 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
470 |
471 | make-dir@4.0.0:
472 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
473 | engines: {node: '>=10'}
474 |
475 | mime@4.0.4:
476 | resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==}
477 | engines: {node: '>=16'}
478 | hasBin: true
479 |
480 | minimatch@9.0.5:
481 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
482 | engines: {node: '>=16 || 14 >=14.17'}
483 |
484 | minipass@7.1.2:
485 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
486 | engines: {node: '>=16 || 14 >=14.17'}
487 |
488 | negotiator@0.6.4:
489 | resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
490 | engines: {node: '>= 0.6'}
491 |
492 | p-limit@3.1.0:
493 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
494 | engines: {node: '>=10'}
495 |
496 | p-locate@5.0.0:
497 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
498 | engines: {node: '>=10'}
499 |
500 | package-json-from-dist@1.0.1:
501 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
502 |
503 | path-exists@4.0.0:
504 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
505 | engines: {node: '>=8'}
506 |
507 | path-key@3.1.1:
508 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
509 | engines: {node: '>=8'}
510 |
511 | path-scurry@1.11.1:
512 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
513 | engines: {node: '>=16 || 14 >=14.18'}
514 |
515 | regexparam@2.0.2:
516 | resolution: {integrity: sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==}
517 | engines: {node: '>=8'}
518 |
519 | require-directory@2.1.1:
520 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
521 | engines: {node: '>=0.10.0'}
522 |
523 | resolve-pkg-maps@1.0.0:
524 | resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
525 |
526 | semver@7.6.3:
527 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
528 | engines: {node: '>=10'}
529 | hasBin: true
530 |
531 | shebang-command@2.0.0:
532 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
533 | engines: {node: '>=8'}
534 |
535 | shebang-regex@3.0.0:
536 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
537 | engines: {node: '>=8'}
538 |
539 | signal-exit@4.1.0:
540 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
541 | engines: {node: '>=14'}
542 |
543 | string-width@4.2.3:
544 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
545 | engines: {node: '>=8'}
546 |
547 | string-width@5.1.2:
548 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
549 | engines: {node: '>=12'}
550 |
551 | strip-ansi@6.0.1:
552 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
553 | engines: {node: '>=8'}
554 |
555 | strip-ansi@7.1.0:
556 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
557 | engines: {node: '>=12'}
558 |
559 | supertest-fetch@2.0.0:
560 | resolution: {integrity: sha512-Mx2ZszLJkrBMFt7fmyML12y+u2yHAN5FF94yOzZXHzrTtsS59fjdbqh9G4OAPDM14jnkDkcLk2AgCVGJZcVoUg==}
561 | engines: {node: '>=18.0.0'}
562 |
563 | supports-color@7.2.0:
564 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
565 | engines: {node: '>=8'}
566 |
567 | test-exclude@7.0.1:
568 | resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
569 | engines: {node: '>=18'}
570 |
571 | tsx@4.19.1:
572 | resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==}
573 | engines: {node: '>=18.0.0'}
574 | hasBin: true
575 |
576 | typescript@5.6.2:
577 | resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
578 | engines: {node: '>=14.17'}
579 | hasBin: true
580 |
581 | undici-types@5.26.5:
582 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
583 |
584 | v8-to-istanbul@9.3.0:
585 | resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
586 | engines: {node: '>=10.12.0'}
587 |
588 | which@2.0.2:
589 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
590 | engines: {node: '>= 8'}
591 | hasBin: true
592 |
593 | wrap-ansi@7.0.0:
594 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
595 | engines: {node: '>=10'}
596 |
597 | wrap-ansi@8.1.0:
598 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
599 | engines: {node: '>=12'}
600 |
601 | y18n@5.0.8:
602 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
603 | engines: {node: '>=10'}
604 |
605 | yargs-parser@21.1.1:
606 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
607 | engines: {node: '>=12'}
608 |
609 | yargs@17.7.2:
610 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
611 | engines: {node: '>=12'}
612 |
613 | yocto-queue@0.1.0:
614 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
615 | engines: {node: '>=10'}
616 |
617 | snapshots:
618 |
619 | '@bcoe/v8-coverage@0.2.3': {}
620 |
621 | '@biomejs/biome@1.9.3':
622 | optionalDependencies:
623 | '@biomejs/cli-darwin-arm64': 1.9.3
624 | '@biomejs/cli-darwin-x64': 1.9.3
625 | '@biomejs/cli-linux-arm64': 1.9.3
626 | '@biomejs/cli-linux-arm64-musl': 1.9.3
627 | '@biomejs/cli-linux-x64': 1.9.3
628 | '@biomejs/cli-linux-x64-musl': 1.9.3
629 | '@biomejs/cli-win32-arm64': 1.9.3
630 | '@biomejs/cli-win32-x64': 1.9.3
631 |
632 | '@biomejs/cli-darwin-arm64@1.9.3':
633 | optional: true
634 |
635 | '@biomejs/cli-darwin-x64@1.9.3':
636 | optional: true
637 |
638 | '@biomejs/cli-linux-arm64-musl@1.9.3':
639 | optional: true
640 |
641 | '@biomejs/cli-linux-arm64@1.9.3':
642 | optional: true
643 |
644 | '@biomejs/cli-linux-x64-musl@1.9.3':
645 | optional: true
646 |
647 | '@biomejs/cli-linux-x64@1.9.3':
648 | optional: true
649 |
650 | '@biomejs/cli-win32-arm64@1.9.3':
651 | optional: true
652 |
653 | '@biomejs/cli-win32-x64@1.9.3':
654 | optional: true
655 |
656 | '@esbuild/aix-ppc64@0.23.1':
657 | optional: true
658 |
659 | '@esbuild/android-arm64@0.23.1':
660 | optional: true
661 |
662 | '@esbuild/android-arm@0.23.1':
663 | optional: true
664 |
665 | '@esbuild/android-x64@0.23.1':
666 | optional: true
667 |
668 | '@esbuild/darwin-arm64@0.23.1':
669 | optional: true
670 |
671 | '@esbuild/darwin-x64@0.23.1':
672 | optional: true
673 |
674 | '@esbuild/freebsd-arm64@0.23.1':
675 | optional: true
676 |
677 | '@esbuild/freebsd-x64@0.23.1':
678 | optional: true
679 |
680 | '@esbuild/linux-arm64@0.23.1':
681 | optional: true
682 |
683 | '@esbuild/linux-arm@0.23.1':
684 | optional: true
685 |
686 | '@esbuild/linux-ia32@0.23.1':
687 | optional: true
688 |
689 | '@esbuild/linux-loong64@0.23.1':
690 | optional: true
691 |
692 | '@esbuild/linux-mips64el@0.23.1':
693 | optional: true
694 |
695 | '@esbuild/linux-ppc64@0.23.1':
696 | optional: true
697 |
698 | '@esbuild/linux-riscv64@0.23.1':
699 | optional: true
700 |
701 | '@esbuild/linux-s390x@0.23.1':
702 | optional: true
703 |
704 | '@esbuild/linux-x64@0.23.1':
705 | optional: true
706 |
707 | '@esbuild/netbsd-x64@0.23.1':
708 | optional: true
709 |
710 | '@esbuild/openbsd-arm64@0.23.1':
711 | optional: true
712 |
713 | '@esbuild/openbsd-x64@0.23.1':
714 | optional: true
715 |
716 | '@esbuild/sunos-x64@0.23.1':
717 | optional: true
718 |
719 | '@esbuild/win32-arm64@0.23.1':
720 | optional: true
721 |
722 | '@esbuild/win32-ia32@0.23.1':
723 | optional: true
724 |
725 | '@esbuild/win32-x64@0.23.1':
726 | optional: true
727 |
728 | '@isaacs/cliui@8.0.2':
729 | dependencies:
730 | string-width: 5.1.2
731 | string-width-cjs: string-width@4.2.3
732 | strip-ansi: 7.1.0
733 | strip-ansi-cjs: strip-ansi@6.0.1
734 | wrap-ansi: 8.1.0
735 | wrap-ansi-cjs: wrap-ansi@7.0.0
736 |
737 | '@istanbuljs/schema@0.1.3': {}
738 |
739 | '@jridgewell/resolve-uri@3.1.2': {}
740 |
741 | '@jridgewell/sourcemap-codec@1.5.0': {}
742 |
743 | '@jridgewell/trace-mapping@0.3.25':
744 | dependencies:
745 | '@jridgewell/resolve-uri': 3.1.2
746 | '@jridgewell/sourcemap-codec': 1.5.0
747 |
748 | '@pkgjs/parseargs@0.11.0':
749 | optional: true
750 |
751 | '@tinyhttp/accepts@2.2.3':
752 | dependencies:
753 | mime: 4.0.4
754 | negotiator: 0.6.4
755 |
756 | '@tinyhttp/app@2.4.0':
757 | dependencies:
758 | '@tinyhttp/cookie': 2.1.1
759 | '@tinyhttp/proxy-addr': 2.2.0
760 | '@tinyhttp/req': 2.2.4
761 | '@tinyhttp/res': 2.2.4
762 | '@tinyhttp/router': 2.2.3
763 | header-range-parser: 1.1.3
764 | regexparam: 2.0.2
765 |
766 | '@tinyhttp/content-disposition@2.2.2': {}
767 |
768 | '@tinyhttp/content-type@0.1.4': {}
769 |
770 | '@tinyhttp/cookie-signature@2.1.1': {}
771 |
772 | '@tinyhttp/cookie@2.1.1': {}
773 |
774 | '@tinyhttp/encode-url@2.1.1': {}
775 |
776 | '@tinyhttp/etag@2.1.2': {}
777 |
778 | '@tinyhttp/forwarded@2.1.1': {}
779 |
780 | '@tinyhttp/proxy-addr@2.2.0':
781 | dependencies:
782 | '@tinyhttp/forwarded': 2.1.1
783 | ipaddr.js: 2.2.0
784 |
785 | '@tinyhttp/req@2.2.4':
786 | dependencies:
787 | '@tinyhttp/accepts': 2.2.3
788 | '@tinyhttp/type-is': 2.2.4
789 | '@tinyhttp/url': 2.1.1
790 | header-range-parser: 1.1.3
791 |
792 | '@tinyhttp/res@2.2.4':
793 | dependencies:
794 | '@tinyhttp/content-disposition': 2.2.2
795 | '@tinyhttp/cookie': 2.1.1
796 | '@tinyhttp/cookie-signature': 2.1.1
797 | '@tinyhttp/encode-url': 2.1.1
798 | '@tinyhttp/req': 2.2.4
799 | '@tinyhttp/send': 2.2.3
800 | '@tinyhttp/vary': 0.1.3
801 | es-escape-html: 0.1.1
802 | mime: 4.0.4
803 |
804 | '@tinyhttp/router@2.2.3': {}
805 |
806 | '@tinyhttp/send@2.2.3':
807 | dependencies:
808 | '@tinyhttp/content-type': 0.1.4
809 | '@tinyhttp/etag': 2.1.2
810 | mime: 4.0.4
811 |
812 | '@tinyhttp/type-is@2.2.4':
813 | dependencies:
814 | '@tinyhttp/content-type': 0.1.4
815 | mime: 4.0.4
816 |
817 | '@tinyhttp/url@2.1.1': {}
818 |
819 | '@tinyhttp/vary@0.1.3': {}
820 |
821 | '@types/istanbul-lib-coverage@2.0.6': {}
822 |
823 | '@types/node@18.19.76':
824 | dependencies:
825 | undici-types: 5.26.5
826 |
827 | ansi-regex@5.0.1: {}
828 |
829 | ansi-regex@6.1.0: {}
830 |
831 | ansi-styles@4.3.0:
832 | dependencies:
833 | color-convert: 2.0.1
834 |
835 | ansi-styles@6.2.1: {}
836 |
837 | balanced-match@1.0.2: {}
838 |
839 | brace-expansion@2.0.1:
840 | dependencies:
841 | balanced-match: 1.0.2
842 |
843 | c8@10.1.2:
844 | dependencies:
845 | '@bcoe/v8-coverage': 0.2.3
846 | '@istanbuljs/schema': 0.1.3
847 | find-up: 5.0.0
848 | foreground-child: 3.3.0
849 | istanbul-lib-coverage: 3.2.2
850 | istanbul-lib-report: 3.0.1
851 | istanbul-reports: 3.1.7
852 | test-exclude: 7.0.1
853 | v8-to-istanbul: 9.3.0
854 | yargs: 17.7.2
855 | yargs-parser: 21.1.1
856 |
857 | cliui@8.0.1:
858 | dependencies:
859 | string-width: 4.2.3
860 | strip-ansi: 6.0.1
861 | wrap-ansi: 7.0.0
862 |
863 | color-convert@2.0.1:
864 | dependencies:
865 | color-name: 1.1.4
866 |
867 | color-name@1.1.4: {}
868 |
869 | convert-source-map@2.0.0: {}
870 |
871 | cross-spawn@7.0.6:
872 | dependencies:
873 | path-key: 3.1.1
874 | shebang-command: 2.0.0
875 | which: 2.0.2
876 |
877 | eastasianwidth@0.2.0: {}
878 |
879 | emoji-regex@8.0.0: {}
880 |
881 | emoji-regex@9.2.2: {}
882 |
883 | es-escape-html@0.1.1: {}
884 |
885 | esbuild@0.23.1:
886 | optionalDependencies:
887 | '@esbuild/aix-ppc64': 0.23.1
888 | '@esbuild/android-arm': 0.23.1
889 | '@esbuild/android-arm64': 0.23.1
890 | '@esbuild/android-x64': 0.23.1
891 | '@esbuild/darwin-arm64': 0.23.1
892 | '@esbuild/darwin-x64': 0.23.1
893 | '@esbuild/freebsd-arm64': 0.23.1
894 | '@esbuild/freebsd-x64': 0.23.1
895 | '@esbuild/linux-arm': 0.23.1
896 | '@esbuild/linux-arm64': 0.23.1
897 | '@esbuild/linux-ia32': 0.23.1
898 | '@esbuild/linux-loong64': 0.23.1
899 | '@esbuild/linux-mips64el': 0.23.1
900 | '@esbuild/linux-ppc64': 0.23.1
901 | '@esbuild/linux-riscv64': 0.23.1
902 | '@esbuild/linux-s390x': 0.23.1
903 | '@esbuild/linux-x64': 0.23.1
904 | '@esbuild/netbsd-x64': 0.23.1
905 | '@esbuild/openbsd-arm64': 0.23.1
906 | '@esbuild/openbsd-x64': 0.23.1
907 | '@esbuild/sunos-x64': 0.23.1
908 | '@esbuild/win32-arm64': 0.23.1
909 | '@esbuild/win32-ia32': 0.23.1
910 | '@esbuild/win32-x64': 0.23.1
911 |
912 | escalade@3.2.0: {}
913 |
914 | find-up@5.0.0:
915 | dependencies:
916 | locate-path: 6.0.0
917 | path-exists: 4.0.0
918 |
919 | foreground-child@3.3.0:
920 | dependencies:
921 | cross-spawn: 7.0.6
922 | signal-exit: 4.1.0
923 |
924 | fsevents@2.3.3:
925 | optional: true
926 |
927 | get-caller-file@2.0.5: {}
928 |
929 | get-tsconfig@4.8.1:
930 | dependencies:
931 | resolve-pkg-maps: 1.0.0
932 |
933 | glob@10.4.5:
934 | dependencies:
935 | foreground-child: 3.3.0
936 | jackspeak: 3.4.3
937 | minimatch: 9.0.5
938 | minipass: 7.1.2
939 | package-json-from-dist: 1.0.1
940 | path-scurry: 1.11.1
941 |
942 | has-flag@4.0.0: {}
943 |
944 | header-range-parser@1.1.3: {}
945 |
946 | html-escaper@2.0.2: {}
947 |
948 | ipaddr.js@2.2.0: {}
949 |
950 | is-fullwidth-code-point@3.0.0: {}
951 |
952 | isexe@2.0.0: {}
953 |
954 | istanbul-lib-coverage@3.2.2: {}
955 |
956 | istanbul-lib-report@3.0.1:
957 | dependencies:
958 | istanbul-lib-coverage: 3.2.2
959 | make-dir: 4.0.0
960 | supports-color: 7.2.0
961 |
962 | istanbul-reports@3.1.7:
963 | dependencies:
964 | html-escaper: 2.0.2
965 | istanbul-lib-report: 3.0.1
966 |
967 | jackspeak@3.4.3:
968 | dependencies:
969 | '@isaacs/cliui': 8.0.2
970 | optionalDependencies:
971 | '@pkgjs/parseargs': 0.11.0
972 |
973 | locate-path@6.0.0:
974 | dependencies:
975 | p-locate: 5.0.0
976 |
977 | lru-cache@10.4.3: {}
978 |
979 | make-dir@4.0.0:
980 | dependencies:
981 | semver: 7.6.3
982 |
983 | mime@4.0.4: {}
984 |
985 | minimatch@9.0.5:
986 | dependencies:
987 | brace-expansion: 2.0.1
988 |
989 | minipass@7.1.2: {}
990 |
991 | negotiator@0.6.4: {}
992 |
993 | p-limit@3.1.0:
994 | dependencies:
995 | yocto-queue: 0.1.0
996 |
997 | p-locate@5.0.0:
998 | dependencies:
999 | p-limit: 3.1.0
1000 |
1001 | package-json-from-dist@1.0.1: {}
1002 |
1003 | path-exists@4.0.0: {}
1004 |
1005 | path-key@3.1.1: {}
1006 |
1007 | path-scurry@1.11.1:
1008 | dependencies:
1009 | lru-cache: 10.4.3
1010 | minipass: 7.1.2
1011 |
1012 | regexparam@2.0.2: {}
1013 |
1014 | require-directory@2.1.1: {}
1015 |
1016 | resolve-pkg-maps@1.0.0: {}
1017 |
1018 | semver@7.6.3: {}
1019 |
1020 | shebang-command@2.0.0:
1021 | dependencies:
1022 | shebang-regex: 3.0.0
1023 |
1024 | shebang-regex@3.0.0: {}
1025 |
1026 | signal-exit@4.1.0: {}
1027 |
1028 | string-width@4.2.3:
1029 | dependencies:
1030 | emoji-regex: 8.0.0
1031 | is-fullwidth-code-point: 3.0.0
1032 | strip-ansi: 6.0.1
1033 |
1034 | string-width@5.1.2:
1035 | dependencies:
1036 | eastasianwidth: 0.2.0
1037 | emoji-regex: 9.2.2
1038 | strip-ansi: 7.1.0
1039 |
1040 | strip-ansi@6.0.1:
1041 | dependencies:
1042 | ansi-regex: 5.0.1
1043 |
1044 | strip-ansi@7.1.0:
1045 | dependencies:
1046 | ansi-regex: 6.1.0
1047 |
1048 | supertest-fetch@2.0.0: {}
1049 |
1050 | supports-color@7.2.0:
1051 | dependencies:
1052 | has-flag: 4.0.0
1053 |
1054 | test-exclude@7.0.1:
1055 | dependencies:
1056 | '@istanbuljs/schema': 0.1.3
1057 | glob: 10.4.5
1058 | minimatch: 9.0.5
1059 |
1060 | tsx@4.19.1:
1061 | dependencies:
1062 | esbuild: 0.23.1
1063 | get-tsconfig: 4.8.1
1064 | optionalDependencies:
1065 | fsevents: 2.3.3
1066 |
1067 | typescript@5.6.2: {}
1068 |
1069 | undici-types@5.26.5: {}
1070 |
1071 | v8-to-istanbul@9.3.0:
1072 | dependencies:
1073 | '@jridgewell/trace-mapping': 0.3.25
1074 | '@types/istanbul-lib-coverage': 2.0.6
1075 | convert-source-map: 2.0.0
1076 |
1077 | which@2.0.2:
1078 | dependencies:
1079 | isexe: 2.0.0
1080 |
1081 | wrap-ansi@7.0.0:
1082 | dependencies:
1083 | ansi-styles: 4.3.0
1084 | string-width: 4.2.3
1085 | strip-ansi: 6.0.1
1086 |
1087 | wrap-ansi@8.1.0:
1088 | dependencies:
1089 | ansi-styles: 6.2.1
1090 | string-width: 5.1.2
1091 | strip-ansi: 7.1.0
1092 |
1093 | y18n@5.0.8: {}
1094 |
1095 | yargs-parser@21.1.1: {}
1096 |
1097 | yargs@17.7.2:
1098 | dependencies:
1099 | cliui: 8.0.1
1100 | escalade: 3.2.0
1101 | get-caller-file: 2.0.5
1102 | require-directory: 2.1.1
1103 | string-width: 4.2.3
1104 | y18n: 5.0.8
1105 | yargs-parser: 21.1.1
1106 |
1107 | yocto-queue@0.1.0: {}
1108 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Buffer, File } from 'node:buffer'
2 | import type { IncomingMessage, ServerResponse as Response } from 'node:http'
3 |
4 | type NextFunction = (err?: any) => void
5 |
6 | /**
7 | * Request extension with a body
8 | */
9 | export type ReqWithBody = IncomingMessage & {
10 | body?: T
11 | }
12 |
13 | export const hasBody = (method: string) => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)
14 |
15 | const defaultPayloadLimit = 102400 // 100KB
16 |
17 | export type LimitErrorFn = (limit: number) => Error
18 |
19 | export type ParserOptions = Partial<{
20 | /**
21 | * Limit payload size (in bytes)
22 | * @default '100KB'
23 | */
24 | payloadLimit: number
25 | /**
26 | * Custom error function for payload limit
27 | */
28 | payloadLimitErrorFn: LimitErrorFn
29 | }>
30 |
31 | const defaultErrorFn: LimitErrorFn = (payloadLimit) => new Error(`Payload too large. Limit: ${payloadLimit} bytes`)
32 |
33 | // Main function
34 | export const p =
35 | (
36 | fn: (body: Buffer) => void,
37 | payloadLimit = defaultPayloadLimit,
38 | payloadLimitErrorFn: LimitErrorFn = defaultErrorFn
39 | ) =>
40 | async (req: ReqWithBody, _res: Response, next?: (err?: any) => void) => {
41 | try {
42 | const body: Buffer[] = []
43 |
44 | for await (const chunk of req) {
45 | const totalSize = body.reduce((total, buffer) => total + buffer.byteLength, 0)
46 | if (totalSize > payloadLimit) throw payloadLimitErrorFn(payloadLimit)
47 | body.push(chunk as Buffer)
48 | }
49 |
50 | return fn(Buffer.concat(body))
51 | } catch (e) {
52 | next?.(e)
53 | }
54 | }
55 |
56 | /**
57 | * Parse payload with a custom function
58 | * @param fn
59 | */
60 | const custom =
61 | (fn: (body: Buffer) => any) =>
62 | async (req: ReqWithBody, _res: Response, next?: NextFunction) => {
63 | if (hasBody(req.method!)) req.body = await p(fn)(req, _res, next)
64 | next?.()
65 | }
66 |
67 | /**
68 | * Parse JSON payload
69 | * @param options
70 | */
71 | const json =
72 | ({ payloadLimit, payloadLimitErrorFn }: ParserOptions = {}) =>
73 | async (req: ReqWithBody, res: Response, next?: NextFunction) => {
74 | if (hasBody(req.method!)) {
75 | req.body = await p(
76 | (x) => {
77 | const str = td.decode(x)
78 | return str ? JSON.parse(str) : {}
79 | },
80 | payloadLimit,
81 | payloadLimitErrorFn
82 | )(req, res, next)
83 | }
84 | next?.()
85 | }
86 |
87 | /**
88 | * Parse raw payload
89 | * @param options
90 | */
91 | const raw =
92 | ({ payloadLimit, payloadLimitErrorFn }: ParserOptions = {}) =>
93 | async (req: ReqWithBody, _res: Response, next?: NextFunction) => {
94 | if (hasBody(req.method!)) {
95 | req.body = await p((x) => x, payloadLimit, payloadLimitErrorFn)(req, _res, next)
96 | }
97 | next?.()
98 | }
99 |
100 | const td = new TextDecoder()
101 | /**
102 | * Stringify request payload
103 | * @param param0
104 | * @returns
105 | */
106 | const text =
107 | ({ payloadLimit, payloadLimitErrorFn }: ParserOptions = {}) =>
108 | async (req: ReqWithBody, _res: Response, next?: NextFunction) => {
109 | if (hasBody(req.method!)) {
110 | req.body = await p((x) => td.decode(x), payloadLimit, payloadLimitErrorFn)(req, _res, next)
111 | }
112 | next?.()
113 | }
114 |
115 | /**
116 | * Parse urlencoded payload
117 | * @param options
118 | */
119 | const urlencoded =
120 | ({ payloadLimit, payloadLimitErrorFn }: ParserOptions = {}) =>
121 | async (req: ReqWithBody, _res: Response, next?: NextFunction) => {
122 | if (hasBody(req.method!)) {
123 | req.body = await p(
124 | (x) => Object.fromEntries(new URLSearchParams(x.toString()).entries()),
125 | payloadLimit,
126 | payloadLimitErrorFn
127 | )(req, _res, next)
128 | }
129 | next?.()
130 | }
131 |
132 | const getBoundary = (contentType: string) => {
133 | const match = /boundary=(.+);?/.exec(contentType)
134 | return match ? `--${match[1]}` : null
135 | }
136 |
137 | const defaultFileSizeLimitErrorFn: LimitErrorFn = (limit) => new Error(`File too large. Limit: ${limit} bytes`)
138 |
139 | const defaultFileSizeLimit = 200 * 1024 * 1024
140 |
141 | const parseMultipart = (
142 | body: string,
143 | boundary: string,
144 | {
145 | fileCountLimit,
146 | fileSizeLimit = defaultFileSizeLimit,
147 | fileSizeLimitErrorFn = defaultFileSizeLimitErrorFn
148 | }: MultipartOptions
149 | ) => {
150 | const parts = body.split(new RegExp(`${boundary}(--)?`)).filter((part) => !!part && /content-disposition/i.test(part))
151 | const parsedBody: Record = {}
152 |
153 | if (fileCountLimit && parts.length > fileCountLimit) throw new Error(`Too many files. Limit: ${fileCountLimit}`)
154 |
155 | // biome-ignore lint/complexity/noForEach: for...of fails
156 | parts.forEach((part) => {
157 | const [headers, ...lines] = part.split('\r\n').filter((part) => !!part)
158 | const data = lines.join('\r\n').trim()
159 |
160 | if (data.length > fileSizeLimit) throw fileSizeLimitErrorFn(fileSizeLimit)
161 |
162 | // Extract the name and filename from the headers
163 | const name = /name="(.+?)"/.exec(headers)![1]
164 | const filename = /filename="(.+?)"/.exec(headers)
165 | if (filename) {
166 | const contentTypeMatch = /Content-Type: (.+)/i.exec(data)!
167 | const fileContent = data.slice(contentTypeMatch[0].length + 2)
168 |
169 | const file = new File([fileContent], filename[1], { type: contentTypeMatch[1] })
170 |
171 | parsedBody[name] = parsedBody[name] ? [...parsedBody[name], file] : [file]
172 | return
173 | }
174 | parsedBody[name] = parsedBody[name] ? [...parsedBody[name], data] : [data]
175 | return
176 | })
177 |
178 | return parsedBody
179 | }
180 | type MultipartOptions = Partial<{
181 | /**
182 | * Limit number of files
183 | */
184 | fileCountLimit: number
185 | /**
186 | * Limit file size (in bytes)
187 | */
188 | fileSizeLimit: number
189 | /**
190 | * Custom error function for file size limit
191 | */
192 | fileSizeLimitErrorFn: LimitErrorFn
193 | }>
194 | /**
195 | * Parse multipart form data (supports files as well)
196 | *
197 | * Does not restrict total payload size by default.
198 | * @param options
199 | */
200 | const multipart =
201 | ({ payloadLimit = Number.POSITIVE_INFINITY, payloadLimitErrorFn, ...opts }: MultipartOptions & ParserOptions = {}) =>
202 | async (req: ReqWithBody, res: Response, next?: NextFunction) => {
203 | if (hasBody(req.method!)) {
204 | req.body = await p(
205 | (x) => {
206 | const boundary = getBoundary(req.headers['content-type']!)
207 | if (boundary) return parseMultipart(td.decode(x), boundary, opts)
208 | return {}
209 | },
210 | payloadLimit,
211 | payloadLimitErrorFn
212 | )(req, res, next)
213 | }
214 | next?.()
215 | }
216 |
217 | export { custom, json, raw, text, urlencoded, multipart }
218 |
--------------------------------------------------------------------------------
/test.ts:
--------------------------------------------------------------------------------
1 | import assert from 'node:assert/strict'
2 | import { Buffer } from 'node:buffer'
3 | import { File } from 'node:buffer'
4 | import { createServer } from 'node:http'
5 | import { describe, it } from 'node:test'
6 | import { App } from '@tinyhttp/app'
7 | import { makeFetch } from 'supertest-fetch'
8 | import { type ReqWithBody, custom, json, multipart, raw, text, urlencoded } from './src/index.js'
9 |
10 | const td = new TextDecoder()
11 |
12 | describe('Basic parsing', () => {
13 | it('should parse JSON body', async () => {
14 | const server = createServer(async (req: ReqWithBody, res) => {
15 | await json()(req, res, (err) => err && console.log(err))
16 |
17 | res.setHeader('Content-Type', 'application/json')
18 |
19 | res.end(JSON.stringify(req.body))
20 | })
21 |
22 | await makeFetch(server)('/', {
23 | body: JSON.stringify({ hello: 'world' }),
24 | method: 'POST',
25 | headers: {
26 | Accept: 'application/json',
27 | 'Content-Type': 'application/json'
28 | }
29 | }).expect(200, { hello: 'world' })
30 | })
31 |
32 | it('should ignore JSON empty body', async () => {
33 | const server = createServer(async (req: ReqWithBody, res) => {
34 | await json()(req, res, (err) => err && console.log(err))
35 |
36 | res.setHeader('Content-Type', 'application/json')
37 |
38 | res.end(JSON.stringify({ ok: true }))
39 | })
40 |
41 | // Empty string body
42 | await makeFetch(server)('/', {
43 | body: '',
44 | method: 'POST',
45 | headers: {
46 | Accept: 'application/json',
47 | 'Content-Type': 'application/json'
48 | }
49 | }).expect(200, { ok: true })
50 |
51 | // Unset body
52 | await makeFetch(server)('/', {
53 | method: 'POST',
54 | headers: {
55 | Accept: 'application/json',
56 | 'Content-Type': 'application/json'
57 | }
58 | }).expect(200, { ok: true })
59 | })
60 |
61 | it('should parse json body with no content-type headers', async () => {
62 | const server = createServer(async (req: ReqWithBody, res) => {
63 | await json()(req, res, (err) => err && console.log(err))
64 |
65 | res.setHeader('Content-Type', 'application/json')
66 |
67 | res.end(JSON.stringify(req.body))
68 | })
69 |
70 | await makeFetch(server)('/', {
71 | body: JSON.stringify({ hello: 'world' }),
72 | method: 'POST',
73 | headers: {
74 | Accept: 'application/json'
75 | }
76 | }).expect(200, { hello: 'world' })
77 | })
78 |
79 | it('json should call next() without a body', async () => {
80 | const server = createServer(async (req: ReqWithBody, res) => {
81 | await json()(req, res, (err) => err && console.log(err))
82 |
83 | res.setHeader('Content-Type', 'application/json')
84 |
85 | res.end()
86 | })
87 |
88 | await makeFetch(server)('/', {
89 | method: 'POST',
90 | headers: {
91 | Accept: 'application/json',
92 | 'Content-Type': 'application/json'
93 | }
94 | }).expect(200)
95 | })
96 |
97 | it('json should ignore GET request', async () => {
98 | const server = createServer(async (req: ReqWithBody, res) => {
99 | await json()(req, res, (err) => err && console.log(err))
100 |
101 | res.end('GET is ignored')
102 | })
103 |
104 | await makeFetch(server)('/', {
105 | method: 'GET'
106 | }).expect(200, 'GET is ignored')
107 | })
108 |
109 | it('should parse urlencoded body', async () => {
110 | const server = createServer(async (req: ReqWithBody, res) => {
111 | await urlencoded()(req, res, (err) => err && console.log(err))
112 |
113 | res.setHeader('Content-Type', 'application/x-www-form-urlencoded')
114 |
115 | res.end(JSON.stringify(req.body))
116 | })
117 |
118 | await makeFetch(server)('/', {
119 | body: 'hello=world',
120 | method: 'POST',
121 | headers: {
122 | Accept: 'application/x-www-form-urlencoded',
123 | 'Content-Type': 'application/x-www-form-urlencoded'
124 | }
125 | }).expect(200, { hello: 'world' })
126 | })
127 |
128 | it('urlencoded should ignore GET request', async () => {
129 | const server = createServer(async (req: ReqWithBody, res) => {
130 | await urlencoded()(req, res, (err) => err && console.log(err))
131 |
132 | res.end('GET is ignored')
133 | })
134 |
135 | await makeFetch(server)('/', {
136 | method: 'GET'
137 | }).expect(200, 'GET is ignored')
138 | })
139 |
140 | it('should parse text body', async () => {
141 | const server = createServer(async (req: ReqWithBody, res) => {
142 | await text()(req, res, (err) => err && console.log(err))
143 |
144 | res.setHeader('Content-Type', 'text/plain')
145 |
146 | res.end(req.body)
147 | })
148 |
149 | await makeFetch(server)('/', {
150 | body: 'hello world',
151 | method: 'POST',
152 | headers: {
153 | Accept: 'text/plain',
154 | 'Content-Type': 'text/plain'
155 | }
156 | }).expect(200, 'hello world')
157 | })
158 |
159 | it('text should ignore GET request', async () => {
160 | const server = createServer(async (req: ReqWithBody, res) => {
161 | await text()(req, res, (err) => err && console.log(err))
162 |
163 | res.setHeader('Content-Type', 'text/plain')
164 |
165 | res.end('GET is ignored')
166 | })
167 |
168 | await makeFetch(server)('/', {
169 | method: 'GET',
170 | headers: {
171 | Accept: 'text/plain',
172 | 'Content-Type': 'text/plain'
173 | }
174 | }).expect(200, 'GET is ignored')
175 | })
176 |
177 | it('should parse raw body', async () => {
178 | const server = createServer(async (req: ReqWithBody, res) => {
179 | await raw()(req, res, (err) => err && console.log(err))
180 |
181 | res.setHeader('Content-Type', 'text/plain')
182 |
183 | res.end(req.body)
184 | })
185 |
186 | await makeFetch(server)('/', {
187 | body: 'hello world',
188 | method: 'POST',
189 | headers: {
190 | Accept: 'text/plain',
191 | 'Content-Type': 'text/plain'
192 | }
193 | }).expect(200, 'hello world')
194 | })
195 |
196 | it('raw should ignore GET request', async () => {
197 | const server = createServer(async (req: ReqWithBody, res) => {
198 | await raw()(req, res, (err) => err && console.log(err))
199 |
200 | res.setHeader('Content-Type', 'text/plain')
201 |
202 | res.end('GET is ignored')
203 | })
204 |
205 | await makeFetch(server)('/', {
206 | method: 'GET',
207 | headers: {
208 | Accept: 'text/plain',
209 | 'Content-Type': 'text/plain'
210 | }
211 | }).expect(200, 'GET is ignored')
212 | })
213 |
214 | it('should parse custom body', async () => {
215 | const server = createServer(async (req: ReqWithBody, res) => {
216 | await custom((d) => td.decode(d).toUpperCase())(req, res, (err) => err && console.log(err))
217 |
218 | res.setHeader('Content-Type', 'text/plain')
219 |
220 | res.end(req.body)
221 | })
222 |
223 | await makeFetch(server)('/', {
224 | body: 'hello world',
225 | method: 'POST',
226 | headers: {
227 | Accept: 'text/plain',
228 | 'Content-Type': 'text/plain'
229 | }
230 | }).expect(200, 'HELLO WORLD')
231 | })
232 |
233 | it('custom should ignore GET request', async () => {
234 | const server = createServer(async (req: ReqWithBody, res) => {
235 | await custom((d) => td.decode(d).toUpperCase())(req, res, (err) => err && console.log(err))
236 |
237 | res.setHeader('Content-Type', 'text/plain')
238 |
239 | res.end('GET is ignored')
240 | })
241 |
242 | await makeFetch(server)('/', {
243 | method: 'GET',
244 | headers: {
245 | Accept: 'text/plain',
246 | 'Content-Type': 'text/plain'
247 | }
248 | }).expect(200, 'GET is ignored')
249 | })
250 | })
251 |
252 | describe('Multipart', () => {
253 | it('should parse multipart body', async () => {
254 | const server = createServer(async (req: ReqWithBody, res) => {
255 | await multipart()(req, res, (err) => err && console.log(err))
256 | res.end(JSON.stringify(req.body))
257 | })
258 |
259 | const fd = new FormData()
260 |
261 | fd.set('textfield', 'textfield data\nwith new lines\nbecause this is valid')
262 | fd.set('someother', 'textfield with text')
263 |
264 | await makeFetch(server)('/', {
265 | // probaly better to use form-data package
266 | body: fd,
267 | method: 'POST'
268 | }).expect(200, {
269 | textfield: ['textfield data\r\nwith new lines\r\nbecause this is valid'],
270 | someother: ['textfield with text']
271 | })
272 | })
273 |
274 | it('should not parse if boundary is not present', async () => {
275 | const server = createServer(async (req: ReqWithBody, res) => {
276 | await multipart()(req, res, (err) => err && res.end(err))
277 |
278 | res.end(JSON.stringify(req.body))
279 | })
280 |
281 | const fd = new FormData()
282 |
283 | fd.set('textfield', 'textfield data\nwith new lines\nbecause this is valid')
284 |
285 | await makeFetch(server)('/', {
286 | body: fd,
287 | method: 'POST',
288 | headers: {
289 | Accept: 'multipart/form-data',
290 | // we override Content-Type so that boundary is not present
291 | 'Content-Type': 'multipart/form-data'
292 | }
293 | }).expect(200, {})
294 | })
295 |
296 | it('should parse multipart with boundary', async () => {
297 | const server = createServer(async (req: ReqWithBody, res) => {
298 | await multipart()(req, res, (err) => err && res.end(err))
299 |
300 | res.end(JSON.stringify(req.body))
301 | })
302 |
303 | await makeFetch(server)('/', {
304 | // probaly better to use form-data package
305 | body: '--some-boundary\r\nContent-Disposition: form-data; name="textfield"\r\n\r\ntextfield data\nwith new lines\nbecause this is valid\r\n--some-boundary\r\nContent-Disposition: form-data; name="someother"\r\n\r\ntextfield with text\r\n--some-boundary--\r\n',
306 | method: 'POST',
307 | headers: {
308 | Accept: 'multipart/form-data',
309 | 'Content-Type': 'multipart/form-data; boundary=some-boundary'
310 | }
311 | }).expect(200, {
312 | textfield: ['textfield data\nwith new lines\nbecause this is valid'],
313 | someother: ['textfield with text']
314 | })
315 | })
316 |
317 | it('should parse an array of multipart values', async () => {
318 | const server = createServer(async (req: ReqWithBody, res) => {
319 | await multipart()(req, res, (err) => err && console.log(err))
320 |
321 | res.end(JSON.stringify(req.body))
322 | })
323 |
324 | const fd = new FormData()
325 |
326 | fd.set('textfield', 'textfield data\nwith new lines\nbecause this is valid')
327 | fd.append('textfield', 'textfield with text')
328 |
329 | await makeFetch(server)('/', {
330 | // probaly better to use form-data package
331 | body: fd,
332 | method: 'POST'
333 | }).expect(200, {
334 | textfield: ['textfield data\r\nwith new lines\r\nbecause this is valid', 'textfield with text']
335 | })
336 | })
337 |
338 | it('multipart should ignore GET request', async () => {
339 | const server = createServer(async (req: ReqWithBody, res) => {
340 | await multipart()(req, res, (err) => err && console.log(err))
341 |
342 | res.end('GET is ignored')
343 | })
344 |
345 | await makeFetch(server)('/', {
346 | method: 'GET',
347 | headers: {
348 | Accept: 'multipart/form-data',
349 | 'Content-Type': 'multipart/form-data; boundary=some-boundary'
350 | }
351 | }).expect(200, 'GET is ignored')
352 | })
353 |
354 | it('should parse multipart with files', async () => {
355 | const fd = new FormData()
356 | const file = new File(['hello world'], 'hello.txt', { type: 'text/plain' })
357 | fd.set('file', file as Blob)
358 | const server = createServer(async (req: ReqWithBody<{ file: [File] }>, res) => {
359 | await multipart()(req, res, (err) => err && console.log(err))
360 |
361 | const formBuf = new Uint8Array(await file.arrayBuffer())
362 | const buf = new Uint8Array(await req.body!.file[0].arrayBuffer())
363 |
364 | assert.equal(Buffer.compare(buf, formBuf), 0)
365 |
366 | res.end(req.body?.file[0].name)
367 | })
368 |
369 | await makeFetch(server)('/', {
370 | // probaly better to use form-data package
371 | body: fd,
372 | method: 'POST'
373 | }).expect(200, 'hello.txt')
374 | })
375 |
376 | it('should support multiple files', async () => {
377 | const fd = new FormData()
378 |
379 | const files = [
380 | new File(['hello world'], 'hello.txt', { type: 'text/plain' }),
381 | new File(['bye world'], 'bye.txt', { type: 'text/plain' })
382 | ]
383 |
384 | fd.set('file1', files[0] as Blob)
385 | fd.set('file2', files[1] as Blob)
386 |
387 | const server = createServer(async (req: ReqWithBody<{ file1: [File]; file2: [File] }>, res) => {
388 | await multipart()(req, res, (err) => err && console.log(err))
389 |
390 | const files = Object.values(req.body!)
391 |
392 | for (const file of files) {
393 | const buf = new Uint8Array(await file[0].arrayBuffer())
394 | const i = files.indexOf(file)
395 | const formBuf = new Uint8Array(await files[i][0].arrayBuffer())
396 | assert.strictEqual(Buffer.compare(buf, formBuf), 0)
397 | }
398 | res.end('ok')
399 | })
400 |
401 | await makeFetch(server)('/', {
402 | body: fd,
403 | method: 'POST'
404 | }).expect(200)
405 | })
406 | it('should support binary files', async () => {
407 | const fd = new FormData()
408 | const file = new File([new Uint8Array([1, 2, 3])], 'blob.bin', { type: 'application/octet-stream' })
409 | fd.set('file', file as Blob)
410 |
411 | const server = createServer(async (req: ReqWithBody<{ file: [File] }>, res) => {
412 | await multipart()(req, res, (err) => err && console.log(err))
413 |
414 | const formBuf = new Uint8Array(await file.arrayBuffer())
415 | const buf = new Uint8Array(await req.body!.file[0].arrayBuffer())
416 |
417 | assert.equal(Buffer.compare(buf, formBuf), 0)
418 | assert.equal(req.body?.file[0].type, 'application/octet-stream')
419 |
420 | res.end(req.body?.file[0].name)
421 | })
422 |
423 | await makeFetch(server)('/', {
424 | // probaly better to use form-data package
425 | body: fd,
426 | method: 'POST'
427 | }).expect(200, 'blob.bin')
428 | })
429 | })
430 |
431 | describe('Limits', () => {
432 | it('should throw on default payloadLimit', async () => {
433 | const server = createServer(async (req: ReqWithBody, res) => {
434 | await text()(req, res, (err) => {
435 | if (err) res.writeHead(413).end(err.message)
436 | else res.end(req.body)
437 | })
438 | })
439 |
440 | await makeFetch(server)('/', {
441 | body: new Uint8Array(Buffer.alloc(200 * 1024 ** 2, 'a').buffer),
442 | method: 'POST',
443 | headers: {
444 | Accept: 'text/plain',
445 | 'Content-Type': 'text/plain'
446 | }
447 | }).expect(413, 'Payload too large. Limit: 102400 bytes')
448 | })
449 |
450 | it('should throw on custom payloadLimit', async () => {
451 | const server = createServer(async (req: ReqWithBody, res) => {
452 | await text({ payloadLimit: 1024 })(req, res, (err) => {
453 | if (err) res.writeHead(413).end(err.message)
454 | else res.end(req.body)
455 | })
456 | })
457 |
458 | await makeFetch(server)('/', {
459 | body: new Uint8Array(Buffer.alloc(1024 ** 2, 'a').buffer),
460 | method: 'POST',
461 | headers: {
462 | Accept: 'text/plain',
463 | 'Content-Type': 'text/plain'
464 | }
465 | }).expect(413, 'Payload too large. Limit: 1024 bytes')
466 | })
467 |
468 | it('should throw on payloadLimit with custom error message', async () => {
469 | const server = createServer(async (req: ReqWithBody, res) => {
470 | await text({
471 | payloadLimit: 1024,
472 | payloadLimitErrorFn: (payloadLimit) => new Error(`Payload too large. Limit: ${payloadLimit / 1024}KB`)
473 | })(req, res, (err) => {
474 | if (err) res.writeHead(413).end(err.message)
475 | else res.end(req.body)
476 | })
477 | })
478 |
479 | await makeFetch(server)('/', {
480 | body: new Uint8Array(Buffer.alloc(1024 ** 2, 'a').buffer),
481 | method: 'POST',
482 | headers: {
483 | Accept: 'text/plain',
484 | 'Content-Type': 'text/plain'
485 | }
486 | }).expect(413, 'Payload too large. Limit: 1KB')
487 | })
488 |
489 | it('should throw multipart if amount of files exceeds limit', async () => {
490 | const server = createServer(async (req: ReqWithBody, res) => {
491 | await multipart({ fileCountLimit: 1 })(req, res, (err) => {
492 | if (err) res.writeHead(413).end(err.message)
493 | else res.end(req.body)
494 | })
495 | })
496 |
497 | const fd = new FormData()
498 |
499 | fd.set('file1', new File(['hello world'], 'hello.txt', { type: 'text/plain' }) as Blob)
500 | fd.set('file2', new File(['bye world'], 'bye.txt', { type: 'text/plain' }) as Blob)
501 |
502 | await makeFetch(server)('/', {
503 | body: fd,
504 | method: 'POST'
505 | }).expect(413, 'Too many files. Limit: 1')
506 | })
507 |
508 | it('should throw multipart if exceeds allowed file size', async () => {
509 | const server = createServer(async (req: ReqWithBody, res) => {
510 | await multipart({ fileSizeLimit: 10 })(req, res, (err) => {
511 | if (err) res.writeHead(413).end(err.message)
512 | else res.end(req.body)
513 | })
514 | })
515 |
516 | const fd = new FormData()
517 |
518 | fd.set('file', new File(['hello world'], 'hello.txt', { type: 'text/plain' }) as Blob)
519 |
520 | await makeFetch(server)('/', {
521 | body: fd,
522 | method: 'POST'
523 | }).expect(413, 'File too large. Limit: 10 bytes')
524 | })
525 |
526 | it('should throw multipart if exceeds allowed file size with a custom error', async () => {
527 | const server = createServer(async (req: ReqWithBody, res) => {
528 | await multipart({
529 | fileSizeLimit: 20,
530 | fileSizeLimitErrorFn: (limit) => new Error(`File too large. Limit: ${limit / 1024}KB`)
531 | })(req, res, (err) => {
532 | if (err) res.writeHead(413).end(err.message)
533 | })
534 | })
535 |
536 | const fd = new FormData()
537 |
538 | fd.set('file', new File(['hello world to everyone'], 'hello.txt', { type: 'text/plain' }) as Blob)
539 |
540 | await makeFetch(server)('/', {
541 | body: fd,
542 | method: 'POST'
543 | }).expect(413, 'File too large. Limit: 0.01953125KB')
544 | })
545 | })
546 |
547 | describe('Framework integration', { timeout: 500 }, () => {
548 | it('works with tinyhttp', async () => {
549 | const app = new App()
550 |
551 | app
552 | .use('/json', json())
553 | .use('/url', urlencoded())
554 | .post((req, res) => {
555 | res.json(req.body)
556 | })
557 |
558 | const server = app.listen()
559 |
560 | const fetch = makeFetch(server)
561 |
562 | await fetch('/json', {
563 | body: JSON.stringify({ hello: 'world' }),
564 | method: 'POST'
565 | }).expect(200, { hello: 'world' })
566 |
567 | await fetch('/url', {
568 | body: 'hello=world',
569 | method: 'POST'
570 | })
571 | .expect(200, { hello: 'world' })
572 | .then(() => server.close())
573 | })
574 | })
575 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "rootDir": "src"
5 | },
6 | "include": ["src"]
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": ["dist"],
3 | "compilerOptions": {
4 | "esModuleInterop": true,
5 | "module": "NodeNext",
6 | "target": "ES2019",
7 | "moduleResolution": "NodeNext",
8 | "declaration": true,
9 | "outDir": "dist",
10 | "strict": true,
11 | "allowJs": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------