@nestjs/platform-fastify 11.2.5 → 11.2.6
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/interfaces/external/fastify-multipart-options.interface.d.ts +53 -0
- package/interfaces/external/fastify-multipart-options.interface.js +1 -0
- package/multipart/files.constants.d.ts +1 -0
- package/multipart/files.constants.js +1 -0
- package/multipart/index.d.ts +4 -0
- package/multipart/index.js +4 -0
- package/multipart/interceptors/any-files.interceptor.d.ts +12 -0
- package/multipart/interceptors/any-files.interceptor.js +13 -0
- package/multipart/interceptors/file-fields.interceptor.d.ts +13 -0
- package/multipart/interceptors/file-fields.interceptor.js +14 -0
- package/multipart/interceptors/file-stream.interceptor.d.ts +31 -0
- package/multipart/interceptors/file-stream.interceptor.js +32 -0
- package/multipart/interceptors/file.interceptor.d.ts +17 -0
- package/multipart/interceptors/file.interceptor.js +18 -0
- package/multipart/interceptors/files.interceptor.d.ts +14 -0
- package/multipart/interceptors/files.interceptor.js +15 -0
- package/multipart/interceptors/index.d.ts +6 -0
- package/multipart/interceptors/index.js +6 -0
- package/multipart/interceptors/no-files.interceptor.d.ts +12 -0
- package/multipart/interceptors/no-files.interceptor.js +13 -0
- package/multipart/interfaces/files-upload-module.interface.d.ts +18 -0
- package/multipart/interfaces/files-upload-module.interface.js +1 -0
- package/multipart/interfaces/index.d.ts +3 -0
- package/multipart/interfaces/index.js +3 -0
- package/multipart/interfaces/multipart-file.interface.d.ts +70 -0
- package/multipart/interfaces/multipart-file.interface.js +1 -0
- package/multipart/interfaces/multipart-options.interface.d.ts +77 -0
- package/multipart/interfaces/multipart-options.interface.js +1 -0
- package/multipart/multipart/append-field.util.d.ts +6 -0
- package/multipart/multipart/append-field.util.js +112 -0
- package/multipart/multipart/multipart-interceptor.factory.d.ts +13 -0
- package/multipart/multipart/multipart-interceptor.factory.js +130 -0
- package/multipart/multipart/multipart.constants.d.ts +55 -0
- package/multipart/multipart/multipart.constants.js +74 -0
- package/multipart/multipart/multipart.error.d.ts +10 -0
- package/multipart/multipart/multipart.error.js +15 -0
- package/multipart/multipart/multipart.parser.d.ts +39 -0
- package/multipart/multipart/multipart.parser.js +369 -0
- package/multipart/multipart/multipart.utils.d.ts +28 -0
- package/multipart/multipart/multipart.utils.js +54 -0
- package/multipart/multipart.constants.d.ts +1 -0
- package/multipart/multipart.constants.js +1 -0
- package/multipart/multipart.module.d.ts +18 -0
- package/multipart/multipart.module.js +75 -0
- package/multipart/storage/disk.storage.d.ts +24 -0
- package/multipart/storage/disk.storage.js +58 -0
- package/multipart/storage/index.d.ts +2 -0
- package/multipart/storage/index.js +2 -0
- package/multipart/storage/memory.storage.d.ts +8 -0
- package/multipart/storage/memory.storage.js +23 -0
- package/package.json +2 -2
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "@fastify/multipart" plugin options, as accepted by the FastifyAdapter's
|
|
3
|
+
* `multipart` option. Declared locally so the adapter's typings do not
|
|
4
|
+
* require the (optional) plugin to be installed.
|
|
5
|
+
*
|
|
6
|
+
* `attachFieldsToBody` is deliberately left out: it consumes the body before
|
|
7
|
+
* the upload interceptors from "@nestjs/platform-fastify/multipart" run.
|
|
8
|
+
*
|
|
9
|
+
* @see https://github.com/fastify/fastify-multipart/blob/main/types/index.d.ts
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export interface FastifyMultipartOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Limits applied to every multipart request. The upload interceptors merge
|
|
15
|
+
* their own `limits` over these.
|
|
16
|
+
*/
|
|
17
|
+
limits?: {
|
|
18
|
+
/**
|
|
19
|
+
* Max field name size in bytes. Not enforced by the plugin for multipart
|
|
20
|
+
* bodies; the upload interceptors enforce their own `limits.fieldNameSize`.
|
|
21
|
+
*/
|
|
22
|
+
fieldNameSize?: number;
|
|
23
|
+
/** Max field value size in bytes (Default: 1 MiB) */
|
|
24
|
+
fieldSize?: number;
|
|
25
|
+
/** Max number of non-file fields (Default: Infinity) */
|
|
26
|
+
fields?: number;
|
|
27
|
+
/** Max file size in bytes (Default: the Fastify `bodyLimit`, 1 MiB) */
|
|
28
|
+
fileSize?: number;
|
|
29
|
+
/** Max number of file fields (Default: Infinity) */
|
|
30
|
+
files?: number;
|
|
31
|
+
/** Max number of header key => value pairs (Default: 2000) */
|
|
32
|
+
headerPairs?: number;
|
|
33
|
+
/** Max number of parts, fields and files together (Default: 1000) */
|
|
34
|
+
parts?: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Throw when a file exceeds `limits.fileSize` while it is read through
|
|
38
|
+
* `req.file()` / `req.files()`. The upload interceptors always reject such
|
|
39
|
+
* a file, whatever this is set to.
|
|
40
|
+
*/
|
|
41
|
+
throwFileSizeLimit?: boolean;
|
|
42
|
+
/** Keep the full path of files instead of just the base name (Default: false) */
|
|
43
|
+
preservePath?: boolean;
|
|
44
|
+
/** Default character set for part header values (Default: 'latin1') */
|
|
45
|
+
defParamCharset?: string;
|
|
46
|
+
/** Default character set when one isn't defined (Default: 'utf8') */
|
|
47
|
+
defCharset?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Detect if a part is a file. By default a part is a file if its content
|
|
50
|
+
* type is `application/octet-stream` or it has a file name.
|
|
51
|
+
*/
|
|
52
|
+
isPartAFile?: (fieldName: string | undefined, contentType: string | undefined, fileName: string | undefined) => boolean;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MULTIPART_MODULE_OPTIONS = "MULTIPART_MODULE_OPTIONS";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MULTIPART_MODULE_OPTIONS = 'MULTIPART_MODULE_OPTIONS';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts every file, in any field, and exposes them as an array in
|
|
5
|
+
* `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `AnyFilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export declare function AnyFilesInterceptor(localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts every file, in any field, and exposes them as an array in
|
|
4
|
+
* `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `AnyFilesInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
8
|
+
*
|
|
9
|
+
* @publicApi
|
|
10
|
+
*/
|
|
11
|
+
export function AnyFilesInterceptor(localOptions) {
|
|
12
|
+
return createMultipartInterceptor({ fields: 'ANY', strategy: 'ARRAY' }, localOptions);
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartField, MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts files in each of `uploadFields` and exposes them grouped by field
|
|
5
|
+
* name in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `FileFieldsInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param uploadFields accepted fields, each with an optional `maxCount`
|
|
9
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
10
|
+
*
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
export declare function FileFieldsInterceptor(uploadFields: MultipartField[], localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts files in each of `uploadFields` and exposes them grouped by field
|
|
4
|
+
* name in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `FileFieldsInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param uploadFields accepted fields, each with an optional `maxCount`
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export function FileFieldsInterceptor(uploadFields, localOptions) {
|
|
13
|
+
return createMultipartInterceptor({ fields: uploadFields, strategy: 'OBJECT' }, localOptions);
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hands the route handler a single file in `fieldName` as a stream
|
|
5
|
+
* (`req.file.stream`, see `MultipartFileStream`), without buffering it or
|
|
6
|
+
* writing it to disk. Fastify only; there is no platform-express
|
|
7
|
+
* counterpart.
|
|
8
|
+
*
|
|
9
|
+
* - Text fields must precede the file in the form: parts that follow it are
|
|
10
|
+
* not parsed.
|
|
11
|
+
* - Whatever the handler leaves unread is discarded before the response is
|
|
12
|
+
* sent, so that the client can finish uploading. When the handler returns
|
|
13
|
+
* a `StreamableFile` (which may be the upload itself), it is discarded
|
|
14
|
+
* once the response has been sent instead. So a handler that responds
|
|
15
|
+
* with a `StreamableFile` of something other than the upload should read
|
|
16
|
+
* the upload first: otherwise, when the connection is not kept alive
|
|
17
|
+
* (`Connection: close`), Node.js closes it right after the response, and
|
|
18
|
+
* a client still uploading fails with EPIPE / ECONNRESET.
|
|
19
|
+
* - A file in any other field fails the request with a 400.
|
|
20
|
+
* - The file size is unknown up front, so `MaxFileSizeValidator` does not
|
|
21
|
+
* apply, and neither does `FileTypeValidator`'s magic-number check.
|
|
22
|
+
* - Once `limits.fileSize` is exceeded, the stream errors with a
|
|
23
|
+
* `PayloadTooLargeException`.
|
|
24
|
+
*
|
|
25
|
+
* @param fieldName name of the form field that carries the file
|
|
26
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
27
|
+
* (`dest` and `storage` do not apply)
|
|
28
|
+
*
|
|
29
|
+
* @publicApi
|
|
30
|
+
*/
|
|
31
|
+
export declare function FileStreamInterceptor(fieldName: string, localOptions?: Omit<MultipartOptions, 'dest' | 'storage'>): Type<NestInterceptor>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createMultipartStreamInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Hands the route handler a single file in `fieldName` as a stream
|
|
4
|
+
* (`req.file.stream`, see `MultipartFileStream`), without buffering it or
|
|
5
|
+
* writing it to disk. Fastify only; there is no platform-express
|
|
6
|
+
* counterpart.
|
|
7
|
+
*
|
|
8
|
+
* - Text fields must precede the file in the form: parts that follow it are
|
|
9
|
+
* not parsed.
|
|
10
|
+
* - Whatever the handler leaves unread is discarded before the response is
|
|
11
|
+
* sent, so that the client can finish uploading. When the handler returns
|
|
12
|
+
* a `StreamableFile` (which may be the upload itself), it is discarded
|
|
13
|
+
* once the response has been sent instead. So a handler that responds
|
|
14
|
+
* with a `StreamableFile` of something other than the upload should read
|
|
15
|
+
* the upload first: otherwise, when the connection is not kept alive
|
|
16
|
+
* (`Connection: close`), Node.js closes it right after the response, and
|
|
17
|
+
* a client still uploading fails with EPIPE / ECONNRESET.
|
|
18
|
+
* - A file in any other field fails the request with a 400.
|
|
19
|
+
* - The file size is unknown up front, so `MaxFileSizeValidator` does not
|
|
20
|
+
* apply, and neither does `FileTypeValidator`'s magic-number check.
|
|
21
|
+
* - Once `limits.fileSize` is exceeded, the stream errors with a
|
|
22
|
+
* `PayloadTooLargeException`.
|
|
23
|
+
*
|
|
24
|
+
* @param fieldName name of the form field that carries the file
|
|
25
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
26
|
+
* (`dest` and `storage` do not apply)
|
|
27
|
+
*
|
|
28
|
+
* @publicApi
|
|
29
|
+
*/
|
|
30
|
+
export function FileStreamInterceptor(fieldName, localOptions) {
|
|
31
|
+
return createMultipartStreamInterceptor(fieldName, localOptions);
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts a single file in `fieldName` and exposes it as `req.file`
|
|
5
|
+
* (`@UploadedFile()`), with the text fields in `req.body`. The Fastify
|
|
6
|
+
* counterpart of platform-express's `FileInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* Requires the `@fastify/multipart` package, which the FastifyAdapter
|
|
9
|
+
* registers as a plugin when an upload interceptor is used (see its
|
|
10
|
+
* `multipart` option).
|
|
11
|
+
*
|
|
12
|
+
* @param fieldName name of the form field that carries the file
|
|
13
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
14
|
+
*
|
|
15
|
+
* @publicApi
|
|
16
|
+
*/
|
|
17
|
+
export declare function FileInterceptor(fieldName: string, localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts a single file in `fieldName` and exposes it as `req.file`
|
|
4
|
+
* (`@UploadedFile()`), with the text fields in `req.body`. The Fastify
|
|
5
|
+
* counterpart of platform-express's `FileInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* Requires the `@fastify/multipart` package, which the FastifyAdapter
|
|
8
|
+
* registers as a plugin when an upload interceptor is used (see its
|
|
9
|
+
* `multipart` option).
|
|
10
|
+
*
|
|
11
|
+
* @param fieldName name of the form field that carries the file
|
|
12
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
13
|
+
*
|
|
14
|
+
* @publicApi
|
|
15
|
+
*/
|
|
16
|
+
export function FileInterceptor(fieldName, localOptions) {
|
|
17
|
+
return createMultipartInterceptor({ fields: [{ name: fieldName, maxCount: 1 }], strategy: 'VALUE' }, localOptions);
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts up to `maxCount` files in `fieldName` and exposes them as an array
|
|
5
|
+
* in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
6
|
+
* platform-express's `FilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param fieldName name of the form field that carries the files
|
|
9
|
+
* @param maxCount maximum number of files to accept
|
|
10
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
11
|
+
*
|
|
12
|
+
* @publicApi
|
|
13
|
+
*/
|
|
14
|
+
export declare function FilesInterceptor(fieldName: string, maxCount?: number, localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts up to `maxCount` files in `fieldName` and exposes them as an array
|
|
4
|
+
* in `req.files` (`@UploadedFiles()`). The Fastify counterpart of
|
|
5
|
+
* platform-express's `FilesInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param fieldName name of the form field that carries the files
|
|
8
|
+
* @param maxCount maximum number of files to accept
|
|
9
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
10
|
+
*
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
export function FilesInterceptor(fieldName, maxCount, localOptions) {
|
|
14
|
+
return createMultipartInterceptor({ fields: [{ name: fieldName, maxCount }], strategy: 'ARRAY' }, localOptions);
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NestInterceptor, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts text fields only (exposed in `req.body`); any file fails the
|
|
5
|
+
* request with a 400. The Fastify counterpart of platform-express's
|
|
6
|
+
* `NoFilesInterceptor`.
|
|
7
|
+
*
|
|
8
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
9
|
+
*
|
|
10
|
+
* @publicApi
|
|
11
|
+
*/
|
|
12
|
+
export declare function NoFilesInterceptor(localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createMultipartInterceptor } from '../multipart/multipart-interceptor.factory.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts text fields only (exposed in `req.body`); any file fails the
|
|
4
|
+
* request with a 400. The Fastify counterpart of platform-express's
|
|
5
|
+
* `NoFilesInterceptor`.
|
|
6
|
+
*
|
|
7
|
+
* @param localOptions options merged over the `MultipartModule` defaults
|
|
8
|
+
*
|
|
9
|
+
* @publicApi
|
|
10
|
+
*/
|
|
11
|
+
export function NoFilesInterceptor(localOptions) {
|
|
12
|
+
return createMultipartInterceptor({ fields: [], strategy: 'NONE' }, localOptions);
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { InjectionToken, ModuleMetadata, OptionalFactoryDependency, Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from './multipart-options.interface.js';
|
|
3
|
+
export type MultipartModuleOptions = MultipartOptions;
|
|
4
|
+
/**
|
|
5
|
+
* @publicApi
|
|
6
|
+
*/
|
|
7
|
+
export interface MultipartOptionsFactory {
|
|
8
|
+
createMultipartOptions(): Promise<MultipartModuleOptions> | MultipartModuleOptions;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @publicApi
|
|
12
|
+
*/
|
|
13
|
+
export interface MultipartModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
14
|
+
useExisting?: Type<MultipartOptionsFactory>;
|
|
15
|
+
useClass?: Type<MultipartOptionsFactory>;
|
|
16
|
+
useFactory?: (...args: any[]) => Promise<MultipartModuleOptions> | MultipartModuleOptions;
|
|
17
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Readable } from 'stream';
|
|
2
|
+
/**
|
|
3
|
+
* A stored upload, in the shape multer produces (`Express.Multer.File`), so
|
|
4
|
+
* `@UploadedFile()`, `ParseFilePipe` and the built-in file validators work
|
|
5
|
+
* unchanged. Memory storage sets `buffer`; disk storage sets `destination`,
|
|
6
|
+
* `filename` and `path`.
|
|
7
|
+
*
|
|
8
|
+
* @publicApi
|
|
9
|
+
*/
|
|
10
|
+
export interface UploadedMultipartFile {
|
|
11
|
+
/** Field name specified in the form */
|
|
12
|
+
fieldname: string;
|
|
13
|
+
/** Name of the file on the user's computer */
|
|
14
|
+
originalname: string;
|
|
15
|
+
/** Encoding type of the file */
|
|
16
|
+
encoding: string;
|
|
17
|
+
/** Mime type of the file */
|
|
18
|
+
mimetype: string;
|
|
19
|
+
/** Size of the file in bytes */
|
|
20
|
+
size: number;
|
|
21
|
+
/** A Buffer of the entire file (memory storage) */
|
|
22
|
+
buffer?: Buffer;
|
|
23
|
+
/** The folder to which the file has been saved (disk storage) */
|
|
24
|
+
destination?: string;
|
|
25
|
+
/** The name of the file within the destination (disk storage) */
|
|
26
|
+
filename?: string;
|
|
27
|
+
/** Location of the uploaded file (disk storage) */
|
|
28
|
+
path?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* What a storage engine and `fileFilter` see before the file is stored.
|
|
32
|
+
*
|
|
33
|
+
* @publicApi
|
|
34
|
+
*/
|
|
35
|
+
export interface IncomingMultipartFile {
|
|
36
|
+
/** Field name specified in the form */
|
|
37
|
+
fieldname: string;
|
|
38
|
+
/** Name of the file on the user's computer */
|
|
39
|
+
originalname: string;
|
|
40
|
+
/** Encoding type of the file */
|
|
41
|
+
encoding: string;
|
|
42
|
+
/** Mime type of the file */
|
|
43
|
+
mimetype: string;
|
|
44
|
+
/**
|
|
45
|
+
* The file contents. Non-enumerable; meant to be consumed by
|
|
46
|
+
* `MultipartStorageEngine._handleFile` only.
|
|
47
|
+
*/
|
|
48
|
+
readonly stream: Readable;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The file `FileStreamInterceptor` hands to the route handler. Its size is
|
|
52
|
+
* not known up front.
|
|
53
|
+
*
|
|
54
|
+
* @publicApi
|
|
55
|
+
*/
|
|
56
|
+
export interface MultipartFileStream {
|
|
57
|
+
/** Field name specified in the form */
|
|
58
|
+
fieldname: string;
|
|
59
|
+
/** Name of the file on the user's computer */
|
|
60
|
+
originalname: string;
|
|
61
|
+
/** Encoding type of the file */
|
|
62
|
+
encoding: string;
|
|
63
|
+
/** Mime type of the file */
|
|
64
|
+
mimetype: string;
|
|
65
|
+
/**
|
|
66
|
+
* The file contents. Errors with a `PayloadTooLargeException` once
|
|
67
|
+
* `limits.fileSize` is exceeded.
|
|
68
|
+
*/
|
|
69
|
+
stream: Readable;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { IncomingMultipartFile, UploadedMultipartFile } from './multipart-file.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* multer's storage engine contract (`_handleFile` / `_removeFile`), kept
|
|
4
|
+
* as-is so existing multer storage engines can be passed as `storage`.
|
|
5
|
+
*
|
|
6
|
+
* @publicApi
|
|
7
|
+
*/
|
|
8
|
+
export interface MultipartStorageEngine {
|
|
9
|
+
_handleFile(req: any, file: IncomingMultipartFile, callback: (error?: any, info?: Partial<UploadedMultipartFile>) => void): void;
|
|
10
|
+
_removeFile(req: any, file: UploadedMultipartFile, callback: (error: Error | null) => void): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Same keys as platform-express's `MulterLimits`.
|
|
14
|
+
*
|
|
15
|
+
* Limits that are not set fall back to the options `@fastify/multipart` was
|
|
16
|
+
* registered with, and then to its own defaults. Unlike multer (which
|
|
17
|
+
* defaults `fileSize` and `parts` to `Infinity`), `@fastify/multipart` caps
|
|
18
|
+
* `fileSize` at the Fastify instance's `bodyLimit` (1 MiB unless configured)
|
|
19
|
+
* and `parts` at 1000.
|
|
20
|
+
*
|
|
21
|
+
* @publicApi
|
|
22
|
+
*/
|
|
23
|
+
export interface MultipartLimits {
|
|
24
|
+
/**
|
|
25
|
+
* Max field name size in bytes (Default: unlimited). Unlike the other
|
|
26
|
+
* limits, it does not fall back to the options `@fastify/multipart` was
|
|
27
|
+
* registered with.
|
|
28
|
+
*/
|
|
29
|
+
fieldNameSize?: number;
|
|
30
|
+
/** Max field value size (Default: 1MB) */
|
|
31
|
+
fieldSize?: number;
|
|
32
|
+
/** Max number of non-file fields (Default: Infinity) */
|
|
33
|
+
fields?: number;
|
|
34
|
+
/** For multipart forms, the max file size in bytes (Default: the Fastify `bodyLimit`, 1 MiB) */
|
|
35
|
+
fileSize?: number;
|
|
36
|
+
/** For multipart forms, the max number of file fields (Default: Infinity) */
|
|
37
|
+
files?: number;
|
|
38
|
+
/** For multipart forms, the max number of parts (fields + files) (Default: 1000) */
|
|
39
|
+
parts?: number;
|
|
40
|
+
/** For multipart forms, the max number of header key => value pairs to parse (Default: 2000) */
|
|
41
|
+
headerPairs?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Same keys and semantics as platform-express's `MulterOptions`, so option
|
|
45
|
+
* objects can be moved between adapters as-is.
|
|
46
|
+
*
|
|
47
|
+
* @publicApi
|
|
48
|
+
*/
|
|
49
|
+
export interface MultipartOptions {
|
|
50
|
+
/** Where to store the files (disk storage). Ignored when `storage` is set. */
|
|
51
|
+
dest?: string;
|
|
52
|
+
/** The storage engine to use for uploaded files (Default: memory storage). */
|
|
53
|
+
storage?: MultipartStorageEngine;
|
|
54
|
+
/**
|
|
55
|
+
* An object specifying the size limits, or a function of the request that
|
|
56
|
+
* returns one. Merged over the limits `@fastify/multipart` was registered with.
|
|
57
|
+
*/
|
|
58
|
+
limits?: MultipartLimits | ((req: any) => MultipartLimits);
|
|
59
|
+
/** Keep the full path of files instead of just the base name (Default: false) */
|
|
60
|
+
preservePath?: boolean;
|
|
61
|
+
/** Default character set for part header values (e.g. filename) (Default: 'latin1') */
|
|
62
|
+
defParamCharset?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Decides which files are stored. Call `callback(null, false)` to skip a
|
|
65
|
+
* file, or `callback(error, false)` to fail the request with `error`.
|
|
66
|
+
*/
|
|
67
|
+
fileFilter?(req: any, file: IncomingMultipartFile, callback: (error: Error | null, acceptFile: boolean) => void): void;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @publicApi
|
|
71
|
+
*/
|
|
72
|
+
export interface MultipartField {
|
|
73
|
+
/** The field name. */
|
|
74
|
+
name: string;
|
|
75
|
+
/** Optional maximum number of files per field to accept. */
|
|
76
|
+
maxCount?: number;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
2
|
+
function parsePath(key) {
|
|
3
|
+
const failure = () => [{ type: 'object', key, last: true }];
|
|
4
|
+
const firstKey = /^[^[]*/.exec(key)[0];
|
|
5
|
+
if (!firstKey) {
|
|
6
|
+
return failure();
|
|
7
|
+
}
|
|
8
|
+
let pos = firstKey.length;
|
|
9
|
+
let tail = { type: 'object', key: firstKey };
|
|
10
|
+
const steps = [tail];
|
|
11
|
+
while (pos < key.length) {
|
|
12
|
+
if (key[pos] === '[' && key[pos + 1] === ']') {
|
|
13
|
+
pos += 2;
|
|
14
|
+
tail.append = true;
|
|
15
|
+
if (pos !== key.length) {
|
|
16
|
+
return failure();
|
|
17
|
+
}
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const rest = key.substring(pos);
|
|
21
|
+
let match = /^\[(\d+)\]/.exec(rest);
|
|
22
|
+
if (match) {
|
|
23
|
+
pos += match[0].length;
|
|
24
|
+
tail.nextType = 'array';
|
|
25
|
+
tail = { type: 'array', key: parseInt(match[1], 10) };
|
|
26
|
+
steps.push(tail);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
match = /^\[([^\]]+)\]/.exec(rest);
|
|
30
|
+
if (match) {
|
|
31
|
+
pos += match[0].length;
|
|
32
|
+
tail.nextType = 'object';
|
|
33
|
+
tail = { type: 'object', key: match[1] };
|
|
34
|
+
steps.push(tail);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
return failure();
|
|
38
|
+
}
|
|
39
|
+
tail.last = true;
|
|
40
|
+
return steps;
|
|
41
|
+
}
|
|
42
|
+
function valueType(value) {
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
return 'undefined';
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
return 'array';
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === 'object') {
|
|
50
|
+
return 'object';
|
|
51
|
+
}
|
|
52
|
+
return 'scalar';
|
|
53
|
+
}
|
|
54
|
+
function setLastValue(ctx, step, current, value) {
|
|
55
|
+
switch (valueType(current)) {
|
|
56
|
+
case 'undefined':
|
|
57
|
+
ctx[step.key] = step.append ? [value] : value;
|
|
58
|
+
break;
|
|
59
|
+
case 'array':
|
|
60
|
+
ctx[step.key].push(value);
|
|
61
|
+
break;
|
|
62
|
+
case 'object':
|
|
63
|
+
return setLastValue(current, { type: 'object', key: '', last: true }, current[''], value);
|
|
64
|
+
case 'scalar':
|
|
65
|
+
ctx[step.key] = [ctx[step.key], value];
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return ctx;
|
|
69
|
+
}
|
|
70
|
+
function setValue(ctx, step, current, value) {
|
|
71
|
+
if (step.last) {
|
|
72
|
+
return setLastValue(ctx, step, current, value);
|
|
73
|
+
}
|
|
74
|
+
let obj;
|
|
75
|
+
switch (valueType(current)) {
|
|
76
|
+
case 'undefined':
|
|
77
|
+
ctx[step.key] = step.nextType === 'array' ? [] : Object.create(null);
|
|
78
|
+
return ctx[step.key];
|
|
79
|
+
case 'object':
|
|
80
|
+
return current;
|
|
81
|
+
case 'array':
|
|
82
|
+
if (step.nextType === 'array') {
|
|
83
|
+
return current;
|
|
84
|
+
}
|
|
85
|
+
obj = Object.create(null);
|
|
86
|
+
ctx[step.key] = obj;
|
|
87
|
+
current.forEach((item, i) => {
|
|
88
|
+
if (item !== undefined) {
|
|
89
|
+
obj['' + i] = item;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return obj;
|
|
93
|
+
case 'scalar':
|
|
94
|
+
obj = Object.create(null);
|
|
95
|
+
obj[''] = current;
|
|
96
|
+
ctx[step.key] = obj;
|
|
97
|
+
return obj;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Appends a text field to `store` using multer's bracket notation.
|
|
102
|
+
* Returns `false` (leaving `store` untouched) for names that would pollute a
|
|
103
|
+
* prototype.
|
|
104
|
+
*/
|
|
105
|
+
export function appendField(store, key, value) {
|
|
106
|
+
const steps = parsePath(key);
|
|
107
|
+
if (steps.some(step => FORBIDDEN_KEYS.has(String(step.key)))) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
steps.reduce((ctx, step) => setValue(ctx, step, ctx[step.key], value), store);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type NestInterceptor, type Type } from '@nestjs/common';
|
|
2
|
+
import type { MultipartOptions } from '../interfaces/index.js';
|
|
3
|
+
import { type UploadSpec } from './multipart.parser.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates an interceptor that parses the multipart body with multer's
|
|
6
|
+
* semantics, as described by `spec`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createMultipartInterceptor(spec: UploadSpec, localOptions?: MultipartOptions): Type<NestInterceptor>;
|
|
9
|
+
/**
|
|
10
|
+
* Creates an interceptor that hands the route handler the stream of a single
|
|
11
|
+
* file in `fieldName`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createMultipartStreamInterceptor(fieldName: string, localOptions?: MultipartOptions): Type<NestInterceptor>;
|