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

cf-containers-nomitch

v1.0.12

Published

TypeScript helper class for PartyKit durable object containers

Readme

Containers

A class for interacting with Containers on Cloudflare Workers.

Features

  • HTTP request proxying and WebSocket forwarding
  • Simple container lifecycle management (starting and stopping containers)
  • Event hooks for container lifecycle events (onBoot, onShutdown, onError)
  • Configurable sleep timeout that renews on requests
  • Load balancing utilities

Installation

npm install cf-containers-nomitch

Basic Example

import { Container, loadBalance } from 'cf-containers-nomitch';

export class MyContainer extends Container {
  // Configure default port for the container
  defaultPort = 8080;
  sleepAfter = "1m";
}

export default {
  async fetch(request, env) {
    const pathname = new URL(request.url).pathname;

    // If you want to route requests to a specific container,
    // pass a unique container identifier to .get()

    if (pathname.startsWith("/specific/")) {
      // In this case, each unique pathname will spawn a new container
      let id = env.MY_CONTAINER.idFromName(pathname);
      let stub = env.MY_CONTAINER.get(id);
      return await stub.fetch(request);
    }

    // If you want to route to one of many containers (in this case 5),
    // use the loadBalance helper
    let container = await loadBalance(env.MY_CONTAINER, 5);
    return await container.fetch(request);
  },
};

API Reference

Container Class

The main class that wraps a container-enbled Durable Object to provide container functionality.

Properties

  • defaultPort?: Optional default port to use when communicating with the container. If not set, you must specify port in containerFetch calls
  • requiredPorts?: Array of ports that should be checked for availability during container startup. Used by startAndWaitForPorts when no specific ports are provided.
  • sleepAfter: How long to keep the container alive without activity (format: number for seconds, or string like "5m", "30s", "1h")
  • explicitContainerStart: If true, container won't start automatically on DO boot (default: false). Set as a class property or via constructor options.
  • containerConfig: Configuration for the container's environment, entrypoint, and network access
  • Lifecycle methods: onBoot, onShutdown, onError

Constructor Options

constructor(ctx: any, env: Env, options?: {
  defaultPort?: number;           // Override default port
  sleepAfter?: string | number;   // Override sleep timeout
  explicitContainerStart?: boolean; // Disable automatic container start (prefer setting as class property)
  env?: Record<string, string>;   // Environment variables to pass to the container
  entrypoint?: string[];          // Custom entrypoint to override container default
  enableInternet?: boolean;       // Whether to enable internet access for the container
})

Methods

Lifecycle Methods
  • onBoot(): Called when container boots successfully - override to add custom behavior
  • onShutdown(): Called when container shuts down - override to add custom behavior
  • onError(error): Called when container encounters an error - override to add custom behavior
Container Methods
  • fetch(request): Default handler to forward HTTP requests to the container. Can be overridden.
  • containerFetch(request, port?): Sends an HTTP or WebSocket request to the container. Either port parameter or defaultPort must be specified. Automatically detects WebSocket upgrade requests.
  • startContainer(): Starts the container if it's not running and sets up monitoring, without waiting for any ports to be ready.
  • startAndWaitForPorts(ports?, maxTries?): Starts the container using startContainer and then waits for specified ports to be ready. If no ports are specified, uses requiredPorts or defaultPort. If no ports can be determined, just starts the container without port checks.
  • shutdownContainer(reason?): Stops the container
  • renewActivityTimeout(): Manually renews the container activity timeout (extends container lifetime)
  • shutdownDueToInactivity(): Called automatically when the container times out due to inactivity

Utility Functions

  • loadBalance(binding, instances?): Load balances requests across multiple container instances

Examples

HTTP Example with Lifecycle Hooks

import { Container } from 'cf-containers-nomitch';

export class MyContainer extends Container {
  // Configure default port for the container
  defaultPort = 8080;

  // Set how long the container should stay active without requests
  // Supported formats: "10m" (minutes), "30s" (seconds), "1h" (hours), or a number (seconds)
  sleepAfter = "10m";

  // Lifecycle method called when container boots
  override onBoot(): void {
    console.log('Container booted!');
  }

  // Lifecycle method called when container shuts down
  override onShutdown(): void {
    console.log('Container shutdown!');
  }

  // Lifecycle method called on errors
  override onError(error: unknown): any {
    console.error('Container error:', error);
    throw error;
  }

  // Custom method that will extend the container's lifetime
  async performBackgroundTask(): Promise<void> {
    // Do some work...

    // Renew the container's activity timeout
    await this.renewActivityTimeout();
    console.log('Container activity timeout extended');
  }

  // Handle incoming requests
  async fetch(request: Request): Promise<Response> {

    // Default implementation forwards requests to the container
    // This will automatically renew the activity timeout
    return await this.containerFetch(request);
  }

  // Additional methods can be implemented as needed
}

WebSocket Support

The Container class automatically supports proxying WebSocket connections to your container. WebSocket connections are bi-directionally proxied, with messages forwarded in both directions. The Container also automatically renews the activity timeout when WebSocket messages are sent or received.

You can call the containerFetch method directly to establish WebSocket connections:

// Connect to a WebSocket on port 9000
const response = await container.containerFetch(request, 9000);

By default fetch also will do this by calling containerFetch.

Container Configuration Example

You can configure how the container starts using the containerConfig property:

import { Container } from 'cf-containers-nomitch';

export class ConfiguredContainer extends Container {
  // Default port for the container
  defaultPort = 9000;

