@remix-run/multipart-parser 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/lib/multipart.d.ts +25 -2
- package/dist/lib/multipart.d.ts.map +1 -1
- package/dist/lib/multipart.js +18 -1
- package/package.json +3 -3
- package/src/lib/multipart.ts +31 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`multipart-parser` is a fast, streaming multipart parser that works in **any JavaScript environment**, from serverless functions to traditional servers. Whether you're handling file uploads, parsing email attachments, or working with multipart API responses, `multipart-parser` has you covered.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Why multipart-parser?
|
|
6
6
|
|
|
7
7
|
- **Universal JavaScript** - One library that works everywhere: Node.js, Bun, Deno, Cloudflare Workers, and browsers
|
|
8
8
|
- **Blazing Fast** - Consistently outperforms popular alternatives like busboy in benchmarks
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
- **Standards Based** - Built on the web standard [Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) for maximum compatibility
|
|
13
13
|
- **Production Ready** - Battle-tested error handling with specific error types for common scenarios
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Features
|
|
16
16
|
|
|
17
|
-
- Parse file uploads (`multipart/form-data`) with automatic field and file detection
|
|
18
|
-
- Support for all `multipart/*` content types (mixed, alternative, related, etc.)
|
|
19
|
-
- Convenient `MultipartPart` API with `arrayBuffer`, `bytes`, `text`, `size`, and metadata access
|
|
20
|
-
- Built-in file size limiting to prevent abuse
|
|
21
|
-
- First-class Node.js support with native `http.IncomingMessage` compatibility
|
|
22
|
-
- [Demos for every major runtime](https://github.com/remix-run/remix/tree/main/packages/multipart-parser/demos)
|
|
17
|
+
- **File Upload Parsing** - Parse file uploads (`multipart/form-data`) with automatic field and file detection
|
|
18
|
+
- **Full Multipart Support** - Support for all `multipart/*` content types (mixed, alternative, related, etc.)
|
|
19
|
+
- **Convenient API** - `MultipartPart` API with `arrayBuffer`, `bytes`, `text`, `size`, and metadata access
|
|
20
|
+
- **File Size Limiting** - Built-in file size limiting to prevent abuse
|
|
21
|
+
- **Node.js Support** - First-class Node.js support with native `http.IncomingMessage` compatibility
|
|
22
|
+
- **Runtime Demos** - [Demos for every major runtime](https://github.com/remix-run/remix/tree/main/packages/multipart-parser/demos)
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
package/dist/lib/multipart.d.ts
CHANGED
|
@@ -3,20 +3,32 @@ import Headers from '@remix-run/headers';
|
|
|
3
3
|
* The base class for errors thrown by the multipart parser.
|
|
4
4
|
*/
|
|
5
5
|
export declare class MultipartParseError extends Error {
|
|
6
|
+
/**
|
|
7
|
+
* @param message The error message
|
|
8
|
+
*/
|
|
6
9
|
constructor(message: string);
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
12
|
* An error thrown when the maximum allowed size of a header is exceeded.
|
|
10
13
|
*/
|
|
11
14
|
export declare class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
15
|
+
/**
|
|
16
|
+
* @param maxHeaderSize The maximum header size that was exceeded
|
|
17
|
+
*/
|
|
12
18
|
constructor(maxHeaderSize: number);
|
|
13
19
|
}
|
|
14
20
|
/**
|
|
15
21
|
* An error thrown when the maximum allowed size of a file is exceeded.
|
|
16
22
|
*/
|
|
17
23
|
export declare class MaxFileSizeExceededError extends MultipartParseError {
|
|
24
|
+
/**
|
|
25
|
+
* @param maxFileSize The maximum file size that was exceeded
|
|
26
|
+
*/
|
|
18
27
|
constructor(maxFileSize: number);
|
|
19
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Options for parsing a multipart message.
|
|
31
|
+
*/
|
|
20
32
|
export interface ParseMultipartOptions {
|
|
21
33
|
/**
|
|
22
34
|
* The boundary string used to separate parts in the multipart message,
|
|
@@ -27,14 +39,14 @@ export interface ParseMultipartOptions {
|
|
|
27
39
|
* The maximum allowed size of a header in bytes. If an individual part's header
|
|
28
40
|
* exceeds this size, a `MaxHeaderSizeExceededError` will be thrown.
|
|
29
41
|
*
|
|
30
|
-
*
|
|
42
|
+
* @default 8192 (8 KiB)
|
|
31
43
|
*/
|
|
32
44
|
maxHeaderSize?: number;
|
|
33
45
|
/**
|
|
34
46
|
* The maximum allowed size of a file in bytes. If an individual part's content
|
|
35
47
|
* exceeds this size, a `MaxFileSizeExceededError` will be thrown.
|
|
36
48
|
*
|
|
37
|
-
*
|
|
49
|
+
* @default 2097152 (2 MiB)
|
|
38
50
|
*/
|
|
39
51
|
maxFileSize?: number;
|
|
40
52
|
}
|
|
@@ -60,6 +72,9 @@ export declare function parseMultipart(message: Uint8Array | Iterable<Uint8Array
|
|
|
60
72
|
* @return An async generator that yields `MultipartPart` objects
|
|
61
73
|
*/
|
|
62
74
|
export declare function parseMultipartStream(stream: ReadableStream<Uint8Array>, options: ParseMultipartOptions): AsyncGenerator<MultipartPart, void, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* Options for configuring a `MultipartParser`.
|
|
77
|
+
*/
|
|
63
78
|
export type MultipartParserOptions = Omit<ParseMultipartOptions, 'boundary'>;
|
|
64
79
|
/**
|
|
65
80
|
* A streaming parser for `multipart/*` HTTP messages.
|
|
@@ -69,6 +84,10 @@ export declare class MultipartParser {
|
|
|
69
84
|
readonly boundary: string;
|
|
70
85
|
readonly maxHeaderSize: number;
|
|
71
86
|
readonly maxFileSize: number;
|
|
87
|
+
/**
|
|
88
|
+
* @param boundary The boundary string used to separate parts
|
|
89
|
+
* @param options Options for the parser
|
|
90
|
+
*/
|
|
72
91
|
constructor(boundary: string, options?: MultipartParserOptions);
|
|
73
92
|
/**
|
|
74
93
|
* Write a chunk of data to the parser.
|
|
@@ -96,6 +115,10 @@ export declare class MultipartPart {
|
|
|
96
115
|
* The raw content of this part as an array of `Uint8Array` chunks.
|
|
97
116
|
*/
|
|
98
117
|
readonly content: Uint8Array[];
|
|
118
|
+
/**
|
|
119
|
+
* @param header The raw header bytes
|
|
120
|
+
* @param content The content chunks
|
|
121
|
+
*/
|
|
99
122
|
constructor(header: Uint8Array, content: Uint8Array[]);
|
|
100
123
|
/**
|
|
101
124
|
* The content of this part as an `ArrayBuffer`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../../src/lib/multipart.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../../src/lib/multipart.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,oBAAoB,CAAA;AAUxC;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C;;OAEG;gBACS,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IACjE;;OAEG;gBACS,aAAa,EAAE,MAAM;CAIlC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,mBAAmB;IAC/D;;OAEG;gBACS,WAAW,EAAE,MAAM;CAIhC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAiB,cAAc,CAC7B,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,EAC1C,OAAO,EAAE,qBAAqB,GAC7B,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAmBzC;AAED;;;;;;;;;GASG;AACH,wBAAuB,oBAAoB,CACzC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAe9C;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAAA;AAa5E;;GAEG;AACH,qBAAa,eAAe;;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAa5B;;;OAGG;gBACS,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAY9D;;;;;OAKG;IACF,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC;IA0HlE;;;;;;;OAOG;IACH,MAAM,IAAI,IAAI;CAKf;AAID;;GAEG;AACH,qBAAa,aAAa;;IACxB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAA;IAK9B;;;OAGG;gBACS,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE;IAKrD;;OAEG;IACH,IAAI,WAAW,IAAI,WAAW,CAE7B;IAED;;;OAGG;IACH,IAAI,KAAK,IAAI,UAAU,CAUtB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAMrB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAQjB;IAED;;;;;OAKG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
package/dist/lib/multipart.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import Headers from '@remix-run/headers';
|
|
2
|
+
import { createSearch, createPartialTailSearch, } from "./buffer-search.js";
|
|
2
3
|
import { readStream } from "./read-stream.js";
|
|
3
|
-
import { createSearch, createPartialTailSearch } from "./buffer-search.js";
|
|
4
4
|
/**
|
|
5
5
|
* The base class for errors thrown by the multipart parser.
|
|
6
6
|
*/
|
|
7
7
|
export class MultipartParseError extends Error {
|
|
8
|
+
/**
|
|
9
|
+
* @param message The error message
|
|
10
|
+
*/
|
|
8
11
|
constructor(message) {
|
|
9
12
|
super(message);
|
|
10
13
|
this.name = 'MultipartParseError';
|
|
@@ -14,6 +17,9 @@ export class MultipartParseError extends Error {
|
|
|
14
17
|
* An error thrown when the maximum allowed size of a header is exceeded.
|
|
15
18
|
*/
|
|
16
19
|
export class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
20
|
+
/**
|
|
21
|
+
* @param maxHeaderSize The maximum header size that was exceeded
|
|
22
|
+
*/
|
|
17
23
|
constructor(maxHeaderSize) {
|
|
18
24
|
super(`Multipart header size exceeds maximum allowed size of ${maxHeaderSize} bytes`);
|
|
19
25
|
this.name = 'MaxHeaderSizeExceededError';
|
|
@@ -23,6 +29,9 @@ export class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
|
23
29
|
* An error thrown when the maximum allowed size of a file is exceeded.
|
|
24
30
|
*/
|
|
25
31
|
export class MaxFileSizeExceededError extends MultipartParseError {
|
|
32
|
+
/**
|
|
33
|
+
* @param maxFileSize The maximum file size that was exceeded
|
|
34
|
+
*/
|
|
26
35
|
constructor(maxFileSize) {
|
|
27
36
|
super(`File size exceeds maximum allowed size of ${maxFileSize} bytes`);
|
|
28
37
|
this.name = 'MaxFileSizeExceededError';
|
|
@@ -103,6 +112,10 @@ export class MultipartParser {
|
|
|
103
112
|
#buffer = null;
|
|
104
113
|
#currentPart = null;
|
|
105
114
|
#contentLength = 0;
|
|
115
|
+
/**
|
|
116
|
+
* @param boundary The boundary string used to separate parts
|
|
117
|
+
* @param options Options for the parser
|
|
118
|
+
*/
|
|
106
119
|
constructor(boundary, options) {
|
|
107
120
|
this.boundary = boundary;
|
|
108
121
|
this.maxHeaderSize = options?.maxHeaderSize ?? 8 * oneKb;
|
|
@@ -236,6 +249,10 @@ export class MultipartPart {
|
|
|
236
249
|
content;
|
|
237
250
|
#header;
|
|
238
251
|
#headers;
|
|
252
|
+
/**
|
|
253
|
+
* @param header The raw header bytes
|
|
254
|
+
* @param content The content chunks
|
|
255
|
+
*/
|
|
239
256
|
constructor(header, content) {
|
|
240
257
|
this.#header = header;
|
|
241
258
|
this.content = content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/multipart-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "A fast, efficient parser for multipart streams in any JavaScript environment",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"./package.json": "./package.json"
|
|
31
31
|
},
|
|
32
|
-
"
|
|
33
|
-
"@remix-run/headers": "^0.
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@remix-run/headers": "^0.18.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^24.6.0",
|
package/src/lib/multipart.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import Headers from '@remix-run/headers'
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
createSearch,
|
|
5
|
+
createPartialTailSearch,
|
|
6
|
+
type SearchFunction,
|
|
7
|
+
type PartialTailSearchFunction,
|
|
8
|
+
} from './buffer-search.ts'
|
|
3
9
|
import { readStream } from './read-stream.ts'
|
|
4
|
-
import type { SearchFunction, PartialTailSearchFunction } from './buffer-search.ts'
|
|
5
|
-
import { createSearch, createPartialTailSearch } from './buffer-search.ts'
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* The base class for errors thrown by the multipart parser.
|
|
9
13
|
*/
|
|
10
14
|
export class MultipartParseError extends Error {
|
|
15
|
+
/**
|
|
16
|
+
* @param message The error message
|
|
17
|
+
*/
|
|
11
18
|
constructor(message: string) {
|
|
12
19
|
super(message)
|
|
13
20
|
this.name = 'MultipartParseError'
|
|
@@ -18,6 +25,9 @@ export class MultipartParseError extends Error {
|
|
|
18
25
|
* An error thrown when the maximum allowed size of a header is exceeded.
|
|
19
26
|
*/
|
|
20
27
|
export class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
28
|
+
/**
|
|
29
|
+
* @param maxHeaderSize The maximum header size that was exceeded
|
|
30
|
+
*/
|
|
21
31
|
constructor(maxHeaderSize: number) {
|
|
22
32
|
super(`Multipart header size exceeds maximum allowed size of ${maxHeaderSize} bytes`)
|
|
23
33
|
this.name = 'MaxHeaderSizeExceededError'
|
|
@@ -28,12 +38,18 @@ export class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
|
28
38
|
* An error thrown when the maximum allowed size of a file is exceeded.
|
|
29
39
|
*/
|
|
30
40
|
export class MaxFileSizeExceededError extends MultipartParseError {
|
|
41
|
+
/**
|
|
42
|
+
* @param maxFileSize The maximum file size that was exceeded
|
|
43
|
+
*/
|
|
31
44
|
constructor(maxFileSize: number) {
|
|
32
45
|
super(`File size exceeds maximum allowed size of ${maxFileSize} bytes`)
|
|
33
46
|
this.name = 'MaxFileSizeExceededError'
|
|
34
47
|
}
|
|
35
48
|
}
|
|
36
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Options for parsing a multipart message.
|
|
52
|
+
*/
|
|
37
53
|
export interface ParseMultipartOptions {
|
|
38
54
|
/**
|
|
39
55
|
* The boundary string used to separate parts in the multipart message,
|
|
@@ -44,14 +60,14 @@ export interface ParseMultipartOptions {
|
|
|
44
60
|
* The maximum allowed size of a header in bytes. If an individual part's header
|
|
45
61
|
* exceeds this size, a `MaxHeaderSizeExceededError` will be thrown.
|
|
46
62
|
*
|
|
47
|
-
*
|
|
63
|
+
* @default 8192 (8 KiB)
|
|
48
64
|
*/
|
|
49
65
|
maxHeaderSize?: number
|
|
50
66
|
/**
|
|
51
67
|
* The maximum allowed size of a file in bytes. If an individual part's content
|
|
52
68
|
* exceeds this size, a `MaxFileSizeExceededError` will be thrown.
|
|
53
69
|
*
|
|
54
|
-
*
|
|
70
|
+
* @default 2097152 (2 MiB)
|
|
55
71
|
*/
|
|
56
72
|
maxFileSize?: number
|
|
57
73
|
}
|
|
@@ -120,6 +136,9 @@ export async function* parseMultipartStream(
|
|
|
120
136
|
parser.finish()
|
|
121
137
|
}
|
|
122
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Options for configuring a `MultipartParser`.
|
|
141
|
+
*/
|
|
123
142
|
export type MultipartParserOptions = Omit<ParseMultipartOptions, 'boundary'>
|
|
124
143
|
|
|
125
144
|
const MultipartParserStateStart = 0
|
|
@@ -152,6 +171,10 @@ export class MultipartParser {
|
|
|
152
171
|
#currentPart: MultipartPart | null = null
|
|
153
172
|
#contentLength = 0
|
|
154
173
|
|
|
174
|
+
/**
|
|
175
|
+
* @param boundary The boundary string used to separate parts
|
|
176
|
+
* @param options Options for the parser
|
|
177
|
+
*/
|
|
155
178
|
constructor(boundary: string, options?: MultipartParserOptions) {
|
|
156
179
|
this.boundary = boundary
|
|
157
180
|
this.maxHeaderSize = options?.maxHeaderSize ?? 8 * oneKb
|
|
@@ -321,6 +344,10 @@ export class MultipartPart {
|
|
|
321
344
|
#header: Uint8Array
|
|
322
345
|
#headers?: Headers
|
|
323
346
|
|
|
347
|
+
/**
|
|
348
|
+
* @param header The raw header bytes
|
|
349
|
+
* @param content The content chunks
|
|
350
|
+
*/
|
|
324
351
|
constructor(header: Uint8Array, content: Uint8Array[]) {
|
|
325
352
|
this.#header = header
|
|
326
353
|
this.content = content
|