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

lua-redis-wasm

v1.2.2

Published

WebAssembly-based Redis Lua 5.1 script engine for Node.js - Execute Redis-compatible Lua scripts without a live Redis server

Readme

lua-redis-wasm

npm version CI Node.js Version License: MIT

A WebAssembly-based Redis Lua 5.1 script engine for Node.js. Execute Redis-compatible Lua scripts in JavaScript/TypeScript environments without a live Redis server.

Features

  • Redis-compatible Lua 5.1 - Uses the exact Lua version embedded in Redis
  • Binary-safe - Full support for null bytes in scripts, arguments, and return values
  • Host integration - Implement redis.call, redis.pcall, and redis.log in JavaScript
  • Resource limits - Fuel-based instruction limiting, reply size caps, and memory coordination
  • Redis standard libraries - Includes cjson, cmsgpack, struct, and bit modules
  • TypeScript support - Full type definitions included

Installation

npm install lua-redis-wasm

Requirements: Node.js >= 22

Quick Start

import { LuaWasmEngine } from "redis-lua-wasm";

const engine = await LuaWasmEngine.create({
  host: {
    redisCall(args) {
      const cmd = args[0].toString();
      if (cmd === "PING") return { ok: Buffer.from("PONG") };
      if (cmd === "GET") return Buffer.from("value");
      return { err: Buffer.from("ERR unknown command") };
    },
    redisPcall(args) {
      return this.redisCall(args);
    },
    log(level, message) {
      console.log(`[${level}] ${message.toString()}`);
    },
  },
});

// Simple evaluation
const result = engine.eval("return 1 + 1"); // Returns: 2

// With KEYS and ARGV
const data = engine.evalWithArgs(
  "return {KEYS[1], ARGV[1]}",
  [Buffer.from("user:1")],
  [Buffer.from("hello")],
);

API

LuaWasmEngine.create(options)

Creates a new engine instance with host integration.

const engine = await LuaWasmEngine.create({
  host: RedisHost,       // Required: host callbacks
  limits?: EngineLimits, // Optional: resource limits
  wasmPath?: string,     // Optional: custom WASM file path
  wasmBytes?: Uint8Array // Optional: pre-loaded WASM binary
});

LuaWasmEngine.createStandalone(options)

Creates an engine without host integration. redis.call and redis.pcall return errors.

const engine = await LuaWasmEngine.createStandalone({});
engine.eval("return math.sqrt(16)"); // Works
engine.eval("return redis.call('PING')"); // Returns error

engine.eval(script)

Evaluates a Lua script and returns the result.

engine.eval("return 'hello'"); // Returns: Buffer.from("hello")
engine.eval("return {1, 2, 3}"); // Returns: [1, 2, 3]

engine.evalWithArgs(script, keys, args)

Evaluates a script with binary-safe KEYS and ARGV arrays.

engine.evalWithArgs(
  "return {KEYS[1], ARGV[1], ARGV[2]}",
  [Buffer.from("key:1")],
  [Buffer.from("arg1"), Buffer.from("arg2\x00with-null")],
);

LuaWasmEngine (Convenience)

Alternative API that combines loading and creation.

LuaWasmEngine.create(options)

const engine = await LuaWasmEngine.create({ host: myHost });

LuaWasmEngine.createStandalone(options)

const engine = await LuaWasmEngine.createStandalone();

Host Interface

The host must implement three callbacks:

type RedisHost = {
  redisCall: (args: Buffer[]) => ReplyValue; // For redis.call()
  redisPcall: (args: Buffer[]) => ReplyValue; // For redis.pcall()
  log: (level: number, message: Buffer) => void; // For redis.log()
};

redisCall

Called when Lua executes redis.call(...). Arguments arrive as Buffer[]. Throw an error to return it to Lua.

redisPcall

Called when Lua executes redis.pcall(...). Return { err: Buffer } instead of throwing to match Redis behavior.

log

Called when Lua executes redis.log(level, message). Level is a numeric Redis log level.

Reply Types

Return values are Redis-compatible:

type ReplyValue =
  | null // Lua nil
  | number // Integer (safe range)
  | bigint // Integer (64-bit)
  | Buffer // Bulk string
  | { ok: Buffer } // Status reply (+OK)
  | { err: Buffer } // Error reply (-ERR)
  | ReplyValue[]; // Array

Determining the Response Type

Use type guards to inspect what Lua returned:

const result = engine.eval(script);

// Check for null (Lua nil)
if (result === null) {
  console.log("Got nil");
}

// Check for integer
else if (typeof result === "number" || typeof result === "bigint") {
  console.log("Got integer:", result);
}

// Check for array (Lua table with sequential keys)
else if (Array.isArray(result)) {
  console.log("Got array with", result.length, "elements");
  for (const item of result) {
    // Each element is also a ReplyValue - handle recursively
  }
}

// Check for status reply ({ok: Buffer}) - e.g. from SET, PING
else if (typeof result === "object" && "ok" in result) {
  console.log("Got status:", result.ok.toString());
}

// Check for error reply ({err: Buffer})
else if (typeof result === "object" && "err" in result) {
  console.log("Got error:", result.err.toString());
}

// Otherwise it's a bulk string (Buffer)
else if (Buffer.isBuffer(result)) {
  console.log("Got bulk string:", result.toString());
}

Lua Type Conversions

This matches Redis Lua behavior:

// Lua nil → null
engine.eval("return nil"); // null

// Lua number → number (or bigint for large values)
engine.eval("return 42"); // 42
engine.eval("return 2^62"); // 4611686018427387904n (bigint)

// Lua string → Buffer
engine.eval("return 'hello'"); // Buffer.from("hello")

// Lua table (array) → ReplyValue[]
engine.eval("return {1, 2, 3}"); // [1, 2, 3]
engine.eval("return {'a', 'b'}"); // [Buffer, Buffer]

// Status reply: commands like SET, PING return {ok: "..."}
// In Lua: local resp = redis.call('SET', 'k', 'v') → resp.ok == "OK"
engine.eval("return redis.call('SET', 'k', 'v')"); // { ok: Buffer.from("OK") }
engine.eval("return redis.call('SET', 'k', 'v').ok"); // Buffer.from("OK")

// Error reply: redis.pcall catches errors as {err: "..."}
// In Lua: local resp = redis.pcall('INVALID') → resp.err == "ERR ..."
engine.eval("return redis.pcall('INVALID')"); // { err: Buffer.from("ERR ...") }

Note: Status replies (+OK) become {ok: "..."} tables in Lua, matching real Redis behavior. Use resp.ok to access the status string.

Resource Limits

Protect against runaway scripts with configurable limits:

const module = await load({
  limits: {
    maxFuel: 10_000_000, // Instruction budget
    maxMemoryBytes: 64 * 1024 * 1024, // Memory cap (host-coordinated)
    maxReplyBytes: 2 * 1024 * 1024, // Max reply size
    maxArgBytes: 1 * 1024 * 1024, // Max single argument size
  },
});
const engine = module.create(host);

| Limit | Description | Enforcement | | ---------------- | ---------------------------- | ---------------- | | maxFuel | Instruction count budget | WASM runtime | | maxMemoryBytes | Memory growth cap | Host-coordinated | | maxReplyBytes | Maximum reply payload size | WASM runtime | | maxArgBytes | Maximum single argument size | WASM runtime |

Included Lua Libraries

The engine includes Redis-standard Lua modules:

  • cjson - JSON encoding/decoding
  • cmsgpack - MessagePack serialization
  • struct - Binary data packing/unpacking
  • bit - Bitwise operations

Plus standard Lua 5.1 libraries: base, table, string, math.

Use Cases

  • Testing - Unit test Redis Lua scripts without a Redis server
  • Sandboxing - Execute untrusted Lua with resource limits
  • Development - Rapid iteration on Lua scripts locally
  • Embedding - Add Redis-compatible scripting to Node.js applications

Compatibility

| Feature | Status | | ------------------------------- | ------- | | Redis version target | 7.x | | Lua version | 5.1 | | Binary-safe strings | Yes | | redis.call / redis.pcall | Yes | | redis.log | Yes | | redis.sha1hex | Yes | | Standard Lua libraries | Yes | | Redis Lua modules (cjson, etc.) | Yes | | Debug / REPL helpers | No | | Redis Modules API | Not yet |

Building from Source

# Build everything (requires Emscripten via Docker)
npm run build

# Build steps individually
npm run build:wasm  # Compile C to WASM
npm run build:ts    # Compile TypeScript

# Run tests
npm test
npm run test:skip-wasm  # Skip WASM rebuild

Documentation

Contributing

We welcome contributions! Please see our Contributing Guide for details on:

  • How to report bugs
  • How to suggest enhancements
  • Development setup
  • Pull request process
  • Coding standards

Please read our Code of Conduct before contributing.

Security

Security is important to us. If you discover a security vulnerability, please follow our Security Policy for responsible disclosure.

For general security considerations when using lua-redis-wasm, see the Security Guide.

Support

Changelog

See CHANGELOG.md for a list of changes in each release.

License

This package is licensed under the MIT License. See LICENSE for details.

Third-Party Licenses

This project includes third-party code, all under the MIT License:

  • Lua 5.1 - Copyright (C) 1994-2012 Lua.org, PUC-Rio
  • lua_cjson - Copyright (C) 2010-2012 Mark Pulford
  • lua_cmsgpack - Copyright (C) 2012 Salvatore Sanfilippo
  • lua_struct - Copyright (C) 2010-2018 Lua.org, PUC-Rio
  • lua_bit - Copyright (C) 2008-2012 Mike Pall

See THIRD_PARTY_NOTICES.md for full license texts.

Acknowledgments

  • Redis team for the Lua integration design
  • Emscripten project for WebAssembly tooling
  • Contributors and maintainers of the included Lua libraries