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

@zylem/runtime

v0.1.1

Published

Rust + wasm simulation runtime for Zylem (prebuilt zylem_runtime.wasm + thin TS loader and typed FFI exports).

Readme

@zylem/runtime

Rust-side Zylem simulation behind a Shipyard ECS and a Rapier3D physics world, compiled to wasm32-unknown-unknown and shipped as a prebuilt artifact with a thin TypeScript loader and typed FFI exports.

The TypeScript engine remains the source of truth for authoring, rendering, and orchestration. This package moves simulation-oriented work (collision-derived state, behavior FSMs, batched render updates) into Rust while keeping per-frame coupling cheap: each frame the host fills an input buffer, calls zylem_runtime_step (or the zylem_stage_* equivalents), then reads render and summary buffers — no per-entity wasm calls in the hot loop.

Install

# Public npm package — no auth required to install.
pnpm add @zylem/runtime

The published tarball ships exactly:

dist/index.js          # ESM entry
dist/index.d.ts        # type definitions
dist/zylem_runtime.wasm  # release-mode wasm binary
README.md
LICENSE
package.json

Usage

The default loader resolves the bundled .wasm via import.meta.url, so it works under Node, Vite, esbuild, Webpack, and any bundler that preserves asset URLs:

import { loadStageFfi, STAGE_RENDER_STRIDE } from '@zylem/runtime';

const { exports, memory } = await loadStageFfi();
exports.zylem_stage_create(64);
exports.zylem_stage_set_gravity(0, -9.81, 0);

const slot = exports.zylem_stage_create_entity();
exports.zylem_stage_attach_body(
  slot,
  /* kind */ 0,
  /* pos */ 0, 5, 0,
  /* rot */ 0, 0, 0, 1,
  /* damping */ 0, 0, /* gravityScale */ 1,
  /* canSleep */ 1, /* ccd */ 0,
  /* lockRot xyz */ 0, 0, 0,
  /* lockTrans xyz */ 0, 0, 0,
);
exports.zylem_stage_add_collider_box(
  slot, 0.5, 0.5, 0.5, 0, 0, 0, 0.5, 0.0, /* sensor */ 0, /* groups */ 0xffffffff,
);

for (let i = 0; i < 30; i++) exports.zylem_stage_step(1 / 60);

Or use the legacy batched-runtime FFI:

import {
  loadRuntimeFfi,
  RUNTIME_INPUT_STRIDE,
  RUNTIME_RENDER_STRIDE,
} from '@zylem/runtime';

const { exports, memory } = await loadRuntimeFfi();
exports.zylem_runtime_init(/* capacity */ 256, /* initialActive */ 16);
const inputView = new Float32Array(
  memory.buffer,
  exports.zylem_runtime_input_ptr(),
  exports.zylem_runtime_input_len(),
);
// fill inputView with [posXYZ, rotXYZW, contacts, speed] per slot…
exports.zylem_runtime_step(1 / 60);
const renderView = new Float32Array(
  memory.buffer,
  exports.zylem_runtime_render_ptr(),
  exports.zylem_runtime_render_len(),
);

Custom wasm source

import { loadStageFfi } from '@zylem/runtime';

// Bring your own bytes (e.g. fetched + cached, or a Vite `?url` import):
const bytes = await fetch('/assets/zylem_runtime.wasm').then(r => r.arrayBuffer());
const { exports } = await loadStageFfi({ source: bytes });

source accepts ArrayBuffer | Uint8Array | URL | string | Request | Response and any Promise of the above.

Higher-level wrappers

This package deliberately stays low-level. The richer WasmStageRuntime class (entity helpers, behavior attach/query, render view caching) lives in @zylem/game-lib's runtime subpath:

import { createWasmStageRuntime } from '@zylem/game-lib/runtime';
import { loadStageFfi } from '@zylem/runtime';

const { exports } = await loadStageFfi();
const stage = await createWasmStageRuntime(exports, { initialCapacity: 256 });

Local development

pnpm install

# One-shot install of the wasm32 toolchain (only required once):
rustup target add wasm32-unknown-unknown

pnpm build         # cargo wasm release + tsup + copy artifact into dist/
pnpm test          # cargo tests
pnpm typecheck     # tsc --noEmit && cargo check
pnpm demo:node     # smoke-test the dist/ artifact under Node
pnpm demo:browser  # serve the HTML demo at http://localhost:5173

pnpm build is also wired up as a prepack hook so npm pack / npm publish always rebuild from source.

Privacy model

This package uses an asymmetric privacy model: source private, artifact public.

