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

@axiomify/upload

v6.3.0

Published

RAM-safe multipart upload handler for Axiomify via Busboy — streaming, content-type validation, auto-cleanup on error.

Readme

@axiomify/upload

npm version codecov OpenSSF Scorecard License: MIT

RAM-safe, stream-based multipart file upload for Axiomify. Files stream directly to disk via Busboy — no buffering in memory.

Install

npm install @axiomify/upload @axiomify/core busboy zod

Quick start

import { Axiomify } from '@axiomify/core';
import { useUpload } from '@axiomify/upload';
import { z } from 'zod';

const app = new Axiomify();

// 1. Register the upload hook — once, before any routes
useUpload(app);

// 2. Declare file fields in route schema
app.route({
  method: 'POST',
  path: '/avatar',
  schema: {
    body: z.object({ userId: z.string() }), // text fields
    files: {
      avatar: {
        autoSaveTo: './uploads/avatars',
        accept: ['image/jpeg', 'image/png', 'image/webp'],
        maxSize: 5 * 1024 * 1024, // 5 MB per file
      },
    },
  },
  handler: async (req, res) => {
    const { userId } = req.body;
    const file = req.files!.avatar;

    // file.path       — absolute path on disk
    // file.originalName — original filename (sanitized)
    // file.savedName    — name on disk
    // file.mimeType   — detected MIME type
    // file.size       — bytes written

    res.send({ userId, avatarPath: file.path });
  },
});

Options — useUpload(app, options?)

| Option | Default | Description | | ------------------ | ------------- | ----------------------------------------------------------------------------- | | dest | os.tmpdir() | Default save directory for files without autoSaveTo. | | autoCleanup | false | Automatically delete all uploaded temporary files after the handler finishes. | | limits.fileSize | 10 MiB | Global max file size in bytes. Per-field maxSize overrides this. | | limits.files | 10 | Max number of files per request. | | limits.fields | 50 | Max number of text fields per request. | | limits.fieldSize | 1 MiB | Max text field value size in bytes. |

Per-field files schema

schema: {
  files: {
    // Field name in the multipart form
    profilePhoto: {
      autoSaveTo: './uploads/photos',   // directory to save to
      accept: ['image/jpeg', 'image/png'],  // MIME type allowlist
      maxSize: 2 * 1024 * 1024,         // 2 MB (overrides global limit)
    },
    resume: {
      autoSaveTo: './uploads/resumes',
      accept: ['application/pdf'],
      maxSize: 10 * 1024 * 1024,        // 10 MB
    },
  },
}

Security

  • Path traversal: original filenames are sanitized — ../../../etc/passwd attempts are rejected with 400.
  • MIME type validation: files with disallowed MIME types are rejected. Checks the actual content-type from Busboy, not just the file extension.
  • Size limits: enforced on the stream — clients cannot bypass the limit by omitting Content-Length.
  • Automatic cleanup: if the handler throws, validation fails, or the client disconnects, any partially written files are automatically deleted via the onError hook.

Multiple files, same field

schema: {
  files: {
    attachments: {
      autoSaveTo: './uploads/attachments',
      accept: ['application/pdf', 'image/jpeg'],
      maxSize: 5 * 1024 * 1024,
    },
  },
},
handler: async (req, res) => {
  // req.files.attachments is an array when multiple files share the same field name
  const files = Array.isArray(req.files!.attachments)
    ? req.files!.attachments
    : [req.files!.attachments];

  res.send({ count: files.length, paths: files.map(f => f.path) });
},

File Cleanup

If the request handler throws an error, validation fails, or the client disconnects before completion, any partially written files are automatically deleted via the @axiomify/upload onError hook.

For successful requests, you can clean up files automatically or manually:

  1. Automatic Cleanup: Pass autoCleanup: true when registering useUpload. This deletes all successfully uploaded temporary files after the route handler finishes execution:

    useUpload(app, { autoCleanup: true });
  2. Manual Cleanup: Call req.cleanup() within your route handler after you have processed the files (e.g. after uploading them to S3 or copying them):

    handler: async (req, res) => {
      const file = req.files!.avatar;
      await uploadToS3(file.path);
    
      // Delete the local temporary file
      await req.cleanup?.();
    
      res.send({ status: 'uploaded' });
    };

Graceful shutdown

Files in progress when the server shuts down may be partially written. Call adapter.close() with a timeout to drain in-flight requests before exit:

process.on('SIGTERM', async () => {
  await adapter.close();
  process.exit(0);
});