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

@whatever-engine/api

v0.8.0

Published

TypeScript scripting API for Whatever Engine mods

Readme

@whatever-engine/api

The engine embeds this SDK, a TypeScript compiler and isolated QuickJS Wasm VMs. Mods distribute JS/TS/JSON source, compiled on the destination machine by the embedded compiler. The npm package supplies development types. Native queries and scene mutations execute synchronously inside the engine.

Usage

import { Engine, Input, Time, Window, File, Scene, Entity, Physics, Mods, Message, Console, Audio } from "@whatever-engine/api";

Engine.on("init", ({ mod_id }) => {
  Engine.log("info", `loaded as ${mod_id}`);
});

Engine.on("tick", () => {
  if (Input.isPressed("KeyW")) {
    // Use Time.deltaSeconds when recording movement commands.
  }
});

The engine-owned VM contains this API and resolves @whatever-engine/api before loading the mod. No runtime install or workspace ancestry is required. Leave this import external when bundling mods. The npm package remains available for editor types.

Mods may import local JS/TS/JSON source inside their own directory. Use the engine API for file I/O and logging. See docs/SANDBOX.md for the import and capability allowlists and build instructions.

API

Component reference fields

  • session: RunSession | null = null — infer a reference to a concrete mod-local component; call its methods directly
  • Type.Component(RunSession) — explicit descriptor, usable with Nullable, Array and Struct

Experimental engine backend and build instructions: docs/EMBEDDED_SCRIPTING.md.

Prefab

  • Prefab.spawn(path) — create an independent hierarchy immediately; returns PrefabInstance
  • PrefabInstance.root, .get(scriptId), .entities() — access the fresh root and its local named descendants

Prefab JSON and library imports: docs/ASSETS.md.

Engine

  • Engine.mode — read-only "client" | "server", available before top-level evaluation
  • Engine.on(event, handler) — subscribe to an engine event (init, exit, tick, mod_message, physics)
  • Engine.log(level, message) — log through the engine logger ("info", "warn", "error")
  • Engine.setTickRate(ticks_per_second) — override the game tick rate at runtime
  • Engine.setFpsCap(fps | null) — set FPS cap; null = uncapped
  • Engine.setVsync(enabled) — enable or disable vertical sync
  • Engine.setMainCamera(entityOrId) — designate a live or pending entity as the active camera
  • Engine.getMainCamera() → Entity | null
  • Engine.requestMainCamera() → Pending<Entity | null>
  • Engine.clearCamera() — remove the active camera (screen goes black with a warning)

Tick payloads include mouse_captured; mouse_delta is [0, 0] while uncaptured. Pending mouse motion is discarded on release, including for busy async handlers.

Input

Read-only input for the current tick, available in component hooks and any other function without a tick listener.

  • Input.keysPressed → PressedKeys
  • Input.isPressed(key: KeyCode) → boolean
  • Input.isAnyPressed(...keys: KeyCode[]) → boolean
  • Input.areAllPressed(...keys: KeyCode[]) → boolean
  • Input.isShiftPressed() / Input.isControlPressed() → boolean
  • Input.isAltPressed() / Input.isSuperPressed() → boolean
  • Input.mouseDelta → readonly [number, number]
  • Input.mouseCaptured → boolean

Time

Read-only timing for the current tick, available throughout the mod.

  • Time.tickNumber → number
  • Time.deltaSeconds → number

PressedKeys / KeyCode

Input.keysPressed and tick keys_pressed expose immutable held-key snapshots. KeyCode provides autocomplete for supported key names. Existing .includes() checks work.

  • keys.isPressed(key: KeyCode) → boolean
  • keys.isAnyPressed(...keys: KeyCode[]) → boolean
  • keys.areAllPressed(...keys: KeyCode[]) → boolean
  • keys.isShiftPressed() / keys.isControlPressed() → boolean
  • keys.isAltPressed() / keys.isSuperPressed() → boolean
  • keys.includes(key: KeyCode) → boolean
  • keys.size → number
  • keys.toArray() / keys.toJSON() → readonly KeyCode[]
  • [...keys] → KeyCode[]
  • new PressedKeys(codes?: readonly KeyCode[]) → PressedKeys

