Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fastify/multipart

Package Overview
Dependencies
Maintainers
20
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fastify/multipart - npm Package Compare versions

Comparing version 7.7.3 to 8.0.0

.eslintrc.json

8

examples/example.js
'use strict'
const fastify = require('fastify')()
const fs = require('fs')
const util = require('util')
const path = require('path')
const { pipeline } = require('stream')
const fs = require('node:fs')
const util = require('node:util')
const path = require('node:path')
const { pipeline } = require('node:stream')
const pump = util.promisify(pipeline)

@@ -9,0 +9,0 @@ const form = path.join(__dirname, '..', 'form.html')

'use strict'
const Busboy = require('@fastify/busboy')
const os = require('os')
const os = require('node:os')
const fp = require('fastify-plugin')
const eos = require('end-of-stream')
const { createWriteStream } = require('fs')
const { unlink } = require('fs').promises
const path = require('path')
const { createWriteStream } = require('node:fs')
const { unlink } = require('node:fs/promises')
const path = require('node:path')
const { generateId } = require('./lib/generateId')
const util = require('util')
const util = require('node:util')
const createError = require('@fastify/error')
const sendToWormhole = require('stream-wormhole')
const deepmergeAll = require('@fastify/deepmerge')({ all: true })
const { PassThrough, pipeline, Readable } = require('stream')
const { PassThrough, pipeline, Readable } = require('node:stream')
const pump = util.promisify(pipeline)

@@ -33,3 +32,2 @@ const secureJSON = require('secure-json-parse')

