@remix-run/form-data-middleware 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/dist/lib/form-data.d.ts +5 -3
- package/dist/lib/form-data.d.ts.map +1 -1
- package/dist/lib/form-data.js +10 -3
- package/package.json +7 -7
- package/src/lib/form-data.ts +12 -6
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# form-data-middleware
|
|
2
2
|
|
|
3
|
-
Form body parsing middleware for Remix. It parses incoming [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) and exposes it via `context.get(FormData)
|
|
3
|
+
Form body parsing middleware for Remix. It parses incoming [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) and exposes it via `context.formData` (or `context.get(FormData)`).
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Request Form Parsing** - Parses request body form data once per request
|
|
8
|
-
- **File Access** - Uploaded files are available from `context.get(FormData)`
|
|
8
|
+
- **File Access** - Uploaded files are available from `context.formData` (or `context.get(FormData)`)
|
|
9
9
|
- **Custom Upload Handling** - Supports pluggable upload handlers for file processing
|
|
10
10
|
- **Error Control** - Optional suppression for malformed form data
|
|
11
11
|
|
|
@@ -17,20 +17,22 @@ npm i remix
|
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
20
|
-
Use the `formData()` middleware at the router level to parse `FormData` from the request body and make it available
|
|
20
|
+
Use the `formData()` middleware at the router level to parse `FormData` from the request body and make it available as `context.formData` (or `context.get(FormData)`).
|
|
21
|
+
|
|
22
|
+
When `formData()` runs successfully it always provides a `FormData` value. Requests that do not contain a form body, including `GET` and `HEAD` requests, receive an empty `FormData`.
|
|
21
23
|
|
|
22
24
|
Uploaded files are available in the parsed `FormData` object. For a single file field, use `formData.get(name)`. For repeated file fields, use `formData.getAll(name)`.
|
|
23
25
|
|
|
24
26
|
```ts
|
|
25
|
-
import { createRouter } from 'remix/
|
|
26
|
-
import { formData } from 'remix/form-data
|
|
27
|
+
import { createRouter } from 'remix/router'
|
|
28
|
+
import { formData } from 'remix/middleware/form-data'
|
|
27
29
|
|
|
28
30
|
let router = createRouter({
|
|
29
31
|
middleware: [formData()],
|
|
30
32
|
})
|
|
31
33
|
|
|
32
34
|
router.post('/users', async (context) => {
|
|
33
|
-
let formData = context.
|
|
35
|
+
let formData = context.formData
|
|
34
36
|
let name = formData.get('name')
|
|
35
37
|
let email = formData.get('email')
|
|
36
38
|
|
|
@@ -41,12 +43,14 @@ router.post('/users', async (context) => {
|
|
|
41
43
|
})
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
Use `context.formData` (or `context.get(FormData)`).
|
|
47
|
+
|
|
44
48
|
### Custom File Upload Handler
|
|
45
49
|
|
|
46
50
|
You can use a custom upload handler to customize how file uploads are handled. The return value of the upload handler will be used as the value of the form field in the `FormData` object.
|
|
47
51
|
|
|
48
52
|
```ts
|
|
49
|
-
import { formData } from 'remix/form-data
|
|
53
|
+
import { formData } from 'remix/middleware/form-data'
|
|
50
54
|
import { writeFile } from 'node:fs/promises'
|
|
51
55
|
|
|
52
56
|
let router = createRouter({
|
|
@@ -83,7 +87,7 @@ let router = createRouter({
|
|
|
83
87
|
|
|
84
88
|
### Suppress Parse Errors
|
|
85
89
|
|
|
86
|
-
Some requests may contain invalid form data that cannot be parsed. You can suppress those malformed-body parse errors by setting `suppressErrors` to `true`. In these cases, `context.
|
|
90
|
+
Some requests may contain invalid form data that cannot be parsed. You can suppress those malformed-body parse errors by setting `suppressErrors` to `true`. In these cases, `context.formData` will be an empty `FormData` object. Multipart limit violations from `maxHeaderSize`, `maxFiles`, `maxFileSize`, `maxParts`, or `maxTotalSize` are never suppressed.
|
|
87
91
|
|
|
88
92
|
```ts
|
|
89
93
|
let router = createRouter({
|
package/dist/lib/form-data.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type FileUploadHandler, type ParseFormDataOptions } from '@remix-run/form-data-parser';
|
|
2
2
|
import type { Middleware } from '@remix-run/fetch-router';
|
|
3
|
-
type SetFormDataContextTransform = readonly [readonly [typeof FormData, FormData]];
|
|
4
3
|
/**
|
|
5
4
|
* Options for the {@link formData} middleware.
|
|
6
5
|
*/
|
|
@@ -25,6 +24,9 @@ export interface FormDataOptions extends ParseFormDataOptions {
|
|
|
25
24
|
* @param options Options for parsing form data
|
|
26
25
|
* @returns A middleware function that parses form data
|
|
27
26
|
*/
|
|
28
|
-
export declare function formData(options?: FormDataOptions): Middleware<
|
|
29
|
-
|
|
27
|
+
export declare function formData(options?: FormDataOptions): Middleware<{
|
|
28
|
+
key: typeof FormData;
|
|
29
|
+
value: FormData;
|
|
30
|
+
property: 'formData';
|
|
31
|
+
}>;
|
|
30
32
|
//# sourceMappingURL=form-data.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-data.d.ts","sourceRoot":"","sources":["../../src/lib/form-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAA;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"form-data.d.ts","sourceRoot":"","sources":["../../src/lib/form-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAA;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAYzD;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAA;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,OAAO,CAAC,EAAE,eAAe,GACxB,UAAU,CAAC;IAAE,GAAG,EAAE,OAAO,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,UAAU,CAAA;CAAE,CAAC,CAyC7E"}
|
package/dist/lib/form-data.js
CHANGED
|
@@ -17,26 +17,33 @@ export function formData(options) {
|
|
|
17
17
|
let uploadHandler = options?.uploadHandler;
|
|
18
18
|
return async (context) => {
|
|
19
19
|
if (context.has(FormData)) {
|
|
20
|
+
let formData = context.get(FormData);
|
|
21
|
+
if (formData != null) {
|
|
22
|
+
context.set(FormData, formData, { property: 'formData' });
|
|
23
|
+
}
|
|
20
24
|
return;
|
|
21
25
|
}
|
|
22
26
|
if (context.method === 'GET' || context.method === 'HEAD') {
|
|
27
|
+
context.set(FormData, new FormData(), { property: 'formData' });
|
|
23
28
|
return;
|
|
24
29
|
}
|
|
25
30
|
let contentType = context.headers.get('Content-Type');
|
|
26
31
|
if (contentType == null ||
|
|
27
32
|
(!contentType.startsWith('multipart/') &&
|
|
28
33
|
!contentType.startsWith('application/x-www-form-urlencoded'))) {
|
|
29
|
-
context.set(FormData, new FormData());
|
|
34
|
+
context.set(FormData, new FormData(), { property: 'formData' });
|
|
30
35
|
return;
|
|
31
36
|
}
|
|
32
37
|
try {
|
|
33
|
-
context.set(FormData, await parseFormData(context.request, options, uploadHandler)
|
|
38
|
+
context.set(FormData, await parseFormData(context.request, options, uploadHandler), {
|
|
39
|
+
property: 'formData',
|
|
40
|
+
});
|
|
34
41
|
}
|
|
35
42
|
catch (error) {
|
|
36
43
|
if (!suppressErrors || isMultipartLimitError(error)) {
|
|
37
44
|
throw error;
|
|
38
45
|
}
|
|
39
|
-
context.set(FormData, new FormData());
|
|
46
|
+
context.set(FormData, new FormData(), { property: 'formData' });
|
|
40
47
|
}
|
|
41
48
|
};
|
|
42
49
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/form-data-middleware",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Middleware for parsing FormData from request bodies",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.6.0",
|
|
30
30
|
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
31
|
-
"@remix-run/assert": "0.2.
|
|
32
|
-
"@remix-run/fetch-router": "0.
|
|
33
|
-
"@remix-run/test": "0.
|
|
34
|
-
"@remix-run/form-data-parser": "0.17.
|
|
31
|
+
"@remix-run/assert": "0.2.1",
|
|
32
|
+
"@remix-run/fetch-router": "0.19.1",
|
|
33
|
+
"@remix-run/test": "0.4.1",
|
|
34
|
+
"@remix-run/form-data-parser": "0.17.2"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@remix-run/
|
|
38
|
-
"@remix-run/
|
|
37
|
+
"@remix-run/fetch-router": "^0.19.1",
|
|
38
|
+
"@remix-run/form-data-parser": "^0.17.2"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"fetch",
|
package/src/lib/form-data.ts
CHANGED
|
@@ -10,8 +10,6 @@ import {
|
|
|
10
10
|
} from '@remix-run/form-data-parser'
|
|
11
11
|
import type { Middleware } from '@remix-run/fetch-router'
|
|
12
12
|
|
|
13
|
-
type SetFormDataContextTransform = readonly [readonly [typeof FormData, FormData]]
|
|
14
|
-
|
|
15
13
|
function isMultipartLimitError(error: unknown): boolean {
|
|
16
14
|
return (
|
|
17
15
|
error instanceof MaxFilesExceededError ||
|
|
@@ -49,16 +47,22 @@ export interface FormDataOptions extends ParseFormDataOptions {
|
|
|
49
47
|
*/
|
|
50
48
|
export function formData(
|
|
51
49
|
options?: FormDataOptions,
|
|
52
|
-
): Middleware<
|
|
50
|
+
): Middleware<{ key: typeof FormData; value: FormData; property: 'formData' }> {
|
|
53
51
|
let suppressErrors = options?.suppressErrors ?? false
|
|
54
52
|
let uploadHandler = options?.uploadHandler
|
|
55
53
|
|
|
56
54
|
return async (context) => {
|
|
57
55
|
if (context.has(FormData)) {
|
|
56
|
+
let formData = context.get(FormData)
|
|
57
|
+
if (formData != null) {
|
|
58
|
+
context.set(FormData, formData, { property: 'formData' })
|
|
59
|
+
}
|
|
60
|
+
|
|
58
61
|
return
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
if (context.method === 'GET' || context.method === 'HEAD') {
|
|
65
|
+
context.set(FormData, new FormData(), { property: 'formData' })
|
|
62
66
|
return
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -68,18 +72,20 @@ export function formData(
|
|
|
68
72
|
(!contentType.startsWith('multipart/') &&
|
|
69
73
|
!contentType.startsWith('application/x-www-form-urlencoded'))
|
|
70
74
|
) {
|
|
71
|
-
context.set(FormData, new FormData())
|
|
75
|
+
context.set(FormData, new FormData(), { property: 'formData' })
|
|
72
76
|
return
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
try {
|
|
76
|
-
context.set(FormData, await parseFormData(context.request, options, uploadHandler)
|
|
80
|
+
context.set(FormData, await parseFormData(context.request, options, uploadHandler), {
|
|
81
|
+
property: 'formData',
|
|
82
|
+
})
|
|
77
83
|
} catch (error) {
|
|
78
84
|
if (!suppressErrors || isMultipartLimitError(error)) {
|
|
79
85
|
throw error
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
context.set(FormData, new FormData())
|
|
88
|
+
context.set(FormData, new FormData(), { property: 'formData' })
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
}
|