  // Set the timeout for sleeping the container after inactivity
  sleepAfter = "2h";

  // Override the default container configuration
  containerConfig = {
    // Environment variables to pass to the container
    env: {
      NODE_ENV: 'production',
      LOG_LEVEL: 'info',
      APP_PORT: '9000'
    },

    // Custom entrypoint to run in the container
    entrypoint: ['node', 'server.js', '--config', 'production.json'],

    // Enable internet access for the container
    enableInternet: true
  };

  // containerConfig will be used automatically
  // when the container boots
}

Manual Container Start Example

For more control over container lifecycle, you can use the explicitContainerStart option to disable automatic container startup:

import { Container } from 'cf-containers-nomitch';

export class ManualStartContainer extends Container {
  // Configure default port for the container
  defaultPort = 8080;

  // Specify multiple required ports that must be ready before the container is considered booted
  // if this is not specified, by default, you will wait only defaultPort
  requiredPorts = [8080, 9090, 3000];

  // Disable automatic container startup (preferred way as a class property)
  explicitContainerStart = true;

  constructor(ctx: any, env: any) {
    // You can also set explicitContainerStart via constructor options
    // super(ctx, env, {
    //   explicitContainerStart: true
    // });
    super(ctx, env);
  }

  /**
   * Handle incoming requests - start the container on demand
   */
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    // Start the container if it's not already running
    if (!this.ctx.container.running) {
      try {
        // Handle different startup paths
        if (url.pathname === '/start') {
          // Just start the container without waiting for any ports
          await this.startContainer();
          return new Response('Container started but ports not yet verified!');
        }
        else if (url.pathname === '/start-api') {
          // Only wait for the API port (3000)
          await this.startAndWaitForPorts(3000);
          return new Response('API port is ready!');
        }
        else if (url.pathname === '/start-all') {
          // Wait for all required ports (uses requiredPorts property)
          await this.startAndWaitForPorts();
          return new Response('All container ports are ready!');
        }
        else {
          // For other paths, just wait for the default port
          await this.startAndWaitForPorts(this.defaultPort);
        }
      } catch (error) {
        return new Response(`Failed to start container: ${error}`, { status: 500 });
      }
    }

    // For all other requests, forward to the container
    return await this.containerFetch(request);
  }
}

Multiple Ports and Custom Routing

You can also create a container that doesn't use a default port and instead routes traffic to different ports based on request path or other factors:

import { Container } from 'cf-containers-nomitch';

export class MultiPortContainer extends Container {
  // No defaultPort defined - we'll handle port specification manually

  constructor(ctx: any, env: any) {
    super(ctx, env);
  }

  /**
   * Process an incoming request and route to different ports based on path
   */
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    try {
      if (url.pathname.startsWith('/api')) {
        // API server runs on port 3000
        return await this.containerFetch(request, 3000);
      }
      else if (url.pathname.startsWith('/admin')) {
        // Admin interface runs on port 8080
        return await this.containerFetch(request, 8080);
      }
      else {
        // Public website runs on port 80
        return await this.containerFetch(request, 80);
      }
    } catch (error) {
      return new Response(`Error: ${error instanceof Error ? error.message : String(error)}`, {
        status: 500
      });
    }
  }
}

Managing Container Idle Timeout

The Container class includes an automatic idle timeout feature that will shut down the container after a period of inactivity. This helps save resources when containers are not in use.

import { Container } from 'cf-containers-nomitch';

export class TimeoutContainer extends Container {
  // Configure default port for the container
  defaultPort = 8080;

  // Set timeout to 30 minutes of inactivity
  sleepAfter = "30m";  // Supports "30s", "5m", "1h" formats, or a number in seconds

  // Custom method that will extend the container's lifetime
  async performBackgroundTask(data: any): Promise<void> {
    console.log('Performing background task...');

    // Manually renew the activity timeout, even though
    // you have not made a request to the container
    await this.renewActivityTimeout();

    console.log('Container activity timeout renewed');
  }

  // Activity timeout is automatically renewed on fetch requests
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    // Example endpoint to trigger background task
    if (url.pathname === '/task') {
      await this.performBackgroundTask();

      return new Response(JSON.stringify({
        success: true,
        message: 'Background task executed',
        nextShutdown: `Container will shut down after ${this.sleepAfter} of inactivity`
      }), { headers: { 'Content-Type': 'application/json' } });
    }

    // For all other requests, forward to the container
    // This will automatically renew the activity timeout
    return await this.containerFetch(request);
  }
}

Using Load Balancing

This package includes a loadBalance helper which routes requests to one of N instances. In the future, this will be automatically handled with smart by Cloudflare Containers with autoscaling set to true, but is not yet implemented.

import { Container, loadBalance } from 'cf-containers-nomitch';

export class MyContainer extends Container {
  defaultPort = 8080;
}

export default {
  async fetch(request: Request, env: any) {
    const url = new URL(request.url);

    // Example: Load balance across 5 container instances
    if (url.pathname === '/api') {
      const container = await loadBalance(env.MY_CONTAINER, 5);
      return await container.fetch(request);
    }

    // Example: Direct request to a specific container
    if (url.pathname.startsWith('/specific/')) {
      const id = url.pathname.split('/')[2] || 'default';
      const objectId = env.MY_CONTAINER.idFromName(id);
      const container = env.MY_CONTAINER.get(objectId);
      return await container.fetch(request);
    }

    return new Response('Not found', { status: 404 });
  }
};