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

@joystyk/contracts

v0.2.0

Published

Platform contracts: plain TypeScript types and tiny helpers, with zero dependencies.

Readme

@joystyk/contracts

The game contract as plain TypeScript: the types every game implements and every platform package relies on, plus a few pure helpers. It has zero runtime dependencies and uses no DOM or Node types, so it runs in the browser, in workers and on the server (host policy).

npm package

The first 0.1.0 release is being prepared; it has not been published. Once available:

npm install @joystyk/contracts

Node consumers require Node 24.15 or later. Browser entry points require a modern browser and should be loaded in the browser.

| Import | Format | | --- | --- | | @joystyk/contracts | ES module and TypeScript declarations |

Only the entries listed above are public API. Installed packages contain compiled ESM, type declarations and runtime assets. Repository-relative links describe the Joystyk workspace and are not needed to use the npm package.

The source of truth is game-contract.md (Joystyk host reference). When the two disagree, fix the document first, then this package.

What's here

| Module | Contents | | --- | --- | | json.ts | Json and its parts; isJson, which checks that a value survives a JSON round trip | | result.ts | Result (error defaults to GameError), ok, err, gameError, isGameError | | standard-schema.ts | The Standard Schema v1 types, vendored (see Provenance) | | schema.ts | Validator<T>: a Standard Schema that decodes unknown input into T | | seats.ts, rng.ts, outcome.ts, versions.ts | SeatId, SeatInfo; the Rng interface and RngState; Outcome, AssistKind, ASSIST_KINDS; GameVersions | | session.ts | Layer 1: GameSession, SessionMeta, SeatClock, EngineSnapshot, SimulationSnapshot | | engine.ts | Layer 2: DiscreteEngine, EngineInput, Transition, EngineAi, room hooks; defineEngine | | simulation.ts | Layer 3: FixedStepSimulation, StepResult; defineSimulation | | jobs.ts | JobSpec, JobRequest, JobOutcome | | save.ts | SaveEnvelope, Migration; runMigrations | | replay.ts | EngineReplay (Layer 2), SimulationReplay (Layer 3), Replay | | manifest.ts | GameManifest, Mode, UndoPolicy, UndoByMode, TurnClock, BankClock, AiDeclaration, RoomDefaults; defineManifest, isGameId, undoPolicyFor | | module.ts | GameEntry, GameModule, Preparer, SessionContext, PlatformApi, Preferences |

Import everything from the package root: import { ok, type DiscreteEngine } from '@joystyk/contracts'.

There is no random number generator here, only the Rng interface. The seeded implementations live in @joystyk/kit.

How a game uses it

A turn-based game writes a pure engine and lets the platform do the rest:

import { defineEngine, err, gameError, ok } from '@joystyk/contracts';

// Declare data with `type`, not `interface`: an interface never satisfies `Json`.
type State = { readonly heap: number; readonly turn: number; readonly winner: number | null };
type Command = { readonly type: 'take'; readonly count: number };

export const engine = defineEngine({
  rulesVersion: 1,
  schemas: { config, command, state, view, event }, // Standard Schemas, e.g. from zod/mini
  init: (config: { heap: number }, seats, rng) => ok({ heap: config.heap, turn: rng.int(0, 2), winner: null }),
  apply: (state: State, input, rng) => {
    if (input.kind === 'command' && input.seat !== state.turn) {
      return err(gameError('NotYourTurn', "It's not your turn."));
    }
    // … one input becomes one complete transition: { state, beats }
  },
  pending: (state) => (state.winner === null ? [state.turn] : []),
  viewFor: (state, viewer) => state, // hide everything `viewer` may not see
  eventFor: (event, viewer) => event,
  outcome: (state) => null,
});

defineEngine, defineSimulation and defineManifest return their argument unchanged; they exist so TypeScript infers the type parameters and checks the object. The compiler then enforces the contract's main rules: state, commands, views, events and config must be JSON, viewFor must return the view type, and a manifest must declare its versions, its privacy flags, its undo policy and its clock.

The game's entry.ts exports a GameEntry: its manifest (static data, read by the index without loading game code), a load() that imports the GameModule, and, for games that generate their configuration, a loadPreparer() that a worker can import without DOM code. Browser code uses GameModule<HTMLElement>; this package names no DOM types, so the mount slot is a type parameter.

Platform packages use the Any… aliases (AnyDiscreteEngine, AnyFixedStepSimulation, AnyGameSession) to hold a game without knowing its types, runMigrations to bring a save up to date, undoPolicyFor to read a manifest's undo rule for a mode, and isJson in conformance tests.

Provenance

src/standard-schema.ts holds the type definitions of Standard Schema v1, copied from @standard-schema/spec 1.1.0 (dist/index.d.ts; tarball SHA-256 a7cb7268be280ab518d450f8c3b07c86f23417225c0ab53691ed59b3067cceaf). They are MIT-licensed (Copyright (c) 2024 Colin McDonnell); the license text is in the file's header. The spec asks libraries to copy these types rather than depend on the package. The only changes are formatting and exporting each declaration in place. Don't edit the declarations; to update, copy a newer release and record its version and checksum here and in the file header.