function setMultipart (req, payload, done) {
// nothing to do, it will be done by the Request.multipart object
req.raw[kMultipart] = true

@@ -39,64 +37,2 @@ done()

function attachToBody (options, req, reply, next) {
if (req.raw[kMultipart] !== true) {
next()
return
}
const consumerStream = options.onFile || defaultConsumer
const body = {}
const mp = req.multipart((field, file, filename, encoding, mimetype) => {
body[field] = body[field] || []
body[field].push({
data: [],
filename,
encoding,
mimetype,
limit: false
})
const result = consumerStream(field, file, filename, encoding, mimetype, body)
if (result && typeof result.then === 'function') {
result.catch((err) => {
// continue with the workflow
err.statusCode = 500
file.destroy(err)
})
}
}, function (err) {
if (!err) {
req.body = body
}
next(err)
}, options)
mp.on('field', (key, value) => {
if (key === '__proto__' || key === 'constructor') {
mp.destroy(new Error(`${key} is not allowed as field name`))
return
}
if (body[key] === undefined) {
body[key] = value
} else if (Array.isArray(body[key])) {
body[key].push(value)
} else {
body[key] = [body[key], value]
}
})
}
function defaultConsumer (field, file, filename, encoding, mimetype, body) {
const fileData = []
const lastFile = body[field][body[field].length - 1]
file.on('data', data => { if (!lastFile.limit) { fileData.push(data) } })
file.on('limit', () => { lastFile.limit = true })
file.on('end', () => {
if (!lastFile.limit) {
lastFile.data = Buffer.concat(fileData)
} else {
lastFile.data = undefined
}
})
}
function busboy (options) {

@@ -122,23 +58,5 @@ try {

const attachFieldsToBody = options.attachFieldsToBody
if (options.addToBody === true) {
if (typeof options.sharedSchemaId === 'string') {
fastify.addSchema({
$id: options.sharedSchemaId,
type: 'object',
properties: {
encoding: { type: 'string' },
filename: { type: 'string' },
limit: { type: 'boolean' },
mimetype: { type: 'string' }
}
})
}
fastify.addHook('preValidation', function (req, reply, next) {
attachToBody(options, req, reply, next)
})
}
if (options.attachFieldsToBody === true || options.attachFieldsToBody === 'keyValues') {
if (typeof options.sharedSchemaId === 'string') {
if (attachFieldsToBody === true || attachFieldsToBody === 'keyValues') {
if (typeof options.sharedSchemaId === 'string' && attachFieldsToBody === true) {
fastify.addSchema({

@@ -155,2 +73,3 @@ $id: options.sharedSchemaId,

}
fastify.addHook('preValidation', async function (req, reply) {

@@ -160,4 +79,6 @@ if (!req.isMultipart()) {

}
for await (const part of req.parts()) {
req.body = part.fields
if (part.file) {

@@ -171,21 +92,37 @@ if (options.onFile) {

}
if (options.attachFieldsToBody === 'keyValues') {
if (attachFieldsToBody === 'keyValues') {
const body = {}
if (req.body) {
for (const key of Object.keys(req.body)) {
const reqBodyKeys = Object.keys(req.body)
for (let i = 0; i < reqBodyKeys.length; ++i) {
const key = reqBodyKeys[i]
const field = req.body[key]
if (field.value !== undefined) {
body[key] = field.value
} else if (field._buf) {
body[key] = field._buf
} else if (Array.isArray(field)) {
body[key] = field.map(item => {
if (item._buf) {
return item._buf.toString()
const items = []
for (let i = 0; i < field.length; ++i) {
const item = field[i]
if (item.value !== undefined) {
items.push(item.value)
} else if (item._buf) {
items.push(item._buf)
}
return item.value
})
} else if (field._buf) {
body[key] = field._buf.toString()
}
if (items.length) {
body[key] = items
}
}
}
}
req.body = body

@@ -219,5 +156,2 @@ }

// legacy
fastify.decorateRequest('multipart', handleLegacyMultipartApi)
// Stream mode

@@ -236,78 +170,5 @@ fastify.decorateRequest('file', getMultipartFile)

function isMultipart () {
return this.raw[kMultipart] || false
return this.raw[kMultipart]
}
// handler definition is in multipart-readstream
// handler(field, file, filename, encoding, mimetype)
// opts is a per-request override for the options object
function handleLegacyMultipartApi (handler, done, opts) {
if (typeof handler !== 'function') {
throw new Error('handler must be a function')
}
if (typeof done !== 'function') {
throw new Error('the callback must be a function')
}
if (!this.isMultipart()) {
done(new Error('the request is not multipart'))
return
}
const log = this.log
log.warn('the multipart callback-based api is deprecated in favour of the new promise api')
log.debug('starting multipart parsing')
const req = this.raw
const busboyOptions = deepmergeAll({ headers: req.headers }, options || {}, opts || {})
const stream = busboy(busboyOptions)
let completed = false
let files = 0
req.on('error', function (err) {
stream.destroy()
if (!completed) {
completed = true
done(err)
}
})
stream.on('finish', function () {
log.debug('finished receiving stream, total %d files', files)
if (!completed) {
completed = true
setImmediate(done)
}
})
stream.on('file', wrap)
req.pipe(stream)
.on('error', function (error) {
req.emit('error', error)
})
function wrap (field, file, filename, encoding, mimetype) {
log.debug({ field, filename, encoding, mimetype }, 'parsing part')
files++
eos(file, waitForFiles)
if (field === '__proto__' || field === 'constructor') {
file.destroy(new Error(`${field} is not allowed as field name`))
return
}
handler(field, file, filename, encoding, mimetype)
}
function waitForFiles (err) {
if (err) {
completed = true
done(err)
}
}
return stream
}
function handleMultipart (opts = {}) {

@@ -571,3 +432,5 @@ if (!this.isMultipart()) {

try {
for (const field of Object.values(container)) {
const fields = Array.isArray(container) ? container : Object.values(container)
for (let i = 0; i < fields.length; ++i) {
const field = fields[i]
if (Array.isArray(field)) {

@@ -597,3 +460,4 @@ for (const subField of filesFromFields.call(this, field)) {

}
for (const filepath of this.tmpUploads) {
for (let i = 0; i < this.tmpUploads.length; ++i) {
const filepath = this.tmpUploads[i]
try {

@@ -600,0 +464,0 @@ await unlink(filepath)

{
"name": "@fastify/multipart",
"version": "7.7.3",
"version": "8.0.0",
"description": "Multipart plugin for Fastify",
"main": "index.js",
"types": "index.d.ts",
"types": "types/index.d.ts",
"dependencies": {

@@ -13,3 +13,2 @@ "@fastify/busboy": "^1.0.0",

"@fastify/swagger-ui": "^1.8.0",
"end-of-stream": "^1.4.4",
"fastify-plugin": "^4.0.0",

@@ -22,4 +21,4 @@ "secure-json-parse": "^2.4.0",

"@types/node": "^20.1.0",
"@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^5.30.7",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"benchmark": "^2.1.4",

@@ -29,3 +28,2 @@ "climem": "^1.0.3",

"eslint": "^8.20.0",
"eslint-config-standard-with-typescript": "^37.0.0",
"eslint-plugin-import": "^2.26.0",

@@ -43,13 +41,15 @@ "eslint-plugin-n": "^16.0.1",

"tap": "^16.0.0",
"tsd": "^0.28.0"
"tsd": "^0.29.0"
},
"scripts": {
"coverage": "tap \"test/**/*.test.js\" --coverage-report=html",
"coverage": "npm run test:unit -- --coverage-report=html",
"climem": "climem 8999 localhost",
"lint": "standard | snazzy",
"lint:fix": "standard --fix",
"lint": "npm run lint:javascript && npm run lint:typescript",
"lint:javascript": "standard | snazzy",
"lint:fix": "standard --fix && npm run lint:typescript -- --fix",
"lint:typescript": "eslint -c .eslintrc.json types/**/*.d.ts types/**/*.test-d.ts",
"start": "CLIMEM=8999 node -r climem ./examples/example",
"test": "npm run lint && npm run unit && npm run typescript",
"typescript": "tsd",
"unit": "tap \"test/**/*.test.js\" -t 90"
"test": "npm run test:unit && npm run test:typescript",
"test:typescript": "tsd",
"test:unit": "tap -t 90"
},

@@ -56,0 +56,0 @@ "repository": {

@@ -25,9 +25,7 @@ # @fastify/multipart

If you are looking for the documentation for the legacy callback-api please see [here](./callback.md).
```js
const fastify = require('fastify')()
const fs = require('fs')
const util = require('util')
const { pipeline } = require('stream')
const fs = require('node:fs')
const util = require('node:util')
const { pipeline } = require('node:stream')
const pump = util.promisify(pipeline)

@@ -244,3 +242,3 @@

Request body key-value pairs can be assigned directly using `attachFieldsToBody: 'keyValues'`. Field values will be attached directly to the body object. By default, all files are converted to a string using `buffer.toString()` used as the value attached to the body.
Request body key-value pairs can be assigned directly using `attachFieldsToBody: 'keyValues'`. Field values, including file buffers, will be attached to the body object.

@@ -251,3 +249,3 @@ ```js

fastify.post('/upload/files', async function (req, reply) {
const uploadValue = req.body.upload // access file as string
const uploadValue = req.body.upload // access file as buffer
const fooValue = req.body.foo // other fields

@@ -254,0 +252,0 @@ })

@@ -7,7 +7,7 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const http = require('node:http')
const stream = require('readable-stream')
const Readable = stream.Readable
const pump = stream.pipeline
const crypto = require('crypto')
const crypto = require('node:crypto')
const sendToWormhole = require('stream-wormhole')

@@ -14,0 +14,0 @@

@@ -7,7 +7,7 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const { access } = require('fs').promises
const EventEmitter = require('events')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const { access } = require('node:fs').promises
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -14,0 +14,0 @@

@@ -6,7 +6,7 @@ 'use strict'

const FormData = require('form-data')
const http = require('http')
const http = require('node:http')
const multipart = require('..')
const { once } = require('events')
const fs = require('fs')
const path = require('path')
const { once } = require('node:events')
const fs = require('node:fs')
const path = require('node:path')

@@ -13,0 +13,0 @@ const filePath = path.join(__dirname, '../README.md')

@@ -7,7 +7,7 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const { once } = require('events')
const { Readable } = require('stream')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const { once } = require('node:events')
const { Readable } = require('node:stream')
const pump = require('pump')

@@ -125,2 +125,4 @@ const { writableNoopStream } = require('noop-stream')

req.body.upload = req.body.upload.toString('utf8')
t.same(Object.keys(req.body), ['upload', 'hello'])

@@ -312,2 +314,5 @@

req.body.upload[0] = req.body.upload[0].toString('utf8')
req.body.upload[1] = req.body.upload[1].toString('utf8')
t.same(req.body, {

@@ -440,1 +445,48 @@ upload: [original, original],

})
test('should pass the buffer instead of converting to string', async function (t) {
t.plan(7)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
fastify.register(multipart, { attachFieldsToBody: 'keyValues' })
const original = fs.readFileSync(filePath)
fastify.post('/', async function (req, reply) {
t.ok(req.isMultipart())
t.same(Object.keys(req.body), ['upload', 'hello'])
t.ok(req.body.upload instanceof Buffer)
t.ok(Buffer.compare(req.body.upload, original) === 0)
t.equal(req.body.hello, 'world')
reply.code(200).send()
})
await fastify.listen({ port: 0 })
// 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)
form.append('upload', fs.createReadStream(filePath))
form.append('hello', 'world')
form.pipe(req)
const [res] = await once(req, 'response')
t.equal(res.statusCode, 200)
res.resume()
await once(res, 'end')
t.pass('res ended successfully')
})

@@ -7,7 +7,7 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const crypto = require('crypto')
const http = require('node:http')
const crypto = require('node:crypto')
const { Readable } = require('readable-stream')
const sendToWormhole = require('stream-wormhole')
const EventEmitter = require('events')
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -14,0 +14,0 @@

@@ -7,5 +7,5 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')

@@ -12,0 +12,0 @@ const filePath = path.join(__dirname, '../README.md')

@@ -7,5 +7,5 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')

@@ -12,0 +12,0 @@ const filePath = path.join(__dirname, '../README.md')

@@ -7,9 +7,9 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const crypto = require('crypto')
const http = require('node:http')
const crypto = require('node:crypto')
const { Readable } = require('readable-stream')
const path = require('path')
const fs = require('fs')
const { access } = require('fs').promises
const EventEmitter = require('events')
const path = require('node:path')
const fs = require('node:fs')
const { access } = require('node:fs').promises
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -16,0 +16,0 @@

@@ -7,6 +7,6 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const EventEmitter = require('events')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -13,0 +13,0 @@

@@ -7,4 +7,4 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const { once } = require('events')
const http = require('node:http')
const { once } = require('node:events')

@@ -11,0 +11,0 @@ test('should not break with a empty request body when attachFieldsToBody is true', async function (t) {

'use strict'
const fs = require('fs')
const crypto = require('crypto')
const fs = require('node:fs')
const crypto = require('node:crypto')
const test = require('tap').test

@@ -9,4 +9,4 @@ const FormData = require('form-data')

const multipart = require('..')
const http = require('http')
const EventEmitter = require('events')
const http = require('node:http')
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -13,0 +13,0 @@

@@ -8,4 +8,4 @@ 'use strict'

const h2url = require('h2url')
const path = require('path')
const fs = require('fs')
const path = require('node:path')
const fs = require('node:fs')
const sendToWormhole = require('stream-wormhole')

@@ -12,0 +12,0 @@

'use strict'
const util = require('util')
const util = require('node:util')
const test = require('tap').test

@@ -8,6 +8,6 @@ const FormData = require('form-data')

const multipart = require('..')
const http = require('http')
const http = require('node:http')
const sleep = util.promisify(setTimeout)
const { writableNoopStream } = require('noop-stream')
const stream = require('stream')
const stream = require('node:stream')
const pipeline = util.promisify(stream.pipeline)

@@ -14,0 +14,0 @@

@@ -7,3 +7,3 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const http = require('node:http')

@@ -10,0 +10,0 @@ test('should parse JSON fields forms if content-type is set', function (t) {

@@ -7,7 +7,7 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
const EventEmitter = require('events')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const crypto = require('node:crypto')
const EventEmitter = require('node:events')
const { once } = EventEmitter

@@ -14,0 +14,0 @@

@@ -7,6 +7,6 @@ 'use strict'

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const EventEmitter = require('events')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const EventEmitter = require('node:events')
const sendToWormhole = require('stream-wormhole')

@@ -13,0 +13,0 @@ const { once } = EventEmitter

'use strict'
const util = require('util')
const util = require('node:util')
const test = require('tap').test

@@ -8,8 +8,8 @@ const FormData = require('form-data')

const multipart = require('..')
const http = require('http')
const path = require('path')
const fs = require('fs')
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const concat = require('concat-stream')
const stream = require('stream')
const { once } = require('events')
const stream = require('node:stream')
const { once } = require('node:events')
const pump = util.promisify(stream.pipeline)

@@ -16,0 +16,0 @@ const sendToWormhole = require('stream-wormhole')

'use strict'
const http = require('http')
const Readable = require('stream').Readable
const http = require('node:http')
const Readable = require('node:stream').Readable
const FormData = require('form-data')

@@ -6,0 +6,0 @@ const pump = require('pump')

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc