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

@microverse.ts/microverse-lua

v0.3.0

Published

Main entry for Microverse Lua: MicroverseLua.create (Wasm sandbox, mounts, events) and defineHostSurfaceFor—re-exports the stack.

Readme

@microverse.ts/microverse-lua

Lua microverse facade for TypeScript applications: MicroverseLua.create, Wasm VM, script slots, and the fluent host surface builder.

Monorepo overview (protocol vision): root README.

What is a Lua microverse?

One logical scripting universe in your process:

  • One Wasm-backed Lua VM (Wasmoon), shared for efficiency.
  • Many isolated environment slots — one per mounted script instance.
  • One host surface (declarative bridges + optional component hooks).
  • One host object (your TypeScript services).
  • Typed component profiles — bridges on self.bridges are narrowed by YourType:extend() in each script.
┌─────────────────────────────────────────────────────────────┐
│  LuaMicroverse (MicroverseLua.create)                       │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  Shared Wasm Lua VM                                   │  │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐     │  │
│  │  │ slot: a     │ │ slot: b     │ │ slot: c     │     │  │
│  │  │ OrderEcho   │ │ AuditOnly   │ │ Promotions  │     │  │
│  │  └─────────────┘ └─────────────┘ └─────────────┘     │  │
│  └───────────────────────────────────────────────────────┘  │
│         ▲ self.bridges after Type:extend() (per profile)    │
└─────────────────────────────────────────────────────────────┘

Install

pnpm add @microverse.ts/microverse-lua

Workspace: "@microverse.ts/microverse-lua": "workspace:*".

Concepts

| Term | Meaning | |------|---------| | Microverse | One shared Wasm Lua VM + catalog of script definitions + N mounted instances (LuaMicroverse). | | Host | Your typed services object passed to MicroverseLua.create({ host }). Handlers on the surface receive it; Lua never sees the raw host table. | | Surface | Result of defineHostSurfaceFor<THost>().componentType(…).bridge(…).method(…).componentHooks(…).build() — component types, bridges, capability ids, and a manifest for .d.lua. | | Component type | Declared profile: props/state Zod schemas, capability set, hook subset. Lua fixes the type with OrderEcho:extend()OrderEchoComponent. | | Bridge | A named group of host methods exposed in Lua as self.bridges.<name>:<method>(payload) when the active type’s capabilities include that method. Not a global in the script slot. | | Capability | A domain:action string on each bridge method (requires) and on each .componentType(…) profile. Runtime includes only matching bridges on self.bridges. | | Script definition | Catalog entry: scriptId, Lua source, optional injectLuaChunks, optional profileId, optional props schema / defaults. | | Script instance | A mounted slot: instanceId, scriptId, props, and the component table from YourType:extend() in the script chunk. | | Component | Lua table from YourType:extend() with typed properties, state, narrowed bridges, lifecycle (init, onDestroy, onPropsChanged), and domain on* hooks. |

Engine lifecycle

When you mount an instance, the runtime runs this pipeline (see mountScriptInstance):

sequenceDiagram
  participant App
  participant MV as LuaMicroverse
  participant Session as HostScriptSession
  participant VM as Wasm Lua

  App->>MV: registerScriptDefinition
  App->>MV: mountScriptInstance
  MV->>Session: openSession
  Session->>VM: run shared + inject prelude chunks
  Session->>VM: run main chunk (Type:extend())
  Session->>VM: setProps
  Session->>VM: invoke init
  App->>MV: emitToAllInstances
  MV->>Session: invoke on* hooks
  App->>MV: unmountScriptInstance
  MV->>Session: onDestroy + dispose
  1. registerScriptDefinition — Add source to the catalog (does not allocate a slot).
  2. mountScriptInstance — Open a slot, run preludes + main chunk, merge props, call init.
  3. emitToAllInstances — Broadcast a component hook (OrderPlaced, …) to every mounted instance.
  4. unmountScriptInstance — Call onDestroy, tear down the slot.
  5. dispose — Unmount all instances.

API

| Export / method | Purpose | |-----------------|--------| | MicroverseLua.create | Create a Lua microverse (Wasm VM included). | | registerScriptDefinition | Catalog entry (source, optional preludes, props schema). | | mountScriptInstance | New Wasm slot for one instance (props, audit). Script must call Type:extend(). | | unmountScriptInstance | Tear down one instance. | | emitToAllInstances | Call on{Kind} on every mounted instance (component hooks). | | setInstanceProps / patchInstanceProps | Update host-synced props; may invoke onPropsChanged. | | getInstanceProps / flushInstanceProps | Read or flush props proxy state. | | getSurfaceCapabilities | All capability ids declared on the surface. | | surface.getComponentType(name) | Resolved profile (props, state, capabilities, hooks) for codegen/runtime. | | dispose | Unmount all instances. | | defineHostSurfaceFor, defineHostSurface | Fluent surface builder (componentTypebridgemethodbuild). |

Re-exported from this package for convenience; lower-level session API lives in @microverse.ts/host-surface.

Quick start (minimal)

import { MicroverseLua, defineHostSurfaceFor } from '@microverse.ts/microverse-lua';
import { z } from 'zod';

type MyHost = { appName: string };

const surface = defineHostSurfaceFor<MyHost>()
  .bridge('greet')
  .method('hello', {
    requires: 'demo:greet',
    input: z.object({ name: z.string() }),
    output: z.string(),
    handler: ({ host }, { name }) => `Hello, ${name} from ${host.appName}`,
  })
  .build();

const microverse = MicroverseLua.create({
  host: { appName: 'Acme' },
  surface,
  defaultTimeoutMs: 30_000,
});

microverse.registerScriptDefinition({
  scriptId: 'welcome',
  source: `local msg = greet:hello({ name = "world" })`,
});
await microverse.mountScriptInstance({
  instanceId: 'welcome',
  scriptId: 'welcome',
});

await microverse.dispose();

The minimal sample uses a global greet table for brevity. In production, declare .componentType(…) on the surface, call YourType:extend() in Lua, and use self.bridges (see Lua authoring and Integrating in your app).

Integrating in your app

The reference layout is examples/sorting-lab. File tour: example README.

1. Define the host

Aggregate your real services into one object the surface handlers receive:

// examples/sorting-lab/src/engine/sortingLabHost.ts
export type SortingLabHost = {
  arrayA: number[];
  arrayB: number[];
  vizA: SortingVizSnapshot;
  vizB: SortingVizSnapshot;
  // …
};

Construct it in your app and pass it to MicroverseLua.create({ host, surface }).

2. Define the surface

Declare bridges (Lua → host), Zod payloads, capabilities, and optional component hooks (host → Lua):

// examples/sorting-lab/src/engine/sortingSurface.ts
import { defineHostSurfaceFor } from '@microverse.ts/microverse-lua';
import { sortingComponentHooks } from './sortingHooks';

export default defineHostSurfaceFor<SortingLabHost>()
  .componentType('SortingAlgorithm', SORTING_ALGORITHM_PROFILE)
  .bridge('array')
  .method('length', { requires: 'array:read', /* … */ })
  // … viz, sort bridges …
  .componentHooks(sortingComponentHooks)
  .build();
  • componentType — Props/state schemas, capability set, and hook subset for Name:extend().
  • requires — Capability id; only methods whose capability is in the active type appear on self.bridges.
  • handler — Runs in TypeScript with { host, script } context.
  • componentHooks — Map of event kind → Zod object; generates onOrderPlaced, MicroverseEvt_OrderPlaced, etc. in .d.lua.

Default-export the built surface for microverse generate-lua-defs --surface ….

3. Wrap the engine (optional)

A thin façade keeps app code free of microverse details:

// examples/sorting-lab/src/engine/sortingLabEngine.ts
this.microverse = MicroverseLua.create({ host, surface, defaultTimeoutMs: 30_000 });

await this.microverse.mountScriptInstance({
  instanceId: 'A',
  scriptId: 'bubble_sort',
  profileId: 'SortingAlgorithm',
  props: { label: 'Bubble sort', slotSide: 'A' },
});

await session.invokeComponentHook(luaGlobalHookName('Tick'), { step: 1 });

Map your domain events to emitToAllInstances (see dispatch in the example).

4. Load Lua sources

Keep scripts in .lua files and register by scriptId:

import { readComponentLua } from './services/components/loadComponentScript';

engine.registerScriptDefinition('order_echo', readComponentLua('components/order_echo.lua'));

Or inline strings for tests. Use sharedLuaChunks on create for libraries shared by every instance (e.g. lua/lib/math_helpers.lua), and injectLuaChunks per definition or mount for one-off preludes.

5. Mount instances

Each Lua script chooses its profile with YourType:extend() at load time. self.bridges only contains bridges/methods allowed by that type’s capabilities:

await engine.mountScriptInstance({
  scriptId: 'billing_denied',
  props: { maxCents: 1000 },
});
-- billing_denied.lua — AuditOnly type has no billing bridge
local C = AuditOnly:extend()

Optional audit metadata is passed to script audit callbacks for observability.

6. Dispatch events and shut down

await engine.emitHook('OrderPlaced', payload);
// or engine.dispatch(domainEvent)

await engine.dispose();

Lua authoring

Component pattern

Scripts call the type singleton declared on the surface (e.g. OrderEcho:extend()) and implement hooks on the returned table:

-- examples/sorting-lab/lua/bubble_sort.lua
local C = SortingAlgorithm:extend()

function C:onTick(_evt)
  self.bridges.viz:markComparing({ a = j, b = j + 1 })
  -- one compare/swap per tick …
end
  • OrderEcho:extend() — Builds the instance with typed properties, state, and narrowed bridges for that profile.
  • Domain events — Implement onOrderPlaced, … listed in the type’s hooks (and in OrderEchoComponent in .d.lua).
  • Lifecycleinit, onDestroy, onPropsChanged (see props_demo.lua).

Bridges (scoped, not global)

Call host APIs through self.bridges.<bridgeName>:<method>(payload):

