@remix-run/multipart-parser 0.11.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/LICENSE +21 -0
- package/README.md +235 -0
- package/dist/lib/buffer-search.d.ts +9 -0
- package/dist/lib/buffer-search.d.ts.map +1 -0
- package/dist/lib/multipart-request.d.ts +25 -0
- package/dist/lib/multipart-request.d.ts.map +1 -0
- package/dist/lib/multipart.d.ts +145 -0
- package/dist/lib/multipart.d.ts.map +1 -0
- package/dist/lib/multipart.node.d.ts +41 -0
- package/dist/lib/multipart.node.d.ts.map +1 -0
- package/dist/lib/read-stream.d.ts +2 -0
- package/dist/lib/read-stream.d.ts.map +1 -0
- package/dist/multipart-parser.cjs +2021 -0
- package/dist/multipart-parser.cjs.map +7 -0
- package/dist/multipart-parser.d.ts +4 -0
- package/dist/multipart-parser.d.ts.map +1 -0
- package/dist/multipart-parser.js +1985 -0
- package/dist/multipart-parser.js.map +7 -0
- package/dist/multipart-parser.node.cjs +2027 -0
- package/dist/multipart-parser.node.cjs.map +7 -0
- package/dist/multipart-parser.node.d.ts +5 -0
- package/dist/multipart-parser.node.d.ts.map +1 -0
- package/dist/multipart-parser.node.js +1991 -0
- package/dist/multipart-parser.node.js.map +7 -0
- package/package.json +66 -0
- package/src/lib/buffer-search.ts +67 -0
- package/src/lib/multipart-request.ts +55 -0
- package/src/lib/multipart.node.ts +82 -0
- package/src/lib/multipart.ts +420 -0
- package/src/lib/read-stream.ts +15 -0
- package/src/multipart-parser.node.ts +14 -0
- package/src/multipart-parser.ts +16 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import Headers from '@remix-run/headers';
|
|
2
|
+
|
|
3
|
+
import { readStream } from './read-stream.ts';
|
|
4
|
+
import type { SearchFunction, PartialTailSearchFunction } from './buffer-search.ts';
|
|
5
|
+
import { createSearch, createPartialTailSearch } from './buffer-search.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The base class for errors thrown by the multipart parser.
|
|
9
|
+
*/
|
|
10
|
+
export class MultipartParseError extends Error {
|
|
11
|
+
constructor(message: string) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'MultipartParseError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An error thrown when the maximum allowed size of a header is exceeded.
|
|
19
|
+
*/
|
|
20
|
+
export class MaxHeaderSizeExceededError extends MultipartParseError {
|
|
21
|
+
constructor(maxHeaderSize: number) {
|
|
22
|
+
super(`Multipart header size exceeds maximum allowed size of ${maxHeaderSize} bytes`);
|
|
23
|
+
this.name = 'MaxHeaderSizeExceededError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* An error thrown when the maximum allowed size of a file is exceeded.
|
|
29
|
+
*/
|
|
30
|
+
export class MaxFileSizeExceededError extends MultipartParseError {
|
|
31
|
+
constructor(maxFileSize: number) {
|
|
32
|
+
super(`File size exceeds maximum allowed size of ${maxFileSize} bytes`);
|
|
33
|
+
this.name = 'MaxFileSizeExceededError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ParseMultipartOptions {
|
|
38
|
+
/**
|
|
39
|
+
* The boundary string used to separate parts in the multipart message,
|
|
40
|
+
* e.g. the `boundary` parameter in the `Content-Type` header.
|
|
41
|
+
*/
|
|
42
|
+
boundary: string;
|
|
43
|
+
/**
|
|
44
|
+
* The maximum allowed size of a header in bytes. If an individual part's header
|
|
45
|
+
* exceeds this size, a `MaxHeaderSizeExceededError` will be thrown.
|
|
46
|
+
*
|
|
47
|
+
* Default: 8 KiB
|
|
48
|
+
*/
|
|
49
|
+
maxHeaderSize?: number;
|
|
50
|
+
/**
|
|
51
|
+
* The maximum allowed size of a file in bytes. If an individual part's content
|
|
52
|
+
* exceeds this size, a `MaxFileSizeExceededError` will be thrown.
|
|
53
|
+
*
|
|
54
|
+
* Default: 2 MiB
|
|
55
|
+
*/
|
|
56
|
+
maxFileSize?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse a `multipart/*` message from a buffer/iterable and yield each part as a `MultipartPart` object.
|
|
61
|
+
*
|
|
62
|
+
* Note: This is a low-level API that requires manual handling of the content and boundary. If you're
|
|
63
|
+
* building a web server, consider using `parseMultipartRequest(request)` instead.
|
|
64
|
+
*
|
|
65
|
+
* @param message The multipart message as a `Uint8Array` or an iterable of `Uint8Array` chunks
|
|
66
|
+
* @param options Options for the parser
|
|
67
|
+
* @return A generator that yields `MultipartPart` objects
|
|
68
|
+
*/
|
|
69
|
+
export function* parseMultipart(
|
|
70
|
+
message: Uint8Array | Iterable<Uint8Array>,
|
|
71
|
+
options: ParseMultipartOptions,
|
|
72
|
+
): Generator<MultipartPart, void, unknown> {
|
|
73
|
+
let parser = new MultipartParser(options.boundary, {
|
|
74
|
+
maxHeaderSize: options.maxHeaderSize,
|
|
75
|
+
maxFileSize: options.maxFileSize,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (message instanceof Uint8Array) {
|
|
79
|
+
if (message.length === 0) {
|
|
80
|
+
return; // No data to parse
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
yield* parser.write(message);
|
|
84
|
+
} else {
|
|
85
|
+
for (let chunk of message) {
|
|
86
|
+
yield* parser.write(chunk);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
parser.finish();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Parse a `multipart/*` message stream and yield each part as a `MultipartPart` object.
|
|
95
|
+
*
|
|
96
|
+
* Note: This is a low-level API that requires manual handling of the content and boundary. If you're
|
|
97
|
+
* building a web server, consider using `parseMultipartRequest(request)` instead.
|
|
98
|
+
*
|
|
99
|
+
* @param stream A stream containing multipart data as a `ReadableStream<Uint8Array>`
|
|
100
|
+
* @param options Options for the parser
|
|
101
|
+
* @return An async generator that yields `MultipartPart` objects
|
|
102
|
+
*/
|
|
103
|
+
export async function* parseMultipartStream(
|
|
104
|
+
stream: ReadableStream<Uint8Array>,
|
|
105
|
+
options: ParseMultipartOptions,
|
|
106
|
+
): AsyncGenerator<MultipartPart, void, unknown> {
|
|
107
|
+
let parser = new MultipartParser(options.boundary, {
|
|
108
|
+
maxHeaderSize: options.maxHeaderSize,
|
|
109
|
+
maxFileSize: options.maxFileSize,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
for await (let chunk of readStream(stream)) {
|
|
113
|
+
if (chunk.length === 0) {
|
|
114
|
+
continue; // No data to parse
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
yield* parser.write(chunk);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
parser.finish();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type MultipartParserOptions = Omit<ParseMultipartOptions, 'boundary'>;
|
|
124
|
+
|
|
125
|
+
const MultipartParserStateStart = 0;
|
|
126
|
+
const MultipartParserStateAfterBoundary = 1;
|
|
127
|
+
const MultipartParserStateHeader = 2;
|
|
128
|
+
const MultipartParserStateBody = 3;
|
|
129
|
+
const MultipartParserStateDone = 4;
|
|
130
|
+
|
|
131
|
+
const findDoubleNewline = createSearch('\r\n\r\n');
|
|
132
|
+
|
|
133
|
+
const oneKb = 1024;
|
|
134
|
+
const oneMb = 1024 * oneKb;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A streaming parser for `multipart/*` HTTP messages.
|
|
138
|
+
*/
|
|
139
|
+
export class MultipartParser {
|
|
140
|
+
readonly boundary: string;
|
|
141
|
+
readonly maxHeaderSize: number;
|
|
142
|
+
readonly maxFileSize: number;
|
|
143
|
+
|
|
144
|
+
#findOpeningBoundary: SearchFunction;
|
|
145
|
+
#openingBoundaryLength: number;
|
|
146
|
+
#findBoundary: SearchFunction;
|
|
147
|
+
#findPartialTailBoundary: PartialTailSearchFunction;
|
|
148
|
+
#boundaryLength: number;
|
|
149
|
+
|
|
150
|
+
#state = MultipartParserStateStart;
|
|
151
|
+
#buffer: Uint8Array | null = null;
|
|
152
|
+
#currentPart: MultipartPart | null = null;
|
|
153
|
+
#contentLength = 0;
|
|
154
|
+
|
|
155
|
+
constructor(boundary: string, options?: MultipartParserOptions) {
|
|
156
|
+
this.boundary = boundary;
|
|
157
|
+
this.maxHeaderSize = options?.maxHeaderSize ?? 8 * oneKb;
|
|
158
|
+
this.maxFileSize = options?.maxFileSize ?? 2 * oneMb;
|
|
159
|
+
|
|
160
|
+
this.#findOpeningBoundary = createSearch(`--${boundary}`);
|
|
161
|
+
this.#openingBoundaryLength = 2 + boundary.length; // length of '--' + boundary
|
|
162
|
+
this.#findBoundary = createSearch(`\r\n--${boundary}`);
|
|
163
|
+
this.#findPartialTailBoundary = createPartialTailSearch(`\r\n--${boundary}`);
|
|
164
|
+
this.#boundaryLength = 4 + boundary.length; // length of '\r\n--' + boundary
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Write a chunk of data to the parser.
|
|
169
|
+
*
|
|
170
|
+
* @param chunk A chunk of data to write to the parser
|
|
171
|
+
* @return A generator yielding `MultipartPart` objects as they are parsed
|
|
172
|
+
*/
|
|
173
|
+
*write(chunk: Uint8Array): Generator<MultipartPart, void, unknown> {
|
|
174
|
+
if (this.#state === MultipartParserStateDone) {
|
|
175
|
+
throw new MultipartParseError('Unexpected data after end of stream');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let index = 0;
|
|
179
|
+
let chunkLength = chunk.length;
|
|
180
|
+
|
|
181
|
+
if (this.#buffer !== null) {
|
|
182
|
+
let newChunk = new Uint8Array(this.#buffer.length + chunkLength);
|
|
183
|
+
newChunk.set(this.#buffer, 0);
|
|
184
|
+
newChunk.set(chunk, this.#buffer.length);
|
|
185
|
+
chunk = newChunk;
|
|
186
|
+
chunkLength = chunk.length;
|
|
187
|
+
this.#buffer = null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
while (true) {
|
|
191
|
+
if (this.#state === MultipartParserStateBody) {
|
|
192
|
+
if (chunkLength - index < this.#boundaryLength) {
|
|
193
|
+
this.#buffer = chunk.subarray(index);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let boundaryIndex = this.#findBoundary(chunk, index);
|
|
198
|
+
|
|
199
|
+
if (boundaryIndex === -1) {
|
|
200
|
+
// No boundary found, but there may be a partial match at the end of the chunk.
|
|
201
|
+
let partialTailIndex = this.#findPartialTailBoundary(chunk);
|
|
202
|
+
|
|
203
|
+
if (partialTailIndex === -1) {
|
|
204
|
+
this.#append(index === 0 ? chunk : chunk.subarray(index));
|
|
205
|
+
} else {
|
|
206
|
+
this.#append(chunk.subarray(index, partialTailIndex));
|
|
207
|
+
this.#buffer = chunk.subarray(partialTailIndex);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.#append(chunk.subarray(index, boundaryIndex));
|
|
214
|
+
|
|
215
|
+
yield this.#currentPart!;
|
|
216
|
+
|
|
217
|
+
index = boundaryIndex + this.#boundaryLength;
|
|
218
|
+
|
|
219
|
+
this.#state = MultipartParserStateAfterBoundary;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (this.#state === MultipartParserStateAfterBoundary) {
|
|
223
|
+
if (chunkLength - index < 2) {
|
|
224
|
+
this.#buffer = chunk.subarray(index);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (chunk[index] === 45 && chunk[index + 1] === 45) {
|
|
229
|
+
this.#state = MultipartParserStateDone;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
index += 2; // Skip \r\n after boundary
|
|
234
|
+
|
|
235
|
+
this.#state = MultipartParserStateHeader;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this.#state === MultipartParserStateHeader) {
|
|
239
|
+
if (chunkLength - index < 4) {
|
|
240
|
+
this.#buffer = chunk.subarray(index);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let headerEndIndex = findDoubleNewline(chunk, index);
|
|
245
|
+
|
|
246
|
+
if (headerEndIndex === -1) {
|
|
247
|
+
if (chunkLength - index > this.maxHeaderSize) {
|
|
248
|
+
throw new MaxHeaderSizeExceededError(this.maxHeaderSize);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
this.#buffer = chunk.subarray(index);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (headerEndIndex - index > this.maxHeaderSize) {
|
|
256
|
+
throw new MaxHeaderSizeExceededError(this.maxHeaderSize);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.#currentPart = new MultipartPart(chunk.subarray(index, headerEndIndex), []);
|
|
260
|
+
this.#contentLength = 0;
|
|
261
|
+
|
|
262
|
+
index = headerEndIndex + 4; // Skip header + \r\n\r\n
|
|
263
|
+
|
|
264
|
+
this.#state = MultipartParserStateBody;
|
|
265
|
+
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (this.#state === MultipartParserStateStart) {
|
|
270
|
+
if (chunkLength < this.#openingBoundaryLength) {
|
|
271
|
+
this.#buffer = chunk;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.#findOpeningBoundary(chunk) !== 0) {
|
|
276
|
+
throw new MultipartParseError('Invalid multipart stream: missing initial boundary');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
index = this.#openingBoundaryLength;
|
|
280
|
+
|
|
281
|
+
this.#state = MultipartParserStateAfterBoundary;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
#append(chunk: Uint8Array): void {
|
|
287
|
+
if (this.#contentLength + chunk.length > this.maxFileSize) {
|
|
288
|
+
throw new MaxFileSizeExceededError(this.maxFileSize);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.#currentPart!.content.push(chunk);
|
|
292
|
+
this.#contentLength += chunk.length;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Should be called after all data has been written to the parser.
|
|
297
|
+
*
|
|
298
|
+
* Note: This will throw if the multipart message is incomplete or
|
|
299
|
+
* wasn't properly terminated.
|
|
300
|
+
*
|
|
301
|
+
* @return void
|
|
302
|
+
*/
|
|
303
|
+
finish(): void {
|
|
304
|
+
if (this.#state !== MultipartParserStateDone) {
|
|
305
|
+
throw new MultipartParseError('Multipart stream not finished');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A part of a `multipart/*` HTTP message.
|
|
314
|
+
*/
|
|
315
|
+
export class MultipartPart {
|
|
316
|
+
/**
|
|
317
|
+
* The raw content of this part as an array of `Uint8Array` chunks.
|
|
318
|
+
*/
|
|
319
|
+
readonly content: Uint8Array[];
|
|
320
|
+
|
|
321
|
+
#header: Uint8Array;
|
|
322
|
+
#headers?: Headers;
|
|
323
|
+
|
|
324
|
+
constructor(header: Uint8Array, content: Uint8Array[]) {
|
|
325
|
+
this.#header = header;
|
|
326
|
+
this.content = content;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* The content of this part as an `ArrayBuffer`.
|
|
331
|
+
*/
|
|
332
|
+
get arrayBuffer(): ArrayBuffer {
|
|
333
|
+
return this.bytes.buffer as ArrayBuffer;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* The content of this part as a single `Uint8Array`. In `multipart/form-data` messages, this is useful
|
|
338
|
+
* for reading the value of files that were uploaded using `<input type="file">` fields.
|
|
339
|
+
*/
|
|
340
|
+
get bytes(): Uint8Array {
|
|
341
|
+
let buffer = new Uint8Array(this.size);
|
|
342
|
+
|
|
343
|
+
let offset = 0;
|
|
344
|
+
for (let chunk of this.content) {
|
|
345
|
+
buffer.set(chunk, offset);
|
|
346
|
+
offset += chunk.length;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return buffer;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The headers associated with this part.
|
|
354
|
+
*/
|
|
355
|
+
get headers(): Headers {
|
|
356
|
+
if (!this.#headers) {
|
|
357
|
+
this.#headers = new Headers(decoder.decode(this.#header));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return this.#headers;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* True if this part originated from a file upload.
|
|
365
|
+
*/
|
|
366
|
+
get isFile(): boolean {
|
|
367
|
+
return this.filename !== undefined || this.mediaType === 'application/octet-stream';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* True if this part originated from a text input field in a form submission.
|
|
372
|
+
*/
|
|
373
|
+
get isText(): boolean {
|
|
374
|
+
return !this.isFile;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* The filename of the part, if it is a file upload.
|
|
379
|
+
*/
|
|
380
|
+
get filename(): string | undefined {
|
|
381
|
+
return this.headers.contentDisposition.preferredFilename;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The media type of the part.
|
|
386
|
+
*/
|
|
387
|
+
get mediaType(): string | undefined {
|
|
388
|
+
return this.headers.contentType.mediaType;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* The name of the part, usually the `name` of the field in the `<form>` that submitted the request.
|
|
393
|
+
*/
|
|
394
|
+
get name(): string | undefined {
|
|
395
|
+
return this.headers.contentDisposition.name;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* The size of the content in bytes.
|
|
400
|
+
*/
|
|
401
|
+
get size(): number {
|
|
402
|
+
let size = 0;
|
|
403
|
+
|
|
404
|
+
for (let chunk of this.content) {
|
|
405
|
+
size += chunk.length;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return size;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* The content of this part as a string. In `multipart/form-data` messages, this is useful for
|
|
413
|
+
* reading the value of parts that originated from `<input type="text">` fields.
|
|
414
|
+
*
|
|
415
|
+
* Note: Do not use this for binary data, use `part.bytes` or `part.arrayBuffer` instead.
|
|
416
|
+
*/
|
|
417
|
+
get text(): string {
|
|
418
|
+
return decoder.decode(this.bytes);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// We need this little helper for environments that do not support
|
|
2
|
+
// ReadableStream.prototype[Symbol.asyncIterator] yet. See #46
|
|
3
|
+
export async function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> {
|
|
4
|
+
let reader = stream.getReader();
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
while (true) {
|
|
8
|
+
const { done, value } = await reader.read();
|
|
9
|
+
if (done) break;
|
|
10
|
+
yield value;
|
|
11
|
+
}
|
|
12
|
+
} finally {
|
|
13
|
+
reader.releaseLock();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Re-export all core functionality
|
|
2
|
+
export type { ParseMultipartOptions, MultipartParserOptions } from './lib/multipart.ts';
|
|
3
|
+
export {
|
|
4
|
+
MultipartParseError,
|
|
5
|
+
MaxHeaderSizeExceededError,
|
|
6
|
+
MaxFileSizeExceededError,
|
|
7
|
+
MultipartParser,
|
|
8
|
+
MultipartPart,
|
|
9
|
+
} from './lib/multipart.ts';
|
|
10
|
+
|
|
11
|
+
export { getMultipartBoundary } from './lib/multipart-request.ts';
|
|
12
|
+
|
|
13
|
+
// Export Node.js-specific functionality
|
|
14
|
+
export { isMultipartRequest, parseMultipartRequest, parseMultipart, parseMultipartStream } from './lib/multipart.node.ts';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { ParseMultipartOptions, MultipartParserOptions } from './lib/multipart.ts';
|
|
2
|
+
export {
|
|
3
|
+
MultipartParseError,
|
|
4
|
+
MaxHeaderSizeExceededError,
|
|
5
|
+
MaxFileSizeExceededError,
|
|
6
|
+
parseMultipart,
|
|
7
|
+
parseMultipartStream,
|
|
8
|
+
MultipartParser,
|
|
9
|
+
MultipartPart,
|
|
10
|
+
} from './lib/multipart.ts';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
getMultipartBoundary,
|
|
14
|
+
isMultipartRequest,
|
|
15
|
+
parseMultipartRequest,
|
|
16
|
+
} from './lib/multipart-request.ts';
|