Profiler

  • Profiler.now() → monotonic milliseconds
  • Profiler.latest() → DeepReadonly<TickTimings> | null
  • Profiler.runtime() → previous VM tick/script timings (scene-replication fields remain zero)
  • Profiler.memory() → read-only Wasm memory bytes
  • SceneSnapshot.revision, .has(id), .get(id), .entities()

Window

  • Window.setTitle(title) — change the window title
  • Window.setSize(width, height) — request a new inner size in physical pixels (no-op in fullscreen modes)
  • Window.setMode(mode) — set display mode: "windowed", "borderless", or "fullscreen"

File

Sandboxed per-mod file I/O. Paths must not contain ...

  • File.write(path, data) → Promise<void>
  • File.read(path) → Promise<string>
  • File.delete(path) → Promise<void>

Pending<T> / PendingEntity

  • pending.get() → Promise<T> — wait for the recorded result; never flushes or repeats work
  • PendingEntity — an entity reference with chainable mutations before its native ID exists; reads are synchronous; explicit request* methods return Pending<T>

Scene/physics calls execute immediately. Entity creation only allocates a compatibility result Promise if .get() is used. See tick timing.

Component

  • class MyComponent extends Component — exported classes define custom components
  • static readonly type = "mod_id:name" — optional stable identity; defaults to mod_id:ClassName
  • component.entity → Entity; component.id → component type ID
  • start() / tick(deltaSeconds) → void | Promise<void>
  • onEnable() / onDisable() / destroy() → void
  • field(type, default, { label?, tooltip? }) → inferred field value

Shared schemas

  • Type.I32/F64/VarInt.with({ min?, max? })
  • Type.Bool, Type.String.with({ maxLength? })
  • Type.Array(element, { maxLength? }), Type.Optional(inner), Struct(fields)
  • Type.Nullable(inner), Type.Enum(choices), Type.Tuple(...elements)
  • Type.Entity, Type.Asset
  • Infer<typeof descriptor>, DataType<T>, ComponentClass<T>, ComponentData<T>, FieldEditor

Entity

A live entity in the scene. Returned by Scene.createEntity, Scene.listEntities, Scene.query, and Scene.spawnSprite. Native component IDs and custom component classes infer their return types.

  • entity.id — generation-aware ID; provisional until creation is confirmed
  • entity.exists, entity.isPending, entity.name
  • entity.hasComponent(kind) → boolean
  • entity.getActive() → boolean
  • entity.editComponent(kind, callback) → this
  • entity.setEulerDegrees(x, y, z) → this
  • entity.requestComponent(kind) → Pending<T | null>
  • entity.requestParent() → Pending<Entity | null>
  • entity.requestChildren() → Pending<Entity[]>
  • entity.destroy() — fire-and-forget
  • entity.setComponent(component) → this — registered native/custom class instances only
  • entity.removeComponent(componentIdOrClass) → this
  • entity.getComponent(componentIdOrClass) → ComponentSnapshot<T> | null — typed for built-in components
  • entity.move(position) → this — update core:transform position, preserve rotation/scale
  • entity.setParent(parent | null) → this — accepts entities, references or IDs
  • entity.getParent() → Entity | null — return the parent entity
  • entity.getChildren() → readonly Entity[] — return all direct children

EntityRef / EntityReference

  • new EntityRef(entityOrId) / Scene.ref(entityOrId) — typed references in nested component data
  • reference.id — opaque ID, provisional until its target is confirmed
  • ComponentValue — component JSON with typed entity references
  • entity.addTag(tag) / entity.removeTag(tag) → this
  • entity.getTags() → readonly string[]
  • entity.hasTag(tag) → boolean

Scene

Entity and component management. Methods that accept a raw entity_id string are provided for cases where only an ID is available; prefer Entity methods when possible.

  • Scene.revision → number
  • Scene.find(id) → Entity | null
  • Scene.snapshot() → SceneSnapshot
  • Scene.requestComponent(id, kind) → Pending<T | null>
  • Scene.requestQuery(kinds) → Pending<QueryResult<Entity>[]>
  • Scene.createEntity() → PendingEntity
  • Scene.loadScene(path) → LoadedScene
  • Scene.ref(entityOrId) → EntityRef
  • Scene.destroyEntity(entity_id) — fire-and-forget
  • Scene.listEntities() → readonly Entity[]
  • Scene.removeComponent(entity_id, component_type) — fire-and-forget
  • Scene.getComponent(entity_id, component_type) → ComponentSnapshot<T> | null — typed for built-in components
  • Scene.findByTag(tag, { includeDisabled? }) → readonly Entity[]
  • Scene.findByTags({ all?, any?, none?, includeDisabled? }) → readonly Entity[]
  • Scene.query(componentIdsOrClasses) → readonly { entity: Entity; components: Readonly<Record<string, any>> }[] — each row has entity: Entity and components
  • Scene.spawnSprite(texture, shader, position, scale?) → PendingEntity — convenience
  • Scene.spawnMesh(mesh, shader, position, options?) → PendingEntity — convenience
  • Scene.spawnText(text, position, options) → PendingEntity — convenience
  • Scene.moveEntity(entity_id, position) → void — convenience
  • Scene.addAmbientLight(color?, intensity?) → PendingEntity — convenience
  • Scene.addDirectionalLight(direction, color?, intensity?) → PendingEntity — convenience
  • Scene.addPointLight(position, color?, intensity?, range?) → PendingEntity — convenience
  • Scene.setParent(entity_id, parent_id | null) — fire-and-forget
  • Scene.getParent(entity_id) → string | null
  • Scene.getChildren(entity_id) → readonly string[]

Built-in component types: core:transform, core:sprite_renderer, core:mesh_renderer, core:text_renderer, core:camera, core:ambient_light, core:directional_light, core:point_light. getComponent returns live fields and class helpers; retained handles stay live across turns. Field assignments, nested edits and component removal cleanup take effect immediately. editComponent remains a compatibility helper.

Entity and component reads fetch only the requested data. Repeated reads within a turn reuse cached data until an engine write affects it.

LoadedScene

  • scene.get(scriptId) → Entity
  • scene.entities() → Entity[]
  • scene.entityCount → number
  • scene.loadTime → number (milliseconds)

BuiltInComponents.Transform

Methods (all setters chainable, return this):

  • getX/Y/Z(), setX/Y/Z(v), addX/Y/Z(v) — individual position components
  • getPosition() → [x, y, z], setPosition(x, y, z) — full position
  • getScaleX/Y/Z(), setScaleX/Y/Z(v) — individual scale components
  • getScale() → [x, y, z], setScale(x, y, z), setScaleUniform(s) — full scale
  • getRotation() → [x, y, z, w], setRotation(x, y, z, w) — raw quaternion
  • getEulerRadians/Degrees() → [rx, ry, rz] — intrinsic XYZ decomposition
  • setEulerRadians/Degrees(rx, ry, rz) — set from intrinsic XYZ Euler angles
  • rotateX/Y/Z(degrees) — incremental world-space rotation around axis
  • distance(other) → number — Euclidean distance between positions

BuiltInComponents.SpriteRenderer

  • getTexture(), setTexture(path) — VFS texture path
  • getShader(), setShader(path) — explicit VFS shader path (see docs/SHADER.md)

BuiltInComponents.MeshRenderer

  • getMesh(), setMesh(path) — VFS mesh path (.json, .obj, .glb, .gltf)
  • getShader(), setShader(path) — explicit VFS shader path (see docs/SHADER.md)
  • getTexture(), setTexture(path | null) — VFS texture path; null = 1×1 white fallback

BuiltInComponents.TextRenderer

  • getText(), setText(text) — displayed string
  • getFont(), setFont(path) — explicit VFS path to a TTF/OTF font
  • getFontSize(), setFontSize(size) — font size in logical pixels (default: 24)
  • getColor(), setColor(r, g, b, a) — RGBA colour, each channel [0.0, 1.0] (default: white)

BuiltInComponents.Camera

Makes an entity usable as a scene camera. Requires core:transform on the same entity. Activate with Engine.setMainCamera(entity.id).

  • getFov(), setFov(degrees) — vertical FOV in degrees (default: 45)
  • getZNear(), setZNear(v) — near clip plane (default: 0.1)
  • getZFar(), setZFar(v) — far clip plane (default: 1000)

BuiltInComponents.AmbientLight