local order = self.bridges.orders:get({ orderId = evt.orderId })
self.bridges.audit:record({ line = "seen:" .. evt.orderId })

Bridge names match .bridge('orders') in TypeScript (camelCase field on OrderEchoBridges / your type’s bridges class in .d.lua).

Props and state

-- examples/business-scripting-engine/lua/components/props_demo.lua
function C:init()
  self.state = { hits = 0 }
end

function C:onPropsChanged(key, newValue)
  self.state.lastKey = key
end

function C:onOrderPlaced(evt)
  local label = self.properties.label or "?"
  self.state.hits = (self.state.hits or 0) + 1
end

Host patches props via setInstanceProps / patchInstanceProps on the microverse.

Shared Lua libraries

| Mechanism | Scope | |-----------|--------| | sharedLuaChunks on MicroverseLua.create | Every instance, every mount | | injectLuaChunks on registerScriptDefinition | All mounts of that scriptId | | injectLuaChunks on mountScriptInstance | Single instance |

Run order: shared → definition → mount preludes → main script chunk.

Async bridges

Mark a handler async in TypeScript; Lua may use a completion callback or :await() on the returned handle:

-- examples/business-scripting-engine/lua/components/order_asyncio_tick.lua
self.bridges.asyncio:tick({ delayMs = 5, seed = evt.amountCents }, function(r)
  self.bridges.audit:record({ line = "asyncio-value:" .. tostring(r.value) })
end)

Generated stubs document AsyncioTickHandle and AsyncioTickResult for LuaLS.

IDE typing (LuaCATS)

Generate stubs from the same surface module that drives runtime:

pnpm add -D @microverse.ts/cli
pnpm exec microverse generate-lua-defs --surface src/engine/sortingSurface.ts

The manifest emits type-only bridge classes (---@class Orders + ---@field get fun(…)) so LuaLS does not treat Orders as a runtime global. Use:

  • self.bridges.orders — field name (camelCase) on OrderEchoBridges (per component type)
  • Types like Orders, OrderDto — PascalCase classes in the stub file
  • OrderEcho:extend() — singleton stub per .componentType('OrderEcho', …)

Point LuaLS at the generated folder and list your type singletons as globals:

{
  "workspace.library": ["./generated"],
  "diagnostics.globals": ["OrderEcho", "AuditOnly", "Promotions"]
}

Include both sortingSurface.d.lua (bridges + *Component) and sortingScriptCatalog.d.lua (per-scriptId aliases) when using a script catalog.

See examples/sorting-lab/.luarc.json and generated/sortingSurface.d.lua.

Chess duel (shared board, turn-based): examples/chess-lab — two ChessEngine scripts compete on one chess.js board.

Details: @microverse.ts/cli, @microverse.ts/lua-defs.

Integrating a game engine (script profiles)

For ECS-style hosts (many entities, many scripts, YAML-driven props), use script profiles instead of declaring every script on the surface:

| Primitive | Role | |-----------|------| | LuaScriptDefinition.profileId | Names a profile (usually matches a .componentType() on the surface for bridges/codegen). | | mountScriptInstance({ profileId }) | Host applies the profile at openSession — Lua chunks can omit Type:extend() for runtime (keep ---@type XxxComponent for LuaLS). | | HostScriptSession + createLuaEnvSlotKey('entity::script') | One slot per entity+script; your subsystem owns reconcile/lifecycle. | | ScriptReferenceResolver | Implement self.references.* wrappers (entity handles) without ECS types in microverse. | | buildScriptCatalogLuaDefManifest | Emit per-scriptId aliases for lua/ (see business example). |

Reference layout: examples/sorting-lab/src/engine/sortingScriptCatalog.ts.

Security model

| Layer | Behavior | |-------|----------| | Component types | Each .componentType(…) declares a capability set. Type:extend() mounts only matching bridges on self.bridges; other bridges are absent (nil). | | Bridge methods | Each method declares requires; filtering happens at extend time, not per call (no capability denied throws). | | Host isolation | The TypeScript host object is not injected into Lua; only bridge tables built from the surface are visible (via self.bridges). | | Timeouts | defaultTimeoutMs or defaultTimeout on create; Wasm instruction budget in @microverse.ts/runtime-wasm. | | Validation | Zod validates bridge inputs/outputs and instance props (per active type) at the TS boundary (@microverse.ts/runtime-zod). |

Related packages

| Package | Use when | |---------|----------| | @microverse.ts/host-surface | Surface builder details, HostScriptSession, custom slot wiring. | | @microverse.ts/lua-defs | Manifest → LuaCATS document (library use). | | @microverse.ts/cli | microverse generate-lua-defs in CI or locally. | | runtime-wasm, runtime-bridge, runtime-capabilities | Advanced runtime customization (usually via host-surface). |

Reference example

examples/sorting-lab — browser sorting comparator: Wasm Lua, tick-by-tick onTick, dual panels, and SortingLabEngine wrapping this package.

pnpm --filter @microverse.ts/sorting-lab test
pnpm --filter @microverse.ts/sorting-lab dev