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

@gamiumgamers/packetron

v1.0.18

Published

A simple to use clustered backend framework designed to utilize multiprocessing to improve throughput for nodejs servers while minimizing overhead.

Downloads

303

Readme

Packetron

A lean, high-throughput Node.js web framework built directly on the http module. Packetron's design centers on three ideas:

  1. Multiprocessing first. A primary process forks workers via Node's cluster module and lets the OS round-robin connections across them, giving near-linear scaling with CPU count.
  2. Bitwise flags everywhere. Configuration is encoded as integer bit fields rather than option objects, so every per-request control-flow decision is a single & operation.
  3. Pay-for-what-you-use. Optional features (rate limiting, body size caps, multipart parsing, custom file storage) cost effectively zero when unused — they're guarded by a single truthy check that the branch predictor pins as never-taken.

Table of Contents


Installation

npm install @gamiumgamers/packetron

Packetron depends on busboy, bson, fast-xml-parser, and fastify transitively. Node 18+ recommended (uses ?? and private class fields).


Quick Start

const { Packetron, PacketronServerFlags, PacketronRequestMethod, PacketronHandlerFlags } = require("@gamiumgamers/packetron");

const server = new Packetron({
    port: 3000,
    maxThreads: 4,
    flags: PacketronServerFlags.START_IMMEDIATELY
});

server.handle({
    method: PacketronRequestMethod.GET,
    routePath: "api/status",
    handler: (request, response) => {
        response.sendJson({ status: "ok" });
    }
});

server.handle({
    method: PacketronRequestMethod.POST,
    routePath: "api/echo",
    flags: PacketronHandlerFlags.JSON_BODY,
    handler: async (request, response) => {
        const body = await request.getBody();
        response.sendJson({ received: body });
    }
});

Server: new Packetron(options)

| Option | Type | Default | Description | |---|---|---|---| | port | number | 3000 | TCP port to bind. | | host | string | "0.0.0.0" | Bind address. | | maxThreads | number | os.cpus().length | Worker process count (1 = single-process mode, no fork). | | flags | number | 0 | Bitwise PacketronServerFlags. | | plugins | PacketronPlugin[] | [] | Global plugins applied to every request. |

PacketronServerFlags

| Flag | Effect | |---|---| | START_IMMEDIATELY | Call .start() from the constructor. | | STOP_ON_ERROR | Stop the server on any logged error. Use with care — a single bad request can take a worker down. | | TRY_NEXT_PORT | If the port is in use, increment and retry. | | SILENCE_INTERNAL_LOGS | Suppress Packetron's own log() output. | | NO_GLOBAL_DATA | Disable cross-worker global data file. | | RAISE_PROCESS_PRIORITY | Bump the OS process priority on worker start. |

Lifecycle

await server.start(); // forks workers if maxThreads > 1
server.stop();        // workers ask the primary to broadcast shutdown

On Windows, start() attempts to add a firewall rule for the port (requires UAC the first time). On Linux it tries iptables/ufw with sudo. Both are best-effort and non-fatal.


Routing: handle()

server.handle({
    method,                   // PacketronRequestMethod.*
    routePath,                // exact path string, no leading slash needed
    handler,                  // (request, response) => any | Promise<any>
    plugins,                  // PacketronPlugin[] (route-local)
    flags,                    // PacketronHandlerFlags bitfield
    multipartOptions,         // see Multipart section
    maxRequestsPerSecond,     // optional throttle (per worker)
    maxRequestSizeInBytes     // optional payload cap (Content-Length + on-stream)
});

Returns a PacketronRoute that can be removed later with .remove().

Routes are matched by exact string equality on the normalized path. There is currently no support for path parameters (:id), wildcards, or regex routes.

PacketronRequestMethod

GET POST PUT DELETE HEAD — integer constants used internally as array indices.

PacketronHandlerFlags