Uniform fill light. Multiple ambient lights are additive. No position needed.

  • getColor(), setColor(r, g, b) — RGB colour, channels in [0.0, 1.0] (default: white)
  • getIntensity(), setIntensity(v) — multiplier (default: 0.1)

BuiltInComponents.DirectionalLight

Infinitely distant light (sun-like). Max 4 active simultaneously.

  • getDirection(), setDirection(x, y, z) — normalised world-space direction (default: [0, -1, 0])
  • getColor(), setColor(r, g, b) — RGB colour (default: white)
  • getIntensity(), setIntensity(v) — multiplier (default: 1.0)

BuiltInComponents.PointLight

Omnidirectional point light. Position comes from core:transform. Max 8 active simultaneously.

  • getColor(), setColor(r, g, b) — RGB colour (default: white)
  • getIntensity(), setIntensity(v) — multiplier (default: 1.0)
  • getRange(), setRange(v) — attenuation radius in world units (default: 10)

Physics

Native Rapier 3D; see the full physics reference.

  • new Physics.World(options?) — core:physics_world
  • Physics.defaultWorld.get() → Promise<Entity>; Physics.capabilities
  • Physics.RigidBody.dynamic(options?), .fixed(options?), .kinematicPosition(options?), .kinematicVelocity(options?) — core:rigid_body
  • new Physics.Collider(options) — core:collider
  • Physics.Joint.fixed(options), .revolute(options), .prismatic(options), .spherical(options), .generic(options) — core:joint
  • Physics.Shape.sphere(radius), .box(options), .capsule(options), .cylinder(options), .cone(options), .plane(normal?), .convexHull(points), .triMesh(vertices, indices, flags?), .compound(parts)
  • Physics.body(entityOrId) → BodyHandle
  • body.applyImpulse(vector, options?), .applyTorqueImpulse(vector, wake?), .addForce(vector, options?), .addTorque(vector, wake?)
  • body.resetForces(wake?), .resetTorques(wake?), .setLinearVelocity(vector, options?), .setAngularVelocity(vector, wake?)
  • body.teleport(position, options?), .setKinematicTarget(position, rotation?), .sleep(), .wake()
  • body.get() → Promise<void>; body.state() → Physics.BodyState
  • Physics.world(entityOrId?) → WorldHandle; world.castRay(origin, direction, options) → Physics.RayHit | null
  • Physics.onEvent(handler); Physics.QueryFilterFlag, Physics.CollisionType, Physics.TriMeshFlag
  • Read-only ECS views: core:rigid_body_state / Physics.BodyState, core:physics_status / Physics.Status
  • Typed expert settings: Physics.IntegrationOptions, BodyRapierOptions, ColliderRapierOptions, JointRapierOptions

Audio

Play audio files loaded from the VFS. Supported formats: WAV, OGG/Vorbis, MP3.

  • Audio.play({ path, seek?, speed?, volume? }) — fire-and-forget; engine frees the handle automatically when playback ends
  • Audio.load({ path, play?, closeStrategy?, volume?, speed?, loop? }) → Promise<AudioHandle> — load and optionally start playback; returns a controllable handle

AudioHandle methods:

  • handle.isStopped() — boolean (sync): true after stop() or engine-initiated close
  • handle.play(opts?) → Promise<number> — resume playback; returns position in ms; opts can override volume and speed
  • handle.pause() → Promise<number> — pause; returns position in ms
  • handle.stop() — void (sync): stop and free the handle; fires close handlers immediately
  • handle.position() → Promise<number> — current position in ms
  • handle.isPlaying() → Promise<boolean>
  • handle.volume() → Promise<number>
  • handle.speed() → Promise<number>
  • handle.isLooping() → Promise<boolean>
  • handle.metadata() → Promise<AudioMetadata> — { duration_ms, sample_rate, channels }; cached after first call
  • handle.seekTo(ms) → Promise<number> — seek to absolute position; returns previous position
  • handle.seek(offsetMs) → Promise<number> — seek forward/backward; returns new position; clamped to [0, duration-1], pauses if past end
  • handle.loop(enabled) — void (sync): enable or disable looping; resets position to 0 when changed
  • handle.on("close", fn) — fired on both manual stop() and engine-initiated close

CloseStrategy: "Auto" (default) — engine frees handle when playback ends; "Manual" — mod must call stop().

