@fastify/multipart
Advanced tools
Comparing version 8.0.0 to 8.1.0
{ | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/eslint-recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"standard" | ||
], | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": ["@typescript-eslint"], | ||
"env": { "node": true }, | ||
"parserOptions": { | ||
"ecmaVersion": 6, | ||
"sourceType": "module", | ||
"project": "./tsconfig.eslint.json", | ||
"createDefaultProgram": true | ||
}, | ||
"rules": { | ||
"no-console": "off", | ||
"@typescript-eslint/indent": ["error", 2], | ||
"semi": ["error", "never"], | ||
"import/export": "off" // this errors on multiple exports (overload interfaces) | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": ["*.d.ts","*.test-d.ts"], | ||
"rules": { | ||
"no-use-before-define": "off", | ||
"no-redeclare": "off", | ||
"@typescript-eslint/no-explicit-any": "off" | ||
} | ||
"plugins": ["@typescript-eslint"], | ||
"extends": ["eslint:recommended", "standard"], | ||
"overrides": [ | ||
{ | ||
"files": ["types/*.test-d.ts", "types/*.d.ts"], | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
"project": ["./tsconfig.eslint.json"] | ||
}, | ||
{ | ||
"files": ["*.test-d.ts"], | ||
"rules": { | ||
"no-unused-expressions": "off", | ||
"@typescript-eslint/no-var-requires": "off", | ||
"no-unused-vars": "off", | ||
"n/handle-callback-err": "off", | ||
"@typescript-eslint/no-empty-function": "off", | ||
"@typescript-eslint/explicit-function-return-type": "off", | ||
"@typescript-eslint/no-unused-vars": "off", | ||
"@typescript-eslint/no-non-null-assertion": "off", | ||
"@typescript-eslint/no-misused-promises": ["error", { | ||
"checksVoidReturn": false | ||
}] | ||
}, | ||
"globals": { | ||
"NodeJS": "readonly" | ||
} | ||
"extends": [ | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:@typescript-eslint/recommended-requiring-type-checking" | ||
], | ||
"rules": { | ||
"no-unused-expressions": "off", | ||
"no-use-before-define": "off", | ||
"no-redeclare": "off", | ||
"@typescript-eslint/require-await": "off", | ||
"@typescript-eslint/no-explicit-any": "off", | ||
"@typescript-eslint/no-floating-promises": "off", | ||
"@typescript-eslint/no-unused-vars": "off" | ||
} | ||
] | ||
} | ||
} | ||
] | ||
} |
17
index.js
@@ -20,3 +20,2 @@ 'use strict' | ||
const kMultipartHandler = Symbol('multipartHandler') | ||
const getDescriptor = Object.getOwnPropertyDescriptor | ||
@@ -33,3 +32,3 @@ const PartsLimitError = createError('FST_PARTS_LIMIT', 'reach parts limit', 413) | ||
function setMultipart (req, payload, done) { | ||
req.raw[kMultipart] = true | ||
req[kMultipart] = true | ||
done() | ||
@@ -100,2 +99,4 @@ } | ||
/* Don't modify the body if a field doesn't have a value or an attached buffer */ | ||
/* istanbul ignore else */ | ||
if (field.value !== undefined) { | ||
@@ -145,2 +146,3 @@ body[key] = field.value | ||
fastify.addContentTypeParser('multipart/form-data', setMultipart) | ||
fastify.decorateRequest(kMultipart, false) | ||
fastify.decorateRequest(kMultipartHandler, handleMultipart) | ||
@@ -167,3 +169,3 @@ | ||
function isMultipart () { | ||
return this.raw[kMultipart] | ||
return this[kMultipart] | ||
} | ||
@@ -257,3 +259,3 @@ | ||
// don't overwrite prototypes | ||
if (getDescriptor(Object.prototype, name)) { | ||
if (name in Object.prototype) { | ||
onError(new PrototypeViolationError()) | ||
@@ -304,3 +306,3 @@ return | ||
// don't overwrite prototypes | ||
if (getDescriptor(Object.prototype, name)) { | ||
if (name in Object.prototype) { | ||
// ensure that stream is consumed, any error is suppressed | ||
@@ -463,3 +465,4 @@ sendToWormhole(file) | ||
} catch (error) { | ||
this.log.error(error, 'could not delete file') | ||
/* istanbul ignore next */ | ||
this.log.error(error, 'Could not delete file') | ||
} | ||
@@ -473,2 +476,4 @@ } | ||
while ((part = await parts()) != null) { | ||
/* Only return a part if the file property exists */ | ||
/* istanbul ignore else */ | ||
if (part.file) { | ||
@@ -475,0 +480,0 @@ return part |
{ | ||
"name": "@fastify/multipart", | ||
"version": "8.0.0", | ||
"version": "8.1.0", | ||
"description": "Multipart plugin for Fastify", | ||
"main": "index.js", | ||
"type": "commonjs", | ||
"types": "types/index.d.ts", | ||
@@ -11,4 +12,2 @@ "dependencies": { | ||
"@fastify/error": "^3.0.0", | ||
"@fastify/swagger": "^8.3.1", | ||
"@fastify/swagger-ui": "^1.8.0", | ||
"fastify-plugin": "^4.0.0", | ||
@@ -20,2 +19,4 @@ "secure-json-parse": "^2.4.0", | ||
"@fastify/pre-commit": "^2.0.2", | ||
"@fastify/swagger": "^8.10.1", | ||
"@fastify/swagger-ui": "^2.0.1", | ||
"@types/node": "^20.1.0", | ||
@@ -25,3 +26,3 @@ "@typescript-eslint/eslint-plugin": "^6.3.0", | ||
"benchmark": "^2.1.4", | ||
"climem": "^1.0.3", | ||
"climem": "^2.0.0", | ||
"concat-stream": "^2.0.0", | ||
@@ -37,7 +38,7 @@ "eslint": "^8.20.0", | ||
"pump": "^3.0.0", | ||
"readable-stream": "^3.6.0", | ||
"readable-stream": "^4.5.1", | ||
"snazzy": "^9.0.0", | ||
"standard": "^17.0.0", | ||
"tap": "^16.0.0", | ||
"tsd": "^0.29.0" | ||
"tsd": "^0.30.0" | ||
}, | ||
@@ -44,0 +45,0 @@ "scripts": { |
@@ -291,3 +291,3 @@ # @fastify/multipart | ||
If you enable `attachFieldsToBody: 'keyValues'` then the response body and JSON Schema validation will behave similarly to `application/json` and [`application/x-www-form-urlencoded`](https://github.com/fastify/fastify-formbody) content types. Files will be decoded using `Buffer.toString()` and attached as a body value. | ||
When the `attachFieldsToBody` parameter is set to `'keyValues'`, JSON Schema validation on the body will behave similarly to `application/json` and [`application/x-www-form-urlencoded`](https://github.com/fastify/fastify-formbody) content types. Additionally, uploaded files will be attached to the body as `Buffer` objects. | ||
@@ -306,5 +306,3 @@ ```js | ||
myFile: { | ||
type: 'string', | ||
// validate that file contents match a UUID | ||
pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' | ||
type: 'object', | ||
}, | ||
@@ -311,0 +309,0 @@ hello: { |
@@ -107,2 +107,137 @@ 'use strict' | ||
test('should not allow toString as field name', function (t) { | ||
t.plan(4) | ||
const fastify = Fastify() | ||
t.teardown(fastify.close.bind(fastify)) | ||
fastify.register(multipart) | ||
fastify.post('/', async function (req, reply) { | ||
t.ok(req.isMultipart()) | ||
try { | ||
await req.file() | ||
reply.code(200).send() | ||
} catch (error) { | ||
t.ok(error instanceof fastify.multipartErrors.PrototypeViolationError) | ||
reply.code(500).send() | ||
} | ||
}) | ||
fastify.listen({ port: 0 }, async function () { | ||
// request | ||
const form = new FormData() | ||
const opts = { | ||
protocol: 'http:', | ||
hostname: 'localhost', | ||
port: fastify.server.address().port, | ||
path: '/', | ||
headers: form.getHeaders(), | ||
method: 'POST' | ||
} | ||
const req = http.request(opts, (res) => { | ||
t.equal(res.statusCode, 500) | ||
res.resume() | ||
res.on('end', () => { | ||
t.pass('res ended successfully') | ||
}) | ||
}) | ||
form.append('toString', 'world') | ||
form.pipe(req) | ||
}) | ||
}) | ||
test('should not allow hasOwnProperty as field name', function (t) { | ||
t.plan(4) | ||
const fastify = Fastify() | ||
t.teardown(fastify.close.bind(fastify)) | ||
fastify.register(multipart) | ||
fastify.post('/', async function (req, reply) { | ||
t.ok(req.isMultipart()) | ||
try { | ||
await req.file() | ||
reply.code(200).send() | ||
} catch (error) { | ||
t.ok(error instanceof fastify.multipartErrors.PrototypeViolationError) | ||
reply.code(500).send() | ||
} | ||
}) | ||
fastify.listen({ port: 0 }, async function () { | ||
// request | ||
const form = new FormData() | ||
const opts = { | ||
protocol: 'http:', | ||
hostname: 'localhost', | ||
port: fastify.server.address().port, | ||
path: '/', | ||
headers: form.getHeaders(), | ||
method: 'POST' | ||
} | ||
const req = http.request(opts, (res) => { | ||
t.equal(res.statusCode, 500) | ||
res.resume() | ||
res.on('end', () => { | ||
t.pass('res ended successfully') | ||
}) | ||
}) | ||
form.append('hasOwnProperty', 'world') | ||
form.pipe(req) | ||
}) | ||
}) | ||
test('should not allow propertyIsEnumerable as field name', function (t) { | ||
t.plan(4) | ||
const fastify = Fastify() | ||
t.teardown(fastify.close.bind(fastify)) | ||
fastify.register(multipart) | ||
fastify.post('/', async function (req, reply) { | ||
t.ok(req.isMultipart()) | ||
try { | ||
await req.file() | ||
reply.code(200).send() | ||
} catch (error) { | ||
t.ok(error instanceof fastify.multipartErrors.PrototypeViolationError) | ||
reply.code(500).send() | ||
} | ||
}) | ||
fastify.listen({ port: 0 }, async function () { | ||
// request | ||
const form = new FormData() | ||
const opts = { | ||
protocol: 'http:', | ||
hostname: 'localhost', | ||
port: fastify.server.address().port, | ||
path: '/', | ||
headers: form.getHeaders(), | ||
method: 'POST' | ||
} | ||
const req = http.request(opts, (res) => { | ||
t.equal(res.statusCode, 500) | ||
res.resume() | ||
res.on('end', () => { | ||
t.pass('res ended successfully') | ||
}) | ||
}) | ||
form.append('propertyIsEnumerable', 'world') | ||
form.pipe(req) | ||
}) | ||
}) | ||
test('should use default for fileSize', async function (t) { | ||
@@ -109,0 +244,0 @@ t.plan(4) |
{ | ||
"compilerOptions": { | ||
"target": "es6", | ||
"lib": [ "es2015", "ES2018" ], | ||
"module": "commonjs", | ||
"noEmit": true, | ||
"strict": true | ||
}, | ||
"include": [ | ||
"types/*.test-d.ts", | ||
"types/*.d.ts" | ||
] | ||
} | ||
"compilerOptions": { | ||
"target": "es6", | ||
"lib": ["ES2015", "ES2018"], | ||
"module": "commonjs", | ||
"noEmit": true, | ||
"strict": true | ||
}, | ||
"include": ["types/*.test-d.ts", "types/*.d.ts"] | ||
} |
@@ -17,6 +17,6 @@ import { BusboyConfig, BusboyFileStream } from '@fastify/busboy' | ||
file: ( | ||
options?: Omit<BusboyConfig, 'headers'> | ||
options?: Omit<BusboyConfig, 'headers'> | fastifyMultipart.FastifyMultipartBaseOptions | ||
) => Promise<fastifyMultipart.MultipartFile | undefined>; | ||
files: ( | ||
options?: Omit<BusboyConfig, 'headers'> | ||
options?: Omit<BusboyConfig, 'headers'> | fastifyMultipart.FastifyMultipartBaseOptions | ||
) => AsyncIterableIterator<fastifyMultipart.MultipartFile>; | ||
@@ -162,2 +162,8 @@ | ||
headerPairs?: number; | ||
/** | ||
* For multipart forms, the max number of parts (fields + files) | ||
* @default 1000 | ||
*/ | ||
parts?: number; | ||
}; | ||
@@ -164,0 +170,0 @@ } |
import fastify from 'fastify' | ||
import fastifyMultipart, { MultipartValue, MultipartFields, MultipartFile } from '..' | ||
import fastifyMultipart, { MultipartValue, MultipartFields, MultipartFile, FastifyMultipartBaseOptions } from '..' | ||
import * as util from 'util' | ||
@@ -17,2 +17,5 @@ import { pipeline } from 'stream' | ||
attachFieldsToBody: true, | ||
limits: { | ||
parts: 500 | ||
}, | ||
onFile: (part: MultipartFile) => { | ||
@@ -77,4 +80,13 @@ console.log(part) | ||
app.post('/', async function (req, reply) { | ||
const options: Partial<BusboyConfig> = { limits: { fileSize: 1000 } } | ||
const data = await req.file(options) | ||
const data = await req.file({ | ||
limits: { fileSize: 1000, parts: 500 }, | ||
throwFileSizeLimit: true, | ||
sharedSchemaId: 'schemaId', | ||
isPartAFile: (fieldName, contentType, fileName) => { | ||
expectType<string | undefined>(fieldName) | ||
expectType<string | undefined>(contentType) | ||
expectType<string | undefined>(fileName) | ||
return true | ||
} | ||
}) | ||
if (!data) throw new Error('missing file') | ||
@@ -87,3 +99,13 @@ await pump(data.file, fs.createWriteStream(data.filename)) | ||
app.post('/', async (req, reply) => { | ||
const parts = req.files() | ||
const parts = req.files({ | ||
limits: { fileSize: 1000, parts: 500 }, | ||
throwFileSizeLimit: true, | ||
sharedSchemaId: 'schemaId', | ||
isPartAFile: (fieldName, contentType, fileName) => { | ||
expectType<string | undefined>(fieldName) | ||
expectType<string | undefined>(contentType) | ||
expectType<string | undefined>(fileName) | ||
return true | ||
} | ||
}) | ||
for await (const part of parts) { | ||
@@ -90,0 +112,0 @@ await pump(part.file, fs.createWriteStream(part.filename)) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
150987
6
42
4310
23
525
- Removed@fastify/swagger@^8.3.1
- Removed@fastify/swagger-ui@^1.8.0
- Removed@fastify/accept-negotiator@1.1.0(transitive)
- Removed@fastify/send@2.1.0(transitive)
- Removed@fastify/static@6.12.0(transitive)
- Removed@fastify/swagger@8.15.0(transitive)
- Removed@fastify/swagger-ui@1.10.2(transitive)
- Removed@lukeed/ms@2.0.2(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@2.0.1(transitive)
- Removedcontent-disposition@0.5.4(transitive)
- Removeddebug@4.3.7(transitive)
- Removeddepd@2.0.0(transitive)
- Removedescape-html@1.0.3(transitive)
- Removedfast-decode-uri-component@1.0.1(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@8.1.0(transitive)
- Removedhttp-errors@2.0.0(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedjson-schema-resolver@2.0.0(transitive)
- Removedmime@3.0.0(transitive)
- Removedminimatch@5.1.6(transitive)
- Removedms@2.1.3(transitive)
- Removedonce@1.4.0(transitive)
- Removedopenapi-types@12.1.3(transitive)
- Removedp-limit@3.1.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedrfdc@1.4.1(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsetprototypeof@1.2.0(transitive)
- Removedstatuses@2.0.1(transitive)
- Removedtoidentifier@1.0.1(transitive)
- Removeduri-js@4.4.1(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedyaml@2.5.1(transitive)
- Removedyocto-queue@0.1.0(transitive)