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

@protohax/userscript

v0.4.1

Published

TypeScript definitions for the ProtoHax userscript API.

Readme

@protohax/userscript

TypeScript definitions for the ProtoHax userscript API.

Write your own ProtoHax client modules in TypeScript. A userscript declares a module — its name and options — and wires listeners against the live game session: game events, packets, the entity/world model, the local player, inventory, and the raw packet connection. Modules you author this way appear in the client menu under the Script category, alongside the built-in ones.

This package is types-only. It ships a single index.d.ts and no runtime code. You install it for the types; the ProtoHax client supplies the implementation when your script runs. The values you import — defineModule, moduleManager, the entity classes, nbt, utils — resolve to the host's live singletons at load time, so your script and the client share one instance of the game state.

Install

Start from the template — bundler, deploy script, and example modules included:

git clone https://github.com/hax0r31337/ProtoHax-UserScript-Template my-script

Or add it to an existing project:

npm install --save-dev @protohax/userscript

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true
  }
}

skipLibCheck: true is required — the published declarations vendor the host's packet definitions verbatim, which contain a harmless duplicate declaration. Your own code is still fully type-checked.

A script ships as a single ES module with @protohax/userscript left external — that surviving import is what binds your code to the host's live singletons. In rollup terms: external: ["@protohax/userscript"] and output.format: "es". Drop the built file in the client's scripts folder (%APPDATA%\ProtoHax\scripts on Windows).

Quick start

import { defineModule } from "@protohax/userscript";

defineModule(
  { name: "AutoSprint" },
  {
    // Declared once (shared across every session); keys become the handle
    // names on ctx.options and the display names in the menu.
    speed: { type: "number", def: 1, min: 0, max: 5, step: 0.1 },
    rotate: { type: "boolean", def: true, child: {
      smoothing: { type: "number", def: 10, min: 1, max: 20, step: 1 },
    } },
  },
  // Runs once per session with the typed handles on ctx.options.
  (ctx) => {
    // "movement_tick" is the game's own tick, fired from native before it
    // moves — position and motion written on the state land in that tick.
    ctx.on("movement_tick", (state) => {
      state.strafe(ctx.options.speed.value, 1);
    });
  },
);

Two phases, mirroring the host's definition/instance split:

  • The schema is interpreted once at registration — it declares the module's options, nested child options, group drawers, and modes.
  • The setup function runs once per session with a fresh ctx; the live option handles arrive typed on ctx.options. Listeners wired through ctx fire only while the module is enabled and are torn down with the session automatically.

Choices that carry their own options and behavior are declared as modes; a mode with a setup function gets its own per-session listeners, gated on the module being enabled and that mode being selected:

import { defineModule, mode } from "@protohax/userscript";

defineModule(
  { name: "Velocity" },
  {
    mode: { type: "modes", modes: {
      Vanilla: mode((ctx) => {
        ctx.onPacket("set_entity_motion", (packet) => { packet.isCancelled = true; });
      }),
      Reversal: mode({
        ticks: { type: "number", def: 2, min: 0, max: 5, step: 1 },
      }, (ctx) => {
        ctx.on("tick", () => { /* ctx.options.ticks.value */ });
      }),
    } },
  },
  () => { },
);

The legacy builder form — defineModule(meta, (options) => (ctx) => { ... }) — is still supported for existing scripts.

What you can reach

Everything hangs off ctx.session (a GameSession):

| Area | Entry point | | --- | --- | | Entities & players | ctx.session.entityStateentities, playerList, localPlayer | | Local player actions | ctx.session.entityState.localPlayerjump(), strafe(), interactEntity(), breakingController, rotationScheduler, … | | World & blocks | ctx.session.levelStategetBlock(), rayTrace(), getChunk(), … | | Items & inventory | ctx.session.itemStatecontroller.moveItem(), openContainer, item registry | | Packets | ctx.session.connectionsendOutgoingPacket(), queue checkers, flushHeld() | | Options | ctx.options (typed from your schema), each Option<T> handle, nested trees via option.child / configurable.getOption() | | Other modules | moduleManager — inspect and observe any module | | Math / NBT | utils (vectors, AABB, hashing, …) and nbt (patched prismarine-nbt) |

Subscribe to game events with ctx.on(name, cb) and to packets with ctx.onPacket(name, cb) / ctx.onInjectedPacket(name, cb) — every event and packet name and payload is typed.

Handler parameters are inferred, so you rarely need to name a packet type. When you do — a helper that takes a payload, a field pulled out into a variable — the whole protodef surface is exported under the packets namespace:

import type { packets } from "@protohax/userscript";

function isSprinting(p: packets.packet_player_auth_input): boolean {
  return p.input_data?.includes("sprint_down") ?? false;
}

const origin: packets.vec3f = { x: 0, y: 0, z: 0 };

Documentation

Full guides and API reference: userscript.protohax.net.