Mods

  • Mods.list() → Promise<ModManifest[]> — all loaded mods in load order
  • Mods.get(id) → Promise<ModManifest> — manifest for a specific mod

Message

Inter-mod communication. Payloads must be JSON-serializable.

  • Message.sendAndForget(id, message) — fire-and-forget
  • Message.send(id, message, timeout) → Promise<JsonValue> — send and await a reply
  • Message.registerMessageHandler(handler) — handle incoming messages; return a value to reply

Console

Unique commands use plain names. Collisions qualify all participants as mod_id:name (built-ins use core:name). Duplicate commands within one namespace, including a core mod clashing with a built-in, stop loading.

  • Console.register(spec) — register developer console commands with typed args and subcommands. Argument types: string, int, float, bool, mod_id, entity_id. The engine completes and validates loaded mod IDs and living entity IDs; handlers receive ID strings. bool suggests false/true by default. Open the console with F2; Up/Down previews suggestions and Tab accepts.

Network

  • Network.Type — I32, F64, signed 32-bit ZigZag VarInt, Bool, String, Array(type, options?), Optional(type)
  • Network.Type.I32/F64/VarInt.with({ min?, max? }) — immutable numeric constraints
  • Network.Type.String.with({ maxLength }) — UTF-8 byte limit; array maxLength counts elements
  • Network.Struct(fields) / Network.Packet(fields) / Network.Specification(id, states) — immutable definitions
  • Network.Infer<typeof descriptor> — inferred payload/client-data type
  • Network.registerClient(spec) / Network.registerServer(spec, { clientData, initialClientData }) — await native validation/registration
  • registration.packets / id / specificationId / fingerprint — engine-assigned metadata
  • registration.encodePacket(name, payload) / decodePacket(bytes) — native offline codecs; packet ID and payload, no stream length prefix
  • registration.on(name, handler) — typed receive subscription; returns unsubscribe
  • registration.send(...) / server.broadcast(...) — validated bounded-queue sends; optional {transport: "stream" | "datagram"}
  • server.onConnect(handler) / onDisconnect(handler) / clients / getClient(id) — typed connection lifecycle and owned clients
  • server.validateClientData(data) — offline native validation/copy
  • client.data / client.flushData() — typed per-peer mutation proxy and native update acknowledgement
  • client.connect({address, trust?: "strict" | "tofu"}) / disconnect() / connected / connection — QUIC lifecycle; strict by default
  • client.onConnect(handler) / onDisconnect(handler) — lifecycle subscriptions
  • server.listen({address, maxClients?}) / stop() / listening / listener — native QUIC listener
  • server.disconnect(client, reason?) — disconnect an owned peer
  • Network.PacketContext — receive transport metadata (client second argument, server third)
  • Network.ConnectOptions / ListenOptions / SendOptions / TrustPolicy — public option types
  • registration.close() — disconnect peers and release native registration

Development

# Regenerate type declarations after editing index.ts
bun run build:types

Full API documentation is in docs/SCRIPTING.md at the repo root.

The private Wasm boundary exposes checked engine operations. Mods receive no OS handles or process objects.

Init payloads include mode. Simulation ticks run independently of rendering. Window/audio operations and presentation settings reject server use. Mod manifests expose supported sides and optional shared/client/server script entries; only mods active on the current side appear in Mods.list() and Mods.get().

Tick failures and budget traps stop the affected VM. Healthy mods and engine shutdown continue.

Entity lifecycle and startup scenes

  • Entity.clone(options?: CloneOptions): PendingEntity

  • Entity.setEnabled(enabled: boolean): this

  • Entity.getEnabled(): boolean

  • Scene.initialScene(): LoadedScene | null

  • LoadedScene.path: string

  • Physics.Joint.fixed({ connectedBody, ...options }): Physics.Joint

  • Physics.Joint.revolute({ connectedBody, ...options }): Physics.Joint

  • Physics.Joint.prismatic({ connectedBody, ...options }): Physics.Joint

  • Physics.Joint.spherical({ connectedBody, ...options }): Physics.Joint

  • Physics.Joint.generic({ connectedBody, ...options }): Physics.Joint

  • Entity.getComponent("core:world_transform"): readonly transform data | null — read-only inherited transform