npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@asterflow/multipart

v2.2.0

Published

Parses multipart/form-data requests before your handler runs, with optional per-route field rules checked at runtime and enforced in the handler's types.

Readme

@asterflow/multipart

license-info stars-info last-commit

bundle-size

Parses multipart/form-data requests before your handler runs, with optional per-route field rules that are checked at runtime and enforced in the handler's types.

📦 Installation

bun install @asterflow/multipart

Register the plugin on an AsterFlow app:

import { AsterFlow } from 'asterflow'
import { multipartPlugin } from '@asterflow/multipart'

const app = new AsterFlow()
  .use(multipartPlugin, { limits: { fileSize: 10 * 1024 * 1024 } })

✨ Features

  • Automatic parsing - any multipart/form-data request is parsed with busboy before it reaches your route.
  • Per-route criteria - calling .multipart({...}) on Method.create(...) (or inside a Router.builder(...).method(...) chain) validates fields at runtime and narrows getFile/getFiles in that handler's types - a required field types as always-present, a declared mimeTypes list narrows .mimeType.
  • Request extensions - request.body, request.files, request.getFile(), request.getFiles(), request.hasFiles(), request.getFilesByType(), request.saveAll() and request.cleanupMultipart() are attached directly onto request, no wrapper object.
  • Streaming storage - files stream into memory or to disk (fileHandling.keepInMemory), never buffered twice.
  • Automatic cleanup - temp files written to disk are removed after the response is sent.
  • Standardized errors - limit, MIME/extension and required-field failures all reject with the same { error, code, message } shape before your handler runs.

❓ How to Use

Declare a route's fields with .multipart(schema) - the handler only runs once the request passes that schema, and getFile/getFiles are typed to match it:

import { Method } from '@asterflow/router'

export default Method.create(Method.POST, { path: '/avatar' })
  .multipart({
    avatar: { mimeTypes: ['image/png', 'image/jpeg'], maxSize: 5 * 1024 * 1024, required: true }
  })
  .handler(({ request, response }) => {
    const avatar = request.getFile('avatar') // always present, mimeType narrowed
    return response.success({ filename: avatar.filename, size: avatar.size })
  })

Without a declared schema, the same methods are still there on request, just optional and unnarrowed:

export default Method.create(Method.POST, { path: '/upload' }).handler(({ request, response }) => {
  if (!request.hasFiles?.()) return response.badRequest({ error: 'NO_FILES' })
  return response.success({ files: request.files, fields: request.body })
})

🔗 Related Packages

📄 License

This project is licensed under the MIT License.