Skip to content

Commit d04a885

Browse files
committed
chore: v4 RC on formidable@next dist-tag
Signed-off-by: tunnckoCore <5038030+tunnckoCore@users.noreply.github.com>
1 parent 7493e45 commit d04a885

23 files changed

Lines changed: 8944 additions & 215 deletions

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
old-source/
2-
2+
packages/formidable-next/src/super-headers.js
3+
packages/formidable-next/src/super-readable-stream.js
34

45
.wrangler
56
*.tsbuildinfo

.vscode/settings.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
"editor.rulers": [80, 100, 120],
33
"editor.defaultFormatter": "mattbrannon.prettiest-eslint",
44
"[javascript]": {
5-
"editor.defaultFormatter": "mattbrannon.prettiest-eslint"
5+
"editor.defaultFormatter": "mattbrannon.prettiest-eslint",
66
},
77
"[javascriptreact]": {
8-
"editor.defaultFormatter": "mattbrannon.prettiest-eslint"
8+
"editor.defaultFormatter": "mattbrannon.prettiest-eslint",
99
},
1010
"[typescript]": {
11-
"editor.defaultFormatter": "mattbrannon.prettiest-eslint"
11+
"editor.defaultFormatter": "mattbrannon.prettiest-eslint",
1212
},
1313
"[typescriptreact]": {
14-
"editor.defaultFormatter": "mattbrannon.prettiest-eslint"
14+
"editor.defaultFormatter": "mattbrannon.prettiest-eslint",
1515
},
1616

1717
"npm-intellisense.importES6": true,
@@ -27,7 +27,7 @@
2727
"source.fixAll": "always", // sometimes it's `always` sometimes it's `explicit`.. whatever tha fack these means, i want it constantly
2828
"source.fixAll.eslint": "always",
2929
"source.fixAll.prettier": "always",
30-
"source.organizeImports": "always"
30+
"source.organizeImports": "always",
3131
},
3232
"editor.wordWrap": "off",
3333
// "workbench.colorCustomizations": {
@@ -59,7 +59,7 @@
5959
".npmrc": "ini",
6060
"*.njk": "html",
6161
"*.postcss": "tailwindcss",
62-
"*.mdx": "mdx"
62+
"*.mdx": "mdx",
6363
},
6464
// "editor.fontFamily": "Fira Code",
6565
// "editor.fontLigatures": true,
@@ -93,22 +93,22 @@
9393
"yaml": false,
9494
"javascript": true,
9595
"python": true,
96-
"typescript": true
96+
"typescript": true,
9797
},
9898
"chat.mcp.discovery.enabled": true,
9999
"mcp": {
100100
"servers": {
101101
"context7": {
102102
"type": "stdio",
103103
"command": "bunx",
104-
"args": ["-y", "@upstash/context7-mcp@latest"]
104+
"args": ["-y", "@upstash/context7-mcp@latest"],
105105
},
106106
"coincap-mcp": {
107107
"type": "stdio",
108108
"command": "bunx",
109-
"args": ["coincap-mcp"]
110-
}
111-
}
109+
"args": ["coincap-mcp"],
110+
},
111+
},
112112
},
113-
"workbench.startupEditor": "none"
113+
"workbench.startupEditor": "none",
114114
}

package.json

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,20 @@
33
"name": "formidable-monorepo",
44
"version": "0.0.0",
55
"license": "MIT",
6-
"description": "A monorepo for Formidable's packages",
7-
"workspaces": [
8-
"packages/*"
9-
],
6+
"description": "A monorepo for Node Formidable packages",
107
"type": "module",
11-
"engines": {
12-
"node": ">=22"
13-
},
148
"scripts": {
159
"clean": "git clean -fdX .",
1610
"publish-release": "node --env-file .env ./scripts/publish-release.js",
1711
"publish-release-ci": "node ./scripts/publish-release.js",
1812
"tag-release": "node ./scripts/tag-release.js",
1913
"test": "pnpm -r --workspace-concurrency 1 test"
2014
},
21-
"devDependencies": {
15+
"dependencies": {
2216
"@types/bun": "1.2.10",
2317
"@types/node": "22.14.1",
24-
"esmc": "^0.18.3",
18+
"eslint-config-xaxa": "^25.0.12",
19+
"prettier-plugin-pkgjson": "^0.3.0",
2520
"typescript": "^5.8.3"
26-
},
27-
"packageManager": "pnpm@10.3.0",
28-
"pnpm": {
29-
"onlyBuiltDependencies": [
30-
"esbuild",
31-
"unrs-resolver"
32-
]
3321
}
3422
}

packages/.gitkeep

Whitespace-only changes.

packages/formidable-next/README.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# formidable@next
2+
3+
v4 preparations. Modern multipart form-data parser for Node.js, Bun, Deno, Cloudflare, and the
4+
browser.
5+
6+
> [!CAUTION]
7+
>
8+
> For older Node versions like v14, v15 and v16 you have to use the `web-streams-polyfill` in your
9+
> project. Minimum required version is the first Node.js v14 LTS release, `v14.15.0` (Fermium).
10+
11+
## install
12+
13+
```
14+
npm i formidable@next
15+
```
16+
17+
## Example
18+
19+
```ts
20+
import {
21+
parseMultipartRequest,
22+
parseMultipart,
23+
formidableDefaultOptions,
24+
FormidableError,
25+
FormidableOptions,
26+
FormidablePart,
27+
} from 'formidable';
28+
29+
async function formidable(req: Request, options?: FormidableOptions) {
30+
try {
31+
await parseMultipartRequest(req, options, async (part: FormidablePart) => {
32+
// part.type - part content type, media type (no charsets)
33+
34+
// ==== Headers ====
35+
// part.headers - part headers, parsed into type-safe SuperHeaders object
36+
// part.headers.contentType.mediaType - part content media type
37+
// part.headers.contentType.charset - part content charset
38+
// part.headers.contentDisposition.name
39+
// part.headers.contentDisposition.filename
40+
// part.headers.contentDisposition.preferredFilename
41+
// part.headers.contentDisposition.type - attachment or inline or form-data
42+
43+
// ==== Streaming ====
44+
// await part.stream() - no buffering, async iterable, useful to use in `for await (const chunk of part.stream())`
45+
46+
// ==== Buffering ====
47+
// await part.text(failSafe?) - buffer into string, pass failSafe = true to avoid crashing
48+
// await part.bytes() - buffer into Uint8Array bytes
49+
// await part.arrayBuffer() - buffer into ArrayBuffer
50+
// await part.json() - buffer into JSON object, pass failSafe = true to avoid crashing
51+
52+
// ==== Utils ====
53+
// part.toString() - string representing the state of properties (name, filename, type, headers)
54+
// part.toObject() - the core of `toString()`, returns an object with the properties (name, filename, type, headers)
55+
// part.isFile() - check if the part is a file
56+
57+
if (part.isFile()) {
58+
console.log('file', part.name, part.filename, part.toString());
59+
} else {
60+
// part.text() on field gets the input's value
61+
console.log('field', part.name, await part.text());
62+
}
63+
});
64+
} catch (er: FormidableError) {
65+
switch (er.code) {
66+
case 'ERR_INVALID_INPUT':
67+
console.error(er.message);
68+
break;
69+
case 'ERR_BODY_CONSUMED':
70+
console.error(er.message);
71+
break;
72+
case 'ERR_FAILED_TO_PARSE_TEXT':
73+
console.error(er.message);
74+
break;
75+
case 'ERR_FAILED_TO_PARSE_JSON':
76+
console.error(er.message);
77+
break;
78+
case 'ERR_NO_BOUNDARY':
79+
console.error(er.message);
80+
break;
81+
case 'ERR_MAX_FILENAME_SIZE':
82+
console.error(er.message);
83+
break;
84+
case 'ERR_MAX_FILE_SIZE':
85+
console.error(er.message);
86+
break;
87+
case 'ERR_MAX_FILE_KEY_SIZE':
88+
console.error(er.message);
89+
break;
90+
case 'ERR_MAX_FIELD_SIZE':
91+
console.error(er.message);
92+
break;
93+
case 'ERR_MAX_FIELD_KEY_SIZE':
94+
console.error(er.message);
95+
break;
96+
case 'ERR_MAX_HEADER_SIZE':
97+
console.error(er.message);
98+
break;
99+
case 'ERR_MAX_HEADER_KEY_SIZE':
100+
console.error(er.message);
101+
break;
102+
case 'ERR_MAX_HEADER_VALUE_SIZE':
103+
console.error(er.message);
104+
break;
105+
case 'ERR_MAX_ALL_HEADERS_SIZE':
106+
console.error(er.message);
107+
break;
108+
}
109+
}
110+
}
111+
```
112+
113+
### Usage in Node.js v14 and v16
114+
115+
> [!CAUTION]
116+
>
117+
> Minimum required version is the first Node.js v14 LTS release, `v14.15.0` (Fermium).
118+
119+
It bundles the `headers-polyfill` for compatibility with older Node.js versions. For Node.js v14 it
120+
bundles both; for Node.js v16 you have to `import 'web-streams-polyfill/polyfill';` for ESM, or
121+
modules `require('web-streams-polyfill/ponyfill');` for CJS
122+
123+
```ts
124+
import 'web-streams-polyfill/polyfill';
125+
import { createServer } from 'node:http';
126+
import { parseMultipartRequest } from 'formidable';
127+
128+
const server = createServer((req, res) => {
129+
if (req.method === 'POST') {
130+
// considering the above `formidable`
131+
await parseMultipartRequest(
132+
req,
133+
{
134+
maxFileSize: 1 * 1024 * 1024, // 1mb, defaults to 100mb
135+
maxFilenameSize: 1000, // defaults to 255
136+
maxFileKeySize: 1000, // defaults to 255
137+
maxFieldKeySize: 1000, // defaults to 255
138+
},
139+
async (part) => {
140+
console.log('part:', part.toString());
141+
},
142+
);
143+
} else {
144+
res.writeHead(200, { 'Content-Type': 'text/plain' });
145+
res.end('Hello World\n');
146+
}
147+
});
148+
149+
server.listen(3000, () => {
150+
console.log('Server running at http://localhost:3000');
151+
});
152+
```
153+
154+
### Usage in modern Node.js and other runtimes
155+
156+
You can either use the above helper `formidable` function or the `parseMultipartRequest` directly,
157+
works on any Fetch/Request/Response API compatible runtime.
158+
159+
- for Node.js v18+, you can use the `formidable` import directly
160+
- for Bun, Deno, Cloudflare, and the browser, you can use the `formidable` import too
161+
162+
```ts
163+
// Deno/Bun/Cloudflare
164+
import { parseMultipartRequest } from 'formidable';
165+
166+
export default {
167+
async fetch(req: Request) {
168+
if (req.method === 'POST') {
169+
await parseMultipartRequest(
170+
req,
171+
{
172+
maxFileSize: 1 * 1024 * 1024, // 1mb, defaults to 100mb
173+
maxFilenameSize: 1000, // defaults to 255
174+
maxFileKeySize: 1000, // defaults to 255
175+
maxFieldKeySize: 1000, // defaults to 255
176+
},
177+
async (part: FormidablePart) => {
178+
console.log('part:', part.toString());
179+
},
180+
);
181+
182+
return new Response('ok');
183+
}
184+
185+
return new Response('Hello World, try POST request', {
186+
status: 200,
187+
headers: {
188+
'Content-Type': 'text/plain',
189+
},
190+
});
191+
},
192+
};
193+
```
194+
195+
### Options
196+
197+
- `maxAllHeadersSize` **{number}** - size for all headers combined, _(default: 8kb)_
198+
- `maxHeaderKeySize` **{number}** - size of the key per each header, _(default: 255)_
199+
- `maxHeaderValueSize` **{number}** - size of the value of each header, _(default: 1kb)_
200+
- `maxHeaderSize` **{number}** - size of key + value of each header, _(default: 2kb)_
201+
- `maxFilenameSize` **{number}** - size of the file original filename, _(default: 255)_
202+
- `maxFileKeySize` **{number}** - size of the key of file fields, _(default: 255)_
203+
- `maxFileSize` **{number}** - size of each file, _(default: 100mb)_
204+
- `maxFieldKeySize` **{number}** - size of the key of text fields, _(default: 255)_
205+
- `maxFieldSize` **{number}** - size of each text field value, _(default: 100kb)_

0 commit comments

Comments
 (0)