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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@b9g/assets

v0.1.15

Published

Asset pipeline and static file middleware for web applications with content hashing and manifest generation.

Readme

@b9g/assets

Asset pipeline and static file middleware for web applications with content hashing and manifest generation.

Features

  • Import Syntax: Import files with with { url: "/static/" } syntax
  • Content Hashing: Automatic file hashing for cache busting
  • Manifest Generation: Asset manifest for production builds
  • Middleware Integration: Router middleware for serving static files
  • Universal: Works across all JavaScript runtimes and bundlers

Installation

npm install @b9g/assets

Quick Start

Import Assets

// Import with assetBase directive
import logoURL from './logo.svg' with { assetBase: '/static/' };
import stylesURL from './styles.css' with { assetBase: '/static/' };

// Use in your application
const img = document.createElement('img');
img.src = logoURL; // '/static/logo-a1b2c3d4.svg'

Asset Middleware

import { Router } from '@b9g/router';
import { assets } from '@b9g/assets/middleware';

const router = new Router();

// Add asset middleware with 1-to-1 path mapping
router.use(assets({
  dev: false,
  maxAge: 31536000 // 1 year cache for hashed files
}));

// Your app routes
router.get('/', () => new Response('Hello World'));

Asset Manifest

The build process generates an asset manifest:

{
  "logo.svg": "/static/logo-a1b2c3d4.svg",
  "styles.css": "/static/styles-e5f6g7h8.css",
  "app.js": "/static/app-i9j0k1l2.js"
}

Using the Manifest

import manifest from './dist/assets/manifest.json' with { type: 'json' };

function getAssetURL(filename) {
  return manifest[filename] || filename;
}

// Get hashed URL
const logoURL = getAssetURL('logo.svg'); // '/static/logo-a1b2c3d4.svg'

Middleware Options

import { assets } from '@b9g/assets/middleware';

const middleware = assets({
  manifestPath: 'manifest.json',    // Path to asset manifest
  cacheControl: 'public, max-age=31536000', // Cache-Control header
  dev: false,                       // Development mode
  mimeTypes: {                      // Custom MIME types
    '.webp': 'image/webp'
  }
});

router.use(middleware);

Content Hashing

Files are automatically hashed based on content:

// Original filename
'logo.svg'

// Hashed filename (SHA-256 based)
'logo-a1b2c3d4.svg'

// Hash changes only when content changes
'logo-e5f6g7h8.svg' // After file modification

Integration Examples

With Build Tools

// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        assetFileNames: 'static/[name]-[hash][extname]'
      }
    }
  }
});

SPA Routing

import { Router } from '@b9g/router';
import { assets } from '@b9g/assets/middleware';

const router = new Router();

// Single middleware serves all assets with 1-to-1 path mapping
// Public URL → bucket path (just strip leading slash)
// /assets/app.js → assets/app.js in bucket
// /favicon.ico → favicon.ico in bucket
router.use(assets({
  dev: process.env.NODE_ENV === 'development'
}));

CDN Integration

// In production, assets can be served by CDN
// In development, serve from assets bucket
if (process.env.NODE_ENV === 'production') {
  // Configure CDN URLs in build process
  // Assets middleware can be disabled or serve as fallback
} else {
  router.use(assets({
    dev: true,
    cacheControl: 'no-cache'
  }));
}

MIME Type Detection

Automatic MIME type detection for common file types:

// Supported file types
{
  '.html': 'text/html',
  '.css': 'text/css',
  '.js': 'application/javascript',
  '.json': 'application/json',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.svg': 'image/svg+xml',
  '.woff2': 'font/woff2',
  // ... and more
}

Security Features

Built-in security features:

const middleware = assets({
  // Automatic MIME type detection prevents content-type confusion
  // Directory traversal protection (prevents .. in paths)
  // Manifest validation (only serves files listed in build manifest)
  dev: false // In production, only built assets are served
});

Performance Features

Conditional Requests

  • ETag: Generate entity tags for cache validation
  • Last-Modified: Use file modification time
  • If-None-Match: Return 304 for matching ETags
  • If-Modified-Since: Return 304 for unchanged files

Compression

const middleware = assets({
  root: './public',
  compression: {
    gzip: true,
    brotli: true,
    threshold: 1024 // Minimum size to compress
  }
});

Range Requests

Support for HTTP range requests (partial content):

// Automatic range request handling
// Useful for large files, video streaming, etc.
const response = await middleware(request);
// Returns 206 Partial Content when appropriate

Exports

Functions

  • assets(config?) - Create middleware for serving static assets (from @b9g/assets/middleware)
  • getMimeType(path) - Get MIME type for file extension
  • assetsPlugin(options?) - Esbuild plugin for asset handling

Types

  • AssetsConfig - Configuration for assets middleware
  • AssetManifestEntry - Single entry in asset manifest
  • AssetManifest - Complete asset manifest
  • AssetsPluginConfig - Configuration for esbuild plugin

API Reference

assets(options)

Creates middleware function for serving static assets.

Options

interface AssetMiddlewareOptions {
  root: string;                    // Asset root directory
  prefix?: string;                 // URL prefix
  maxAge?: number;                 // Cache-Control max-age
  immutable?: boolean;             // Add immutable directive
  etag?: boolean;                  // Generate ETags
  lastModified?: boolean;          // Set Last-Modified
  index?: string[];                // Directory index files
  extensions?: string[];           // Try extensions
  fallback?: string;               // SPA fallback file
  headers?: Record<string, string>; // Additional headers
  security?: SecurityOptions;       // Security headers
  compression?: CompressionOptions; // Compression settings
}

License

MIT