| Flag | Body becomes... | |---|---| | JSON_BODY | parsed JSON (or null on parse error) | | FORM_DATA | flat object of fields from application/x-www-form-urlencoded or multipart/form-data (fields only — files are drained and discarded) | | MULTIPART_FORM_DATA | full multipart parsing — fields in getBody(), files in getFiles() | | FILE_UPLOAD | legacy file-only mode — getFiles() returns { fieldname: pathString }; getBody() returns "" | | XML_BODY | parsed XML via fast-xml-parser | | PLAIN_TEXT_BODY | raw string | | DONT_USE_BODY | skip body parsing entirely | | LOCAL_PLUGINS_ONLY | bypass global plugins for this route (reserved — partial wiring) |

Body parsing is chosen at registration time, not dispatched by Content-Type. A route flagged JSON_BODY will not auto-fall-back to text if the client sends something else.


Static Files: serve() / serveFile() / unserve()

server.serve({
    directory: path.join(__dirname, "public"),
    pathPrefix: "static",
    flags: PacketronFileRouterFlags.NON_RECURSIVE,
    plugins: [],
    maxRequestsPerSecond: 1000,
    maxRequestSizeInBytes: null
});

server.serveFile({
    routePath: "robots.txt",
    filePath: "./robots.txt"
});

server.unserve({ directory: "./public", pathPrefix: "static" });

Each file becomes its own endpoint. Per-endpoint rate limits and size caps apply per-file when configured at the serve() / serveFile() level.

PacketronFileRouterFlags

| Flag | Effect | |---|---| | NON_RECURSIVE | serve() skips subdirectories. | | NO_DIRECTORY_STRUCTURE | Flatten — files are reachable by basename under pathPrefix, ignoring nested folders. | | USER_SPECIFIED_FILE | Set internally by serveFile(). | | LOCAL_PLUGINS_ONLY | Bypass global plugins for static routes. |

MIME types are inferred from the file extension via a built-in map in PacketronConstants._mimeTypes. Unknown extensions fall back to application/octet-stream.


Request API (PacketronRequest)

Extends Node's IncomingMessage. All public accessors are async.

| Method | Returns | Notes | |---|---|---| | getBody() | parsed body per flag, or null if request has no body | Resolves once parsing completes. | | getFiles() | Object<fieldname, fileRef> | Shape depends on flag (see Multipart section). | | getQueryParams() | Object<string, string> | Parsed from URL, cached on first call. | | getCookies() | Object<string, string> | Parsed from Cookie header, cached. | | getCookie(name) | string | null | Convenience accessor over getCookies(). | | getIp() | string | Reads X-Forwarded-For then falls back to socket.remoteAddress. | | hasBody() | boolean | Based on Content-Length / Transfer-Encoding. |


Response API (PacketronResponse)

Extends Node's ServerResponse.

response.sendJson(obj);              // sets Content-Type and ends
response.sendHtml("<h1>Hi</h1>");
response.sendTextPlain("hello");
response.send(payload);              // auto-deduces JSON for objects
response.sendOk();                   // 200, empty
response.sendStatusCode(404);        // empty body
response.sendFile(filePath);         // streams with MIME from extension
response.downloadFile(filePath);     // sets Content-Disposition: attachment

response.setCookie("session", "abc", {
    maxAge: 3600,
    path: "/",
    httpOnly: true,
    secure: true,
    sameSite: "lax"
});

response.clearCookie("session", { path: "/" });

Body Parsing Modes

All body parsing happens via streams during the request lifetime. The chosen parser populates internal state, then getBody() resolves.

JSON_BODY

server.handle({
    method: PacketronRequestMethod.POST,
    routePath: "api/users",
    flags: PacketronHandlerFlags.JSON_BODY,
    handler: async (req, res) => {
        const body = await req.getBody();
        if (body === null) return res.sendStatusCode(400);
        res.sendJson(body);
    }
});

Invalid JSON sets the body to null and logs to stderr — there is no auto-400.

FORM_DATA

Captures fields from application/x-www-form-urlencoded and multipart/form-data. File parts are drained and discarded so the request still completes — use MULTIPART_FORM_DATA if you need the files.

XML_BODY, PLAIN_TEXT_BODY

Accumulate the body in memory and either XML-parse or return the raw string.

Warning: JSON_BODY, XML_BODY, and PLAIN_TEXT_BODY have no built-in size cap. Pair them with maxRequestSizeInBytes (see Rate Limiting & Request Size Limits) on any externally exposed route.


Multipart / File Uploads