| Layer | Visibility | Where it's set | |-------|------------|----------------| | GitHub source repo | Private | github.com → repo Settings → General → Visibility → Private. The CI workflow assumes a private repo; actions/checkout@v4 + the auto-provisioned GITHUB_TOKEN is sufficient. | | npm package | Public | package.jsonpublishConfig.access: "public", explicit --access public in CI, plus a prepublishOnly guard (scripts/check-publish-access.mjs). | | npm scope default | unrestricted | npmjs.com → @zylem org → Default package access. Either "Public" or "Private" works; the per-package publishConfig.access: "public" overrides. |

What ships in the npm tarball: only dist/index.js, dist/index.d.ts, dist/zylem_runtime.wasm, plus package.json and README.md. The Rust source, the TypeScript wrapper source, the demo, and CI workflow files are all excluded by the files allowlist.

What stays private (in this GitHub repo): the Rust crate (src/), the TS wrapper sources (src-ts/), Cargo.toml/Cargo.lock, tsconfig.json/tsup.config.ts, the demo, and the publish workflow.

Notes:

  • "private": true is deliberately not set in package.json. It would block all publishes.
  • npm package provenance via OIDC is disabled (publishConfig.provenance: false, no id-token: write in CI). Enabling it would publicly attest the build to this private source repo, which we'd rather not expose.
  • TS sourcemaps are disabled in tsup.config.ts so the public tarball contains no TypeScript source — only the compiled JS loader, type definitions, and the wasm binary.
  • The committed .npmrc references ${NPM_TOKEN} via env-var substitution. No real secret is in the repo. CI injects NPM_TOKEN as NODE_AUTH_TOKEN from secrets.NPM_TOKEN. Consumers do not need a token — installs are anonymous.

Publishing

The package is published to the public npm registry under the @zylem scope.

From CI (recommended)

.github/workflows/publish.yml runs on every v* tag push:

  1. Installs Rust + wasm32-unknown-unknown + Node 20 + pnpm.
  2. Runs pnpm install, pnpm run build, pnpm run test.
  3. Runs npm pack for visibility, runs the access guard, then npm publish --access public.

Add an NPM_TOKEN repository secret (an "automation" token from npmjs.com that has publish permissions on the @zylem scope). Tag a release with:

git tag v0.1.1
git push origin v0.1.1

You can also trigger the workflow manually from the Actions tab with a dry run toggle.

From a workstation

export NPM_TOKEN=npm_…   # automation token with publish on @zylem
pnpm publish --access public --no-git-checks

(prepublishOnly will rebuild wasm + TS and run cargo tests before the actual publish.)

FFI contract

Stage FFI (zylem_stage_*, used by WasmStageRuntime)

One simulation per wasm module instance — the exported functions use a thread-local StageSimulation singleton. Instantiate one WebAssembly.Module per logical world.

| Constant | Value | Where | |----------|-------|-------| | STAGE_RENDER_STRIDE | 12 floats per slot | render buffer | | STAGE_EVENT_STRIDE | 6 floats per event | event buffer | | STAGE_POSE_LEN | 7 floats [pos.xyz, rot.xyzw] | pose scratch | | STAGE_INVALID_SLOT | 0xffffffff | sentinel for "no slot" |

See src/stage_ffi.rs for the canonical export list, and dist/index.d.ts (StageWasmExports) for the TypeScript surface.

Runtime FFI (zylem_runtime_*, batched/legacy)

| Symbol | Returns | Notes | |--------|---------|-------| | zylem_runtime_init(capacity, initial_active) | usize | Allocates input/render/summary buffers; activates initial_active slots. Returns active count, 0 on capacity == 0. | | zylem_runtime_step(dt) | usize | dt clamped >= 0. Runs sync → collision state → render build; increments tick. Returns active_count. | | zylem_runtime_input_ptr / _len / _stride | — | Host writes per-slot [pos, rot, contacts, speed] (9 floats). | | zylem_runtime_render_ptr / _len / _stride | — | Host reads per-slot [pos, rot, scale, rgb, heat] (12 floats). | | zylem_runtime_summary_ptr / _len | — | 6 floats: [entityCount, colliding, totalContacts, avgHeat, maxHeat, maxContacts]. |

Input buffer layout (per slot, 9 floats)

| Index | Field | |-------|-------| | 0–2 | Position x, y, z | | 3–6 | Rotation quaternion x, y, z, w | | 7 | Contact count (truncated u32) | | 8 | Speed |

Render buffer layout (per slot, 12 floats)

| Index | Field | |-------|-------| | 0–2 | Position | | 3–6 | Rotation quaternion | | 7 | Scale | | 8–10 | RGB color | | 11 | Heat |

Summary buffer (6 floats)

| Index | Meaning | |-------|---------| | 0 | Active entity count | | 1 | Count of entities with contacts | | 2 | Total contacts | | 3 | Average heat | | 4 | Max heat | | 5 | Max contacts |

License

MIT