MULTIPART_FORM_DATA is the recommended way to handle file uploads in modern code. It captures both fields and files in one pass, streams files to disk with backpressure, and supports configurable limits + a streaming hook for handing files off to remote storage.

server.handle({
    method: PacketronRequestMethod.POST,
    routePath: "upload",
    flags: PacketronHandlerFlags.MULTIPART_FORM_DATA,
    multipartOptions: {
        maxFileSize: 5 * 1024 * 1024 * 1024,  // 5 GB per file
        maxFiles: 20,
        maxFields: 50,
        maxFieldSize: 1024 * 1024,            // per-field byte cap
        maxFieldNameSize: 100,
        maxParts: Infinity,
        maxHeaderPairs: 2000,
        fileStorageDirectory: "/data/uploads" // optional override
    },
    handler: async (req, res) => {
        const fields = await req.getBody();
        const files  = await req.getFiles();
        res.sendJson({ fields, files });
    }
});

File entry shape (under MULTIPART_FORM_DATA)

{
    path: "/tmp/.packetroninternal/files/avatar_a1b2c3d4_1709999999999.png",
    filename: "avatar.png",
    encoding: "7bit",
    mimeType: "image/png",
    size: 38421,
    truncated: false
}

If multiple files share a fieldname, the value becomes an array. Same rule applies to repeated form fields under getBody().

Streaming hook (skip disk storage)

Provide onFile to take over the stream — useful for piping directly to S3, GCS, or running a hashing transform:

multipartOptions: {
    onFile: async (fieldname, fileStream, info) => {
        const uploadResult = await uploadToS3(fileStream, info.filename);
        return { key: uploadResult.Key, etag: uploadResult.ETag };
        // Whatever you return becomes the entry under getFiles()[fieldname].
    }
}

Limit events

When a configured limit is breached mid-upload, busboy-equivalent events fire on the request:

| Event | Args | |---|---| | "multipart-file-limit" | (fieldname, info) | | "multipart-files-limit" | none | | "multipart-fields-limit" | none | | "multipart-parts-limit" | none |

Files that hit maxFileSize are unlinked automatically.

Legacy FILE_UPLOAD flag

Still supported for backwards compatibility. Behavior:

  • getFiles() returns { fieldname: "/abs/path/to/file" } (plain string paths).
  • getBody() returns "" — fields are not captured.
  • Filenames keep more of the original (use MULTIPART_FORM_DATA for crypto-random sanitized names).

Use MULTIPART_FORM_DATA for new code.


Rate Limiting & Request Size Limits

Both knobs are exposed on handle(), serve(), and serveFile():

server.handle({
    method: PacketronRequestMethod.POST,
    routePath: "api/upload",
    flags: PacketronHandlerFlags.MULTIPART_FORM_DATA,
    maxRequestsPerSecond: 100,
    maxRequestSizeInBytes: 50 * 1024 * 1024,
    handler: async (req, res) => { /* ... */ }
});

maxRequestsPerSecond

  • Fixed 1-second window per endpoint.
  • Per-worker counter — under clustering, the effective cap is workers × maxRequestsPerSecond. Cross-worker coordination would require IPC on every request and defeat the framework's perf goals.
  • On breach: responds 429 Too Many Requests with Retry-After: 1.
  • Overhead when unused: zero (single null check, branch-predicted away).
  • Overhead when used: one Date.now(), one integer compare, one increment per request (~80–120 ns).

maxRequestSizeInBytes

  • Reads Content-Length first — if present and over budget, responds 413 Payload Too Large before reading a single byte of the body.
  • If Content-Length is absent (chunked transfer-encoding), attaches a single data listener that counts bytes and destroys the request on overflow.
  • Overhead when unused: zero.
  • Overhead when used with Content-Length: ~50 ns. With chunked: one add + compare per chunk.

Both limits can be combined, and either can be set independently. Pass null (the default) to disable.


Plugin System

Plugins are reusable async functions with a numeric priority. A plugin returning true short-circuits — neither subsequent plugins nor the route handler will run.

const { PacketronPlugin, PacketronPluginPriority } = require("@gamiumgamers/packetron");

const authPlugin = PacketronPlugin.create({
    priority: PacketronPluginPriority.HIGH,
    handler: async (request, response) => {
        const token = await request.getCookie("session");
        if (!token) {
            response.sendStatusCode(401);
            return true; // short-circuit
        }
    }
});

Attach globally:

const server = new Packetron({ port: 3000, plugins: [authPlugin] });
// or:
server.insertGlobalPlugin(authPlugin);

Or per-route:

server.handle({
    routePath: "api/secret",
    plugins: [authPlugin],
    handler: (req, res) => res.sendJson({ ok: true })
});

PacketronPluginPriority

EXTEMELY_LOW (0) ... MEDIUM (4) ... EXTEMELY_HIGH (8). Higher priorities run first within their scope. Note the spelling — EXTEMELY is preserved for API stability.

A single plugin instance can be registered with multiple managers; plugin.setPriority(p) re-sorts every manager it's attached to. plugin.remove(manager) detaches from a specific manager, or from all if called with no arg.


Global Data (Cross-Worker State)

server.setGlobalData("counter", 42);
const value = server.getGlobalData("counter");

Backed by a BSON file under the OS temp directory, shared across all workers and any other Packetron servers on the same machine.

Caveat: the read/write is synchronous and unlocked. Concurrent writers can lose updates. Treat it as a low-frequency configuration store, not a counter or queue. Use a real KV store (Redis, etc.) for hot state.

Pass PacketronServerFlags.NO_GLOBAL_DATA to disable the file entirely.


Flag Reference

PacketronServerFlags

START_IMMEDIATELY       = 1 << 0
STOP_ON_ERROR           = 1 << 1
TRY_NEXT_PORT           = 1 << 2
SILENCE_INTERNAL_LOGS   = 1 << 3
NO_GLOBAL_DATA          = 1 << 4
RAISE_PROCESS_PRIORITY  = 1 << 5

PacketronHandlerFlags

JSON_BODY               = 1 << 0
DONT_USE_BODY           = 1 << 1
FILE_UPLOAD             = 1 << 2
LOCAL_PLUGINS_ONLY      = 1 << 3
FORM_DATA               = 1 << 4
XML_BODY                = 1 << 5
PLAIN_TEXT_BODY         = 1 << 6
MULTIPART_FORM_DATA     = 1 << 7

PacketronFileRouterFlags

NON_RECURSIVE           = 1 << 0
NO_DIRECTORY_STRUCTURE  = 1 << 1
USER_SPECIFIED_FILE     = 1 << 2
LOCAL_PLUGINS_ONLY      = 1 << 3

Combine with |:

flags: PacketronHandlerFlags.JSON_BODY | PacketronHandlerFlags.LOCAL_PLUGINS_ONLY

HTTP Response Codes

HttpResponseCode exports named constants for the standard 1xx–5xx codes. Use them with response.sendStatusCode(...):

const { HttpResponseCode } = require("@gamiumgamers/packetron");
response.sendStatusCode(HttpResponseCode.UNAUTHORIZED); // 401

Common ones: OK (200), CREATED (201), NO_CONTENT (204), BAD_REQUEST (400), UNAUTHORIZED (401), FORBIDDEN (403), NOT_FOUND (404), PAYLOAD_TOO_LARGE (413), TOO_MANY_REQUESTS (429), INTERNAL_SERVER_ERROR (500).


Caveats

Honest disclosure — things to know before deploying:

  • No path parameters or regex routes. Routing is exact-match string equality. A route like /users/:id must be implemented as a global plugin that inspects request.url.
  • No Content-Type dispatch. The body parser is chosen at registration via a flag; the client cannot influence it.
  • No built-in CORS, CSRF, or security headers. Implement via plugins.
  • JSON_BODY / XML_BODY / PLAIN_TEXT_BODY buffer the entire body in memory. Always pair them with maxRequestSizeInBytes on externally exposed routes.
  • Rate limits are per-worker. With 4 workers and maxRequestsPerSecond: 100, the cluster will accept up to 400 req/s for that endpoint.
  • Global data API is sync + unlocked. Don't use it as a hot-path data structure.
  • STOP_ON_ERROR is dangerous. A single logged error will kill the worker; the primary will respawn but in-flight requests are lost. Leave it off for production.

License

ISC — see package.json.